1. /*
  2. * @(#)Properties.java 1.84 04/05/18
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.util;
  8. import java.io.IOException;
  9. import java.io.PrintStream;
  10. import java.io.PrintWriter;
  11. import java.io.InputStream;
  12. import java.io.OutputStream;
  13. import java.io.OutputStreamWriter;
  14. import java.io.BufferedWriter;
  15. /**
  16. * The <code>Properties</code> class represents a persistent set of
  17. * properties. The <code>Properties</code> can be saved to a stream
  18. * or loaded from a stream. Each key and its corresponding value in
  19. * the property list is a string.
  20. * <p>
  21. * A property list can contain another property list as its
  22. * "defaults"; this second property list is searched if
  23. * the property key is not found in the original property list.
  24. * <p>
  25. * Because <code>Properties</code> inherits from <code>Hashtable</code>, the
  26. * <code>put</code> and <code>putAll</code> methods can be applied to a
  27. * <code>Properties</code> object. Their use is strongly discouraged as they
  28. * allow the caller to insert entries whose keys or values are not
  29. * <code>Strings</code>. The <code>setProperty</code> method should be used
  30. * instead. If the <code>store</code> or <code>save</code> method is called
  31. * on a "compromised" <code>Properties</code> object that contains a
  32. * non-<code>String</code> key or value, the call will fail.
  33. * <p>
  34. * <a name="encoding"></a>
  35. * <p> The {@link #load load} and {@link #store store} methods load and store
  36. * properties in a simple line-oriented format specified below. This format
  37. * uses the ISO 8859-1 character encoding. Characters that cannot be directly
  38. * represented in this encoding can be written using
  39. * <a href="http://java.sun.com/docs/books/jls/html/3.doc.html#100850">Unicode escapes</a>
  40. * ; only a single 'u' character is allowed in an escape
  41. * sequence. The native2ascii tool can be used to convert property files to and
  42. * from other character encodings.
  43. * <p>
  44. * <p> The {@link #loadFromXML(InputStream)} and {@link
  45. * #storeToXML(OutputStream, String, String)} methods load and store properties
  46. * in a simple XML format. By default the UTF-8 character encoding is used,
  47. * however a specific encoding may be specified if required. An XML properties
  48. * document has the following DOCTYPE declaration:
  49. *
  50. * <pre>
  51. * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
  52. * </pre>
  53. * Note that the system URI (http://java.sun.com/dtd/properties.dtd) is
  54. * <i>not</i> accessed when exporting or importing properties; it merely
  55. * serves as a string to uniquely identify the DTD, which is:
  56. * <pre>
  57. * <?xml version="1.0" encoding="UTF-8"?>
  58. *
  59. * <!-- DTD for properties -->
  60. *
  61. * <!ELEMENT properties ( comment?, entry* ) >
  62. *
  63. * <!ATTLIST properties version CDATA #FIXED "1.0">
  64. *
  65. * <!ELEMENT comment (#PCDATA) >
  66. *
  67. * <!ELEMENT entry (#PCDATA) >
  68. *
  69. * <!ATTLIST entry key CDATA #REQUIRED>
  70. * </pre>
  71. *
  72. * @see <a href="../../../tooldocs/solaris/native2ascii.html">native2ascii tool for Solaris</a>
  73. * @see <a href="../../../tooldocs/windows/native2ascii.html">native2ascii tool for Windows</a>
  74. *
  75. * @author Arthur van Hoff
  76. * @author Michael McCloskey
  77. * @version 1.84, 05/18/04
  78. * @since JDK1.0
  79. */
  80. public
  81. class Properties extends Hashtable<Object,Object> {
  82. /**
  83. * use serialVersionUID from JDK 1.1.X for interoperability
  84. */
  85. private static final long serialVersionUID = 4112578634029874840L;
  86. /**
  87. * A property list that contains default values for any keys not
  88. * found in this property list.
  89. *
  90. * @serial
  91. */
  92. protected Properties defaults;
  93. /**
  94. * Creates an empty property list with no default values.
  95. */
  96. public Properties() {
  97. this(null);
  98. }
  99. /**
  100. * Creates an empty property list with the specified defaults.
  101. *
  102. * @param defaults the defaults.
  103. */
  104. public Properties(Properties defaults) {
  105. this.defaults = defaults;
  106. }
  107. /**
  108. * Calls the <tt>Hashtable</tt> method <code>put</code>. Provided for
  109. * parallelism with the <tt>getProperty</tt> method. Enforces use of
  110. * strings for property keys and values. The value returned is the
  111. * result of the <tt>Hashtable</tt> call to <code>put</code>.
  112. *
  113. * @param key the key to be placed into this property list.
  114. * @param value the value corresponding to <tt>key</tt>.
  115. * @return the previous value of the specified key in this property
  116. * list, or <code>null</code> if it did not have one.
  117. * @see #getProperty
  118. * @since 1.2
  119. */
  120. public synchronized Object setProperty(String key, String value) {
  121. return put(key, value);
  122. }
  123. /**
  124. * Reads a property list (key and element pairs) from the input
  125. * stream. The stream is assumed to be using the ISO 8859-1
  126. * character encoding; that is each byte is one Latin1 character.
  127. * Characters not in Latin1, and certain special characters, can
  128. * be represented in keys and elements using escape sequences
  129. * similar to those used for character and string literals (see <a
  130. * href="http://java.sun.com/docs/books/jls/second_edition/html/lexical.doc.html#100850">§3.3</a>
  131. * and <a
  132. * href="http://java.sun.com/docs/books/jls/second_edition/html/lexical.doc.html#101089">§3.10.6</a>
  133. * of the <i>Java Language Specification</i>).
  134. *
  135. * The differences from the character escape sequences used for
  136. * characters and strings are:
  137. *
  138. * <ul>
  139. * <li> Octal escapes are not recognized.
  140. *
  141. * <li> The character sequence <code>\b</code> does <i>not</i>
  142. * represent a backspace character.
  143. *
  144. * <li> The method does not treat a backslash character,
  145. * <code>\</code>, before a non-valid escape character as an
  146. * error; the backslash is silently dropped. For example, in a
  147. * Java string the sequence <code>"\z"</code> would cause a
  148. * compile time error. In contrast, this method silently drops
  149. * the backslash. Therefore, this method treats the two character
  150. * sequence <code>"\b"</code> as equivalent to the single
  151. * character <code>'b'</code>.
  152. *
  153. * <li> Escapes are not necessary for single and double quotes;
  154. * however, by the rule above, single and double quote characters
  155. * preceded by a backslash still yield single and double quote
  156. * characters, respectively.
  157. *
  158. * </ul>
  159. *
  160. * An <code>IllegalArgumentException</code> is thrown if a
  161. * malformed Unicode escape appears in the input.
  162. *
  163. * <p>
  164. * This method processes input in terms of lines. A natural line
  165. * of input is terminated either by a set of line terminator
  166. * characters (<code>\n</code> or <code>\r</code> or
  167. * <code>\r\n</code>) or by the end of the file. A natural line
  168. * may be either a blank line, a comment line, or hold some part
  169. * of a key-element pair. The logical line holding all the data
  170. * for a key-element pair may be spread out across several adjacent
  171. * natural lines by escaping the line terminator sequence with a
  172. * backslash character, <code>\</code>. Note that a comment line
  173. * cannot be extended in this manner; every natural line that is a
  174. * comment must have its own comment indicator, as described
  175. * below. If a logical line is continued over several natural
  176. * lines, the continuation lines receive further processing, also
  177. * described below. Lines are read from the input stream until
  178. * end of file is reached.
  179. *
  180. * <p>
  181. * A natural line that contains only white space characters is
  182. * considered blank and is ignored. A comment line has an ASCII
  183. * <code>'#'</code> or <code>'!'</code> as its first non-white
  184. * space character; comment lines are also ignored and do not
  185. * encode key-element information. In addition to line
  186. * terminators, this method considers the characters space
  187. * (<code>' '</code>, <code>'\u0020'</code>), tab
  188. * (<code>'\t'</code>, <code>'\u0009'</code>), and form feed
  189. * (<code>'\f'</code>, <code>'\u000C'</code>) to be white
  190. * space.
  191. *
  192. * <p>
  193. * If a logical line is spread across several natural lines, the
  194. * backslash escaping the line terminator sequence, the line
  195. * terminator sequence, and any white space at the start the
  196. * following line have no affect on the key or element values.
  197. * The remainder of the discussion of key and element parsing will
  198. * assume all the characters constituting the key and element
  199. * appear on a single natural line after line continuation
  200. * characters have been removed. Note that it is <i>not</i>
  201. * sufficient to only examine the character preceding a line
  202. * terminator sequence to see if the line terminator is
  203. * escaped; there must be an odd number of contiguous backslashes
  204. * for the line terminator to be escaped. Since the input is
  205. * processed from left to right, a non-zero even number of
  206. * 2<i>n</i> contiguous backslashes before a line terminator (or
  207. * elsewhere) encodes <i>n</i> backslashes after escape
  208. * processing.
  209. *
  210. * <p>
  211. * The key contains all of the characters in the line starting
  212. * with the first non-white space character and up to, but not
  213. * including, the first unescaped <code>'='</code>,
  214. * <code>':'</code>, or white space character other than a line
  215. * terminator. All of these key termination characters may be
  216. * included in the key by escaping them with a preceding backslash
  217. * character; for example,<p>
  218. *
  219. * <code>\:\=</code><p>
  220. *
  221. * would be the two-character key <code>":="</code>. Line
  222. * terminator characters can be included using <code>\r</code> and
  223. * <code>\n</code> escape sequences. Any white space after the
  224. * key is skipped; if the first non-white space character after
  225. * the key is <code>'='</code> or <code>':'</code>, then it is
  226. * ignored and any white space characters after it are also
  227. * skipped. All remaining characters on the line become part of
  228. * the associated element string; if there are no remaining
  229. * characters, the element is the empty string
  230. * <code>""</code>. Once the raw character sequences
  231. * constituting the key and element are identified, escape
  232. * processing is performed as described above.
  233. *
  234. * <p>
  235. * As an example, each of the following three lines specifies the key
  236. * <code>"Truth"</code> and the associated element value
  237. * <code>"Beauty"</code>:
  238. * <p>
  239. * <pre>
  240. * Truth = Beauty
  241. * Truth:Beauty
  242. * Truth :Beauty
  243. * </pre>
  244. * As another example, the following three lines specify a single
  245. * property:
  246. * <p>
  247. * <pre>
  248. * fruits apple, banana, pear, \
  249. * cantaloupe, watermelon, \
  250. * kiwi, mango
  251. * </pre>
  252. * The key is <code>"fruits"</code> and the associated element is:
  253. * <p>
  254. * <pre>"apple, banana, pear, cantaloupe, watermelon, kiwi, mango"</pre>
  255. * Note that a space appears before each <code>\</code> so that a space
  256. * will appear after each comma in the final result; the <code>\</code>,
  257. * line terminator, and leading white space on the continuation line are
  258. * merely discarded and are <i>not</i> replaced by one or more other
  259. * characters.
  260. * <p>
  261. * As a third example, the line:
  262. * <p>
  263. * <pre>cheeses
  264. * </pre>
  265. * specifies that the key is <code>"cheeses"</code> and the associated
  266. * element is the empty string <code>""</code>.<p>
  267. *
  268. * @param inStream the input stream.
  269. * @exception IOException if an error occurred when reading from the
  270. * input stream.
  271. * @throws IllegalArgumentException if the input stream contains a
  272. * malformed Unicode escape sequence.
  273. */
  274. public synchronized void load(InputStream inStream) throws IOException {
  275. char[] convtBuf = new char[1024];
  276. LineReader lr = new LineReader(inStream);
  277. int limit;
  278. int keyLen;
  279. int valueStart;
  280. char c;
  281. boolean hasSep;
  282. boolean precedingBackslash;
  283. while ((limit = lr.readLine()) >= 0) {
  284. c = 0;
  285. keyLen = 0;
  286. valueStart = limit;
  287. hasSep = false;
  288. //System.out.println("line=<" + new String(lineBuf, 0, limit) + ">");
  289. precedingBackslash = false;
  290. while (keyLen < limit) {
  291. c = lr.lineBuf[keyLen];
  292. //need check if escaped.
  293. if ((c == '=' || c == ':') && !precedingBackslash) {
  294. valueStart = keyLen + 1;
  295. hasSep = true;
  296. break;
  297. } else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) {
  298. valueStart = keyLen + 1;
  299. break;
  300. }
  301. if (c == '\\') {
  302. precedingBackslash = !precedingBackslash;
  303. } else {
  304. precedingBackslash = false;
  305. }
  306. keyLen++;
  307. }
  308. while (valueStart < limit) {
  309. c = lr.lineBuf[valueStart];
  310. if (c != ' ' && c != '\t' && c != '\f') {
  311. if (!hasSep && (c == '=' || c == ':')) {
  312. hasSep = true;
  313. } else {
  314. break;
  315. }
  316. }
  317. valueStart++;
  318. }
  319. String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
  320. String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
  321. put(key, value);
  322. }
  323. }
  324. /* read in a "logical line" from input stream, skip all comment and
  325. * blank lines and filter out those leading whitespace characters
  326. * (\u0020, \u0009 and \u000c) from the beginning of a "natural line".
  327. * Method returns the char length of the "logical line" and stores
  328. * the line in "lineBuf".
  329. */
  330. class LineReader {
  331. public LineReader(InputStream inStream) {
  332. this.inStream = inStream;
  333. }
  334. byte[] inBuf = new byte[8192];
  335. char[] lineBuf = new char[1024];
  336. int inLimit = 0;
  337. int inOff = 0;
  338. InputStream inStream;
  339. int readLine() throws IOException {
  340. int len = 0;
  341. char c = 0;
  342. boolean skipWhiteSpace = true;
  343. boolean isCommentLine = false;
  344. boolean isNewLine = true;
  345. boolean appendedLineBegin = false;
  346. boolean precedingBackslash = false;
  347. boolean skipLF = false;
  348. while (true) {
  349. if (inOff >= inLimit) {
  350. inLimit = inStream.read(inBuf);
  351. inOff = 0;
  352. if (inLimit <= 0) {
  353. if (len == 0 || isCommentLine) {
  354. return -1;
  355. }
  356. return len;
  357. }
  358. }
  359. //The line below is equivalent to calling a
  360. //ISO8859-1 decoder.
  361. c = (char) (0xff & inBuf[inOff++]);
  362. if (skipLF) {
  363. skipLF = false;
  364. if (c == '\n') {
  365. continue;
  366. }
  367. }
  368. if (skipWhiteSpace) {
  369. if (c == ' ' || c == '\t' || c == '\f') {
  370. continue;
  371. }
  372. if (!appendedLineBegin && (c == '\r' || c == '\n')) {
  373. continue;
  374. }
  375. skipWhiteSpace = false;
  376. appendedLineBegin = false;
  377. }
  378. if (isNewLine) {
  379. isNewLine = false;
  380. if (c == '#' || c == '!') {
  381. isCommentLine = true;
  382. continue;
  383. }
  384. }
  385. if (c != '\n' && c != '\r') {
  386. lineBuf[len++] = c;
  387. if (len == lineBuf.length) {
  388. int newLength = lineBuf.length * 2;
  389. if (newLength < 0) {
  390. newLength = Integer.MAX_VALUE;
  391. }
  392. char[] buf = new char[newLength];
  393. System.arraycopy(lineBuf, 0, buf, 0, lineBuf.length);
  394. lineBuf = buf;
  395. }
  396. //flip the preceding backslash flag
  397. if (c == '\\') {
  398. precedingBackslash = !precedingBackslash;
  399. } else {
  400. precedingBackslash = false;
  401. }
  402. }
  403. else {
  404. // reached EOL
  405. if (isCommentLine || len == 0) {
  406. isCommentLine = false;
  407. isNewLine = true;
  408. skipWhiteSpace = true;
  409. len = 0;
  410. continue;
  411. }
  412. if (inOff >= inLimit) {
  413. inLimit = inStream.read(inBuf);
  414. inOff = 0;
  415. if (inLimit <= 0) {
  416. return len;
  417. }
  418. }
  419. if (precedingBackslash) {
  420. len -= 1;
  421. //skip the leading whitespace characters in following line
  422. skipWhiteSpace = true;
  423. appendedLineBegin = true;
  424. precedingBackslash = false;
  425. if (c == '\r') {
  426. skipLF = true;
  427. }
  428. } else {
  429. return len;
  430. }
  431. }
  432. }
  433. }
  434. }
  435. /*
  436. * Converts encoded \uxxxx to unicode chars
  437. * and changes special saved chars to their original forms
  438. */
  439. private String loadConvert (char[] in, int off, int len, char[] convtBuf) {
  440. if (convtBuf.length < len) {
  441. int newLen = len * 2;
  442. if (newLen < 0) {
  443. newLen = Integer.MAX_VALUE;
  444. }
  445. convtBuf = new char[newLen];
  446. }
  447. char aChar;
  448. char[] out = convtBuf;
  449. int outLen = 0;
  450. int end = off + len;
  451. while (off < end) {
  452. aChar = in[off++];
  453. if (aChar == '\\') {
  454. aChar = in[off++];
  455. if(aChar == 'u') {
  456. // Read the xxxx
  457. int value=0;
  458. for (int i=0; i<4; i++) {
  459. aChar = in[off++];
  460. switch (aChar) {
  461. case '0': case '1': case '2': case '3': case '4':
  462. case '5': case '6': case '7': case '8': case '9':
  463. value = (value << 4) + aChar - '0';
  464. break;
  465. case 'a': case 'b': case 'c':
  466. case 'd': case 'e': case 'f':
  467. value = (value << 4) + 10 + aChar - 'a';
  468. break;
  469. case 'A': case 'B': case 'C':
  470. case 'D': case 'E': case 'F':
  471. value = (value << 4) + 10 + aChar - 'A';
  472. break;
  473. default:
  474. throw new IllegalArgumentException(
  475. "Malformed \\uxxxx encoding.");
  476. }
  477. }
  478. out[outLen++] = (char)value;
  479. } else {
  480. if (aChar == 't') aChar = '\t';
  481. else if (aChar == 'r') aChar = '\r';
  482. else if (aChar == 'n') aChar = '\n';
  483. else if (aChar == 'f') aChar = '\f';
  484. out[outLen++] = aChar;
  485. }
  486. } else {
  487. out[outLen++] = (char)aChar;
  488. }
  489. }
  490. return new String (out, 0, outLen);
  491. }
  492. /*
  493. * Converts unicodes to encoded \uxxxx and escapes
  494. * special characters with a preceding slash
  495. */
  496. private String saveConvert(String theString, boolean escapeSpace) {
  497. int len = theString.length();
  498. int bufLen = len * 2;
  499. if (bufLen < 0) {
  500. bufLen = Integer.MAX_VALUE;
  501. }
  502. StringBuffer outBuffer = new StringBuffer(bufLen);
  503. for(int x=0; x<len; x++) {
  504. char aChar = theString.charAt(x);
  505. // Handle common case first, selecting largest block that
  506. // avoids the specials below
  507. if ((aChar > 61) && (aChar < 127)) {
  508. if (aChar == '\\') {
  509. outBuffer.append('\\'); outBuffer.append('\\');
  510. continue;
  511. }
  512. outBuffer.append(aChar);
  513. continue;
  514. }
  515. switch(aChar) {
  516. case ' ':
  517. if (x == 0 || escapeSpace)
  518. outBuffer.append('\\');
  519. outBuffer.append(' ');
  520. break;
  521. case '\t':outBuffer.append('\\'); outBuffer.append('t');
  522. break;
  523. case '\n':outBuffer.append('\\'); outBuffer.append('n');
  524. break;
  525. case '\r':outBuffer.append('\\'); outBuffer.append('r');
  526. break;
  527. case '\f':outBuffer.append('\\'); outBuffer.append('f');
  528. break;
  529. case '=': // Fall through
  530. case ':': // Fall through
  531. case '#': // Fall through
  532. case '!':
  533. outBuffer.append('\\'); outBuffer.append(aChar);
  534. break;
  535. default:
  536. if ((aChar < 0x0020) || (aChar > 0x007e)) {
  537. outBuffer.append('\\');
  538. outBuffer.append('u');
  539. outBuffer.append(toHex((aChar >> 12) & 0xF));
  540. outBuffer.append(toHex((aChar >> 8) & 0xF));
  541. outBuffer.append(toHex((aChar >> 4) & 0xF));
  542. outBuffer.append(toHex( aChar & 0xF));
  543. } else {
  544. outBuffer.append(aChar);
  545. }
  546. }
  547. }
  548. return outBuffer.toString();
  549. }
  550. /**
  551. * Calls the <code>store(OutputStream out, String comments)</code> method
  552. * and suppresses IOExceptions that were thrown.
  553. *
  554. * @deprecated This method does not throw an IOException if an I/O error
  555. * occurs while saving the property list. The preferred way to save a
  556. * properties list is via the <code>store(OutputStream out,
  557. * String comments)</code> method or the
  558. * <code>storeToXML(OutputStream os, String comment)</code> method.
  559. *
  560. * @param out an output stream.
  561. * @param comments a description of the property list.
  562. * @exception ClassCastException if this <code>Properties</code> object
  563. * contains any keys or values that are not
  564. * <code>Strings</code>.
  565. */
  566. @Deprecated
  567. public synchronized void save(OutputStream out, String comments) {
  568. try {
  569. store(out, comments);
  570. } catch (IOException e) {
  571. }
  572. }
  573. /**
  574. * Writes this property list (key and element pairs) in this
  575. * <code>Properties</code> table to the output stream in a format suitable
  576. * for loading into a <code>Properties</code> table using the
  577. * {@link #load(InputStream) load} method.
  578. * The stream is written using the ISO 8859-1 character encoding.
  579. * <p>
  580. * Properties from the defaults table of this <code>Properties</code>
  581. * table (if any) are <i>not</i> written out by this method.
  582. * <p>
  583. * If the comments argument is not null, then an ASCII <code>#</code>
  584. * character, the comments string, and a line separator are first written
  585. * to the output stream. Thus, the <code>comments</code> can serve as an
  586. * identifying comment.
  587. * <p>
  588. * Next, a comment line is always written, consisting of an ASCII
  589. * <code>#</code> character, the current date and time (as if produced
  590. * by the <code>toString</code> method of <code>Date</code> for the
  591. * current time), and a line separator as generated by the Writer.
  592. * <p>
  593. * Then every entry in this <code>Properties</code> table is
  594. * written out, one per line. For each entry the key string is
  595. * written, then an ASCII <code>=</code>, then the associated
  596. * element string. Each character of the key and element strings
  597. * is examined to see whether it should be rendered as an escape
  598. * sequence. The ASCII characters <code>\</code>, tab, form feed,
  599. * newline, and carriage return are written as <code>\\</code>,
  600. * <code>\t</code>, <code>\f</code> <code>\n</code>, and
  601. * <code>\r</code>, respectively. Characters less than
  602. * <code>\u0020</code> and characters greater than
  603. * <code>\u007E</code> are written as
  604. * <code>\u</code><i>xxxx</i> for the appropriate hexadecimal
  605. * value <i>xxxx</i>. For the key, all space characters are
  606. * written with a preceding <code>\</code> character. For the
  607. * element, leading space characters, but not embedded or trailing
  608. * space characters, are written with a preceding <code>\</code>
  609. * character. The key and element characters <code>#</code>,
  610. * <code>!</code>, <code>=</code>, and <code>:</code> are written
  611. * with a preceding backslash to ensure that they are properly loaded.
  612. * <p>
  613. * After the entries have been written, the output stream is flushed. The
  614. * output stream remains open after this method returns.
  615. *
  616. * @param out an output stream.
  617. * @param comments a description of the property list.
  618. * @exception IOException if writing this property list to the specified
  619. * output stream throws an <tt>IOException</tt>.
  620. * @exception ClassCastException if this <code>Properties</code> object
  621. * contains any keys or values that are not <code>Strings</code>.
  622. * @exception NullPointerException if <code>out</code> is null.
  623. * @since 1.2
  624. */
  625. public synchronized void store(OutputStream out, String comments)
  626. throws IOException
  627. {
  628. BufferedWriter awriter;
  629. awriter = new BufferedWriter(new OutputStreamWriter(out, "8859_1"));
  630. if (comments != null)
  631. writeln(awriter, "#" + comments);
  632. writeln(awriter, "#" + new Date().toString());
  633. for (Enumeration e = keys(); e.hasMoreElements();) {
  634. String key = (String)e.nextElement();
  635. String val = (String)get(key);
  636. key = saveConvert(key, true);
  637. /* No need to escape embedded and trailing spaces for value, hence
  638. * pass false to flag.
  639. */
  640. val = saveConvert(val, false);
  641. writeln(awriter, key + "=" + val);
  642. }
  643. awriter.flush();
  644. }
  645. private static void writeln(BufferedWriter bw, String s) throws IOException {
  646. bw.write(s);
  647. bw.newLine();
  648. }
  649. /**
  650. * Loads all of the properties represented by the XML document on the
  651. * specified input stream into this properties table.
  652. *
  653. * <p>The XML document must have the following DOCTYPE declaration:
  654. * <pre>
  655. * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
  656. * </pre>
  657. * Furthermore, the document must satisfy the properties DTD described
  658. * above.
  659. *
  660. * <p>The specified stream remains open after this method returns.
  661. *
  662. * @param in the input stream from which to read the XML document.
  663. * @throws IOException if reading from the specified input stream
  664. * results in an <tt>IOException</tt>.
  665. * @throws InvalidPropertiesFormatException Data on input stream does not
  666. * constitute a valid XML document with the mandated document type.
  667. * @throws NullPointerException if <code>in</code> is null.
  668. * @see #storeToXML(OutputStream, String, String)
  669. * @since 1.5
  670. */
  671. public synchronized void loadFromXML(InputStream in)
  672. throws IOException, InvalidPropertiesFormatException
  673. {
  674. if (in == null)
  675. throw new NullPointerException();
  676. XMLUtils.load(this, in);
  677. }
  678. /**
  679. * Emits an XML document representing all of the properties contained
  680. * in this table.
  681. *
  682. * <p> An invocation of this method of the form <tt>props.storeToXML(os,
  683. * comment)</tt> behaves in exactly the same way as the invocation
  684. * <tt>props.storeToXML(os, comment, "UTF-8");</tt>.
  685. *
  686. * @param os the output stream on which to emit the XML document.
  687. * @param comment a description of the property list, or <code>null</code>
  688. * if no comment is desired.
  689. * @throws IOException if writing to the specified output stream
  690. * results in an <tt>IOException</tt>.
  691. * @throws NullPointerException if <code>os</code> is null.
  692. * @see #loadFromXML(InputStream)
  693. * @since 1.5
  694. */
  695. public synchronized void storeToXML(OutputStream os, String comment)
  696. throws IOException
  697. {
  698. if (os == null)
  699. throw new NullPointerException();
  700. storeToXML(os, comment, "UTF-8");
  701. }
  702. /**
  703. * Emits an XML document representing all of the properties contained
  704. * in this table, using the specified encoding.
  705. *
  706. * <p>The XML document will have the following DOCTYPE declaration:
  707. * <pre>
  708. * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
  709. * </pre>
  710. *
  711. *<p>If the specified comment is <code>null</code> then no comment
  712. * will be stored in the document.
  713. *
  714. * <p>The specified stream remains open after this method returns.
  715. *
  716. * @param os the output stream on which to emit the XML document.
  717. * @param comment a description of the property list, or <code>null</code>
  718. * if no comment is desired.
  719. * @throws IOException if writing to the specified output stream
  720. * results in an <tt>IOException</tt>.
  721. * @throws NullPointerException if <code>os</code> is <code>null</code>,
  722. * or if <code>encoding</code> is <code>null</code>.
  723. * @see #loadFromXML(InputStream)
  724. * @since 1.5
  725. */
  726. public synchronized void storeToXML(OutputStream os, String comment,
  727. String encoding)
  728. throws IOException
  729. {
  730. if (os == null)
  731. throw new NullPointerException();
  732. XMLUtils.save(this, os, comment, encoding);
  733. }
  734. /**
  735. * Searches for the property with the specified key in this property list.
  736. * If the key is not found in this property list, the default property list,
  737. * and its defaults, recursively, are then checked. The method returns
  738. * <code>null</code> if the property is not found.
  739. *
  740. * @param key the property key.
  741. * @return the value in this property list with the specified key value.
  742. * @see #setProperty
  743. * @see #defaults
  744. */
  745. public String getProperty(String key) {
  746. Object oval = super.get(key);
  747. String sval = (oval instanceof String) ? (String)oval : null;
  748. return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval;
  749. }
  750. /**
  751. * Searches for the property with the specified key in this property list.
  752. * If the key is not found in this property list, the default property list,
  753. * and its defaults, recursively, are then checked. The method returns the
  754. * default value argument if the property is not found.
  755. *
  756. * @param key the hashtable key.
  757. * @param defaultValue a default value.
  758. *
  759. * @return the value in this property list with the specified key value.
  760. * @see #setProperty
  761. * @see #defaults
  762. */
  763. public String getProperty(String key, String defaultValue) {
  764. String val = getProperty(key);
  765. return (val == null) ? defaultValue : val;
  766. }
  767. /**
  768. * Returns an enumeration of all the keys in this property list,
  769. * including distinct keys in the default property list if a key
  770. * of the same name has not already been found from the main
  771. * properties list.
  772. *
  773. * @return an enumeration of all the keys in this property list, including
  774. * the keys in the default property list.
  775. * @see java.util.Enumeration
  776. * @see java.util.Properties#defaults
  777. */
  778. public Enumeration<?> propertyNames() {
  779. Hashtable h = new Hashtable();
  780. enumerate(h);
  781. return h.keys();
  782. }
  783. /**
  784. * Prints this property list out to the specified output stream.
  785. * This method is useful for debugging.
  786. *
  787. * @param out an output stream.
  788. */
  789. public void list(PrintStream out) {
  790. out.println("-- listing properties --");
  791. Hashtable h = new Hashtable();
  792. enumerate(h);
  793. for (Enumeration e = h.keys() ; e.hasMoreElements() ;) {
  794. String key = (String)e.nextElement();
  795. String val = (String)h.get(key);
  796. if (val.length() > 40) {
  797. val = val.substring(0, 37) + "...";
  798. }
  799. out.println(key + "=" + val);
  800. }
  801. }
  802. /**
  803. * Prints this property list out to the specified output stream.
  804. * This method is useful for debugging.
  805. *
  806. * @param out an output stream.
  807. * @since JDK1.1
  808. */
  809. /*
  810. * Rather than use an anonymous inner class to share common code, this
  811. * method is duplicated in order to ensure that a non-1.1 compiler can
  812. * compile this file.
  813. */
  814. public void list(PrintWriter out) {
  815. out.println("-- listing properties --");
  816. Hashtable h = new Hashtable();
  817. enumerate(h);
  818. for (Enumeration e = h.keys() ; e.hasMoreElements() ;) {
  819. String key = (String)e.nextElement();
  820. String val = (String)h.get(key);
  821. if (val.length() > 40) {
  822. val = val.substring(0, 37) + "...";
  823. }
  824. out.println(key + "=" + val);
  825. }
  826. }
  827. /**
  828. * Enumerates all key/value pairs in the specified hashtable.
  829. * @param h the hashtable
  830. */
  831. private synchronized void enumerate(Hashtable h) {
  832. if (defaults != null) {
  833. defaults.enumerate(h);
  834. }
  835. for (Enumeration e = keys() ; e.hasMoreElements() ;) {
  836. String key = (String)e.nextElement();
  837. h.put(key, get(key));
  838. }
  839. }
  840. /**
  841. * Convert a nibble to a hex character
  842. * @param nibble the nibble to convert.
  843. */
  844. private static char toHex(int nibble) {
  845. return hexDigit[(nibble & 0xF)];
  846. }
  847. /** A table of hex digits */
  848. private static final char[] hexDigit = {
  849. '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
  850. };
  851. }