001: // XMLProperties.java
002: // $Id: XMLProperties.java,v 1.5 2000/08/16 21:37:58 ylafon Exp $
003: // (c) COPYRIGHT MIT, INRIA and Keio, 1999.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005: package org.w3c.util;
006:
007: import org.xml.sax.DocumentHandler;
008: import org.xml.sax.SAXException;
009: import org.xml.sax.Locator;
010: import org.xml.sax.AttributeList;
011: import org.xml.sax.Parser;
012: import org.xml.sax.InputSource;
013:
014: import java.io.IOException;
015: import java.io.OutputStream;
016: import java.io.InputStream;
017: import java.io.BufferedInputStream;
018: import java.io.FileInputStream;
019: import java.io.File;
020: import java.io.PrintWriter;
021: import java.util.Properties;
022: import java.util.Enumeration;
023:
024: /**
025: * The <code>Properties</code> class represents a persistent set of
026: * properties. The <code>Properties</code> can be saved to a stream or
027: * loaded from a stream. Each key and its corresponding value in the
028: * property list is a string.
029: * <p>
030: * A property list can contain another property list as its "defaults"; this
031: * second property list is searched if the property key is not found in the
032: * original property list. Because Properties inherits from Hashtable, the
033: * put and putAll methods can be applied to a Properties object. Their use
034: * is strongly discouraged as they allow the caller to insert entries whose
035: * keys or values are not Strings. The setProperty method should be used
036: * instead. If the store or save method is called on a "compromised"
037: * Properties object that contains a non-String key or value, the call will
038: * fail.
039: * <p>
040: * This is a special implementation for XML :
041: * <pre>
042: * <properties>
043: * <key name="My_key1">My_Value1</key>
044: * <key name="My_key2">My_Value2</key>
045: * </properties>
046: * </pre>
047: * @version $Revision: 1.5 $
048: * @author Philippe Le Hégaret (plh@w3.org)
049: * @author Benoît Mahé (bmahe@w3.org)
050: */
051: public class XMLProperties extends Properties {
052:
053: public static final String PARSER_P = "com.jclark.xml.sax.Driver";
054:
055: public boolean debug = false;
056:
057: class XMLParser implements DocumentHandler {
058:
059: final int IN_NOTHING = 0;
060: final int IN_DOCUMENT = 1;
061: final int IN_KEY = 2;
062: int state = IN_NOTHING;
063:
064: String key;
065: StringBuffer value;
066:
067: Parser parser;
068:
069: XMLParser(InputStream in) throws IOException, SAXException {
070: state = IN_NOTHING;
071: value = new StringBuffer();
072: try {
073: parser = getParser();
074: parser.setDocumentHandler(this );
075: } catch (Exception e) {
076: e.printStackTrace();
077: throw new SAXException("can't create parser ");
078: }
079: parser.parse(new InputSource(in));
080: }
081:
082: public void startElement(String name, AttributeList atts)
083: throws SAXException {
084: if (state == IN_NOTHING) {
085: if (name.equals("properties")) {
086: state = IN_DOCUMENT;
087: } else {
088: throw new SAXException(
089: "attempt to find root properties");
090: }
091: } else if (state == IN_DOCUMENT) {
092: if (name.equals("key")) {
093: state = IN_KEY;
094: key = atts.getValue("name");
095:
096: if (key == null) {
097: throw new SAXException("no name for key "
098: + atts);
099: }
100: } else {
101: throw new SAXException("attempt to find keys");
102: }
103: } else {
104: throw new SAXException("invalid element " + name);
105: }
106: }
107:
108: public void endElement(String name) throws SAXException {
109: if (state == IN_KEY) {
110: setProperty(key, value.toString());
111: if (debug) {
112: System.out.print("<key name=\"" + key + "\">");
113: System.out.println(value.toString() + "</key>\n");
114: }
115: state = IN_DOCUMENT;
116: name = null;
117: value = new StringBuffer();
118: } else if (state == IN_DOCUMENT) {
119: state = IN_NOTHING;
120: }
121: }
122:
123: public void characters(char ch[], int start, int length)
124: throws SAXException {
125: if (state == IN_KEY) {
126: compute(ch, start, length);
127: }
128: }
129:
130: public void ignorableWhitespace(char ch[], int start, int length)
131: throws SAXException {
132: // nothing to do
133: }
134:
135: public void startDocument() throws SAXException {
136: // nothing to do
137: }
138:
139: public void endDocument() throws SAXException {
140: // nothing to do
141: }
142:
143: public void processingInstruction(String target, String data)
144: throws SAXException {
145: // nothing to do
146: }
147:
148: public void setDocumentLocator(Locator locator) {
149: // nothing to do
150: }
151:
152: private void compute(char[] ch, int start, int length) {
153: int st = start;
154: int len = length - 1;
155: while (st < length
156: && ((ch[st] == '\n') || (ch[st] == '\t')
157: || (ch[st] == ' ') || (ch[st] == '\r'))) {
158: st++;
159: }
160: while (len > 0
161: && ((ch[len] == '\n') || (ch[len] == '\t')
162: || (ch[len] == ' ') || (ch[len] == '\r'))) {
163: len--;
164: }
165:
166: while (st <= len) {
167: value.append(ch[st]);
168: st++;
169: }
170: }
171: } //XMLParser
172:
173: private Class parser_class = null;
174:
175: /**
176: * Reads a property list from an input stream.
177: * @param in the input stream.
178: * @exception IOException if an error occurred when reading from the
179: * input stream.
180: * @since JDK1.0
181: */
182: public synchronized void load(InputStream in) throws IOException {
183: XMLParser p = null;
184: try {
185: p = new XMLParser(in);
186: } catch (SAXException e) {
187: throw new IOException(e.getMessage());
188: }
189: }
190:
191: /**
192: * Reads a property list from an input stream. This method try to load
193: * properties with super.load() if the XMLParser failed. Use this method
194: * to translate an Property set to an XML Property set.
195: * @param file the properties file.
196: * @exception IOException if an error occurred when reading from the
197: * input stream.
198: * @since JDK1.0
199: */
200: public synchronized void load(File file) throws IOException {
201: InputStream in = new BufferedInputStream(new FileInputStream(
202: file));
203: XMLParser p = null;
204: try {
205: p = new XMLParser(in);
206: } catch (SAXException e) {
207: try {
208: in = new BufferedInputStream(new FileInputStream(file));
209: super .load(in);
210: in.close();
211: } catch (IOException ex) {
212: throw new IOException(e.getMessage());
213: }
214: }
215: }
216:
217: /**
218: * Calls the <code>store(OutputStream out, String header)</code> method
219: * and suppresses IOExceptions that were thrown.
220: *
221: * @deprecated This method does not throw an IOException if an I/O error
222: * occurs while saving the property list. As of JDK 1.2, the preferred
223: * way to save a properties list is via the <code>store(OutputStream out,
224: * String header)</code> method.
225: *
226: * @param out an output stream.
227: * @param header a description of the property list.
228: * @exception ClassCastException if this <code>Properties</code> object
229: * contains any keys or values that are not <code>Strings</code>.
230: */
231: public synchronized void save(OutputStream out, String header) {
232: try {
233: store(out, header);
234: } catch (IOException ex) {
235: ex.printStackTrace();
236: }
237: }
238:
239: /**
240: * Writes this property list (key and element pairs) in this
241: * <code>Properties</code> table to the output stream in a format suitable
242: * for loading into a <code>Properties</code> table using the
243: * <code>load</code> method.
244: * <p>
245: * After the entries have been written, the output stream is flushed. The
246: * output stream remains open after this method returns.
247: *
248: * @param out an output stream.
249: * @param header a description of the property list.
250: * @exception ClassCastException if this <code>Properties</code> object
251: * contains any keys or values that are not <code>Strings</code>.
252: */
253: public synchronized void store(OutputStream out, String header)
254: throws IOException {
255: PrintWriter wout = new PrintWriter(out);
256: wout.println("<?xml version='1.0'?>");
257: if (header != null) {
258: wout.println("<!--" + header + "-->");
259: }
260:
261: wout.print("<properties>");
262: for (Enumeration e = keys(); e.hasMoreElements();) {
263: String key = (String) e.nextElement();
264: String val = (String) get(key);
265: wout.print("\n <key name=\"" + key + "\">");
266: wout.print(encode(val));
267: wout.print("</key>");
268: }
269: wout.print("\n</properties>");
270: wout.flush();
271: }
272:
273: protected StringBuffer encode(String string) {
274: int len = string.length();
275: StringBuffer buffer = new StringBuffer(len);
276: char c;
277:
278: for (int i = 0; i < len; i++) {
279: switch (c = string.charAt(i)) {
280: case '&':
281: buffer.append("&");
282: break;
283: case '<':
284: buffer.append("<");
285: break;
286: case '>':
287: buffer.append(">");
288: break;
289: default:
290: buffer.append(c);
291: }
292: }
293:
294: return buffer;
295: }
296:
297: private Class getParserClass() throws ClassNotFoundException {
298: if (parser_class == null)
299: parser_class = Class.forName(PARSER_P);
300: return parser_class;
301: }
302:
303: private Parser getParser() {
304: try {
305: return (Parser) getParserClass().newInstance();
306: } catch (Exception ex) {
307: throw new RuntimeException("Unable to intantiate : "
308: + PARSER_P);
309: }
310: }
311:
312: /**
313: * Creates an empty property list with no default values.
314: */
315: public XMLProperties() {
316: super ();
317: }
318:
319: /**
320: * Creates an empty property list with the specified defaults.
321: *
322: * @param defaults the defaults.
323: */
324: public XMLProperties(Properties defaults) {
325: super (defaults);
326: }
327:
328: /**
329: * Creates an empty property list with the specified defaults.
330: * @param parser the XML Parser classname (default is PARSER_P)
331: * @param defaults the defaults.
332: */
333: public XMLProperties(String parser, Properties defaults) {
334: super (defaults);
335: try {
336: parser_class = Class.forName(parser);
337: } catch (ClassNotFoundException ex) {
338: System.err.println("Unable to instanciate parser class: "
339: + parser);
340: System.err.println("Using default parser.");
341: }
342: }
343:
344: public void setDebug(boolean debug) {
345: this.debug = debug;
346: }
347:
348: }
|