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