Java 5中的Properties类现在可以使用XML存取,通过loadFromXML和storeToXML方法实现。假设有下面这个属性表:
windowSize: 400,400
windowLocation: 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(".").<br>
* <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.<br>
* 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.<br>
* 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.
* <br>
* Example: getPath("Inspirento.window.location") returns
* the array {"Inspirento.window", "location"}.<br>
* <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>