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