| /* Properties.java -- a set of persistent properties | |
| Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package java.util; | |
| import java.io.BufferedReader; | |
| import java.io.IOException; | |
| import java.io.InputStream; | |
| import java.io.InputStreamReader; | |
| import java.io.OutputStream; | |
| import java.io.OutputStreamWriter; | |
| import java.io.PrintStream; | |
| import java.io.PrintWriter; | |
| import javax.xml.stream.XMLInputFactory; | |
| import javax.xml.stream.XMLStreamConstants; | |
| import javax.xml.stream.XMLStreamException; | |
| import javax.xml.stream.XMLStreamReader; | |
| import org.w3c.dom.Document; | |
| import org.w3c.dom.DocumentType; | |
| import org.w3c.dom.DOMImplementation; | |
| import org.w3c.dom.Element; | |
| import org.w3c.dom.bootstrap.DOMImplementationRegistry; | |
| import org.w3c.dom.ls.DOMImplementationLS; | |
| import org.w3c.dom.ls.LSOutput; | |
| import org.w3c.dom.ls.LSSerializer; | |
| /** | |
| * A set of persistent properties, which can be saved or loaded from a stream. | |
| * A property list may also contain defaults, searched if the main list | |
| * does not contain a property for a given key. | |
| * | |
| * An example of a properties file for the german language is given | |
| * here. This extends the example given in ListResourceBundle. | |
| * Create a file MyResource_de.properties with the following contents | |
| * and put it in the CLASSPATH. (The character | |
| * <code>\</code><code>u00e4</code> is the german umlaut) | |
| * | |
| * | |
| <pre>s1=3 | |
| s2=MeineDisk | |
| s3=3. M\<code></code>u00e4rz 96 | |
| s4=Die Diskette ''{1}'' enth\<code></code>u00e4lt {0} in {2}. | |
| s5=0 | |
| s6=keine Dateien | |
| s7=1 | |
| s8=eine Datei | |
| s9=2 | |
| s10={0,number} Dateien | |
| s11=Das Formatieren schlug fehl mit folgender Exception: {0} | |
| s12=FEHLER | |
| s13=Ergebnis | |
| s14=Dialog | |
| s15=Auswahlkriterium | |
| s16=1,3</pre> | |
| * | |
| * <p>Although this is a sub class of a hash table, you should never | |
| * insert anything other than strings to this property, or several | |
| * methods, that need string keys and values, will fail. To ensure | |
| * this, you should use the <code>get/setProperty</code> method instead | |
| * of <code>get/put</code>. | |
| * | |
| * Properties are saved in ISO 8859-1 encoding, using Unicode escapes with | |
| * a single <code>u</code> for any character which cannot be represented. | |
| * | |
| * @author Jochen Hoenicke | |
| * @author Eric Blake (ebb9@email.byu.edu) | |
| * @see PropertyResourceBundle | |
| * @status updated to 1.4 | |
| */ | |
| public class Properties extends Hashtable | |
| { | |
| // WARNING: Properties is a CORE class in the bootstrap cycle. See the | |
| // comments in vm/reference/java/lang/Runtime for implications of this fact. | |
| /** | |
| * The property list that contains default values for any keys not | |
| * in this property list. | |
| * | |
| * @serial the default properties | |
| */ | |
| protected Properties defaults; | |
| /** | |
| * Compatible with JDK 1.0+. | |
| */ | |
| private static final long serialVersionUID = 4112578634029874840L; | |
| /** | |
| * Creates a new empty property list with no default values. | |
| */ | |
| public Properties() | |
| { | |
| } | |
| /** | |
| * Create a new empty property list with the specified default values. | |
| * | |
| * @param defaults a Properties object containing the default values | |
| */ | |
| public Properties(Properties defaults) | |
| { | |
| this.defaults = defaults; | |
| } | |
| /** | |
| * Adds the given key/value pair to this properties. This calls | |
| * the hashtable method put. | |
| * | |
| * @param key the key for this property | |
| * @param value the value for this property | |
| * @return The old value for the given key | |
| * @see #getProperty(String) | |
| * @since 1.2 | |
| */ | |
| public Object setProperty(String key, String value) | |
| { | |
| return put(key, value); | |
| } | |
| /** | |
| * Reads a property list from an input stream. The stream should | |
| * have the following format: <br> | |
| * | |
| * An empty line or a line starting with <code>#</code> or | |
| * <code>!</code> is ignored. An backslash (<code>\</code>) at the | |
| * end of the line makes the line continueing on the next line | |
| * (but make sure there is no whitespace after the backslash). | |
| * Otherwise, each line describes a key/value pair. <br> | |
| * | |
| * The chars up to the first whitespace, = or : are the key. You | |
| * can include this caracters in the key, if you precede them with | |
| * a backslash (<code>\</code>). The key is followed by optional | |
| * whitespaces, optionally one <code>=</code> or <code>:</code>, | |
| * and optionally some more whitespaces. The rest of the line is | |
| * the resource belonging to the key. <br> | |
| * | |
| * Escape sequences <code>\t, \n, \r, \\, \", \', \!, \#, \ </code>(a | |
| * space), and unicode characters with the | |
| * <code>\\u</code><em>xxxx</em> notation are detected, and | |
| * converted to the corresponding single character. <br> | |
| * | |
| * | |
| <pre># This is a comment | |
| key = value | |
| k\:5 \ a string starting with space and ending with newline\n | |
| # This is a multiline specification; note that the value contains | |
| # no white space. | |
| weekdays: Sunday,Monday,Tuesday,Wednesday,\\ | |
| Thursday,Friday,Saturday | |
| # The safest way to include a space at the end of a value: | |
| label = Name:\\u0020</pre> | |
| * | |
| * @param inStream the input stream | |
| * @throws IOException if an error occurred when reading the input | |
| * @throws NullPointerException if in is null | |
| */ | |
| public void load(InputStream inStream) throws IOException | |
| { | |
| // The spec says that the file must be encoded using ISO-8859-1. | |
| BufferedReader reader = | |
| new BufferedReader(new InputStreamReader(inStream, "ISO-8859-1")); | |
| String line; | |
| while ((line = reader.readLine()) != null) | |
| { | |
| char c = 0; | |
| int pos = 0; | |
| // Leading whitespaces must be deleted first. | |
| while (pos < line.length() | |
| && Character.isWhitespace(c = line.charAt(pos))) | |
| pos++; | |
| // If empty line or begins with a comment character, skip this line. | |
| if ((line.length() - pos) == 0 | |
| || line.charAt(pos) == '#' || line.charAt(pos) == '!') | |
| continue; | |
| // The characters up to the next Whitespace, ':', or '=' | |
| // describe the key. But look for escape sequences. | |
| // Try to short-circuit when there is no escape char. | |
| int start = pos; | |
| boolean needsEscape = line.indexOf('\\', pos) != -1; | |
| StringBuilder key = needsEscape ? new StringBuilder() : null; | |
| while (pos < line.length() | |
| && ! Character.isWhitespace(c = line.charAt(pos++)) | |
| && c != '=' && c != ':') | |
| { | |
| if (needsEscape && c == '\\') | |
| { | |
| if (pos == line.length()) | |
| { | |
| // The line continues on the next line. If there | |
| // is no next line, just treat it as a key with an | |
| // empty value. | |
| line = reader.readLine(); | |
| if (line == null) | |
| line = ""; | |
| pos = 0; | |
| while (pos < line.length() | |
| && Character.isWhitespace(c = line.charAt(pos))) | |
| pos++; | |
| } | |
| else | |
| { | |
| c = line.charAt(pos++); | |
| switch (c) | |
| { | |
| case 'n': | |
| key.append('\n'); | |
| break; | |
| case 't': | |
| key.append('\t'); | |
| break; | |
| case 'r': | |
| key.append('\r'); | |
| break; | |
| case 'u': | |
| if (pos + 4 <= line.length()) | |
| { | |
| char uni = (char) Integer.parseInt | |
| (line.substring(pos, pos + 4), 16); | |
| key.append(uni); | |
| pos += 4; | |
| } // else throw exception? | |
| break; | |
| default: | |
| key.append(c); | |
| break; | |
| } | |
| } | |
| } | |
| else if (needsEscape) | |
| key.append(c); | |
| } | |
| boolean isDelim = (c == ':' || c == '='); | |
| String keyString; | |
| if (needsEscape) | |
| keyString = key.toString(); | |
| else if (isDelim || Character.isWhitespace(c)) | |
| keyString = line.substring(start, pos - 1); | |
| else | |
| keyString = line.substring(start, pos); | |
| while (pos < line.length() | |
| && Character.isWhitespace(c = line.charAt(pos))) | |
| pos++; | |
| if (! isDelim && (c == ':' || c == '=')) | |
| { | |
| pos++; | |
| while (pos < line.length() | |
| && Character.isWhitespace(c = line.charAt(pos))) | |
| pos++; | |
| } | |
| // Short-circuit if no escape chars found. | |
| if (!needsEscape) | |
| { | |
| put(keyString, line.substring(pos)); | |
| continue; | |
| } | |
| // Escape char found so iterate through the rest of the line. | |
| StringBuilder element = new StringBuilder(line.length() - pos); | |
| while (pos < line.length()) | |
| { | |
| c = line.charAt(pos++); | |
| if (c == '\\') | |
| { | |
| if (pos == line.length()) | |
| { | |
| // The line continues on the next line. | |
| line = reader.readLine(); | |
| // We might have seen a backslash at the end of | |
| // the file. The JDK ignores the backslash in | |
| // this case, so we follow for compatibility. | |
| if (line == null) | |
| break; | |
| pos = 0; | |
| while (pos < line.length() | |
| && Character.isWhitespace(c = line.charAt(pos))) | |
| pos++; | |
| element.ensureCapacity(line.length() - pos + | |
| element.length()); | |
| } | |
| else | |
| { | |
| c = line.charAt(pos++); | |
| switch (c) | |
| { | |
| case 'n': | |
| element.append('\n'); | |
| break; | |
| case 't': | |
| element.append('\t'); | |
| break; | |
| case 'r': | |
| element.append('\r'); | |
| break; | |
| case 'u': | |
| if (pos + 4 <= line.length()) | |
| { | |
| char uni = (char) Integer.parseInt | |
| (line.substring(pos, pos + 4), 16); | |
| element.append(uni); | |
| pos += 4; | |
| } // else throw exception? | |
| break; | |
| default: | |
| element.append(c); | |
| break; | |
| } | |
| } | |
| } | |
| else | |
| element.append(c); | |
| } | |
| put(keyString, element.toString()); | |
| } | |
| } | |
| /** | |
| * Calls <code>store(OutputStream out, String header)</code> and | |
| * ignores the IOException that may be thrown. | |
| * | |
| * @param out the stream to write to | |
| * @param header a description of the property list | |
| * @throws ClassCastException if this property contains any key or | |
| * value that are not strings | |
| * @deprecated use {@link #store(OutputStream, String)} instead | |
| */ | |
| public void save(OutputStream out, String header) | |
| { | |
| try | |
| { | |
| store(out, header); | |
| } | |
| catch (IOException ex) | |
| { | |
| } | |
| } | |
| /** | |
| * Writes the key/value pairs to the given output stream, in a format | |
| * suitable for <code>load</code>.<br> | |
| * | |
| * If header is not null, this method writes a comment containing | |
| * the header as first line to the stream. The next line (or first | |
| * line if header is null) contains a comment with the current date. | |
| * Afterwards the key/value pairs are written to the stream in the | |
| * following format.<br> | |
| * | |
| * Each line has the form <code>key = value</code>. Newlines, | |
| * Returns and tabs are written as <code>\n,\t,\r</code> resp. | |
| * The characters <code>\, !, #, =</code> and <code>:</code> are | |
| * preceeded by a backslash. Spaces are preceded with a backslash, | |
| * if and only if they are at the beginning of the key. Characters | |
| * that are not in the ascii range 33 to 127 are written in the | |
| * <code>\</code><code>u</code>xxxx Form.<br> | |
| * | |
| * Following the listing, the output stream is flushed but left open. | |
| * | |
| * @param out the output stream | |
| * @param header the header written in the first line, may be null | |
| * @throws ClassCastException if this property contains any key or | |
| * value that isn't a string | |
| * @throws IOException if writing to the stream fails | |
| * @throws NullPointerException if out is null | |
| * @since 1.2 | |
| */ | |
| public void store(OutputStream out, String header) throws IOException | |
| { | |
| // The spec says that the file must be encoded using ISO-8859-1. | |
| PrintWriter writer | |
| = new PrintWriter(new OutputStreamWriter(out, "ISO-8859-1")); | |
| if (header != null) | |
| writer.println("#" + header); | |
| writer.println ("#" + Calendar.getInstance ().getTime ()); | |
| Iterator iter = entrySet ().iterator (); | |
| int i = size (); | |
| StringBuilder s = new StringBuilder (); // Reuse the same buffer. | |
| while (--i >= 0) | |
| { | |
| Map.Entry entry = (Map.Entry) iter.next (); | |
| formatForOutput ((String) entry.getKey (), s, true); | |
| s.append ('='); | |
| formatForOutput ((String) entry.getValue (), s, false); | |
| writer.println (s); | |
| } | |
| writer.flush (); | |
| } | |
| /** | |
| * Gets the property with the specified key in this property list. | |
| * If the key is not found, the default property list is searched. | |
| * If the property is not found in the default, null is returned. | |
| * | |
| * @param key The key for this property | |
| * @return the value for the given key, or null if not found | |
| * @throws ClassCastException if this property contains any key or | |
| * value that isn't a string | |
| * @see #defaults | |
| * @see #setProperty(String, String) | |
| * @see #getProperty(String, String) | |
| */ | |
| public String getProperty(String key) | |
| { | |
| Properties prop = this; | |
| // Eliminate tail recursion. | |
| do | |
| { | |
| String value = (String) prop.get(key); | |
| if (value != null) | |
| return value; | |
| prop = prop.defaults; | |
| } | |
| while (prop != null); | |
| return null; | |
| } | |
| /** | |
| * Gets the property with the specified key in this property list. If | |
| * the key is not found, the default property list is searched. If the | |
| * property is not found in the default, the specified defaultValue is | |
| * returned. | |
| * | |
| * @param key The key for this property | |
| * @param defaultValue A default value | |
| * @return The value for the given key | |
| * @throws ClassCastException if this property contains any key or | |
| * value that isn't a string | |
| * @see #defaults | |
| * @see #setProperty(String, String) | |
| */ | |
| public String getProperty(String key, String defaultValue) | |
| { | |
| String prop = getProperty(key); | |
| if (prop == null) | |
| prop = defaultValue; | |
| return prop; | |
| } | |
| /** | |
| * Returns an enumeration of all keys in this property list, including | |
| * the keys in the default property list. | |
| * | |
| * @return an Enumeration of all defined keys | |
| */ | |
| public Enumeration propertyNames() | |
| { | |
| // We make a new Set that holds all the keys, then return an enumeration | |
| // for that. This prevents modifications from ruining the enumeration, | |
| // as well as ignoring duplicates. | |
| Properties prop = this; | |
| Set s = new HashSet(); | |
| // Eliminate tail recursion. | |
| do | |
| { | |
| s.addAll(prop.keySet()); | |
| prop = prop.defaults; | |
| } | |
| while (prop != null); | |
| return Collections.enumeration(s); | |
| } | |
| /** | |
| * Prints the key/value pairs to the given print stream. This is | |
| * mainly useful for debugging purposes. | |
| * | |
| * @param out the print stream, where the key/value pairs are written to | |
| * @throws ClassCastException if this property contains a key or a | |
| * value that isn't a string | |
| * @see #list(PrintWriter) | |
| */ | |
| public void list(PrintStream out) | |
| { | |
| PrintWriter writer = new PrintWriter (out); | |
| list (writer); | |
| } | |
| /** | |
| * Prints the key/value pairs to the given print writer. This is | |
| * mainly useful for debugging purposes. | |
| * | |
| * @param out the print writer where the key/value pairs are written to | |
| * @throws ClassCastException if this property contains a key or a | |
| * value that isn't a string | |
| * @see #list(PrintStream) | |
| * @since 1.1 | |
| */ | |
| public void list(PrintWriter out) | |
| { | |
| out.println ("-- listing properties --"); | |
| Iterator iter = entrySet ().iterator (); | |
| int i = size (); | |
| while (--i >= 0) | |
| { | |
| Map.Entry entry = (Map.Entry) iter.next (); | |
| out.print ((String) entry.getKey () + "="); | |
| // JDK 1.3/1.4 restrict the printed value, but not the key, | |
| // to 40 characters, including the truncating ellipsis. | |
| String s = (String ) entry.getValue (); | |
| if (s != null && s.length () > 40) | |
| out.println (s.substring (0, 37) + "..."); | |
| else | |
| out.println (s); | |
| } | |
| out.flush (); | |
| } | |
| /** | |
| * Formats a key or value for output in a properties file. | |
| * See store for a description of the format. | |
| * | |
| * @param str the string to format | |
| * @param buffer the buffer to add it to | |
| * @param key true if all ' ' must be escaped for the key, false if only | |
| * leading spaces must be escaped for the value | |
| * @see #store(OutputStream, String) | |
| */ | |
| private void formatForOutput(String str, StringBuilder buffer, boolean key) | |
| { | |
| if (key) | |
| { | |
| buffer.setLength(0); | |
| buffer.ensureCapacity(str.length()); | |
| } | |
| else | |
| buffer.ensureCapacity(buffer.length() + str.length()); | |
| boolean head = true; | |
| int size = str.length(); | |
| for (int i = 0; i < size; i++) | |
| { | |
| char c = str.charAt(i); | |
| switch (c) | |
| { | |
| case '\n': | |
| buffer.append("\\n"); | |
| break; | |
| case '\r': | |
| buffer.append("\\r"); | |
| break; | |
| case '\t': | |
| buffer.append("\\t"); | |
| break; | |
| case ' ': | |
| buffer.append(head ? "\\ " : " "); | |
| break; | |
| case '\\': | |
| case '!': | |
| case '#': | |
| case '=': | |
| case ':': | |
| buffer.append('\\').append(c); | |
| break; | |
| default: | |
| if (c < ' ' || c > '~') | |
| { | |
| String hex = Integer.toHexString(c); | |
| buffer.append("\\u0000".substring(0, 6 - hex.length())); | |
| buffer.append(hex); | |
| } | |
| else | |
| buffer.append(c); | |
| } | |
| if (c != ' ') | |
| head = key; | |
| } | |
| } | |
| /** | |
| * <p> | |
| * Encodes the properties as an XML file using the UTF-8 encoding. | |
| * The format of the XML file matches the DTD | |
| * <a href="http://java.sun.com/dtd/properties.dtd"> | |
| * http://java.sun.com/dtd/properties.dtd</a>. | |
| * </p> | |
| * <p> | |
| * Invoking this method provides the same behaviour as invoking | |
| * <code>storeToXML(os, comment, "UTF-8")</code>. | |
| * </p> | |
| * | |
| * @param os the stream to output to. | |
| * @param comment a comment to include at the top of the XML file, or | |
| * <code>null</code> if one is not required. | |
| * @throws IOException if the serialization fails. | |
| * @throws NullPointerException if <code>os</code> is null. | |
| * @since 1.5 | |
| */ | |
| public void storeToXML(OutputStream os, String comment) | |
| throws IOException | |
| { | |
| storeToXML(os, comment, "UTF-8"); | |
| } | |
| /** | |
| * <p> | |
| * Encodes the properties as an XML file using the supplied encoding. | |
| * The format of the XML file matches the DTD | |
| * <a href="http://java.sun.com/dtd/properties.dtd"> | |
| * http://java.sun.com/dtd/properties.dtd</a>. | |
| * </p> | |
| * | |
| * @param os the stream to output to. | |
| * @param comment a comment to include at the top of the XML file, or | |
| * <code>null</code> if one is not required. | |
| * @param encoding the encoding to use for the XML output. | |
| * @throws IOException if the serialization fails. | |
| * @throws NullPointerException if <code>os</code> or <code>encoding</code> | |
| * is null. | |
| * @since 1.5 | |
| */ | |
| public void storeToXML(OutputStream os, String comment, String encoding) | |
| throws IOException | |
| { | |
| if (os == null) | |
| throw new NullPointerException("Null output stream supplied."); | |
| if (encoding == null) | |
| throw new NullPointerException("Null encoding supplied."); | |
| try | |
| { | |
| DOMImplementationRegistry registry = | |
| DOMImplementationRegistry.newInstance(); | |
| DOMImplementation domImpl = registry.getDOMImplementation("LS 3.0"); | |
| DocumentType doctype = | |
| domImpl.createDocumentType("properties", null, | |
| "http://java.sun.com/dtd/properties.dtd"); | |
| Document doc = domImpl.createDocument(null, "properties", doctype); | |
| Element root = doc.getDocumentElement(); | |
| if (comment != null) | |
| { | |
| Element commentElement = doc.createElement("comment"); | |
| commentElement.appendChild(doc.createTextNode(comment)); | |
| root.appendChild(commentElement); | |
| } | |
| Iterator iterator = entrySet().iterator(); | |
| while (iterator.hasNext()) | |
| { | |
| Map.Entry entry = (Map.Entry) iterator.next(); | |
| Element entryElement = doc.createElement("entry"); | |
| entryElement.setAttribute("key", (String) entry.getKey()); | |
| entryElement.appendChild(doc.createTextNode((String) | |
| entry.getValue())); | |
| root.appendChild(entryElement); | |
| } | |
| DOMImplementationLS loadAndSave = (DOMImplementationLS) domImpl; | |
| LSSerializer serializer = loadAndSave.createLSSerializer(); | |
| LSOutput output = loadAndSave.createLSOutput(); | |
| output.setByteStream(os); | |
| output.setEncoding(encoding); | |
| serializer.write(doc, output); | |
| } | |
| catch (ClassNotFoundException e) | |
| { | |
| throw (IOException) | |
| new IOException("The XML classes could not be found.").initCause(e); | |
| } | |
| catch (InstantiationException e) | |
| { | |
| throw (IOException) | |
| new IOException("The XML classes could not be instantiated.") | |
| .initCause(e); | |
| } | |
| catch (IllegalAccessException e) | |
| { | |
| throw (IOException) | |
| new IOException("The XML classes could not be accessed.") | |
| .initCause(e); | |
| } | |
| } | |
| /** | |
| * <p> | |
| * Decodes the contents of the supplied <code>InputStream</code> as | |
| * an XML file, which represents a set of properties. The format of | |
| * the XML file must match the DTD | |
| * <a href="http://java.sun.com/dtd/properties.dtd"> | |
| * http://java.sun.com/dtd/properties.dtd</a>. | |
| * </p> | |
| * | |
| * @param in the input stream from which to receive the XML data. | |
| * @throws IOException if an I/O error occurs in reading the input data. | |
| * @throws InvalidPropertiesFormatException if the input data does not | |
| * constitute an XML properties | |
| * file. | |
| * @throws NullPointerException if <code>in</code> is null. | |
| * @since 1.5 | |
| */ | |
| public void loadFromXML(InputStream in) | |
| throws IOException, InvalidPropertiesFormatException | |
| { | |
| if (in == null) | |
| throw new NullPointerException("Null input stream supplied."); | |
| try | |
| { | |
| XMLInputFactory factory = XMLInputFactory.newInstance(); | |
| // Don't resolve external entity references | |
| factory.setProperty("javax.xml.stream.isSupportingExternalEntities", | |
| Boolean.FALSE); | |
| XMLStreamReader reader = factory.createXMLStreamReader(in); | |
| String name, key = null; | |
| StringBuffer buf = null; | |
| while (reader.hasNext()) | |
| { | |
| switch (reader.next()) | |
| { | |
| case XMLStreamConstants.START_ELEMENT: | |
| name = reader.getLocalName(); | |
| if (buf == null && "entry".equals(name)) | |
| { | |
| key = reader.getAttributeValue(null, "key"); | |
| if (key == null) | |
| { | |
| String msg = "missing 'key' attribute"; | |
| throw new InvalidPropertiesFormatException(msg); | |
| } | |
| buf = new StringBuffer(); | |
| } | |
| else if (!"properties".equals(name) && !"comment".equals(name)) | |
| { | |
| String msg = "unexpected element name '" + name + "'"; | |
| throw new InvalidPropertiesFormatException(msg); | |
| } | |
| break; | |
| case XMLStreamConstants.END_ELEMENT: | |
| name = reader.getLocalName(); | |
| if (buf != null && "entry".equals(name)) | |
| { | |
| put(key, buf.toString()); | |
| buf = null; | |
| } | |
| else if (!"properties".equals(name) && !"comment".equals(name)) | |
| { | |
| String msg = "unexpected element name '" + name + "'"; | |
| throw new InvalidPropertiesFormatException(msg); | |
| } | |
| break; | |
| case XMLStreamConstants.CHARACTERS: | |
| case XMLStreamConstants.SPACE: | |
| case XMLStreamConstants.CDATA: | |
| if (buf != null) | |
| buf.append(reader.getText()); | |
| break; | |
| } | |
| } | |
| reader.close(); | |
| } | |
| catch (XMLStreamException e) | |
| { | |
| throw (InvalidPropertiesFormatException) | |
| new InvalidPropertiesFormatException("Error in parsing XML."). | |
| initCause(e); | |
| } | |
| } | |
| } // class Properties |