xml Java 5中的Properties类现在可以使用XML存取,通过loadFromXML和storeToXML方法实现。假设有下面这个属性表:windowSize: 400,400windowLocation: 456,300 使用storeToXML后会得到这样的XML文件<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>Comment</comment>
<entry key="windowLocation">400,400</entry>
<entry key="windowSize">456,300</entry>
</properties>
但是如果要获得更具层次感的属性文件,可以使用这里我写的一个Utility。它建立在一个读取和存储XML的类库上。这个类库采集于Columba Project的util包,并有所修改。 首先是XmlElement,用于表示XML文件里的一个entry /*
* (#)XmlElement.java
* Created on 2005-8-12
*/
package com.allenstudio.ir.util;import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Observable;
import java.util.Vector;/**
* The XmlElement is a generic containment class for elements within an XML
* file.
* <p>
*
* It extends Observable which should be used for gui elements which are
* interested in configuration changes.
* <p>
*
* Show interested in:
*
* <pre>
* xmlElement.addObserver(yourObserver);
* </pre>
*
* <p>
* When making bigger changes on XmlElement and probably its subnodes and/or a
* greater number of attributes at once, you should just change XmlElement
* directly and manually notify the Observers by calling:
* <p>
*
* <pre>
* xmlElement.setChanged();
* xmlElement.notifyObservers();
* </pre>
*
* <p>
* There a good introduction for the Observable/Observer pattern in
* Model/View/Controller based applications at www.javaworld.com: -
* {link http://www.javaworld.com/javaworld/jw-10-1996/jw-10-howto.html}
*
* author fdietz
*/
public class XmlElement extends Observable implements Cloneable {
String name; String data; Hashtable<String, String> attributes; List<XmlElement> subElements; XmlElement parent; /**
*
*
* Constructor
*
*/
public XmlElement() {
subElements = new Vector<XmlElement>();
this.attributes = new Hashtable<String, String>(10);
} /**
* **
*
* Constructor
*
* param String
* Name
*
*/
public XmlElement(String name) {
this.name = name;
this.attributes = new Hashtable<String, String>(10);
subElements = new Vector<XmlElement>();
data = "";
} /**
* **
*
* Constructor
*
* param String
* Name
* param Hashtable
* Attributes
*
*/
public XmlElement(String name, Hashtable<String, String> attributes) {
this.name = name;
this.attributes = attributes;
subElements = new Vector<XmlElement>();
} /**
* **
*
* Constructor
*
* param Name
* String
* param Data
* String
*
*/
public XmlElement(String name, String data) {
this.name = name;
this.data = data;
subElements = new Vector<XmlElement>();
this.attributes = new Hashtable<String, String>(10);
} /**
* Add attribute to this xml element.
*
* param name
* name of key
* param value
* new attribute value
* return old attribute value
*
*/
public Object addAttribute(String name, String value) {
if ((value != null) && (name != null)) {
Object returnValue = attributes.put(name, value); return returnValue;
} return null;
} /**
* **
*
* return String
* param String
* Name
*
*/
public String getAttribute(String name) {
return ((String) attributes.get(name));
} public String getAttribute(String name, String defaultValue) {
if (getAttribute(name) == null) {
addAttribute(name, defaultValue);
} return getAttribute(name);
} /**
* **
*
* return String
* param String
* Name
*
*/
public Hashtable<String, String> getAttributes() {
return attributes;
} /**
* **
*
*
* param Attrs
* Hashtable to use as the attributes
*
*/
public void setAttributes(Hashtable<String, String> attrs) {
attributes = attrs;
} /**
* **
*
* return Enumeration
*
*/
public Enumeration getAttributeNames() {
return (attributes.keys());
} /**
* **
*
* return boolean
* param XmlElement
* E
*
*/
public boolean addElement(XmlElement e) {
e.setParent(this); return (subElements.add(e));
} public XmlElement removeElement(XmlElement e) {
XmlElement child = null; for (int i = 0; i < subElements.size(); i++) {
child = (XmlElement) subElements.get(i); // FIXME -- This will most likely not work.
// You want the element removed if the contents are the same
// Not just if the element reference is the same.
if (child == e) {
subElements.remove(i);
}
} return (child);
} public XmlElement removeElement(int index) {
return (XmlElement) subElements.remove(index);
} public void removeAllElements() {
subElements.clear();
} /**
* convienience method for the TreeView
*
* this method is modeled after the DefaultMutableTreeNode-class
*
* DefaultMutableTreeNode wraps XmlElement for this purpose
*
*/
public void removeFromParent() {
if (parent == null) {
return;
} parent.removeElement(this);
parent = null;
} public void append(XmlElement e) {
e.removeFromParent(); addElement(e);
} /**
*
* convienience method for the TreeView
*
* param e
* param index
*/
public void insertElement(XmlElement e, int index) {
e.removeFromParent(); subElements.add(index, e);
e.setParent(this);
} /**
* **
*
* return Vector
*
*/
public List getElements() {
return subElements;
} public int count() {
return subElements.size();
} /**
* Returns the element whose hierachy is indicated
* by <code>path</code>. The path is separated with
* periods(".").
* <em>Note: if one node has more than one elements
* that have the same name, that is, if its subnodes
* have the same path, only the first one is returned.
* </em>
* return the first element qualified with the path
* param path the path string of the specified element
*/
public XmlElement getElement(String path) {
int i = path.indexOf('.');
String topName;
String subName; if (i == 0) {
path = path.substring(1);
i = path.indexOf('.');
} if (i > 0) {
topName = path.substring(0, i);
subName = path.substring(i + 1);
} else {
topName = path;
subName = null;
} int j; for (j = 0; j < subElements.size(); j++) {
if (((XmlElement) subElements.get(j)).getName().equals(topName)) {
if (subName != null) {
return (((XmlElement) subElements.get(j))
.getElement(subName));
} else {
return ((XmlElement) subElements.get(j));
}
}
} return null;
} public XmlElement getElement(int index) {
return (XmlElement) subElements.get(index);
} /**
* Adds a sub element to this one. The path
* is separated with dots(".").
*
* return the <code>XmlElement</code> added
* param path The subpath of the sub element to add
*
*/
public XmlElement addSubElement(String path) {
XmlElement parent = this;
XmlElement child;
String name; while (path.indexOf('.') != -1) {
name = path.substring(0, path.indexOf('.'));
path = path.substring(path.indexOf('.') + 1); // if path startsWith "/" -> skip
if (name.length() == 0)
continue; if (parent.getElement(name) != null) {
parent = parent.getElement(name);
} else {
child = new XmlElement(name); parent.addElement(child);
parent = child;
} } child = new XmlElement(path);
parent.addElement(child); return child;
} /**
* Adds a sub element to this one
*
* return XmlElement
* param element
* The XmlElement to add
*
*/
public XmlElement addSubElement(XmlElement e) {
e.setParent(this);
subElements.add(e); return e;
} /**
* Adds a sub element to this one
*
* return XmlElement
* param Name
* The name of the sub element to add
* param Data
* String Data for this element
*/
public XmlElement addSubElement(String name, String data) {
XmlElement e = new XmlElement(name);
e.setData(data);
e.setParent(this);
subElements.add(e); return e;
} /**
* Sets the parent element
*
* param Parent
* The XmlElement that contains this one
*
*/
public void setParent(XmlElement parent) {
this.parent = parent;
} /**
* Gives the XmlElement containing the current element
*
* return XmlElement
*
*/
public XmlElement getParent() {
return parent;
} /**
* Sets the data for this element
*
* param D
* The String representation of the data
*
*/
public void setData(String d) {
data = d;
} /**
* Returns the data associated with the current Xml element
*
* return String
*
*/
public String getData() {
return data;
} /**
* Returns the name of the current Xml element
*
* return String
*
*/
public String getName() {
return name;
} /**
* **
*
* param out
* OutputStream to print the data to
*
*/ /*
* public void write(OutputStream out) throws IOException { PrintWriter PW =
* new PrintWriter(out); PW.println(" <?xml version=\"1.0\"
* encoding=\"UTF-8\"?>"); if (SubElements.size() > 0) { for (int i = 0; i <
* SubElements.size(); i++) { ((XmlElement)
* SubElements.get(i))._writeSubNode(PW, 4); } } PW.flush(); }
*/ /**
* Prints sub nodes to the given data stream
*
* param out
* PrintWriter to use for printing
* param indent
* Number of spaces to indent things
*
*/ /*
* private void _writeSubNode(PrintWriter out, int indent) throws
* IOException { _writeSpace(out, indent); out.print(" <" + Name); //if (
* Attributes.size()>1) out.print(" ");
*
* for (Enumeration e = Attributes.keys(); e.hasMoreElements();) { String K =
* (String) e.nextElement(); out.print(K + "=\"" + Attributes.get(K) + "\"
* b");
* } out.print(">");
*
* if (Data != null && !Data.equals("")) { if (Data.length() > 20) {
* out.println(""); _writeSpace(out, indent + 2); } out.print(Data); } if
* (SubElements.size() > 0) { out.println(""); for (int i = 0; i <
* SubElements.size(); i++) { ((XmlElement)
* SubElements.get(i))._writeSubNode( out, indent + 4); } _writeSpace(out,
* indent); } out.println(" </" + Name + ">");
* }
*/ /**
* Prints out a given number of spaces
*
* param out
* PrintWriter to use for printing
* param numSpaces
* Number of spaces to print
*
*/ /*
* private void _writeSpace(PrintWriter out, int numSpaces) throws
* IOException {
*
* for (int i = 0; i < numSpaces; i++) out.print(" "); }
*
* public static void printNode(XmlElement Node, String indent) { String
* Data = Node.getData(); if (Data == null || Data.equals("")) {
* System.out.println(indent + Node.getName()); } else {
* System.out.println(indent + Node.getName() + " = '" + Data + "'"); }
* Vector Subs = Node.getElements(); int i, j; for (i = 0; i < Subs.size();
* i++) { printNode((XmlElement) Subs.get(i), indent + " "); } }
*/
public static void printNode(XmlElement node, String indent) {
String data = node.getData(); if ((data == null) || data.equals("")) {
System.out.println(indent + node.getName());
} else {
System.out.println(indent + node.getName() + " = '" + data + "'");
} // print attributes
for (Enumeration enumeration = node.getAttributes().keys(); enumeration
.hasMoreElements();) {
String key = (String) enumeration.nextElement();
String value = node.getAttribute(key);
System.out.println(indent + key + ":" + value);
} List subs = node.getElements(); for (Iterator it = subs.iterator(); it.hasNext();) {
printNode((XmlElement) it.next(), indent + " "); // for (i = 0; i < subs.size(); i++) {
// printNode((XmlElement) subs.get(i), indent + " ");
}
} /** {inheritDoc} */
SuppressWarnings("unchecked")
Override
public Object clone() {
try {
XmlElement clone = (XmlElement) super.clone(); // creates a shallow
// copy of this
// object if (attributes != null) {
clone.setAttributes((Hashtable<String, String>) getAttributes().clone());
} if (subElements != null) {
clone.subElements = new Vector(); List childs = getElements();
XmlElement child; for (Iterator it = childs.iterator(); it.hasNext();) {
child = (XmlElement) it.next(); // for( int i=0; i<childs.size(); i++ ) {
// child = (XmlElement) childs.get(i);
clone.addSubElement((XmlElement) child.clone());
}
} return clone;
} catch (CloneNotSupportedException cnse) {
throw new InternalError("Could not clone XmlElement: " + cnse);
}
} /**
* Sets the name.
*
* param name
* The name to set
*/
public void setName(String name) {
this.name = name;
} /**
* Notify all Observers.
*
* see java.util.Observable#notifyObservers()
*/
Override
public void notifyObservers() {
setChanged();
super.notifyObservers();
} /**
* Returns true if the specified objects are equal. They are equal if they
* are both null OR if the <code>equals()</code> method return true. (
* <code>obj1.equals(obj2)</code>).
*
* param obj1
* first object to compare with.
* param obj2
* second object to compare with.
* return true if they represent the same object; false if one of them is
* null or the <code>equals()</code> method returns false.
*/
private boolean equals(Object obj1, Object obj2) {
boolean equal = false; if ((obj1 == null) && (obj2 == null)) {
equal = true;
} else if ((obj1 != null) && (obj2 != null)) {
equal = obj1.equals(obj2);
} return equal;
} /** {inheritDoc}
*Recursive comparison.
*/
Override
public boolean equals(Object obj) {
boolean equal = false; if ((obj != null) && (obj instanceof XmlElement)) {
XmlElement other = (XmlElement) obj; if (equals(attributes, other.attributes)
&& equals(data, other.data) && equals(name, other.name)
&& equals(subElements, other.subElements)) {
equal = true;
}
} return equal;
} /** {inheritDoc} */
Override
public int hashCode() {
//Hashcode value should be buffered.
int hashCode = 23; if (attributes != null) {
hashCode += (attributes.hashCode() * 13);
} if (data != null) {
hashCode += (data.hashCode() * 17);
} if (name != null) {
hashCode += (name.hashCode() * 29);
} if (subElements != null) {
hashCode += (subElements.hashCode() * 57);
} return hashCode;
}
}然后是XmlIO,用于读写。 /*
* (#)XmlIO.java
* Created on 2005-8-12
*/
package com.allenstudio.ir.util;import java.io.BufferedWriter;
import java.io.CharArrayWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URL;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import java.util.logging.Logger;import javax.swing.JOptionPane;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;/**
* XML IO reading and writing utility.
*
* author fdietz
*/
public class XmlIO extends DefaultHandler { private static final Logger LOG = Logger.getLogger("org.columba.core.xml"); private static final String ROOT_XML_ELEMENT_NAME = "__INSPIRENTO_XML_TREE_TOP__"; // List of sub-elements
SuppressWarnings("unused")
private List<XmlElement> elements; // Top level element (Used to hold everything else)
private XmlElement rootElement; // The current element you are working on
private XmlElement currentElement; // For writing out the data
// Indent for each level
private int writeIndent = 2; // Maximum data to put on a "one liner"
private int maxOneLineData = 20; // The SAX 2 parser...
SuppressWarnings("unused")
private XMLReader xr; // Buffer for collecting data from
// the "characters" SAX event.
private CharArrayWriter contents = new CharArrayWriter();
private URL url = null; /*
// Default constructor
public XmlIO() {
}
*/
/*
// setup and load constructor
public XmlIO(String FilePath) {
currentElement = null;
}
*/
public XmlIO(URL url) {
super();
this.url = url;
} // setup and load constructor
public XmlIO() {
currentElement = null;
} // setup and load constructor /**
* Creates a XmlIO object with the specified element at the top.
* param element the element at the top.
*/
public XmlIO(XmlElement element) {
rootElement = new XmlElement(ROOT_XML_ELEMENT_NAME);
rootElement.addElement(element);
} public void setURL(URL url) {
this.url = url;
} public boolean load() {
//this.file = F;
return load(url);
} // Load a file. This is what starts things off. /**
* Loads from the InputStream into the root Xml Element.
* param input the input stream to load from.
*/
public boolean load(InputStream input) {
elements = new Vector<XmlElement>();
rootElement = new XmlElement(ROOT_XML_ELEMENT_NAME);
currentElement = rootElement; try {
// Create the XML reader...
// xr = XMLReaderFactory.createXMLReader();
SAXParserFactory factory = SAXParserFactory.newInstance(); // Set the ContentHandler...
// xr.setContentHandler( this );
SAXParser saxParser = factory.newSAXParser(); saxParser.parse(input, this);
} catch (javax.xml.parsers.ParserConfigurationException ex) {
LOG.severe("XML config error while attempting to read from the input stream \n'" + input + "'");
LOG.severe(ex.toString());
ex.printStackTrace(); return (false);
} catch (SAXException ex) {
// Error
LOG.severe("XML parse error while attempting to read from the input stream \n'" + input + "'");
LOG.severe(ex.toString());
ex.printStackTrace(); return (false);
} catch (IOException ex) {
LOG.severe("I/O error while attempting to read from the input stream \n'" + input + "'");
LOG.severe(ex.toString());
ex.printStackTrace(); return (false);
} //XmlElement.printNode( getRoot(), "");
return (true);
} /**
* Load a file. This is what starts things off.
* param inputURL the URL to load XML from.
*/
public boolean load(URL inputURL) {
elements = new Vector<XmlElement>();
rootElement = new XmlElement(ROOT_XML_ELEMENT_NAME);
currentElement = rootElement; try {
// Create the XML reader...
// xr = XMLReaderFactory.createXMLReader();
SAXParserFactory factory = SAXParserFactory.newInstance(); // Set the ContentHandler...
// xr.setContentHandler( this );
SAXParser saxParser = factory.newSAXParser(); saxParser.parse(inputURL.toString(), this);
} catch (javax.xml.parsers.ParserConfigurationException ex) {
LOG.severe("XML config error while attempting to read XML file \n'" + inputURL + "'");
LOG.severe(ex.toString());
ex.printStackTrace(); return (false);
} catch (SAXException ex) {
// Error
LOG.severe("XML parse error while attempting to read XML file \n'" + inputURL + "'");
LOG.severe(ex.toString());
ex.printStackTrace(); return (false);
} catch (IOException ex) {
LOG.severe("I/O error while attempting to read XML file \n'" + inputURL + "'");
LOG.severe(ex.toString());
ex.printStackTrace(); return (false);
} //XmlElement.printNode( getRoot(), "");
return (true);
} // Implement the content hander methods that
// will delegate SAX events to the tag tracker network.
Override
public void startElement(String namespaceURI, String localName,
String qName, Attributes attrs) throws SAXException {
// Resetting contents buffer.
// Assuming that tags either tag content or children, not both.
// This is usually the case with XML that is representing
// data strucutures in a programming language independant way.
// This assumption is not typically valid where XML is being
// used in the classical text mark up style where tagging
// is used to style content and several styles may overlap
// at once.
try {
contents.reset(); String name = localName; // element name if (name.equals("")) {
name = qName; // namespaceAware = false
} XmlElement p = currentElement; currentElement = currentElement.addSubElement(name);
currentElement.setParent(p); if (attrs != null) {
for (int i = 0; i < attrs.getLength(); i++) {
String aName = attrs.getLocalName(i); // Attr name if (aName.equals("")) {
aName = attrs.getQName(i);
} currentElement.addAttribute(aName, attrs.getValue(i));
}
}
} catch (java.lang.NullPointerException ex) {
LOG.severe("Null!!!");
LOG.severe(ex.toString());
ex.printStackTrace();
}
} Override
public void endElement(String namespaceURI, String localName, String qName)
throws SAXException {
currentElement.setData(contents.toString().trim());
contents.reset(); currentElement = currentElement.getParent();
} Override
public void characters(char[] ch, int start, int length)
throws SAXException {
// accumulate the contents into a buffer.
contents.write(ch, start, length);
} /**
* Returns the root for the XmlElement hiearchy.
* Note that this Xml Element will always have the name <code>__COLUMBA_XML_TREE_TOP__</code>.
* <p>
* Methods that want to retrieve elements from this root should use
* the {link XmlElement#getElement(String)} in order to get the wanted
* element.
* return a XmlElement if it has been loaded or initialized with it; null otherwise.
*/
public XmlElement getRoot() {
return (rootElement);
} public void errorDialog(String Msg) {
JOptionPane.showMessageDialog(null, "Error: " + Msg);
} public void warningDialog(String Msg) {
JOptionPane.showMessageDialog(null, "Warning: " + Msg);
} public void infoDialog(String Msg) {
JOptionPane.showMessageDialog(null, "Info: " + Msg);
} public void save() throws Exception {
write(new FileOutputStream(url.getPath()));
} //
// Writer interface
//
public void write(OutputStream out) throws IOException {
BufferedWriter PW = new BufferedWriter(new OutputStreamWriter(out,
"UTF-8"));
PW.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); if (rootElement.subElements.size() > 0) {
for (int i = 0; i < rootElement.subElements.size(); i++) {
_writeSubNode(PW, (XmlElement) rootElement.subElements.get(i), 0);
}
} PW.flush();
} private void _writeSubNode(Writer out, XmlElement element, int indent)
throws IOException {
_writeSpace(out, indent);
out.write("<");
out.write(element.getName()); for (Enumeration e = element.getAttributeNames(); e.hasMoreElements();) {
String K = (String) e.nextElement();
out.write(" " + K + "=\"" + InspirentoUtilities.escapeText(element.getAttribute(K)) + "\"");
} out.write(">"); String data = element.getData(); if ((data != null) && !data.equals("")) {
if (data.length() > maxOneLineData) {
out.write("\n");
_writeSpace(out, indent + writeIndent);
} out.write(InspirentoUtilities.escapeText(data));
} List subElements = element.getElements(); if (subElements.size() > 0) {
out.write("\n"); for (Iterator it = subElements.iterator(); it.hasNext();) {
_writeSubNode(out, (XmlElement) it.next(), indent + writeIndent); // for (int i = 0; i < subElements.size(); i++) {
// _writeSubNode(
// out,
// (XmlElement) subElements.get(i),
// indent + writeIndent);
} _writeSpace(out, indent);
} if (data.length() > maxOneLineData) {
out.write("\n");
_writeSpace(out, indent);
} out.write("</" + InspirentoUtilities.escapeText(element.getName()) + ">\n");
} private void _writeSpace(Writer out, int numSpaces)
throws IOException {
for (int i = 0; i < numSpaces; i++) {
out.write(" ");
}
}
} 下面是继承Properties的ConfigurationManager,其中的getProperty和setProperty方法已经被Overridden。 /*
* (#)ConfigurationManager.java
* Created on 2005-8-10
*/
package com.allenstudio.ir.core;import java.util.*;
import java.io.*;import com.allenstudio.ir.util.*;/**
* Manages the configuration for Inspirento.
* This manager uses XML format to store information.
* The configuration file is, by default, saved in the
* "config" directory and named "config.xml". Clearly,
* this class should be a singleton, so we use
* {link #getInstance()} to get an instance and call
* other instance methods to get the settings needed
* by Inspirento, such as "windowSize", "windowLocation",
* and etc.
* The program first tries to get the configuration from
* this <code>ConfigurationManager</code>. If it fails to
* get any key, it uses the default settings presetted in
* the protected <code>default</code> field.
*
* author Allen Chue
*/
public class ConfigurationManager extends Properties {
public static final String CONFIG_DIRECTORY = "config";
public static final String CONFIG_FILE = "config.xml";
public static final String COMMON_PREFIX = "Inspirento.";
private static ConfigurationManager instance = null;
private XmlIO xmlIO;
/**
* Private constructor for singleton use.
*/
private ConfigurationManager() {
initDefaultSettings();
readIn();
}
public static ConfigurationManager getInstance() {
if (instance != null) {
return instance;
} else {
instance = new ConfigurationManager();
return instance;
}
}
public void readIn() {
try {
File configFile = new File(
CONFIG_DIRECTORY +
System.getProperty("file.separator") +
CONFIG_FILE);//$NON-NLS-1$
if (configFile.exists()) {
FileInputStream configStream = new FileInputStream(configFile);
xmlIO = new XmlIO();
xmlIO.load(configStream);
configStream.close();
}
} catch (Exception e) {
System.out.println("Cannot load configuration file" +
" supposed to be at \"config\\config.xml\"" +
"\nDefault settings will be stored as the replacement.");//$NON-NLS-1$
writeDefaultsToFile();
e.printStackTrace();
}
}
public void writeBack() {
try {
FileOutputStream configFile = new FileOutputStream(
CONFIG_DIRECTORY +
System.getProperty("file.separator") +
CONFIG_FILE);
xmlIO.write(configFile);
configFile.close();
} catch (Exception e) {
System.out.println("Cannot write configuration file" +
" to \"config\\config.xml\"");//$NON-NLS-1$
e.printStackTrace();
}
}
/**
* Uses XML parser to get the specified property.
* If there is no such a key, the method returns
* <code>null</code>.
* param key the key of the property
* return the property value
*/
Override
public synchronized String getProperty(String key) {
String value = xmlIO.getRoot().getElement(Constants.PROJECT_NAME +
"." + getPath(key)[0]).getAttribute(getPath(key)[1]);
if (value == null) {//Perhaps some element is lost in the file
value = defaults.getProperty(key);
setProperty(key, value);//null value has no side effect
new Thread(){
Override
public void run() {
writeBack();
}
}.start();
}
return value;
}
Override
public synchronized Object setProperty(String key, String value) {
xmlIO.getRoot().getElement(Constants.PROJECT_NAME +
"." + getPath(key)[0]).addAttribute(getPath(key)[1], value);
return value;
}
/**
* When the configuration file is lost, this method
* is used to write the default settings stored in
* the program itself to file.
*
*/
private void writeDefaultsToFile() {
Enumeration keys = defaults.keys();
XmlElement xe = new XmlElement(Constants.PROJECT_NAME);
xmlIO = new XmlIO(xe);
for (; keys.hasMoreElements(); ) {
String pathText = (String)keys.nextElement(); String[] path = getPath(pathText);
//Test if the element to be modified exists
XmlElement elementAdded = xe.getElement(path[0]);
if (elementAdded == null){
elementAdded = xe.addSubElement(path[0]);
}
elementAdded.addAttribute(path[1], defaults.getProperty(pathText));
}
try {
FileOutputStream configFile = new FileOutputStream(
CONFIG_DIRECTORY +
System.getProperty("file.separator") +
CONFIG_FILE);//$NON-NLS-1$
xmlIO.write(configFile);
configFile.close();
} catch (Exception e) {
System.out.println("Cannot write configuration file" +
" to \"config\\config.xml\"");//$NON-NLS-1$
e.printStackTrace();
}
}
/**
* Returns an string array of length 2.
* The parameter <code>pathText</code> is supposed to
* be a string separated with dots. For example,
* "Inspirento.window.location" is a valid parameter.
* This method puts the token after the last dot in
* the second position of the result array, and the
* remaining string(excluding the last dot) in the first
* position of the result array. It is a rivate helping method.
*
* Example: getPath("Inspirento.window.location") returns
* the array {"Inspirento.window", "location"}.
* <em>No format checking is done in this method! <code>
* ArrayOutOfBoundsException</code> will be thrown
* when no dots are found in the string.</em>
* param pathText the path text to be processed
* return an array containing the result
*/
private static String[] getPath(String pathText) {
int dotPos = pathText.lastIndexOf('.');
String[] result = new String[2];
result[0] = pathText.substring(0, dotPos);
result[1] = pathText.substring(dotPos + 1);
return result;
}
private void initDefaultSettings() {
String[] configDefaults = {
"window.location", "400,300",
"window.size", "450,300"
};
defaults = new Properties();
for(int i = 0, max = configDefaults.length; i < max; i += 2) {
String value = configDefaults[i + 1];
defaults.setProperty(configDefaults[i], value);
}
}
}
在上面的代码中,Inspirento是我的项目,可以根据情况修改。实际这个类是使用在整个程序的配置获取和修改的。如果用它处理前面的属性,可以得到这样的文件,可以看到它有层次感,更便于处理和阅读。 <?xml version="1.0" encoding="UTF-8"?>
<Inspirento>
<window location="202 ,179" size="532 ,455"></window>
</Inspirento>
一个自写的XML读写/存取属性的Java工具类库
80酷酷网 80kuku.com