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