Frames | No Frames |
1: /* Properties.java -- a set of persistent properties 2: Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package java.util; 40: 41: import gnu.java.lang.CPStringBuilder; 42: 43: import java.io.BufferedReader; 44: import java.io.IOException; 45: import java.io.InputStream; 46: import java.io.InputStreamReader; 47: import java.io.OutputStream; 48: import java.io.OutputStreamWriter; 49: import java.io.PrintStream; 50: import java.io.PrintWriter; 51: import java.io.Reader; 52: 53: import javax.xml.stream.XMLInputFactory; 54: import javax.xml.stream.XMLStreamConstants; 55: import javax.xml.stream.XMLStreamException; 56: import javax.xml.stream.XMLStreamReader; 57: 58: import org.w3c.dom.Document; 59: import org.w3c.dom.DocumentType; 60: import org.w3c.dom.DOMImplementation; 61: import org.w3c.dom.Element; 62: import org.w3c.dom.bootstrap.DOMImplementationRegistry; 63: import org.w3c.dom.ls.DOMImplementationLS; 64: import org.w3c.dom.ls.LSOutput; 65: import org.w3c.dom.ls.LSSerializer; 66: 67: /** 68: * A set of persistent properties, which can be saved or loaded from a stream. 69: * A property list may also contain defaults, searched if the main list 70: * does not contain a property for a given key. 71: * 72: * An example of a properties file for the german language is given 73: * here. This extends the example given in ListResourceBundle. 74: * Create a file MyResource_de.properties with the following contents 75: * and put it in the CLASSPATH. (The character 76: * <code>\</code><code>u00e4</code> is the german umlaut) 77: * 78: * 79: <pre>s1=3 80: s2=MeineDisk 81: s3=3. M\<code></code>u00e4rz 96 82: s4=Die Diskette ''{1}'' enth\<code></code>u00e4lt {0} in {2}. 83: s5=0 84: s6=keine Dateien 85: s7=1 86: s8=eine Datei 87: s9=2 88: s10={0,number} Dateien 89: s11=Das Formatieren schlug fehl mit folgender Exception: {0} 90: s12=FEHLER 91: s13=Ergebnis 92: s14=Dialog 93: s15=Auswahlkriterium 94: s16=1,3</pre> 95: * 96: * <p>Although this is a sub class of a hash table, you should never 97: * insert anything other than strings to this property, or several 98: * methods, that need string keys and values, will fail. To ensure 99: * this, you should use the <code>get/setProperty</code> method instead 100: * of <code>get/put</code>. 101: * 102: * Properties are saved in ISO 8859-1 encoding, using Unicode escapes with 103: * a single <code>u</code> for any character which cannot be represented. 104: * 105: * @author Jochen Hoenicke 106: * @author Eric Blake (ebb9@email.byu.edu) 107: * @see PropertyResourceBundle 108: * @status updated to 1.4 109: */ 110: public class Properties extends Hashtable<Object, Object> 111: { 112: // WARNING: Properties is a CORE class in the bootstrap cycle. See the 113: // comments in vm/reference/java/lang/Runtime for implications of this fact. 114: 115: /** 116: * The property list that contains default values for any keys not 117: * in this property list. 118: * 119: * @serial the default properties 120: */ 121: protected Properties defaults; 122: 123: /** 124: * Compatible with JDK 1.0+. 125: */ 126: private static final long serialVersionUID = 4112578634029874840L; 127: 128: /** 129: * Creates a new empty property list with no default values. 130: */ 131: public Properties() 132: { 133: } 134: 135: /** 136: * Create a new empty property list with the specified default values. 137: * 138: * @param defaults a Properties object containing the default values 139: */ 140: public Properties(Properties defaults) 141: { 142: this.defaults = defaults; 143: } 144: 145: /** 146: * Adds the given key/value pair to this properties. This calls 147: * the hashtable method put. 148: * 149: * @param key the key for this property 150: * @param value the value for this property 151: * @return The old value for the given key 152: * @see #getProperty(String) 153: * @since 1.2 154: */ 155: public Object setProperty(String key, String value) 156: { 157: return put(key, value); 158: } 159: 160: /** 161: * Reads a property list from a character stream. The stream should 162: * have the following format: <br> 163: * 164: * An empty line or a line starting with <code>#</code> or 165: * <code>!</code> is ignored. An backslash (<code>\</code>) at the 166: * end of the line makes the line continueing on the next line 167: * (but make sure there is no whitespace after the backslash). 168: * Otherwise, each line describes a key/value pair. <br> 169: * 170: * The chars up to the first whitespace, = or : are the key. You 171: * can include this caracters in the key, if you precede them with 172: * a backslash (<code>\</code>). The key is followed by optional 173: * whitespaces, optionally one <code>=</code> or <code>:</code>, 174: * and optionally some more whitespaces. The rest of the line is 175: * the resource belonging to the key. <br> 176: * 177: * Escape sequences <code>\t, \n, \r, \\, \", \', \!, \#, \ </code>(a 178: * space), and unicode characters with the 179: * <code>\\u</code><em>xxxx</em> notation are detected, and 180: * converted to the corresponding single character. <br> 181: * 182: * 183: <pre># This is a comment 184: key = value 185: k\:5 \ a string starting with space and ending with newline\n 186: # This is a multiline specification; note that the value contains 187: # no white space. 188: weekdays: Sunday,Monday,Tuesday,Wednesday,\\ 189: Thursday,Friday,Saturday 190: # The safest way to include a space at the end of a value: 191: label = Name:\\u0020</pre> 192: * 193: * @param inReader the input {@link java.io.Reader}. 194: * @throws IOException if an error occurred when reading the input 195: * @throws NullPointerException if in is null 196: * @since 1.6 197: */ 198: public void load(Reader inReader) throws IOException 199: { 200: BufferedReader reader = new BufferedReader(inReader); 201: String line; 202: 203: while ((line = reader.readLine()) != null) 204: { 205: char c = 0; 206: int pos = 0; 207: // Leading whitespaces must be deleted first. 208: while (pos < line.length() 209: && Character.isWhitespace(c = line.charAt(pos))) 210: pos++; 211: 212: // If empty line or begins with a comment character, skip this line. 213: if ((line.length() - pos) == 0 214: || line.charAt(pos) == '#' || line.charAt(pos) == '!') 215: continue; 216: 217: // The characters up to the next Whitespace, ':', or '=' 218: // describe the key. But look for escape sequences. 219: // Try to short-circuit when there is no escape char. 220: int start = pos; 221: boolean needsEscape = line.indexOf('\\', pos) != -1; 222: CPStringBuilder key = needsEscape ? new CPStringBuilder() : null; 223: while (pos < line.length() 224: && ! Character.isWhitespace(c = line.charAt(pos++)) 225: && c != '=' && c != ':') 226: { 227: if (needsEscape && c == '\\') 228: { 229: if (pos == line.length()) 230: { 231: // The line continues on the next line. If there 232: // is no next line, just treat it as a key with an 233: // empty value. 234: line = reader.readLine(); 235: if (line == null) 236: line = ""; 237: pos = 0; 238: while (pos < line.length() 239: && Character.isWhitespace(c = line.charAt(pos))) 240: pos++; 241: } 242: else 243: { 244: c = line.charAt(pos++); 245: switch (c) 246: { 247: case 'n': 248: key.append('\n'); 249: break; 250: case 't': 251: key.append('\t'); 252: break; 253: case 'r': 254: key.append('\r'); 255: break; 256: case 'u': 257: if (pos + 4 <= line.length()) 258: { 259: char uni = (char) Integer.parseInt 260: (line.substring(pos, pos + 4), 16); 261: key.append(uni); 262: pos += 4; 263: } // else throw exception? 264: break; 265: default: 266: key.append(c); 267: break; 268: } 269: } 270: } 271: else if (needsEscape) 272: key.append(c); 273: } 274: 275: boolean isDelim = (c == ':' || c == '='); 276: 277: String keyString; 278: if (needsEscape) 279: keyString = key.toString(); 280: else if (isDelim || Character.isWhitespace(c)) 281: keyString = line.substring(start, pos - 1); 282: else 283: keyString = line.substring(start, pos); 284: 285: while (pos < line.length() 286: && Character.isWhitespace(c = line.charAt(pos))) 287: pos++; 288: 289: if (! isDelim && (c == ':' || c == '=')) 290: { 291: pos++; 292: while (pos < line.length() 293: && Character.isWhitespace(c = line.charAt(pos))) 294: pos++; 295: } 296: 297: // Short-circuit if no escape chars found. 298: if (!needsEscape) 299: { 300: put(keyString, line.substring(pos)); 301: continue; 302: } 303: 304: // Escape char found so iterate through the rest of the line. 305: StringBuilder element = new StringBuilder(line.length() - pos); 306: while (pos < line.length()) 307: { 308: c = line.charAt(pos++); 309: if (c == '\\') 310: { 311: if (pos == line.length()) 312: { 313: // The line continues on the next line. 314: line = reader.readLine(); 315: 316: // We might have seen a backslash at the end of 317: // the file. The JDK ignores the backslash in 318: // this case, so we follow for compatibility. 319: if (line == null) 320: break; 321: 322: pos = 0; 323: while (pos < line.length() 324: && Character.isWhitespace(c = line.charAt(pos))) 325: pos++; 326: element.ensureCapacity(line.length() - pos + 327: element.length()); 328: } 329: else 330: { 331: c = line.charAt(pos++); 332: switch (c) 333: { 334: case 'n': 335: element.append('\n'); 336: break; 337: case 't': 338: element.append('\t'); 339: break; 340: case 'r': 341: element.append('\r'); 342: break; 343: case 'u': 344: if (pos + 4 <= line.length()) 345: { 346: char uni = (char) Integer.parseInt 347: (line.substring(pos, pos + 4), 16); 348: element.append(uni); 349: pos += 4; 350: } // else throw exception? 351: break; 352: default: 353: element.append(c); 354: break; 355: } 356: } 357: } 358: else 359: element.append(c); 360: } 361: put(keyString, element.toString()); 362: } 363: } 364: 365: /** 366: * Reads a property list from the supplied input stream. 367: * This method has the same functionality as {@link #load(Reader)} 368: * but the character encoding is assumed to be ISO-8859-1. 369: * Unicode characters not within the Latin1 set supplied by 370: * ISO-8859-1 should be escaped using '\\uXXXX' where XXXX 371: * is the UTF-16 code unit in hexadecimal. 372: * 373: * @param inStream the byte stream to read the property list from. 374: * @throws IOException if an I/O error occurs. 375: * @see #load(Reader) 376: * @since 1.2 377: */ 378: public void load(InputStream inStream) throws IOException 379: { 380: load(new InputStreamReader(inStream, "ISO-8859-1")); 381: } 382: 383: /** 384: * Calls <code>store(OutputStream out, String header)</code> and 385: * ignores the IOException that may be thrown. 386: * 387: * @param out the stream to write to 388: * @param header a description of the property list 389: * @throws ClassCastException if this property contains any key or 390: * value that are not strings 391: * @deprecated use {@link #store(OutputStream, String)} instead 392: */ 393: @Deprecated 394: public void save(OutputStream out, String header) 395: { 396: try 397: { 398: store(out, header); 399: } 400: catch (IOException ex) 401: { 402: } 403: } 404: 405: /** 406: * Writes the key/value pairs to the given output stream, in a format 407: * suitable for <code>load</code>.<br> 408: * 409: * If header is not null, this method writes a comment containing 410: * the header as first line to the stream. The next line (or first 411: * line if header is null) contains a comment with the current date. 412: * Afterwards the key/value pairs are written to the stream in the 413: * following format.<br> 414: * 415: * Each line has the form <code>key = value</code>. Newlines, 416: * Returns and tabs are written as <code>\n,\t,\r</code> resp. 417: * The characters <code>\, !, #, =</code> and <code>:</code> are 418: * preceeded by a backslash. Spaces are preceded with a backslash, 419: * if and only if they are at the beginning of the key. Characters 420: * that are not in the ascii range 33 to 127 are written in the 421: * <code>\</code><code>u</code>xxxx Form.<br> 422: * 423: * Following the listing, the output stream is flushed but left open. 424: * 425: * @param out the output stream 426: * @param header the header written in the first line, may be null 427: * @throws ClassCastException if this property contains any key or 428: * value that isn't a string 429: * @throws IOException if writing to the stream fails 430: * @throws NullPointerException if out is null 431: * @since 1.2 432: */ 433: public void store(OutputStream out, String header) throws IOException 434: { 435: // The spec says that the file must be encoded using ISO-8859-1. 436: PrintWriter writer 437: = new PrintWriter(new OutputStreamWriter(out, "ISO-8859-1")); 438: if (header != null) 439: writer.println("#" + header); 440: writer.println ("#" + Calendar.getInstance ().getTime ()); 441: 442: Iterator iter = entrySet ().iterator (); 443: int i = size (); 444: CPStringBuilder s = new CPStringBuilder (); // Reuse the same buffer. 445: while (--i >= 0) 446: { 447: Map.Entry entry = (Map.Entry) iter.next (); 448: formatForOutput ((String) entry.getKey (), s, true); 449: s.append ('='); 450: formatForOutput ((String) entry.getValue (), s, false); 451: writer.println (s); 452: } 453: 454: writer.flush (); 455: } 456: 457: /** 458: * Gets the property with the specified key in this property list. 459: * If the key is not found, the default property list is searched. 460: * If the property is not found in the default, null is returned. 461: * 462: * @param key The key for this property 463: * @return the value for the given key, or null if not found 464: * @throws ClassCastException if this property contains any key or 465: * value that isn't a string 466: * @see #defaults 467: * @see #setProperty(String, String) 468: * @see #getProperty(String, String) 469: */ 470: public String getProperty(String key) 471: { 472: Properties prop = this; 473: // Eliminate tail recursion. 474: do 475: { 476: String value = (String) prop.get(key); 477: if (value != null) 478: return value; 479: prop = prop.defaults; 480: } 481: while (prop != null); 482: return null; 483: } 484: 485: /** 486: * Gets the property with the specified key in this property list. If 487: * the key is not found, the default property list is searched. If the 488: * property is not found in the default, the specified defaultValue is 489: * returned. 490: * 491: * @param key The key for this property 492: * @param defaultValue A default value 493: * @return The value for the given key 494: * @throws ClassCastException if this property contains any key or 495: * value that isn't a string 496: * @see #defaults 497: * @see #setProperty(String, String) 498: */ 499: public String getProperty(String key, String defaultValue) 500: { 501: String prop = getProperty(key); 502: if (prop == null) 503: prop = defaultValue; 504: return prop; 505: } 506: 507: /** 508: * Returns an enumeration of all keys in this property list, including 509: * the keys in the default property list. 510: * 511: * @return an Enumeration of all defined keys 512: */ 513: public Enumeration<?> propertyNames() 514: { 515: // We make a new Set that holds all the keys, then return an enumeration 516: // for that. This prevents modifications from ruining the enumeration, 517: // as well as ignoring duplicates. 518: Properties prop = this; 519: Set s = new HashSet(); 520: // Eliminate tail recursion. 521: do 522: { 523: s.addAll(prop.keySet()); 524: prop = prop.defaults; 525: } 526: while (prop != null); 527: return Collections.enumeration(s); 528: } 529: 530: /** 531: * Prints the key/value pairs to the given print stream. This is 532: * mainly useful for debugging purposes. 533: * 534: * @param out the print stream, where the key/value pairs are written to 535: * @throws ClassCastException if this property contains a key or a 536: * value that isn't a string 537: * @see #list(PrintWriter) 538: */ 539: public void list(PrintStream out) 540: { 541: PrintWriter writer = new PrintWriter (out); 542: list (writer); 543: } 544: 545: /** 546: * Prints the key/value pairs to the given print writer. This is 547: * mainly useful for debugging purposes. 548: * 549: * @param out the print writer where the key/value pairs are written to 550: * @throws ClassCastException if this property contains a key or a 551: * value that isn't a string 552: * @see #list(PrintStream) 553: * @since 1.1 554: */ 555: public void list(PrintWriter out) 556: { 557: out.println ("-- listing properties --"); 558: 559: Iterator iter = entrySet ().iterator (); 560: int i = size (); 561: while (--i >= 0) 562: { 563: Map.Entry entry = (Map.Entry) iter.next (); 564: out.print ((String) entry.getKey () + "="); 565: 566: // JDK 1.3/1.4 restrict the printed value, but not the key, 567: // to 40 characters, including the truncating ellipsis. 568: String s = (String ) entry.getValue (); 569: if (s != null && s.length () > 40) 570: out.println (s.substring (0, 37) + "..."); 571: else 572: out.println (s); 573: } 574: out.flush (); 575: } 576: 577: /** 578: * Formats a key or value for output in a properties file. 579: * See store for a description of the format. 580: * 581: * @param str the string to format 582: * @param buffer the buffer to add it to 583: * @param key true if all ' ' must be escaped for the key, false if only 584: * leading spaces must be escaped for the value 585: * @see #store(OutputStream, String) 586: */ 587: private void formatForOutput(String str, CPStringBuilder buffer, boolean key) 588: { 589: if (key) 590: { 591: buffer.setLength(0); 592: buffer.ensureCapacity(str.length()); 593: } 594: else 595: buffer.ensureCapacity(buffer.length() + str.length()); 596: boolean head = true; 597: int size = str.length(); 598: for (int i = 0; i < size; i++) 599: { 600: char c = str.charAt(i); 601: switch (c) 602: { 603: case '\n': 604: buffer.append("\\n"); 605: break; 606: case '\r': 607: buffer.append("\\r"); 608: break; 609: case '\t': 610: buffer.append("\\t"); 611: break; 612: case ' ': 613: buffer.append(head ? "\\ " : " "); 614: break; 615: case '\\': 616: case '!': 617: case '#': 618: case '=': 619: case ':': 620: buffer.append('\\').append(c); 621: break; 622: default: 623: if (c < ' ' || c > '~') 624: { 625: String hex = Integer.toHexString(c); 626: buffer.append("\\u0000".substring(0, 6 - hex.length())); 627: buffer.append(hex); 628: } 629: else 630: buffer.append(c); 631: } 632: if (c != ' ') 633: head = key; 634: } 635: } 636: 637: /** 638: * <p> 639: * Encodes the properties as an XML file using the UTF-8 encoding. 640: * The format of the XML file matches the DTD 641: * <a href="http://java.sun.com/dtd/properties.dtd"> 642: * http://java.sun.com/dtd/properties.dtd</a>. 643: * </p> 644: * <p> 645: * Invoking this method provides the same behaviour as invoking 646: * <code>storeToXML(os, comment, "UTF-8")</code>. 647: * </p> 648: * 649: * @param os the stream to output to. 650: * @param comment a comment to include at the top of the XML file, or 651: * <code>null</code> if one is not required. 652: * @throws IOException if the serialization fails. 653: * @throws NullPointerException if <code>os</code> is null. 654: * @since 1.5 655: */ 656: public void storeToXML(OutputStream os, String comment) 657: throws IOException 658: { 659: storeToXML(os, comment, "UTF-8"); 660: } 661: 662: /** 663: * <p> 664: * Encodes the properties as an XML file using the supplied encoding. 665: * The format of the XML file matches the DTD 666: * <a href="http://java.sun.com/dtd/properties.dtd"> 667: * http://java.sun.com/dtd/properties.dtd</a>. 668: * </p> 669: * 670: * @param os the stream to output to. 671: * @param comment a comment to include at the top of the XML file, or 672: * <code>null</code> if one is not required. 673: * @param encoding the encoding to use for the XML output. 674: * @throws IOException if the serialization fails. 675: * @throws NullPointerException if <code>os</code> or <code>encoding</code> 676: * is null. 677: * @since 1.5 678: */ 679: public void storeToXML(OutputStream os, String comment, String encoding) 680: throws IOException 681: { 682: if (os == null) 683: throw new NullPointerException("Null output stream supplied."); 684: if (encoding == null) 685: throw new NullPointerException("Null encoding supplied."); 686: try 687: { 688: DOMImplementationRegistry registry = 689: DOMImplementationRegistry.newInstance(); 690: DOMImplementation domImpl = registry.getDOMImplementation("LS 3.0"); 691: DocumentType doctype = 692: domImpl.createDocumentType("properties", null, 693: "http://java.sun.com/dtd/properties.dtd"); 694: Document doc = domImpl.createDocument(null, "properties", doctype); 695: Element root = doc.getDocumentElement(); 696: if (comment != null) 697: { 698: Element commentElement = doc.createElement("comment"); 699: commentElement.appendChild(doc.createTextNode(comment)); 700: root.appendChild(commentElement); 701: } 702: Iterator iterator = entrySet().iterator(); 703: while (iterator.hasNext()) 704: { 705: Map.Entry entry = (Map.Entry) iterator.next(); 706: Element entryElement = doc.createElement("entry"); 707: entryElement.setAttribute("key", (String) entry.getKey()); 708: entryElement.appendChild(doc.createTextNode((String) 709: entry.getValue())); 710: root.appendChild(entryElement); 711: } 712: DOMImplementationLS loadAndSave = (DOMImplementationLS) domImpl; 713: LSSerializer serializer = loadAndSave.createLSSerializer(); 714: LSOutput output = loadAndSave.createLSOutput(); 715: output.setByteStream(os); 716: output.setEncoding(encoding); 717: serializer.write(doc, output); 718: } 719: catch (ClassNotFoundException e) 720: { 721: throw (IOException) 722: new IOException("The XML classes could not be found.").initCause(e); 723: } 724: catch (InstantiationException e) 725: { 726: throw (IOException) 727: new IOException("The XML classes could not be instantiated.") 728: .initCause(e); 729: } 730: catch (IllegalAccessException e) 731: { 732: throw (IOException) 733: new IOException("The XML classes could not be accessed.") 734: .initCause(e); 735: } 736: } 737: 738: /** 739: * <p> 740: * Decodes the contents of the supplied <code>InputStream</code> as 741: * an XML file, which represents a set of properties. The format of 742: * the XML file must match the DTD 743: * <a href="http://java.sun.com/dtd/properties.dtd"> 744: * http://java.sun.com/dtd/properties.dtd</a>. 745: * </p> 746: * 747: * @param in the input stream from which to receive the XML data. 748: * @throws IOException if an I/O error occurs in reading the input data. 749: * @throws InvalidPropertiesFormatException if the input data does not 750: * constitute an XML properties 751: * file. 752: * @throws NullPointerException if <code>in</code> is null. 753: * @since 1.5 754: */ 755: public void loadFromXML(InputStream in) 756: throws IOException, InvalidPropertiesFormatException 757: { 758: if (in == null) 759: throw new NullPointerException("Null input stream supplied."); 760: try 761: { 762: XMLInputFactory factory = XMLInputFactory.newInstance(); 763: // Don't resolve external entity references 764: factory.setProperty("javax.xml.stream.isSupportingExternalEntities", 765: Boolean.FALSE); 766: XMLStreamReader reader = factory.createXMLStreamReader(in); 767: String name, key = null; 768: CPStringBuilder buf = null; 769: while (reader.hasNext()) 770: { 771: switch (reader.next()) 772: { 773: case XMLStreamConstants.START_ELEMENT: 774: name = reader.getLocalName(); 775: if (buf == null && "entry".equals(name)) 776: { 777: key = reader.getAttributeValue(null, "key"); 778: if (key == null) 779: { 780: String msg = "missing 'key' attribute"; 781: throw new InvalidPropertiesFormatException(msg); 782: } 783: buf = new CPStringBuilder(); 784: } 785: else if (!"properties".equals(name) && !"comment".equals(name)) 786: { 787: String msg = "unexpected element name '" + name + "'"; 788: throw new InvalidPropertiesFormatException(msg); 789: } 790: break; 791: case XMLStreamConstants.END_ELEMENT: 792: name = reader.getLocalName(); 793: if (buf != null && "entry".equals(name)) 794: { 795: put(key, buf.toString()); 796: buf = null; 797: } 798: else if (!"properties".equals(name) && !"comment".equals(name)) 799: { 800: String msg = "unexpected element name '" + name + "'"; 801: throw new InvalidPropertiesFormatException(msg); 802: } 803: break; 804: case XMLStreamConstants.CHARACTERS: 805: case XMLStreamConstants.SPACE: 806: case XMLStreamConstants.CDATA: 807: if (buf != null) 808: buf.append(reader.getText()); 809: break; 810: } 811: } 812: reader.close(); 813: } 814: catch (XMLStreamException e) 815: { 816: throw (InvalidPropertiesFormatException) 817: new InvalidPropertiesFormatException("Error in parsing XML."). 818: initCause(e); 819: } 820: } 821: 822: } // class Properties