1 /* 2 * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.util; 27 28 import java.io.IOException; 29 import java.io.PrintStream; 30 import java.io.PrintWriter; 31 import java.io.InputStream; 32 import java.io.OutputStream; 33 import java.io.Reader; 34 import java.io.Writer; 35 import java.io.OutputStreamWriter; 36 import java.io.BufferedWriter; 37 38 /** 39 * The <code>Properties</code> class represents a persistent set of 40 * properties. The <code>Properties</code> can be saved to a stream 41 * or loaded from a stream. Each key and its corresponding value in 42 * the property list is a string. 43 * <p> 44 * A property list can contain another property list as its 45 * "defaults"; this second property list is searched if 46 * the property key is not found in the original property list. 47 * <p> 48 * Because <code>Properties</code> inherits from <code>Hashtable</code>, the 49 * <code>put</code> and <code>putAll</code> methods can be applied to a 50 * <code>Properties</code> object. Their use is strongly discouraged as they 51 * allow the caller to insert entries whose keys or values are not 52 * <code>Strings</code>. The <code>setProperty</code> method should be used 53 * instead. If the <code>store</code> or <code>save</code> method is called 54 * on a "compromised" <code>Properties</code> object that contains a 55 * non-<code>String</code> key or value, the call will fail. Similarly, 56 * the call to the <code>propertyNames</code> or <code>list</code> method 57 * will fail if it is called on a "compromised" <code>Properties</code> 58 * object that contains a non-<code>String</code> key. 59 * 60 * <p> 61 * The {@link #load(java.io.Reader) load(Reader)} <tt>/</tt> 62 * {@link #store(java.io.Writer, java.lang.String) store(Writer, String)} 63 * methods load and store properties from and to a character based stream 64 * in a simple line-oriented format specified below. 65 * 66 * The {@link #load(java.io.InputStream) load(InputStream)} <tt>/</tt> 67 * {@link #store(java.io.OutputStream, java.lang.String) store(OutputStream, String)} 68 * methods work the same way as the load(Reader)/store(Writer, String) pair, except 69 * the input/output stream is encoded in ISO 8859-1 character encoding. 70 * Characters that cannot be directly represented in this encoding can be written using 71 * Unicode escapes as defined in section 3.3 of 72 * <cite>The Java™ Language Specification</cite>; 73 * only a single 'u' character is allowed in an escape 74 * sequence. The native2ascii tool can be used to convert property files to and 75 * from other character encodings. 76 * 77 * <p> The {@link #loadFromXML(InputStream)} and {@link 78 * #storeToXML(OutputStream, String, String)} methods load and store properties 79 * in a simple XML format. By default the UTF-8 character encoding is used, 80 * however a specific encoding may be specified if required. An XML properties 81 * document has the following DOCTYPE declaration: 82 * 83 * <pre> 84 * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> 85 * </pre> 86 * Note that the system URI (http://java.sun.com/dtd/properties.dtd) is 87 * <i>not</i> accessed when exporting or importing properties; it merely 88 * serves as a string to uniquely identify the DTD, which is: 89 * <pre> 90 * <?xml version="1.0" encoding="UTF-8"?> 91 * 92 * <!-- DTD for properties --> 93 * 94 * <!ELEMENT properties ( comment?, entry* ) > 95 * 96 * <!ATTLIST properties version CDATA #FIXED "1.0"> 97 * 98 * <!ELEMENT comment (#PCDATA) > 99 * 100 * <!ELEMENT entry (#PCDATA) > 101 * 102 * <!ATTLIST entry key CDATA #REQUIRED> 103 * </pre> 104 * 105 * <p>This class is thread-safe: multiple threads can share a single 106 * <tt>Properties</tt> object without the need for external synchronization. 107 * 108 * @see <a href="../../../technotes/tools/solaris/native2ascii.html">native2ascii tool for Solaris</a> 109 * @see <a href="../../../technotes/tools/windows/native2ascii.html">native2ascii tool for Windows</a> 110 * 111 * @author Arthur van Hoff 112 * @author Michael McCloskey 113 * @author Xueming Shen 114 * @since JDK1.0 115 */ 116 public 117 class Properties extends Hashtable<Object,Object> { 118 /** 119 * use serialVersionUID from JDK 1.1.X for interoperability 120 */ 121 private static final long serialVersionUID = 4112578634029874840L; 122 123 /** 124 * A property list that contains default values for any keys not 125 * found in this property list. 126 * 127 * @serial 128 */ 129 protected Properties defaults; 130 131 /** 132 * Creates an empty property list with no default values. 133 */ 134 public Properties() { 135 this(null); 136 } 137 138 /** 139 * Creates an empty property list with the specified defaults. 140 * 141 * @param defaults the defaults. 142 */ 143 public Properties(Properties defaults) { 144 this.defaults = defaults; 145 } 146 147 /** 148 * Calls the <tt>Hashtable</tt> method <code>put</code>. Provided for 149 * parallelism with the <tt>getProperty</tt> method. Enforces use of 150 * strings for property keys and values. The value returned is the 151 * result of the <tt>Hashtable</tt> call to <code>put</code>. 152 * 153 * @param key the key to be placed into this property list. 154 * @param value the value corresponding to <tt>key</tt>. 155 * @return the previous value of the specified key in this property 156 * list, or <code>null</code> if it did not have one. 157 * @see #getProperty 158 * @since 1.2 159 */ 160 public synchronized Object setProperty(String key, String value) { 161 return put(key, value); 162 } 163 164 165 /** 166 * Reads a property list (key and element pairs) from the input 167 * character stream in a simple line-oriented format. 168 * <p> 169 * Properties are processed in terms of lines. There are two 170 * kinds of line, <i>natural lines</i> and <i>logical lines</i>. 171 * A natural line is defined as a line of 172 * characters that is terminated either by a set of line terminator 173 * characters (<code>\n</code> or <code>\r</code> or <code>\r\n</code>) 174 * or by the end of the stream. A natural line may be either a blank line, 175 * a comment line, or hold all or some of a key-element pair. A logical 176 * line holds all the data of a key-element pair, which may be spread 177 * out across several adjacent natural lines by escaping 178 * the line terminator sequence with a backslash character 179 * <code>\</code>. Note that a comment line cannot be extended 180 * in this manner; every natural line that is a comment must have 181 * its own comment indicator, as described below. Lines are read from 182 * input until the end of the stream is reached. 183 * 184 * <p> 185 * A natural line that contains only white space characters is 186 * considered blank and is ignored. A comment line has an ASCII 187 * <code>'#'</code> or <code>'!'</code> as its first non-white 188 * space character; comment lines are also ignored and do not 189 * encode key-element information. In addition to line 190 * terminators, this format considers the characters space 191 * (<code>' '</code>, <code>'\u0020'</code>), tab 192 * (<code>'\t'</code>, <code>'\u0009'</code>), and form feed 193 * (<code>'\f'</code>, <code>'\u000C'</code>) to be white 194 * space. 195 * 196 * <p> 197 * If a logical line is spread across several natural lines, the 198 * backslash escaping the line terminator sequence, the line 199 * terminator sequence, and any white space at the start of the 200 * following line have no affect on the key or element values. 201 * The remainder of the discussion of key and element parsing 202 * (when loading) will assume all the characters constituting 203 * the key and element appear on a single natural line after 204 * line continuation characters have been removed. Note that 205 * it is <i>not</i> sufficient to only examine the character 206 * preceding a line terminator sequence to decide if the line 207 * terminator is escaped; there must be an odd number of 208 * contiguous backslashes for the line terminator to be escaped. 209 * Since the input is processed from left to right, a 210 * non-zero even number of 2<i>n</i> contiguous backslashes 211 * before a line terminator (or elsewhere) encodes <i>n</i> 212 * backslashes after escape processing. 213 * 214 * <p> 215 * The key contains all of the characters in the line starting 216 * with the first non-white space character and up to, but not 217 * including, the first unescaped <code>'='</code>, 218 * <code>':'</code>, or white space character other than a line 219 * terminator. All of these key termination characters may be 220 * included in the key by escaping them with a preceding backslash 221 * character; for example,<p> 222 * 223 * <code>\:\=</code><p> 224 * 225 * would be the two-character key <code>":="</code>. Line 226 * terminator characters can be included using <code>\r</code> and 227 * <code>\n</code> escape sequences. Any white space after the 228 * key is skipped; if the first non-white space character after 229 * the key is <code>'='</code> or <code>':'</code>, then it is 230 * ignored and any white space characters after it are also 231 * skipped. All remaining characters on the line become part of 232 * the associated element string; if there are no remaining 233 * characters, the element is the empty string 234 * <code>""</code>. Once the raw character sequences 235 * constituting the key and element are identified, escape 236 * processing is performed as described above. 237 * 238 * <p> 239 * As an example, each of the following three lines specifies the key 240 * <code>"Truth"</code> and the associated element value 241 * <code>"Beauty"</code>: 242 * <p> 243 * <pre> 244 * Truth = Beauty 245 * Truth:Beauty 246 * Truth :Beauty 247 * </pre> 248 * As another example, the following three lines specify a single 249 * property: 250 * <p> 251 * <pre> 252 * fruits apple, banana, pear, \ 253 * cantaloupe, watermelon, \ 254 * kiwi, mango 255 * </pre> 256 * The key is <code>"fruits"</code> and the associated element is: 257 * <p> 258 * <pre>"apple, banana, pear, cantaloupe, watermelon, kiwi, mango"</pre> 259 * Note that a space appears before each <code>\</code> so that a space 260 * will appear after each comma in the final result; the <code>\</code>, 261 * line terminator, and leading white space on the continuation line are 262 * merely discarded and are <i>not</i> replaced by one or more other 263 * characters. 264 * <p> 265 * As a third example, the line: 266 * <p> 267 * <pre>cheeses 268 * </pre> 269 * specifies that the key is <code>"cheeses"</code> and the associated 270 * element is the empty string <code>""</code>.<p> 271 * <p> 272 * 273 * <a name="unicodeescapes"></a> 274 * Characters in keys and elements can be represented in escape 275 * sequences similar to those used for character and string literals 276 * (see sections 3.3 and 3.10.6 of 277 * <cite>The Java™ Language Specification</cite>). 278 * 279 * The differences from the character escape sequences and Unicode 280 * escapes used for characters and strings are: 281 * 282 * <ul> 283 * <li> Octal escapes are not recognized. 284 * 285 * <li> The character sequence <code>\b</code> does <i>not</i> 286 * represent a backspace character. 287 * 288 * <li> The method does not treat a backslash character, 289 * <code>\</code>, before a non-valid escape character as an 290 * error; the backslash is silently dropped. For example, in a 291 * Java string the sequence <code>"\z"</code> would cause a 292 * compile time error. In contrast, this method silently drops 293 * the backslash. Therefore, this method treats the two character 294 * sequence <code>"\b"</code> as equivalent to the single 295 * character <code>'b'</code>. 296 * 297 * <li> Escapes are not necessary for single and double quotes; 298 * however, by the rule above, single and double quote characters 299 * preceded by a backslash still yield single and double quote 300 * characters, respectively. 301 * 302 * <li> Only a single 'u' character is allowed in a Uniocde escape 303 * sequence. 304 * 305 * </ul> 306 * <p> 307 * The specified stream remains open after this method returns. 308 * 309 * @param reader the input character stream. 310 * @throws IOException if an error occurred when reading from the 311 * input stream. 312 * @throws IllegalArgumentException if a malformed Unicode escape 313 * appears in the input. 314 * @since 1.6 315 */ 316 public synchronized void load(Reader reader) throws IOException { 317 load0(new LineReader(reader)); 318 } 319 320 /** 321 * Reads a property list (key and element pairs) from the input 322 * byte stream. The input stream is in a simple line-oriented 323 * format as specified in 324 * {@link #load(java.io.Reader) load(Reader)} and is assumed to use 325 * the ISO 8859-1 character encoding; that is each byte is one Latin1 326 * character. Characters not in Latin1, and certain special characters, 327 * are represented in keys and elements using Unicode escapes as defined in 328 * section 3.3 of 329 * <cite>The Java™ Language Specification</cite>. 330 * <p> 331 * The specified stream remains open after this method returns. 332 * 333 * @param inStream the input stream. 334 * @exception IOException if an error occurred when reading from the 335 * input stream. 336 * @throws IllegalArgumentException if the input stream contains a 337 * malformed Unicode escape sequence. 338 * @since 1.2 339 */ 340 public synchronized void load(InputStream inStream) throws IOException { 341 load0(new LineReader(inStream)); 342 } 343 344 private void load0 (LineReader lr) throws IOException { 345 char[] convtBuf = new char[1024]; 346 int limit; 347 int keyLen; 348 int valueStart; 349 char c; 350 boolean hasSep; 351 boolean precedingBackslash; 352 353 while ((limit = lr.readLine()) >= 0) { 354 c = 0; 355 keyLen = 0; 356 valueStart = limit; 357 hasSep = false; 358 359 //System.out.println("line=<" + new String(lineBuf, 0, limit) + ">"); 360 precedingBackslash = false; 361 while (keyLen < limit) { 362 c = lr.lineBuf[keyLen]; 363 //need check if escaped. 364 if ((c == '=' || c == ':') && !precedingBackslash) { 365 valueStart = keyLen + 1; 366 hasSep = true; 367 break; 368 } else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) { 369 valueStart = keyLen + 1; 370 break; 371 } 372 if (c == '\\') { 373 precedingBackslash = !precedingBackslash; 374 } else { 375 precedingBackslash = false; 376 } 377 keyLen++; 378 } 379 while (valueStart < limit) { 380 c = lr.lineBuf[valueStart]; 381 if (c != ' ' && c != '\t' && c != '\f') { 382 if (!hasSep && (c == '=' || c == ':')) { 383 hasSep = true; 384 } else { 385 break; 386 } 387 } 388 valueStart++; 389 } 390 String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf); 391 String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf); 392 put(key, value); 393 } 394 } 395 396 /* Read in a "logical line" from an InputStream/Reader, skip all comment 397 * and blank lines and filter out those leading whitespace characters 398 * (\u0020, \u0009 and \u000c) from the beginning of a "natural line". 399 * Method returns the char length of the "logical line" and stores 400 * the line in "lineBuf". 401 */ 402 class LineReader { 403 public LineReader(InputStream inStream) { 404 this.inStream = inStream; 405 inByteBuf = new byte[8192]; 406 } 407 408 public LineReader(Reader reader) { 409 this.reader = reader; 410 inCharBuf = new char[8192]; 411 } 412 413 byte[] inByteBuf; 414 char[] inCharBuf; 415 char[] lineBuf = new char[1024]; 416 int inLimit = 0; 417 int inOff = 0; 418 InputStream inStream; 419 Reader reader; 420 421 int readLine() throws IOException { 422 int len = 0; 423 char c = 0; 424 425 boolean skipWhiteSpace = true; 426 boolean isCommentLine = false; 427 boolean isNewLine = true; 428 boolean appendedLineBegin = false; 429 boolean precedingBackslash = false; 430 boolean skipLF = false; 431 432 while (true) { 433 if (inOff >= inLimit) { 434 inLimit = (inStream==null)?reader.read(inCharBuf) 435 :inStream.read(inByteBuf); 436 inOff = 0; 437 if (inLimit <= 0) { 438 if (len == 0 || isCommentLine) { 439 return -1; 440 } 441 return len; 442 } 443 } 444 if (inStream != null) { 445 //The line below is equivalent to calling a 446 //ISO8859-1 decoder. 447 c = (char) (0xff & inByteBuf[inOff++]); 448 } else { 449 c = inCharBuf[inOff++]; 450 } 451 if (skipLF) { 452 skipLF = false; 453 if (c == '\n') { 454 continue; 455 } 456 } 457 if (skipWhiteSpace) { 458 if (c == ' ' || c == '\t' || c == '\f') { 459 continue; 460 } 461 if (!appendedLineBegin && (c == '\r' || c == '\n')) { 462 continue; 463 } 464 skipWhiteSpace = false; 465 appendedLineBegin = false; 466 } 467 if (isNewLine) { 468 isNewLine = false; 469 if (c == '#' || c == '!') { 470 isCommentLine = true; 471 continue; 472 } 473 } 474 475 if (c != '\n' && c != '\r') { 476 lineBuf[len++] = c; 477 if (len == lineBuf.length) { 478 int newLength = lineBuf.length * 2; 479 if (newLength < 0) { 480 newLength = Integer.MAX_VALUE; 481 } 482 char[] buf = new char[newLength]; 483 System.arraycopy(lineBuf, 0, buf, 0, lineBuf.length); 484 lineBuf = buf; 485 } 486 //flip the preceding backslash flag 487 if (c == '\\') { 488 precedingBackslash = !precedingBackslash; 489 } else { 490 precedingBackslash = false; 491 } 492 } 493 else { 494 // reached EOL 495 if (isCommentLine || len == 0) { 496 isCommentLine = false; 497 isNewLine = true; 498 skipWhiteSpace = true; 499 len = 0; 500 continue; 501 } 502 if (inOff >= inLimit) { 503 inLimit = (inStream==null) 504 ?reader.read(inCharBuf) 505 :inStream.read(inByteBuf); 506 inOff = 0; 507 if (inLimit <= 0) { 508 return len; 509 } 510 } 511 if (precedingBackslash) { 512 len -= 1; 513 //skip the leading whitespace characters in following line 514 skipWhiteSpace = true; 515 appendedLineBegin = true; 516 precedingBackslash = false; 517 if (c == '\r') { 518 skipLF = true; 519 } 520 } else { 521 return len; 522 } 523 } 524 } 525 } 526 } 527 528 /* 529 * Converts encoded \uxxxx to unicode chars 530 * and changes special saved chars to their original forms 531 */ 532 private String loadConvert (char[] in, int off, int len, char[] convtBuf) { 533 if (convtBuf.length < len) { 534 int newLen = len * 2; 535 if (newLen < 0) { 536 newLen = Integer.MAX_VALUE; 537 } 538 convtBuf = new char[newLen]; 539 } 540 char aChar; 541 char[] out = convtBuf; 542 int outLen = 0; 543 int end = off + len; 544 545 while (off < end) { 546 aChar = in[off++]; 547 if (aChar == '\\') { 548 aChar = in[off++]; 549 if(aChar == 'u') { 550 // Read the xxxx 551 int value=0; 552 for (int i=0; i<4; i++) { 553 aChar = in[off++]; 554 switch (aChar) { 555 case '0': case '1': case '2': case '3': case '4': 556 case '5': case '6': case '7': case '8': case '9': 557 value = (value << 4) + aChar - '0'; 558 break; 559 case 'a': case 'b': case 'c': 560 case 'd': case 'e': case 'f': 561 value = (value << 4) + 10 + aChar - 'a'; 562 break; 563 case 'A': case 'B': case 'C': 564 case 'D': case 'E': case 'F': 565 value = (value << 4) + 10 + aChar - 'A'; 566 break; 567 default: 568 throw new IllegalArgumentException( 569 "Malformed \\uxxxx encoding."); 570 } 571 } 572 out[outLen++] = (char)value; 573 } else { 574 if (aChar == 't') aChar = '\t'; 575 else if (aChar == 'r') aChar = '\r'; 576 else if (aChar == 'n') aChar = '\n'; 577 else if (aChar == 'f') aChar = '\f'; 578 out[outLen++] = aChar; 579 } 580 } else { 581 out[outLen++] = aChar; 582 } 583 } 584 return new String (out, 0, outLen); 585 } 586 587 /* 588 * Converts unicodes to encoded \uxxxx and escapes 589 * special characters with a preceding slash 590 */ 591 private String saveConvert(String theString, 592 boolean escapeSpace, 593 boolean escapeUnicode) { 594 int len = theString.length(); 595 int bufLen = len * 2; 596 if (bufLen < 0) { 597 bufLen = Integer.MAX_VALUE; 598 } 599 StringBuffer outBuffer = new StringBuffer(bufLen); 600 601 for(int x=0; x<len; x++) { 602 char aChar = theString.charAt(x); 603 // Handle common case first, selecting largest block that 604 // avoids the specials below 605 if ((aChar > 61) && (aChar < 127)) { 606 if (aChar == '\\') { 607 outBuffer.append('\\'); outBuffer.append('\\'); 608 continue; 609 } 610 outBuffer.append(aChar); 611 continue; 612 } 613 switch(aChar) { 614 case ' ': 615 if (x == 0 || escapeSpace) 616 outBuffer.append('\\'); 617 outBuffer.append(' '); 618 break; 619 case '\t':outBuffer.append('\\'); outBuffer.append('t'); 620 break; 621 case '\n':outBuffer.append('\\'); outBuffer.append('n'); 622 break; 623 case '\r':outBuffer.append('\\'); outBuffer.append('r'); 624 break; 625 case '\f':outBuffer.append('\\'); outBuffer.append('f'); 626 break; 627 case '=': // Fall through 628 case ':': // Fall through 629 case '#': // Fall through 630 case '!': 631 outBuffer.append('\\'); outBuffer.append(aChar); 632 break; 633 default: 634 if (((aChar < 0x0020) || (aChar > 0x007e)) & escapeUnicode ) { 635 outBuffer.append('\\'); 636 outBuffer.append('u'); 637 outBuffer.append(toHex((aChar >> 12) & 0xF)); 638 outBuffer.append(toHex((aChar >> 8) & 0xF)); 639 outBuffer.append(toHex((aChar >> 4) & 0xF)); 640 outBuffer.append(toHex( aChar & 0xF)); 641 } else { 642 outBuffer.append(aChar); 643 } 644 } 645 } 646 return outBuffer.toString(); 647 } 648 649 private static void writeComments(BufferedWriter bw, String comments) 650 throws IOException { 651 bw.write("#"); 652 int len = comments.length(); 653 int current = 0; 654 int last = 0; 655 char[] uu = new char[6]; 656 uu[0] = '\\'; 657 uu[1] = 'u'; 658 while (current < len) { 659 char c = comments.charAt(current); 660 if (c > '\u00ff' || c == '\n' || c == '\r') { 661 if (last != current) 662 bw.write(comments.substring(last, current)); 663 if (c > '\u00ff') { 664 uu[2] = toHex((c >> 12) & 0xf); 665 uu[3] = toHex((c >> 8) & 0xf); 666 uu[4] = toHex((c >> 4) & 0xf); 667 uu[5] = toHex( c & 0xf); 668 bw.write(new String(uu)); 669 } else { 670 bw.newLine(); 671 if (c == '\r' && 672 current != len - 1 && 673 comments.charAt(current + 1) == '\n') { 674 current++; 675 } 676 if (current == len - 1 || 677 (comments.charAt(current + 1) != '#' && 678 comments.charAt(current + 1) != '!')) 679 bw.write("#"); 680 } 681 last = current + 1; 682 } 683 current++; 684 } 685 if (last != current) 686 bw.write(comments.substring(last, current)); 687 bw.newLine(); 688 } 689 690 /** 691 * Calls the <code>store(OutputStream out, String comments)</code> method 692 * and suppresses IOExceptions that were thrown. 693 * 694 * @deprecated This method does not throw an IOException if an I/O error 695 * occurs while saving the property list. The preferred way to save a 696 * properties list is via the <code>store(OutputStream out, 697 * String comments)</code> method or the 698 * <code>storeToXML(OutputStream os, String comment)</code> method. 699 * 700 * @param out an output stream. 701 * @param comments a description of the property list. 702 * @exception ClassCastException if this <code>Properties</code> object 703 * contains any keys or values that are not 704 * <code>Strings</code>. 705 */ 706 @Deprecated 707 public void save(OutputStream out, String comments) { 708 try { 709 store(out, comments); 710 } catch (IOException e) { 711 } 712 } 713 714 /** 715 * Writes this property list (key and element pairs) in this 716 * <code>Properties</code> table to the output character stream in a 717 * format suitable for using the {@link #load(java.io.Reader) load(Reader)} 718 * method. 719 * <p> 720 * Properties from the defaults table of this <code>Properties</code> 721 * table (if any) are <i>not</i> written out by this method. 722 * <p> 723 * If the comments argument is not null, then an ASCII <code>#</code> 724 * character, the comments string, and a line separator are first written 725 * to the output stream. Thus, the <code>comments</code> can serve as an 726 * identifying comment. Any one of a line feed ('\n'), a carriage 727 * return ('\r'), or a carriage return followed immediately by a line feed 728 * in comments is replaced by a line separator generated by the <code>Writer</code> 729 * and if the next character in comments is not character <code>#</code> or 730 * character <code>!</code> then an ASCII <code>#</code> is written out 731 * after that line separator. 732 * <p> 733 * Next, a comment line is always written, consisting of an ASCII 734 * <code>#</code> character, the current date and time (as if produced 735 * by the <code>toString</code> method of <code>Date</code> for the 736 * current time), and a line separator as generated by the <code>Writer</code>. 737 * <p> 738 * Then every entry in this <code>Properties</code> table is 739 * written out, one per line. For each entry the key string is 740 * written, then an ASCII <code>=</code>, then the associated 741 * element string. For the key, all space characters are 742 * written with a preceding <code>\</code> character. For the 743 * element, leading space characters, but not embedded or trailing 744 * space characters, are written with a preceding <code>\</code> 745 * character. The key and element characters <code>#</code>, 746 * <code>!</code>, <code>=</code>, and <code>:</code> are written 747 * with a preceding backslash to ensure that they are properly loaded. 748 * <p> 749 * After the entries have been written, the output stream is flushed. 750 * The output stream remains open after this method returns. 751 * <p> 752 * 753 * @param writer an output character stream writer. 754 * @param comments a description of the property list. 755 * @exception IOException if writing this property list to the specified 756 * output stream throws an <tt>IOException</tt>. 757 * @exception ClassCastException if this <code>Properties</code> object 758 * contains any keys or values that are not <code>Strings</code>. 759 * @exception NullPointerException if <code>writer</code> is null. 760 * @since 1.6 761 */ 762 public void store(Writer writer, String comments) 763 throws IOException 764 { 765 store0((writer instanceof BufferedWriter)?(BufferedWriter)writer 766 : new BufferedWriter(writer), 767 comments, 768 false); 769 } 770 771 /** 772 * Writes this property list (key and element pairs) in this 773 * <code>Properties</code> table to the output stream in a format suitable 774 * for loading into a <code>Properties</code> table using the 775 * {@link #load(InputStream) load(InputStream)} method. 776 * <p> 777 * Properties from the defaults table of this <code>Properties</code> 778 * table (if any) are <i>not</i> written out by this method. 779 * <p> 780 * This method outputs the comments, properties keys and values in 781 * the same format as specified in 782 * {@link #store(java.io.Writer, java.lang.String) store(Writer)}, 783 * with the following differences: 784 * <ul> 785 * <li>The stream is written using the ISO 8859-1 character encoding. 786 * 787 * <li>Characters not in Latin-1 in the comments are written as 788 * <code>\u</code><i>xxxx</i> for their appropriate unicode 789 * hexadecimal value <i>xxxx</i>. 790 * 791 * <li>Characters less than <code>\u0020</code> and characters greater 792 * than <code>\u007E</code> in property keys or values are written 793 * as <code>\u</code><i>xxxx</i> for the appropriate hexadecimal 794 * value <i>xxxx</i>. 795 * </ul> 796 * <p> 797 * After the entries have been written, the output stream is flushed. 798 * The output stream remains open after this method returns. 799 * <p> 800 * @param out an output stream. 801 * @param comments a description of the property list. 802 * @exception IOException if writing this property list to the specified 803 * output stream throws an <tt>IOException</tt>. 804 * @exception ClassCastException if this <code>Properties</code> object 805 * contains any keys or values that are not <code>Strings</code>. 806 * @exception NullPointerException if <code>out</code> is null. 807 * @since 1.2 808 */ 809 public void store(OutputStream out, String comments) 810 throws IOException 811 { 812 store0(new BufferedWriter(new OutputStreamWriter(out, "8859_1")), 813 comments, 814 true); 815 } 816 817 private void store0(BufferedWriter bw, String comments, boolean escUnicode) 818 throws IOException 819 { 820 if (comments != null) { 821 writeComments(bw, comments); 822 } 823 bw.write("#" + new Date().toString()); 824 bw.newLine(); 825 synchronized (this) { 826 for (Enumeration e = keys(); e.hasMoreElements();) { 827 String key = (String)e.nextElement(); 828 String val = (String)get(key); 829 key = saveConvert(key, true, escUnicode); 830 /* No need to escape embedded and trailing spaces for value, hence 831 * pass false to flag. 832 */ 833 val = saveConvert(val, false, escUnicode); 834 bw.write(key + "=" + val); 835 bw.newLine(); 836 } 837 } 838 bw.flush(); 839 } 840 841 /** 842 * Loads all of the properties represented by the XML document on the 843 * specified input stream into this properties table. 844 * 845 * <p>The XML document must have the following DOCTYPE declaration: 846 * <pre> 847 * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> 848 * </pre> 849 * Furthermore, the document must satisfy the properties DTD described 850 * above. 851 * 852 * <p>The specified stream is closed after this method returns. 853 * 854 * @param in the input stream from which to read the XML document. 855 * @throws IOException if reading from the specified input stream 856 * results in an <tt>IOException</tt>. 857 * @throws InvalidPropertiesFormatException Data on input stream does not 858 * constitute a valid XML document with the mandated document type. 859 * @throws NullPointerException if <code>in</code> is null. 860 * @see #storeToXML(OutputStream, String, String) 861 * @since 1.5 862 */ 863 public synchronized void loadFromXML(InputStream in) 864 throws IOException, InvalidPropertiesFormatException 865 { 866 if (in == null) 867 throw new NullPointerException(); 868 XMLUtils.load(this, in); 869 in.close(); 870 } 871 872 /** 873 * Emits an XML document representing all of the properties contained 874 * in this table. 875 * 876 * <p> An invocation of this method of the form <tt>props.storeToXML(os, 877 * comment)</tt> behaves in exactly the same way as the invocation 878 * <tt>props.storeToXML(os, comment, "UTF-8");</tt>. 879 * 880 * @param os the output stream on which to emit the XML document. 881 * @param comment a description of the property list, or <code>null</code> 882 * if no comment is desired. 883 * @throws IOException if writing to the specified output stream 884 * results in an <tt>IOException</tt>. 885 * @throws NullPointerException if <code>os</code> is null. 886 * @throws ClassCastException if this <code>Properties</code> object 887 * contains any keys or values that are not 888 * <code>Strings</code>. 889 * @see #loadFromXML(InputStream) 890 * @since 1.5 891 */ 892 public void storeToXML(OutputStream os, String comment) 893 throws IOException 894 { 895 if (os == null) 896 throw new NullPointerException(); 897 storeToXML(os, comment, "UTF-8"); 898 } 899 900 /** 901 * Emits an XML document representing all of the properties contained 902 * in this table, using the specified encoding. 903 * 904 * <p>The XML document will have the following DOCTYPE declaration: 905 * <pre> 906 * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> 907 * </pre> 908 * 909 *<p>If the specified comment is <code>null</code> then no comment 910 * will be stored in the document. 911 * 912 * <p>The specified stream remains open after this method returns. 913 * 914 * @param os the output stream on which to emit the XML document. 915 * @param comment a description of the property list, or <code>null</code> 916 * if no comment is desired. 917 * @param encoding the name of a supported 918 * <a href="../lang/package-summary.html#charenc"> 919 * character encoding</a> 920 * 921 * @throws IOException if writing to the specified output stream 922 * results in an <tt>IOException</tt>. 923 * @throws NullPointerException if <code>os</code> is <code>null</code>, 924 * or if <code>encoding</code> is <code>null</code>. 925 * @throws ClassCastException if this <code>Properties</code> object 926 * contains any keys or values that are not 927 * <code>Strings</code>. 928 * @see #loadFromXML(InputStream) 929 * @since 1.5 930 */ 931 public void storeToXML(OutputStream os, String comment, String encoding) 932 throws IOException 933 { 934 if (os == null) 935 throw new NullPointerException(); 936 XMLUtils.save(this, os, comment, encoding); 937 } 938 939 /** 940 * Searches for the property with the specified key in this property list. 941 * If the key is not found in this property list, the default property list, 942 * and its defaults, recursively, are then checked. The method returns 943 * <code>null</code> if the property is not found. 944 * 945 * @param key the property key. 946 * @return the value in this property list with the specified key value. 947 * @see #setProperty 948 * @see #defaults 949 */ 950 public String getProperty(String key) { 951 Object oval = super.get(key); 952 String sval = (oval instanceof String) ? (String)oval : null; 953 return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval; 954 } 955 956 /** 957 * Searches for the property with the specified key in this property list. 958 * If the key is not found in this property list, the default property list, 959 * and its defaults, recursively, are then checked. The method returns the 960 * default value argument if the property is not found. 961 * 962 * @param key the hashtable key. 963 * @param defaultValue a default value. 964 * 965 * @return the value in this property list with the specified key value. 966 * @see #setProperty 967 * @see #defaults 968 */ 969 public String getProperty(String key, String defaultValue) { 970 String val = getProperty(key); 971 return (val == null) ? defaultValue : val; 972 } 973 974 /** 975 * Returns an enumeration of all the keys in this property list, 976 * including distinct keys in the default property list if a key 977 * of the same name has not already been found from the main 978 * properties list. 979 * 980 * @return an enumeration of all the keys in this property list, including 981 * the keys in the default property list. 982 * @throws ClassCastException if any key in this property list 983 * is not a string. 984 * @see java.util.Enumeration 985 * @see java.util.Properties#defaults 986 * @see #stringPropertyNames 987 */ 988 public Enumeration<?> propertyNames() { 989 Hashtable h = new Hashtable(); 990 enumerate(h); 991 return h.keys(); 992 } 993 994 /** 995 * Returns a set of keys in this property list where 996 * the key and its corresponding value are strings, 997 * including distinct keys in the default property list if a key 998 * of the same name has not already been found from the main 999 * properties list. Properties whose key or value is not 1000 * of type <tt>String</tt> are omitted. 1001 * <p> 1002 * The returned set is not backed by the <tt>Properties</tt> object. 1003 * Changes to this <tt>Properties</tt> are not reflected in the set, 1004 * or vice versa. 1005 * 1006 * @return a set of keys in this property list where 1007 * the key and its corresponding value are strings, 1008 * including the keys in the default property list. 1009 * @see java.util.Properties#defaults 1010 * @since 1.6 1011 */ 1012 public Set<String> stringPropertyNames() { 1013 Hashtable<String, String> h = new Hashtable<>(); 1014 enumerateStringProperties(h); 1015 return h.keySet(); 1016 } 1017 1018 /** 1019 * Prints this property list out to the specified output stream. 1020 * This method is useful for debugging. 1021 * 1022 * @param out an output stream. 1023 * @throws ClassCastException if any key in this property list 1024 * is not a string. 1025 */ 1026 public void list(PrintStream out) { 1027 out.println("-- listing properties --"); 1028 Hashtable h = new Hashtable(); 1029 enumerate(h); 1030 for (Enumeration e = h.keys() ; e.hasMoreElements() ;) { 1031 String key = (String)e.nextElement(); 1032 String val = (String)h.get(key); 1033 if (val.length() > 40) { 1034 val = val.substring(0, 37) + "..."; 1035 } 1036 out.println(key + "=" + val); 1037 } 1038 } 1039 1040 /** 1041 * Prints this property list out to the specified output stream. 1042 * This method is useful for debugging. 1043 * 1044 * @param out an output stream. 1045 * @throws ClassCastException if any key in this property list 1046 * is not a string. 1047 * @since JDK1.1 1048 */ 1049 /* 1050 * Rather than use an anonymous inner class to share common code, this 1051 * method is duplicated in order to ensure that a non-1.1 compiler can 1052 * compile this file. 1053 */ 1054 public void list(PrintWriter out) { 1055 out.println("-- listing properties --"); 1056 Hashtable h = new Hashtable(); 1057 enumerate(h); 1058 for (Enumeration e = h.keys() ; e.hasMoreElements() ;) { 1059 String key = (String)e.nextElement(); 1060 String val = (String)h.get(key); 1061 if (val.length() > 40) { 1062 val = val.substring(0, 37) + "..."; 1063 } 1064 out.println(key + "=" + val); 1065 } 1066 } 1067 1068 /** 1069 * Enumerates all key/value pairs in the specified hashtable. 1070 * @param h the hashtable 1071 * @throws ClassCastException if any of the property keys 1072 * is not of String type. 1073 */ 1074 private synchronized void enumerate(Hashtable h) { 1075 if (defaults != null) { 1076 defaults.enumerate(h); 1077 } 1078 for (Enumeration e = keys() ; e.hasMoreElements() ;) { 1079 String key = (String)e.nextElement(); 1080 h.put(key, get(key)); 1081 } 1082 } 1083 1084 /** 1085 * Enumerates all key/value pairs in the specified hashtable 1086 * and omits the property if the key or value is not a string. 1087 * @param h the hashtable 1088 */ 1089 private synchronized void enumerateStringProperties(Hashtable<String, String> h) { 1090 if (defaults != null) { 1091 defaults.enumerateStringProperties(h); 1092 } 1093 for (Enumeration e = keys() ; e.hasMoreElements() ;) { 1094 Object k = e.nextElement(); 1095 Object v = get(k); 1096 if (k instanceof String && v instanceof String) { 1097 h.put((String) k, (String) v); 1098 } 1099 } 1100 } 1101 1102 /** 1103 * Convert a nibble to a hex character 1104 * @param nibble the nibble to convert. 1105 */ 1106 private static char toHex(int nibble) { 1107 return hexDigit[(nibble & 0xF)]; 1108 } 1109 1110 /** A table of hex digits */ 1111 private static final char[] hexDigit = { 1112 '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' 1113 }; 1114 }