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