// XMLProperties.java
// $Id: XMLProperties.java,v 1.5 2000/08/16 21:37:58 ylafon Exp $
// (c) COPYRIGHT MIT, INRIA and Keio, 1999.
// Please first read the full copyright statement in file COPYRIGHT.html
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Enumeration;
import java.util.Properties;
import org.xml.sax.AttributeList;
import org.xml.sax.DocumentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.Parser;
import org.xml.sax.SAXException;
/**
* The <code>Properties</code> class represents a persistent set of
* properties. The <code>Properties</code> can be saved to a stream or loaded
* from a stream. Each key and its corresponding value in the property list is a
* string.
* <p>
* A property list can contain another property list as its "defaults"; this
* second property list is searched if the property key is not found in the
* original property list. Because Properties inherits from Hashtable, the put
* and putAll methods can be applied to a Properties object. Their use is
* strongly discouraged as they allow the caller to insert entries whose keys or
* values are not Strings. The setProperty method should be used instead. If the
* store or save method is called on a "compromised" Properties object that
* contains a non-String key or value, the call will fail.
* <p>
* This is a special implementation for XML :
*
* <pre>
* <properties>
* <key name="My_key1">My_Value1</key>
* <key name="My_key2">My_Value2</key>
* </properties>
* </pre>
*
* @version $Revision: 1.5 $
* @author Philippe Le Hégaret (plh@w3.org)
* @author Benoît Mahé (bmahe@w3.org)
*/
public class XMLProperties extends Properties {
public static final String PARSER_P = "com.jclark.xml.sax.Driver";
public boolean debug = false;
class XMLParser implements DocumentHandler {
final int IN_NOTHING = 0;
final int IN_DOCUMENT = 1;
final int IN_KEY = 2;
int state = IN_NOTHING;
String key;
StringBuffer value;
Parser parser;
XMLParser(InputStream in) throws IOException, SAXException {
state = IN_NOTHING;
value = new StringBuffer();
try {
parser = getParser();
parser.setDocumentHandler(this);
} catch (Exception e) {
e.printStackTrace();
throw new SAXException("can't create parser ");
}
parser.parse(new InputSource(in));
}
public void startElement(String name, AttributeList atts) throws SAXException {
if (state == IN_NOTHING) {
if (name.equals("properties")) {
state = IN_DOCUMENT;
} else {
throw new SAXException("attempt to find root properties");
}
} else if (state == IN_DOCUMENT) {
if (name.equals("key")) {
state = IN_KEY;
key = atts.getValue("name");
if (key == null) {
throw new SAXException("no name for key " + atts);
}
} else {
throw new SAXException("attempt to find keys");
}
} else {
throw new SAXException("invalid element " + name);
}
}
public void endElement(String name) throws SAXException {
if (state == IN_KEY) {
setProperty(key, value.toString());
if (debug) {
System.out.print("<key name=\"" + key + "\">");
System.out.println(value.toString() + "</key>\n");
}
state = IN_DOCUMENT;
name = null;
value = new StringBuffer();
} else if (state == IN_DOCUMENT) {
state = IN_NOTHING;
}
}
public void characters(char ch[], int start, int length) throws SAXException {
if (state == IN_KEY) {
compute(ch, start, length);
}
}
public void ignorableWhitespace(char ch[], int start, int length) throws SAXException {
// nothing to do
}
public void startDocument() throws SAXException {
// nothing to do
}
public void endDocument() throws SAXException {
// nothing to do
}
public void processingInstruction(String target, String data) throws SAXException {
// nothing to do
}
public void setDocumentLocator(Locator locator) {
// nothing to do
}
private void compute(char[] ch, int start, int length) {
int st = start;
int len = length - 1;
while (st < length
&& ((ch[st] == '\n') || (ch[st] == '\t') || (ch[st] == ' ') || (ch[st] == '\r'))) {
st++;
}
while (len > 0
&& ((ch[len] == '\n') || (ch[len] == '\t') || (ch[len] == ' ') || (ch[len] == '\r'))) {
len--;
}
while (st <= len) {
value.append(ch[st]);
st++;
}
}
} // XMLParser
private Class parser_class = null;
/**
* Reads a property list from an input stream.
*
* @param in
* the input stream.
* @exception IOException
* if an error occurred when reading from the input stream.
* @since JDK1.0
*/
public synchronized void load(InputStream in) throws IOException {
XMLParser p = null;
try {
p = new XMLParser(in);
} catch (SAXException e) {
throw new IOException(e.getMessage());
}
}
/**
* Reads a property list from an input stream. This method try to load
* properties with super.load() if the XMLParser failed. Use this method to
* translate an Property set to an XML Property set.
*
* @param file
* the properties file.
* @exception IOException
* if an error occurred when reading from the input stream.
* @since JDK1.0
*/
public synchronized void load(File file) throws IOException {
InputStream in = new BufferedInputStream(new FileInputStream(file));
XMLParser p = null;
try {
p = new XMLParser(in);
} catch (SAXException e) {
try {
in = new BufferedInputStream(new FileInputStream(file));
super.load(in);
in.close();
} catch (IOException ex) {
throw new IOException(e.getMessage());
}
}
}
/**
* Calls the <code>store(OutputStream out, String header)</code> method and
* suppresses IOExceptions that were thrown.
*
* @deprecated This method does not throw an IOException if an I/O error
* occurs while saving the property list. As of JDK 1.2, the
* preferred way to save a properties list is via the
* <code>store(OutputStream out,
* String header)</code> method.
*
* @param out
* an output stream.
* @param header
* a description of the property list.
* @exception ClassCastException
* if this <code>Properties</code> object contains any keys or
* values that are not <code>Strings</code>.
*/
public synchronized void save(OutputStream out, String header) {
try {
store(out, header);
} catch (IOException ex) {
ex.printStackTrace();
}
}
/**
* Writes this property list (key and element pairs) in this
* <code>Properties</code> table to the output stream in a format suitable
* for loading into a <code>Properties</code> table using the
* <code>load</code> method.
* <p>
* After the entries have been written, the output stream is flushed. The
* output stream remains open after this method returns.
*
* @param out
* an output stream.
* @param header
* a description of the property list.
* @exception ClassCastException
* if this <code>Properties</code> object contains any keys or
* values that are not <code>Strings</code>.
*/
public synchronized void store(OutputStream out, String header) throws IOException {
PrintWriter wout = new PrintWriter(out);
wout.println("<?xml version='1.0'?>");
if (header != null) {
wout.println("<!--" + header + "-->");
}
wout.print("<properties>");
for (Enumeration e = keys(); e.hasMoreElements();) {
String key = (String) e.nextElement();
String val = (String) get(key);
wout.print("\n <key name=\"" + key + "\">");
wout.print(encode(val));
wout.print("</key>");
}
wout.print("\n</properties>");
wout.flush();
}
protected StringBuffer encode(String string) {
int len = string.length();
StringBuffer buffer = new StringBuffer(len);
char c;
for (int i = 0; i < len; i++) {
switch (c = string.charAt(i)) {
case '&':
buffer.append("&");
break;
case '<':
buffer.append("<");
break;
case '>':
buffer.append(">");
break;
default:
buffer.append(c);
}
}
return buffer;
}
private Class getParserClass() throws ClassNotFoundException {
if (parser_class == null)
parser_class = Class.forName(PARSER_P);
return parser_class;
}
private Parser getParser() {
try {
return (Parser) getParserClass().newInstance();
} catch (Exception ex) {
throw new RuntimeException("Unable to intantiate : " + PARSER_P);
}
}
/**
* Creates an empty property list with no default values.
*/
public XMLProperties() {
super();
}
/**
* Creates an empty property list with the specified defaults.
*
* @param defaults
* the defaults.
*/
public XMLProperties(Properties defaults) {
super(defaults);
}
/**
* Creates an empty property list with the specified defaults.
*
* @param parser
* the XML Parser classname (default is PARSER_P)
* @param defaults
* the defaults.
*/
public XMLProperties(String parser, Properties defaults) {
super(defaults);
try {
parser_class = Class.forName(parser);
} catch (ClassNotFoundException ex) {
System.err.println("Unable to instanciate parser class: " + parser);
System.err.println("Using default parser.");
}
}
public void setDebug(boolean debug) {
this.debug = debug;
}
}
|