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