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