1. /*
  2. * @(#)Properties.java 1.60 00/02/02
  3. *
  4. * Copyright 1995-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package java.util;
  11. import java.io.IOException;
  12. import java.io.PrintStream;
  13. import java.io.PrintWriter;
  14. import java.io.InputStream;
  15. import java.io.InputStreamReader;
  16. import java.io.BufferedReader;
  17. import java.io.OutputStream;
  18. import java.io.OutputStreamWriter;
  19. import java.io.BufferedWriter;
  20. import java.util.Hashtable;
  21. /**
  22. * The <code>Properties</code> class represents a persistent set of
  23. * properties. The <code>Properties</code> can be saved to a stream
  24. * or loaded from a stream. Each key and its corresponding value in
  25. * the property list is a string.
  26. * <p>
  27. * A property list can contain another property list as its
  28. * "defaults"; this second property list is searched if
  29. * the property key is not found in the original property list.
  30. * <p>
  31. * Because <code>Properties</code> inherits from <code>Hashtable</code>, the
  32. * <code>put</code> and <code>putAll</code> methods can be applied to a
  33. * <code>Properties</code> object. Their use is strongly discouraged as they
  34. * allow the caller to insert entries whose keys or values are not
  35. * <code>Strings</code>. The <code>setProperty</code> method should be used
  36. * instead. If the <code>store</code> or <code>save</code> method is called
  37. * on a "compromised" <code>Properties</code> object that contains a
  38. * non-<code>String</code> key or value, the call will fail.
  39. * <p>
  40. * <a name="encoding"></a>
  41. * When saving properties to a stream or loading them from a stream, the
  42. * ISO 8859-1 character encoding is used. For characters that cannot be directly
  43. * represented in this encoding,
  44. * <a href="http://java.sun.com/docs/books/jls/html/3.doc.html#100850">Unicode escapes</a>
  45. * are used; however, only a single 'u' character is allowed in an escape sequence.
  46. * The native2ascii tool can be used to convert property files to and from
  47. * other character encodings.
  48. *
  49. * @see <a href="../../../tooldocs/solaris/native2ascii.html">native2ascii tool for Solaris</a>
  50. * @see <a href="../../../tooldocs/win32/native2ascii.html">native2ascii tool for Windows</a>
  51. *
  52. * @author Arthur van Hoff
  53. * @author Michael McCloskey
  54. * @version 1.60, 02/02/00
  55. * @since JDK1.0
  56. */
  57. public
  58. class Properties extends Hashtable {
  59. /**
  60. * use serialVersionUID from JDK 1.1.X for interoperability
  61. */
  62. private static final long serialVersionUID = 4112578634029874840L;
  63. /**
  64. * A property list that contains default values for any keys not
  65. * found in this property list.
  66. *
  67. * @serial
  68. */
  69. protected Properties defaults;
  70. /**
  71. * Creates an empty property list with no default values.
  72. */
  73. public Properties() {
  74. this(null);
  75. }
  76. /**
  77. * Creates an empty property list with the specified defaults.
  78. *
  79. * @param defaults the defaults.
  80. */
  81. public Properties(Properties defaults) {
  82. this.defaults = defaults;
  83. }
  84. /**
  85. * Calls the hashtable method <code>put</code>. Provided for
  86. * parallelism with the <tt>getProperty</tt> method. Enforces use of
  87. * strings for property keys and values.
  88. *
  89. * @param key the key to be placed into this property list.
  90. * @param value the value corresponding to <tt>key</tt>.
  91. * @see #getProperty
  92. * @since 1.2
  93. */
  94. public synchronized Object setProperty(String key, String value) {
  95. return put(key, value);
  96. }
  97. private static final String keyValueSeparators = "=: \t\r\n\f";
  98. private static final String strictKeyValueSeparators = "=:";
  99. private static final String specialSaveChars = "=: \t\r\n\f#!";
  100. private static final String whiteSpaceChars = " \t\r\n\f";
  101. /**
  102. * Reads a property list (key and element pairs) from the input stream.
  103. * The stream is assumed to be using the ISO 8859-1 character encoding.
  104. * <p>
  105. * Every property occupies one line of the input stream. Each line
  106. * is terminated by a line terminator (<code>\n</code> or <code>\r</code>
  107. * or <code>\r\n</code>). Lines from the input stream are processed until
  108. * end of file is reached on the input stream.
  109. * <p>
  110. * A line that contains only whitespace or whose first non-whitespace
  111. * character is an ASCII <code>#</code> or <code>!</code> is ignored
  112. * (thus, <code>#</code> or <code>!</code> indicate comment lines).
  113. * <p>
  114. * Every line other than a blank line or a comment line describes one
  115. * property to be added to the table (except that if a line ends with \,
  116. * then the following line, if it exists, is treated as a continuation
  117. * line, as described
  118. * below). The key consists of all the characters in the line starting
  119. * with the first non-whitespace character and up to, but not including,
  120. * the first ASCII <code>=</code>, <code>:</code>, or whitespace
  121. * character. All of the key termination characters may be included in
  122. * the key by preceding them with a \.
  123. * Any whitespace after the key is skipped; if the first non-whitespace
  124. * character after the key is <code>=</code> or <code>:</code>, then it
  125. * is ignored and any whitespace characters after it are also skipped.
  126. * All remaining characters on the line become part of the associated
  127. * element string. Within the element string, the ASCII
  128. * escape sequences <code>\t</code>, <code>\n</code>,
  129. * <code>\r</code>, <code>\\</code>, <code>\"</code>, <code>\'</code>,
  130. * <code>\ </code> (a backslash and a space), and
  131. * <code>\u</code><i>xxxx</i> are recognized and converted to single
  132. * characters. Moreover, if the last character on the line is
  133. * <code>\</code>, then the next line is treated as a continuation of the
  134. * current line; the <code>\</code> and line terminator are simply
  135. * discarded, and any leading whitespace characters on the continuation
  136. * line are also discarded and are not part of the element string.
  137. * <p>
  138. * As an example, each of the following four lines specifies the key
  139. * <code>"Truth"</code> and the associated element value
  140. * <code>"Beauty"</code>:
  141. * <p>
  142. * <pre>
  143. * Truth = Beauty
  144. * Truth:Beauty
  145. * Truth :Beauty
  146. * </pre>
  147. * As another example, the following three lines specify a single
  148. * property:
  149. * <p>
  150. * <pre>
  151. * fruits apple, banana, pear, \
  152. * cantaloupe, watermelon, \
  153. * kiwi, mango
  154. * </pre>
  155. * The key is <code>"fruits"</code> and the associated element is:
  156. * <p>
  157. * <pre>"apple, banana, pear, cantaloupe, watermelon,kiwi, mango"</pre>
  158. * Note that a space appears before each <code>\</code> so that a space
  159. * will appear after each comma in the final result; the <code>\</code>,
  160. * line terminator, and leading whitespace on the continuation line are
  161. * merely discarded and are <i>not</i> replaced by one or more other
  162. * characters.
  163. * <p>
  164. * As a third example, the line:
  165. * <p>
  166. * <pre>cheeses
  167. * </pre>
  168. * specifies that the key is <code>"cheeses"</code> and the associated
  169. * element is the empty string.<p>
  170. *
  171. * @param inStream the input stream.
  172. * @exception IOException if an error occurred when reading from the
  173. * input stream.
  174. */
  175. public synchronized void load(InputStream inStream) throws IOException {
  176. BufferedReader in = new BufferedReader(new InputStreamReader(inStream, "8859_1"));
  177. while (true) {
  178. // Get next line
  179. String line = in.readLine();
  180. if (line == null)
  181. return;
  182. if (line.length() > 0) {
  183. // Continue lines that end in slashes if they are not comments
  184. char firstChar = line.charAt(0);
  185. if ((firstChar != '#') && (firstChar != '!')) {
  186. while (continueLine(line)) {
  187. String nextLine = in.readLine();
  188. if(nextLine == null)
  189. nextLine = new String("");
  190. String loppedLine = line.substring(0, line.length()-1);
  191. // Advance beyond whitespace on new line
  192. int startIndex=0;
  193. for(startIndex=0; startIndex<nextLine.length(); startIndex++)
  194. if (whiteSpaceChars.indexOf(nextLine.charAt(startIndex)) == -1)
  195. break;
  196. nextLine = nextLine.substring(startIndex,nextLine.length());
  197. line = new String(loppedLine+nextLine);
  198. }
  199. // Find start of key
  200. int len = line.length();
  201. int keyStart;
  202. for(keyStart=0; keyStart<len; keyStart++) {
  203. if(whiteSpaceChars.indexOf(line.charAt(keyStart)) == -1)
  204. break;
  205. }
  206. // Blank lines are ignored
  207. if (keyStart == len)
  208. continue;
  209. // Find separation between key and value
  210. int separatorIndex;
  211. for(separatorIndex=keyStart; separatorIndex<len; separatorIndex++) {
  212. char currentChar = line.charAt(separatorIndex);
  213. if (currentChar == '\\')
  214. separatorIndex++;
  215. else if(keyValueSeparators.indexOf(currentChar) != -1)
  216. break;
  217. }
  218. // Skip over whitespace after key if any
  219. int valueIndex;
  220. for (valueIndex=separatorIndex; valueIndex<len; valueIndex++)
  221. if (whiteSpaceChars.indexOf(line.charAt(valueIndex)) == -1)
  222. break;
  223. // Skip over one non whitespace key value separators if any
  224. if (valueIndex < len)
  225. if (strictKeyValueSeparators.indexOf(line.charAt(valueIndex)) != -1)
  226. valueIndex++;
  227. // Skip over white space after other separators if any
  228. while (valueIndex < len) {
  229. if (whiteSpaceChars.indexOf(line.charAt(valueIndex)) == -1)
  230. break;
  231. valueIndex++;
  232. }
  233. String key = line.substring(keyStart, separatorIndex);
  234. String value = (separatorIndex < len) ? line.substring(valueIndex, len) : "";
  235. // Convert then store key and value
  236. key = loadConvert(key);
  237. value = loadConvert(value);
  238. put(key, value);
  239. }
  240. }
  241. }
  242. }
  243. /*
  244. * Returns true if the given line is a line that must
  245. * be appended to the next line
  246. */
  247. private boolean continueLine (String line) {
  248. int slashCount = 0;
  249. int index = line.length() - 1;
  250. while((index >= 0) && (line.charAt(index--) == '\\'))
  251. slashCount++;
  252. return (slashCount % 2 == 1);
  253. }
  254. /*
  255. * Converts encoded \uxxxx to unicode chars
  256. * and changes special saved chars to their original forms
  257. */
  258. private String loadConvert (String theString) {
  259. char aChar;
  260. int len = theString.length();
  261. StringBuffer outBuffer = new StringBuffer(len);
  262. for(int x=0; x<len; ) {
  263. aChar = theString.charAt(x++);
  264. if (aChar == '\\') {
  265. aChar = theString.charAt(x++);
  266. if(aChar == 'u') {
  267. // Read the xxxx
  268. int value=0;
  269. for (int i=0; i<4; i++) {
  270. aChar = theString.charAt(x++);
  271. switch (aChar) {
  272. case '0': case '1': case '2': case '3': case '4':
  273. case '5': case '6': case '7': case '8': case '9':
  274. value = (value << 4) + aChar - '0';
  275. break;
  276. case 'a': case 'b': case 'c':
  277. case 'd': case 'e': case 'f':
  278. value = (value << 4) + 10 + aChar - 'a';
  279. break;
  280. case 'A': case 'B': case 'C':
  281. case 'D': case 'E': case 'F':
  282. value = (value << 4) + 10 + aChar - 'A';
  283. break;
  284. default:
  285. throw new IllegalArgumentException(
  286. "Malformed \\uxxxx encoding.");
  287. }
  288. }
  289. outBuffer.append((char)value);
  290. } else {
  291. if (aChar == 't') aChar = '\t';
  292. else if (aChar == 'r') aChar = '\r';
  293. else if (aChar == 'n') aChar = '\n';
  294. else if (aChar == 'f') aChar = '\f';
  295. outBuffer.append(aChar);
  296. }
  297. } else
  298. outBuffer.append(aChar);
  299. }
  300. return outBuffer.toString();
  301. }
  302. /*
  303. * Converts unicodes to encoded \uxxxx
  304. * and writes out any of the characters in specialSaveChars
  305. * with a preceding slash
  306. */
  307. private String saveConvert(String theString, boolean escapeSpace) {
  308. int len = theString.length();
  309. StringBuffer outBuffer = new StringBuffer(len*2);
  310. for(int x=0; x<len; x++) {
  311. char aChar = theString.charAt(x);
  312. switch(aChar) {
  313. case ' ':
  314. if (x == 0 || escapeSpace)
  315. outBuffer.append('\\');
  316. outBuffer.append(' ');
  317. break;
  318. case '\\':outBuffer.append('\\'); outBuffer.append('\\');
  319. break;
  320. case '\t':outBuffer.append('\\'); outBuffer.append('t');
  321. break;
  322. case '\n':outBuffer.append('\\'); outBuffer.append('n');
  323. break;
  324. case '\r':outBuffer.append('\\'); outBuffer.append('r');
  325. break;
  326. case '\f':outBuffer.append('\\'); outBuffer.append('f');
  327. break;
  328. default:
  329. if ((aChar < 0x0020) || (aChar > 0x007e)) {
  330. outBuffer.append('\\');
  331. outBuffer.append('u');
  332. outBuffer.append(toHex((aChar >> 12) & 0xF));
  333. outBuffer.append(toHex((aChar >> 8) & 0xF));
  334. outBuffer.append(toHex((aChar >> 4) & 0xF));
  335. outBuffer.append(toHex( aChar & 0xF));
  336. } else {
  337. if (specialSaveChars.indexOf(aChar) != -1)
  338. outBuffer.append('\\');
  339. outBuffer.append(aChar);
  340. }
  341. }
  342. }
  343. return outBuffer.toString();
  344. }
  345. /**
  346. * Calls the <code>store(OutputStream out, String header)</code> method
  347. * and suppresses IOExceptions that were thrown.
  348. *
  349. * @deprecated This method does not throw an IOException if an I/O error
  350. * occurs while saving the property list. As of the Java 2 platform v1.2, the preferred
  351. * way to save a properties list is via the <code>store(OutputStream out,
  352. * String header)</code> method.
  353. *
  354. * @param out an output stream.
  355. * @param header a description of the property list.
  356. * @exception ClassCastException if this <code>Properties</code> object
  357. * contains any keys or values that are not <code>Strings</code>.
  358. */
  359. public synchronized void save(OutputStream out, String header) {
  360. try {
  361. store(out, header);
  362. } catch (IOException e) {
  363. }
  364. }
  365. /**
  366. * Writes this property list (key and element pairs) in this
  367. * <code>Properties</code> table to the output stream in a format suitable
  368. * for loading into a <code>Properties</code> table using the
  369. * <code>load</code> method.
  370. * The stream is written using the ISO 8859-1 character encoding.
  371. * <p>
  372. * Properties from the defaults table of this <code>Properties</code>
  373. * table (if any) are <i>not</i> written out by this method.
  374. * <p>
  375. * If the header argument is not null, then an ASCII <code>#</code>
  376. * character, the header string, and a line separator are first written
  377. * to the output stream. Thus, the <code>header</code> can serve as an
  378. * identifying comment.
  379. * <p>
  380. * Next, a comment line is always written, consisting of an ASCII
  381. * <code>#</code> character, the current date and time (as if produced
  382. * by the <code>toString</code> method of <code>Date</code> for the
  383. * current time), and a line separator as generated by the Writer.
  384. * <p>
  385. * Then every entry in this <code>Properties</code> table is written out,
  386. * one per line. For each entry the key string is written, then an ASCII
  387. * <code>=</code>, then the associated element string. Each character of
  388. * the element string is examined to see whether it should be rendered as
  389. * an escape sequence. The ASCII characters <code>\</code>, tab, newline,
  390. * and carriage return are written as <code>\\</code>, <code>\t</code>,
  391. * <code>\n</code>, and <code>\r</code>, respectively. Characters less
  392. * than <code>\u0020</code> and characters greater than
  393. * <code>\u007E</code> are written as <code>\u</code><i>xxxx</i> for
  394. * the appropriate hexadecimal value <i>xxxx</i>. Leading space characters,
  395. * but not embedded or trailing space characters, are written with a
  396. * preceding <code>\</code>. The key and value characters <code>#</code>,
  397. * <code>!</code>, <code>=</code>, and <code>:</code> are written with a
  398. * preceding slash to ensure that they are properly loaded.
  399. * <p>
  400. * After the entries have been written, the output stream is flushed. The
  401. * output stream remains open after this method returns.
  402. *
  403. * @param out an output stream.
  404. * @param header a description of the property list.
  405. * @exception IOException if writing this property list to the specified
  406. * output stream throws an <tt>IOException</tt>.
  407. * @exception ClassCastException if this <code>Properties</code> object
  408. * contains any keys or values that are not <code>Strings</code>.
  409. */
  410. public synchronized void store(OutputStream out, String header)
  411. throws IOException
  412. {
  413. BufferedWriter awriter;
  414. awriter = new BufferedWriter(new OutputStreamWriter(out, "8859_1"));
  415. if (header != null)
  416. writeln(awriter, "#" + header);
  417. writeln(awriter, "#" + new Date().toString());
  418. for (Enumeration e = keys(); e.hasMoreElements();) {
  419. String key = (String)e.nextElement();
  420. String val = (String)get(key);
  421. key = saveConvert(key, true);
  422. /* No need to escape embedded and trailing spaces for value, hence
  423. * pass false to flag.
  424. */
  425. val = saveConvert(val, false);
  426. writeln(awriter, key + "=" + val);
  427. }
  428. awriter.flush();
  429. }
  430. private static void writeln(BufferedWriter bw, String s) throws IOException {
  431. bw.write(s);
  432. bw.newLine();
  433. }
  434. /**
  435. * Searches for the property with the specified key in this property list.
  436. * If the key is not found in this property list, the default property list,
  437. * and its defaults, recursively, are then checked. The method returns
  438. * <code>null</code> if the property is not found.
  439. *
  440. * @param key the property key.
  441. * @return the value in this property list with the specified key value.
  442. * @see #setProperty
  443. * @see #defaults
  444. */
  445. public String getProperty(String key) {
  446. Object oval = super.get(key);
  447. String sval = (oval instanceof String) ? (String)oval : null;
  448. return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval;
  449. }
  450. /**
  451. * Searches for the property with the specified key in this property list.
  452. * If the key is not found in this property list, the default property list,
  453. * and its defaults, recursively, are then checked. The method returns the
  454. * default value argument if the property is not found.
  455. *
  456. * @param key the hashtable key.
  457. * @param defaultValue a default value.
  458. *
  459. * @return the value in this property list with the specified key value.
  460. * @see #setProperty
  461. * @see #defaults
  462. */
  463. public String getProperty(String key, String defaultValue) {
  464. String val = getProperty(key);
  465. return (val == null) ? defaultValue : val;
  466. }
  467. /**
  468. * Returns an enumeration of all the keys in this property list, including
  469. * the keys in the default property list.
  470. *
  471. * @return an enumeration of all the keys in this property list, including
  472. * the keys in the default property list.
  473. * @see java.util.Enumeration
  474. * @see java.util.Properties#defaults
  475. */
  476. public Enumeration propertyNames() {
  477. Hashtable h = new Hashtable();
  478. enumerate(h);
  479. return h.keys();
  480. }
  481. /**
  482. * Prints this property list out to the specified output stream.
  483. * This method is useful for debugging.
  484. *
  485. * @param out an output stream.
  486. */
  487. public void list(PrintStream out) {
  488. out.println("-- listing properties --");
  489. Hashtable h = new Hashtable();
  490. enumerate(h);
  491. for (Enumeration e = h.keys() ; e.hasMoreElements() ;) {
  492. String key = (String)e.nextElement();
  493. String val = (String)h.get(key);
  494. if (val.length() > 40) {
  495. val = val.substring(0, 37) + "...";
  496. }
  497. out.println(key + "=" + val);
  498. }
  499. }
  500. /**
  501. * Prints this property list out to the specified output stream.
  502. * This method is useful for debugging.
  503. *
  504. * @param out an output stream.
  505. * @since JDK1.1
  506. */
  507. /*
  508. * Rather than use an anonymous inner class to share common code, this
  509. * method is duplicated in order to ensure that a non-1.1 compiler can
  510. * compile this file.
  511. */
  512. public void list(PrintWriter out) {
  513. out.println("-- listing properties --");
  514. Hashtable h = new Hashtable();
  515. enumerate(h);
  516. for (Enumeration e = h.keys() ; e.hasMoreElements() ;) {
  517. String key = (String)e.nextElement();
  518. String val = (String)h.get(key);
  519. if (val.length() > 40) {
  520. val = val.substring(0, 37) + "...";
  521. }
  522. out.println(key + "=" + val);
  523. }
  524. }
  525. /**
  526. * Enumerates all key/value pairs in the specified hastable.
  527. * @param h the hashtable
  528. */
  529. private synchronized void enumerate(Hashtable h) {
  530. if (defaults != null) {
  531. defaults.enumerate(h);
  532. }
  533. for (Enumeration e = keys() ; e.hasMoreElements() ;) {
  534. String key = (String)e.nextElement();
  535. h.put(key, get(key));
  536. }
  537. }
  538. /**
  539. * Convert a nibble to a hex character
  540. * @param nibble the nibble to convert.
  541. */
  542. private static char toHex(int nibble) {
  543. return hexDigit[(nibble & 0xF)];
  544. }
  545. /** A table of hex digits */
  546. private static final char[] hexDigit = {
  547. '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
  548. };
  549. }