2 * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
28 import java.io.IOException;
29 import java.io.PrintStream;
30 import java.io.PrintWriter;
31 import java.io.InputStream;
32 import java.io.OutputStream;
33 import java.io.Reader;
34 import java.io.Writer;
35 import java.io.OutputStreamWriter;
36 import java.io.BufferedWriter;
39 * The <code>Properties</code> class represents a persistent set of
40 * properties. The <code>Properties</code> can be saved to a stream
41 * or loaded from a stream. Each key and its corresponding value in
42 * the property list is a string.
44 * A property list can contain another property list as its
45 * "defaults"; this second property list is searched if
46 * the property key is not found in the original property list.
48 * Because <code>Properties</code> inherits from <code>Hashtable</code>, the
49 * <code>put</code> and <code>putAll</code> methods can be applied to a
50 * <code>Properties</code> object. Their use is strongly discouraged as they
51 * allow the caller to insert entries whose keys or values are not
52 * <code>Strings</code>. The <code>setProperty</code> method should be used
53 * instead. If the <code>store</code> or <code>save</code> method is called
54 * on a "compromised" <code>Properties</code> object that contains a
55 * non-<code>String</code> key or value, the call will fail. Similarly,
56 * the call to the <code>propertyNames</code> or <code>list</code> method
57 * will fail if it is called on a "compromised" <code>Properties</code>
58 * object that contains a non-<code>String</code> key.
61 * The {@link #load(java.io.Reader) load(Reader)} <tt>/</tt>
62 * {@link #store(java.io.Writer, java.lang.String) store(Writer, String)}
63 * methods load and store properties from and to a character based stream
64 * in a simple line-oriented format specified below.
66 * The {@link #load(java.io.InputStream) load(InputStream)} <tt>/</tt>
67 * {@link #store(java.io.OutputStream, java.lang.String) store(OutputStream, String)}
68 * methods work the same way as the load(Reader)/store(Writer, String) pair, except
69 * the input/output stream is encoded in ISO 8859-1 character encoding.
70 * Characters that cannot be directly represented in this encoding can be written using
71 * Unicode escapes as defined in section 3.3 of
72 * <cite>The Java™ Language Specification</cite>;
73 * only a single 'u' character is allowed in an escape
74 * sequence. The native2ascii tool can be used to convert property files to and
75 * from other character encodings.
77 * <p> The {@link #loadFromXML(InputStream)} and {@link
78 * #storeToXML(OutputStream, String, String)} methods load and store properties
79 * in a simple XML format. By default the UTF-8 character encoding is used,
80 * however a specific encoding may be specified if required. An XML properties
81 * document has the following DOCTYPE declaration:
84 * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
86 * Note that the system URI (http://java.sun.com/dtd/properties.dtd) is
87 * <i>not</i> accessed when exporting or importing properties; it merely
88 * serves as a string to uniquely identify the DTD, which is:
90 * <?xml version="1.0" encoding="UTF-8"?>
92 * <!-- DTD for properties -->
94 * <!ELEMENT properties ( comment?, entry* ) >
96 * <!ATTLIST properties version CDATA #FIXED "1.0">
98 * <!ELEMENT comment (#PCDATA) >
100 * <!ELEMENT entry (#PCDATA) >
102 * <!ATTLIST entry key CDATA #REQUIRED>
105 * <p>This class is thread-safe: multiple threads can share a single
106 * <tt>Properties</tt> object without the need for external synchronization.
108 * @see <a href="../../../technotes/tools/solaris/native2ascii.html">native2ascii tool for Solaris</a>
109 * @see <a href="../../../technotes/tools/windows/native2ascii.html">native2ascii tool for Windows</a>
111 * @author Arthur van Hoff
112 * @author Michael McCloskey
113 * @author Xueming Shen
117 class Properties extends Hashtable<Object,Object> {
119 * use serialVersionUID from JDK 1.1.X for interoperability
121 private static final long serialVersionUID = 4112578634029874840L;
124 * A property list that contains default values for any keys not
125 * found in this property list.
129 protected Properties defaults;
132 * Creates an empty property list with no default values.
134 public Properties() {
139 * Creates an empty property list with the specified defaults.
141 * @param defaults the defaults.
143 public Properties(Properties defaults) {
144 this.defaults = defaults;
148 * Calls the <tt>Hashtable</tt> method <code>put</code>. Provided for
149 * parallelism with the <tt>getProperty</tt> method. Enforces use of
150 * strings for property keys and values. The value returned is the
151 * result of the <tt>Hashtable</tt> call to <code>put</code>.
153 * @param key the key to be placed into this property list.
154 * @param value the value corresponding to <tt>key</tt>.
155 * @return the previous value of the specified key in this property
156 * list, or <code>null</code> if it did not have one.
160 public synchronized Object setProperty(String key, String value) {
161 return put(key, value);
166 * Reads a property list (key and element pairs) from the input
167 * character stream in a simple line-oriented format.
169 * Properties are processed in terms of lines. There are two
170 * kinds of line, <i>natural lines</i> and <i>logical lines</i>.
171 * A natural line is defined as a line of
172 * characters that is terminated either by a set of line terminator
173 * characters (<code>\n</code> or <code>\r</code> or <code>\r\n</code>)
174 * or by the end of the stream. A natural line may be either a blank line,
175 * a comment line, or hold all or some of a key-element pair. A logical
176 * line holds all the data of a key-element pair, which may be spread
177 * out across several adjacent natural lines by escaping
178 * the line terminator sequence with a backslash character
179 * <code>\</code>. Note that a comment line cannot be extended
180 * in this manner; every natural line that is a comment must have
181 * its own comment indicator, as described below. Lines are read from
182 * input until the end of the stream is reached.
185 * A natural line that contains only white space characters is
186 * considered blank and is ignored. A comment line has an ASCII
187 * <code>'#'</code> or <code>'!'</code> as its first non-white
188 * space character; comment lines are also ignored and do not
189 * encode key-element information. In addition to line
190 * terminators, this format considers the characters space
191 * (<code>' '</code>, <code>'\u0020'</code>), tab
192 * (<code>'\t'</code>, <code>'\u0009'</code>), and form feed
193 * (<code>'\f'</code>, <code>'\u000C'</code>) to be white
197 * If a logical line is spread across several natural lines, the
198 * backslash escaping the line terminator sequence, the line
199 * terminator sequence, and any white space at the start of the
200 * following line have no affect on the key or element values.
201 * The remainder of the discussion of key and element parsing
202 * (when loading) will assume all the characters constituting
203 * the key and element appear on a single natural line after
204 * line continuation characters have been removed. Note that
205 * it is <i>not</i> sufficient to only examine the character
206 * preceding a line terminator sequence to decide if the line
207 * terminator is escaped; there must be an odd number of
208 * contiguous backslashes for the line terminator to be escaped.
209 * Since the input is processed from left to right, a
210 * non-zero even number of 2<i>n</i> contiguous backslashes
211 * before a line terminator (or elsewhere) encodes <i>n</i>
212 * backslashes after escape processing.
215 * The key contains all of the characters in the line starting
216 * with the first non-white space character and up to, but not
217 * including, the first unescaped <code>'='</code>,
218 * <code>':'</code>, or white space character other than a line
219 * terminator. All of these key termination characters may be
220 * included in the key by escaping them with a preceding backslash
221 * character; for example,<p>
223 * <code>\:\=</code><p>
225 * would be the two-character key <code>":="</code>. Line
226 * terminator characters can be included using <code>\r</code> and
227 * <code>\n</code> escape sequences. Any white space after the
228 * key is skipped; if the first non-white space character after
229 * the key is <code>'='</code> or <code>':'</code>, then it is
230 * ignored and any white space characters after it are also
231 * skipped. All remaining characters on the line become part of
232 * the associated element string; if there are no remaining
233 * characters, the element is the empty string
234 * <code>""</code>. Once the raw character sequences
235 * constituting the key and element are identified, escape
236 * processing is performed as described above.
239 * As an example, each of the following three lines specifies the key
240 * <code>"Truth"</code> and the associated element value
241 * <code>"Beauty"</code>:
248 * As another example, the following three lines specify a single
252 * fruits apple, banana, pear, \
253 * cantaloupe, watermelon, \
256 * The key is <code>"fruits"</code> and the associated element is:
258 * <pre>"apple, banana, pear, cantaloupe, watermelon, kiwi, mango"</pre>
259 * Note that a space appears before each <code>\</code> so that a space
260 * will appear after each comma in the final result; the <code>\</code>,
261 * line terminator, and leading white space on the continuation line are
262 * merely discarded and are <i>not</i> replaced by one or more other
265 * As a third example, the line:
269 * specifies that the key is <code>"cheeses"</code> and the associated
270 * element is the empty string <code>""</code>.<p>
273 * <a name="unicodeescapes"></a>
274 * Characters in keys and elements can be represented in escape
275 * sequences similar to those used for character and string literals
276 * (see sections 3.3 and 3.10.6 of
277 * <cite>The Java™ Language Specification</cite>).
279 * The differences from the character escape sequences and Unicode
280 * escapes used for characters and strings are:
283 * <li> Octal escapes are not recognized.
285 * <li> The character sequence <code>\b</code> does <i>not</i>
286 * represent a backspace character.
288 * <li> The method does not treat a backslash character,
289 * <code>\</code>, before a non-valid escape character as an
290 * error; the backslash is silently dropped. For example, in a
291 * Java string the sequence <code>"\z"</code> would cause a
292 * compile time error. In contrast, this method silently drops
293 * the backslash. Therefore, this method treats the two character
294 * sequence <code>"\b"</code> as equivalent to the single
295 * character <code>'b'</code>.
297 * <li> Escapes are not necessary for single and double quotes;
298 * however, by the rule above, single and double quote characters
299 * preceded by a backslash still yield single and double quote
300 * characters, respectively.
302 * <li> Only a single 'u' character is allowed in a Uniocde escape
307 * The specified stream remains open after this method returns.
309 * @param reader the input character stream.
310 * @throws IOException if an error occurred when reading from the
312 * @throws IllegalArgumentException if a malformed Unicode escape
313 * appears in the input.
316 public synchronized void load(Reader reader) throws IOException {
317 load0(new LineReader(reader));
321 * Reads a property list (key and element pairs) from the input
322 * byte stream. The input stream is in a simple line-oriented
323 * format as specified in
324 * {@link #load(java.io.Reader) load(Reader)} and is assumed to use
325 * the ISO 8859-1 character encoding; that is each byte is one Latin1
326 * character. Characters not in Latin1, and certain special characters,
327 * are represented in keys and elements using Unicode escapes as defined in
329 * <cite>The Java™ Language Specification</cite>.
331 * The specified stream remains open after this method returns.
333 * @param inStream the input stream.
334 * @exception IOException if an error occurred when reading from the
336 * @throws IllegalArgumentException if the input stream contains a
337 * malformed Unicode escape sequence.
340 public synchronized void load(InputStream inStream) throws IOException {
341 load0(new LineReader(inStream));
344 private void load0 (LineReader lr) throws IOException {
345 char[] convtBuf = new char[1024];
351 boolean precedingBackslash;
353 while ((limit = lr.readLine()) >= 0) {
359 //System.out.println("line=<" + new String(lineBuf, 0, limit) + ">");
360 precedingBackslash = false;
361 while (keyLen < limit) {
362 c = lr.lineBuf[keyLen];
363 //need check if escaped.
364 if ((c == '=' || c == ':') && !precedingBackslash) {
365 valueStart = keyLen + 1;
368 } else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) {
369 valueStart = keyLen + 1;
373 precedingBackslash = !precedingBackslash;
375 precedingBackslash = false;
379 while (valueStart < limit) {
380 c = lr.lineBuf[valueStart];
381 if (c != ' ' && c != '\t' && c != '\f') {
382 if (!hasSep && (c == '=' || c == ':')) {
390 String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
391 String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
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".
403 public LineReader(InputStream inStream) {
404 this.inStream = inStream;
405 inByteBuf = new byte[8192];
408 public LineReader(Reader reader) {
409 this.reader = reader;
410 inCharBuf = new char[8192];
415 char[] lineBuf = new char[1024];
418 InputStream inStream;
421 int readLine() throws IOException {
425 boolean skipWhiteSpace = true;
426 boolean isCommentLine = false;
427 boolean isNewLine = true;
428 boolean appendedLineBegin = false;
429 boolean precedingBackslash = false;
430 boolean skipLF = false;
433 if (inOff >= inLimit) {
434 inLimit = (inStream==null)?reader.read(inCharBuf)
435 :inStream.read(inByteBuf);
438 if (len == 0 || isCommentLine) {
444 if (inStream != null) {
445 //The line below is equivalent to calling a
447 c = (char) (0xff & inByteBuf[inOff++]);
449 c = inCharBuf[inOff++];
457 if (skipWhiteSpace) {
458 if (c == ' ' || c == '\t' || c == '\f') {
461 if (!appendedLineBegin && (c == '\r' || c == '\n')) {
464 skipWhiteSpace = false;
465 appendedLineBegin = false;
469 if (c == '#' || c == '!') {
470 isCommentLine = true;
475 if (c != '\n' && c != '\r') {
477 if (len == lineBuf.length) {
478 int newLength = lineBuf.length * 2;
480 newLength = Integer.MAX_VALUE;
482 char[] buf = new char[newLength];
483 System.arraycopy(lineBuf, 0, buf, 0, lineBuf.length);
486 //flip the preceding backslash flag
488 precedingBackslash = !precedingBackslash;
490 precedingBackslash = false;
495 if (isCommentLine || len == 0) {
496 isCommentLine = false;
498 skipWhiteSpace = true;
502 if (inOff >= inLimit) {
503 inLimit = (inStream==null)
504 ?reader.read(inCharBuf)
505 :inStream.read(inByteBuf);
511 if (precedingBackslash) {
513 //skip the leading whitespace characters in following line
514 skipWhiteSpace = true;
515 appendedLineBegin = true;
516 precedingBackslash = false;
529 * Converts encoded \uxxxx to unicode chars
530 * and changes special saved chars to their original forms
532 private String loadConvert (char[] in, int off, int len, char[] convtBuf) {
533 if (convtBuf.length < len) {
534 int newLen = len * 2;
536 newLen = Integer.MAX_VALUE;
538 convtBuf = new char[newLen];
541 char[] out = convtBuf;
552 for (int i=0; i<4; i++) {
555 case '0': case '1': case '2': case '3': case '4':
556 case '5': case '6': case '7': case '8': case '9':
557 value = (value << 4) + aChar - '0';
559 case 'a': case 'b': case 'c':
560 case 'd': case 'e': case 'f':
561 value = (value << 4) + 10 + aChar - 'a';
563 case 'A': case 'B': case 'C':
564 case 'D': case 'E': case 'F':
565 value = (value << 4) + 10 + aChar - 'A';
568 throw new IllegalArgumentException(
569 "Malformed \\uxxxx encoding.");
572 out[outLen++] = (char)value;
574 if (aChar == 't') aChar = '\t';
575 else if (aChar == 'r') aChar = '\r';
576 else if (aChar == 'n') aChar = '\n';
577 else if (aChar == 'f') aChar = '\f';
578 out[outLen++] = aChar;
581 out[outLen++] = aChar;
584 return new String (out, 0, outLen);
588 * Converts unicodes to encoded \uxxxx and escapes
589 * special characters with a preceding slash
591 private String saveConvert(String theString,
593 boolean escapeUnicode) {
594 int len = theString.length();
595 int bufLen = len * 2;
597 bufLen = Integer.MAX_VALUE;
599 StringBuffer outBuffer = new StringBuffer(bufLen);
601 for(int x=0; x<len; x++) {
602 char aChar = theString.charAt(x);
603 // Handle common case first, selecting largest block that
604 // avoids the specials below
605 if ((aChar > 61) && (aChar < 127)) {
607 outBuffer.append('\\'); outBuffer.append('\\');
610 outBuffer.append(aChar);
615 if (x == 0 || escapeSpace)
616 outBuffer.append('\\');
617 outBuffer.append(' ');
619 case '\t':outBuffer.append('\\'); outBuffer.append('t');
621 case '\n':outBuffer.append('\\'); outBuffer.append('n');
623 case '\r':outBuffer.append('\\'); outBuffer.append('r');
625 case '\f':outBuffer.append('\\'); outBuffer.append('f');
627 case '=': // Fall through
628 case ':': // Fall through
629 case '#': // Fall through
631 outBuffer.append('\\'); outBuffer.append(aChar);
634 if (((aChar < 0x0020) || (aChar > 0x007e)) & escapeUnicode ) {
635 outBuffer.append('\\');
636 outBuffer.append('u');
637 outBuffer.append(toHex((aChar >> 12) & 0xF));
638 outBuffer.append(toHex((aChar >> 8) & 0xF));
639 outBuffer.append(toHex((aChar >> 4) & 0xF));
640 outBuffer.append(toHex( aChar & 0xF));
642 outBuffer.append(aChar);
646 return outBuffer.toString();
649 private static void writeComments(BufferedWriter bw, String comments)
652 int len = comments.length();
655 char[] uu = new char[6];
658 while (current < len) {
659 char c = comments.charAt(current);
660 if (c > '\u00ff' || c == '\n' || c == '\r') {
662 bw.write(comments.substring(last, current));
664 uu[2] = toHex((c >> 12) & 0xf);
665 uu[3] = toHex((c >> 8) & 0xf);
666 uu[4] = toHex((c >> 4) & 0xf);
667 uu[5] = toHex( c & 0xf);
668 bw.write(new String(uu));
672 current != len - 1 &&
673 comments.charAt(current + 1) == '\n') {
676 if (current == len - 1 ||
677 (comments.charAt(current + 1) != '#' &&
678 comments.charAt(current + 1) != '!'))
686 bw.write(comments.substring(last, current));
691 * Calls the <code>store(OutputStream out, String comments)</code> method
692 * and suppresses IOExceptions that were thrown.
694 * @deprecated This method does not throw an IOException if an I/O error
695 * occurs while saving the property list. The preferred way to save a
696 * properties list is via the <code>store(OutputStream out,
697 * String comments)</code> method or the
698 * <code>storeToXML(OutputStream os, String comment)</code> method.
700 * @param out an output stream.
701 * @param comments a description of the property list.
702 * @exception ClassCastException if this <code>Properties</code> object
703 * contains any keys or values that are not
704 * <code>Strings</code>.
707 public void save(OutputStream out, String comments) {
709 store(out, comments);
710 } catch (IOException e) {
715 * Writes this property list (key and element pairs) in this
716 * <code>Properties</code> table to the output character stream in a
717 * format suitable for using the {@link #load(java.io.Reader) load(Reader)}
720 * Properties from the defaults table of this <code>Properties</code>
721 * table (if any) are <i>not</i> written out by this method.
723 * If the comments argument is not null, then an ASCII <code>#</code>
724 * character, the comments string, and a line separator are first written
725 * to the output stream. Thus, the <code>comments</code> can serve as an
726 * identifying comment. Any one of a line feed ('\n'), a carriage
727 * return ('\r'), or a carriage return followed immediately by a line feed
728 * in comments is replaced by a line separator generated by the <code>Writer</code>
729 * and if the next character in comments is not character <code>#</code> or
730 * character <code>!</code> then an ASCII <code>#</code> is written out
731 * after that line separator.
733 * Next, a comment line is always written, consisting of an ASCII
734 * <code>#</code> character, the current date and time (as if produced
735 * by the <code>toString</code> method of <code>Date</code> for the
736 * current time), and a line separator as generated by the <code>Writer</code>.
738 * Then every entry in this <code>Properties</code> table is
739 * written out, one per line. For each entry the key string is
740 * written, then an ASCII <code>=</code>, then the associated
741 * element string. For the key, all space characters are
742 * written with a preceding <code>\</code> character. For the
743 * element, leading space characters, but not embedded or trailing
744 * space characters, are written with a preceding <code>\</code>
745 * character. The key and element characters <code>#</code>,
746 * <code>!</code>, <code>=</code>, and <code>:</code> are written
747 * with a preceding backslash to ensure that they are properly loaded.
749 * After the entries have been written, the output stream is flushed.
750 * The output stream remains open after this method returns.
753 * @param writer an output character stream writer.
754 * @param comments a description of the property list.
755 * @exception IOException if writing this property list to the specified
756 * output stream throws an <tt>IOException</tt>.
757 * @exception ClassCastException if this <code>Properties</code> object
758 * contains any keys or values that are not <code>Strings</code>.
759 * @exception NullPointerException if <code>writer</code> is null.
762 public void store(Writer writer, String comments)
765 store0((writer instanceof BufferedWriter)?(BufferedWriter)writer
766 : new BufferedWriter(writer),
772 * Writes this property list (key and element pairs) in this
773 * <code>Properties</code> table to the output stream in a format suitable
774 * for loading into a <code>Properties</code> table using the
775 * {@link #load(InputStream) load(InputStream)} method.
777 * Properties from the defaults table of this <code>Properties</code>
778 * table (if any) are <i>not</i> written out by this method.
780 * This method outputs the comments, properties keys and values in
781 * the same format as specified in
782 * {@link #store(java.io.Writer, java.lang.String) store(Writer)},
783 * with the following differences:
785 * <li>The stream is written using the ISO 8859-1 character encoding.
787 * <li>Characters not in Latin-1 in the comments are written as
788 * <code>\u</code><i>xxxx</i> for their appropriate unicode
789 * hexadecimal value <i>xxxx</i>.
791 * <li>Characters less than <code>\u0020</code> and characters greater
792 * than <code>\u007E</code> in property keys or values are written
793 * as <code>\u</code><i>xxxx</i> for the appropriate hexadecimal
797 * After the entries have been written, the output stream is flushed.
798 * The output stream remains open after this method returns.
800 * @param out an output stream.
801 * @param comments a description of the property list.
802 * @exception IOException if writing this property list to the specified
803 * output stream throws an <tt>IOException</tt>.
804 * @exception ClassCastException if this <code>Properties</code> object
805 * contains any keys or values that are not <code>Strings</code>.
806 * @exception NullPointerException if <code>out</code> is null.
809 public void store(OutputStream out, String comments)
812 store0(new BufferedWriter(new OutputStreamWriter(out, "8859_1")),
817 private void store0(BufferedWriter bw, String comments, boolean escUnicode)
820 if (comments != null) {
821 writeComments(bw, comments);
823 bw.write("#" + new Date().toString());
825 synchronized (this) {
826 for (Enumeration e = keys(); e.hasMoreElements();) {
827 String key = (String)e.nextElement();
828 String val = (String)get(key);
829 key = saveConvert(key, true, escUnicode);
830 /* No need to escape embedded and trailing spaces for value, hence
831 * pass false to flag.
833 val = saveConvert(val, false, escUnicode);
834 bw.write(key + "=" + val);
842 * Loads all of the properties represented by the XML document on the
843 * specified input stream into this properties table.
845 * <p>The XML document must have the following DOCTYPE declaration:
847 * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
849 * Furthermore, the document must satisfy the properties DTD described
852 * <p>The specified stream is closed after this method returns.
854 * @param in the input stream from which to read the XML document.
855 * @throws IOException if reading from the specified input stream
856 * results in an <tt>IOException</tt>.
857 * @throws InvalidPropertiesFormatException Data on input stream does not
858 * constitute a valid XML document with the mandated document type.
859 * @throws NullPointerException if <code>in</code> is null.
860 * @see #storeToXML(OutputStream, String, String)
863 public synchronized void loadFromXML(InputStream in)
864 throws IOException, InvalidPropertiesFormatException
867 throw new NullPointerException();
868 XMLUtils.load(this, in);
873 * Emits an XML document representing all of the properties contained
876 * <p> An invocation of this method of the form <tt>props.storeToXML(os,
877 * comment)</tt> behaves in exactly the same way as the invocation
878 * <tt>props.storeToXML(os, comment, "UTF-8");</tt>.
880 * @param os the output stream on which to emit the XML document.
881 * @param comment a description of the property list, or <code>null</code>
882 * if no comment is desired.
883 * @throws IOException if writing to the specified output stream
884 * results in an <tt>IOException</tt>.
885 * @throws NullPointerException if <code>os</code> is null.
886 * @throws ClassCastException if this <code>Properties</code> object
887 * contains any keys or values that are not
888 * <code>Strings</code>.
889 * @see #loadFromXML(InputStream)
892 public void storeToXML(OutputStream os, String comment)
896 throw new NullPointerException();
897 storeToXML(os, comment, "UTF-8");
901 * Emits an XML document representing all of the properties contained
902 * in this table, using the specified encoding.
904 * <p>The XML document will have the following DOCTYPE declaration:
906 * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
909 *<p>If the specified comment is <code>null</code> then no comment
910 * will be stored in the document.
912 * <p>The specified stream remains open after this method returns.
914 * @param os the output stream on which to emit the XML document.
915 * @param comment a description of the property list, or <code>null</code>
916 * if no comment is desired.
917 * @param encoding the name of a supported
918 * <a href="../lang/package-summary.html#charenc">
919 * character encoding</a>
921 * @throws IOException if writing to the specified output stream
922 * results in an <tt>IOException</tt>.
923 * @throws NullPointerException if <code>os</code> is <code>null</code>,
924 * or if <code>encoding</code> is <code>null</code>.
925 * @throws ClassCastException if this <code>Properties</code> object
926 * contains any keys or values that are not
927 * <code>Strings</code>.
928 * @see #loadFromXML(InputStream)
931 public void storeToXML(OutputStream os, String comment, String encoding)
935 throw new NullPointerException();
936 XMLUtils.save(this, os, comment, encoding);
940 * Searches for the property with the specified key in this property list.
941 * If the key is not found in this property list, the default property list,
942 * and its defaults, recursively, are then checked. The method returns
943 * <code>null</code> if the property is not found.
945 * @param key the property key.
946 * @return the value in this property list with the specified key value.
950 public String getProperty(String key) {
951 Object oval = super.get(key);
952 String sval = (oval instanceof String) ? (String)oval : null;
953 return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval;
957 * Searches for the property with the specified key in this property list.
958 * If the key is not found in this property list, the default property list,
959 * and its defaults, recursively, are then checked. The method returns the
960 * default value argument if the property is not found.
962 * @param key the hashtable key.
963 * @param defaultValue a default value.
965 * @return the value in this property list with the specified key value.
969 public String getProperty(String key, String defaultValue) {
970 String val = getProperty(key);
971 return (val == null) ? defaultValue : val;
975 * Returns an enumeration of all the keys in this property list,
976 * including distinct keys in the default property list if a key
977 * of the same name has not already been found from the main
980 * @return an enumeration of all the keys in this property list, including
981 * the keys in the default property list.
982 * @throws ClassCastException if any key in this property list
984 * @see java.util.Enumeration
985 * @see java.util.Properties#defaults
986 * @see #stringPropertyNames
988 public Enumeration<?> propertyNames() {
989 Hashtable h = new Hashtable();
995 * Returns a set of keys in this property list where
996 * the key and its corresponding value are strings,
997 * including distinct keys in the default property list if a key
998 * of the same name has not already been found from the main
999 * properties list. Properties whose key or value is not
1000 * of type <tt>String</tt> are omitted.
1002 * The returned set is not backed by the <tt>Properties</tt> object.
1003 * Changes to this <tt>Properties</tt> are not reflected in the set,
1006 * @return a set of keys in this property list where
1007 * the key and its corresponding value are strings,
1008 * including the keys in the default property list.
1009 * @see java.util.Properties#defaults
1012 public Set<String> stringPropertyNames() {
1013 Hashtable<String, String> h = new Hashtable<>();
1014 enumerateStringProperties(h);
1019 * Prints this property list out to the specified output stream.
1020 * This method is useful for debugging.
1022 * @param out an output stream.
1023 * @throws ClassCastException if any key in this property list
1026 public void list(PrintStream out) {
1027 out.println("-- listing properties --");
1028 Hashtable h = new Hashtable();
1030 for (Enumeration e = h.keys() ; e.hasMoreElements() ;) {
1031 String key = (String)e.nextElement();
1032 String val = (String)h.get(key);
1033 if (val.length() > 40) {
1034 val = val.substring(0, 37) + "...";
1036 out.println(key + "=" + val);
1041 * Prints this property list out to the specified output stream.
1042 * This method is useful for debugging.
1044 * @param out an output stream.
1045 * @throws ClassCastException if any key in this property list
1050 * Rather than use an anonymous inner class to share common code, this
1051 * method is duplicated in order to ensure that a non-1.1 compiler can
1052 * compile this file.
1054 public void list(PrintWriter out) {
1055 out.println("-- listing properties --");
1056 Hashtable h = new Hashtable();
1058 for (Enumeration e = h.keys() ; e.hasMoreElements() ;) {
1059 String key = (String)e.nextElement();
1060 String val = (String)h.get(key);
1061 if (val.length() > 40) {
1062 val = val.substring(0, 37) + "...";
1064 out.println(key + "=" + val);
1069 * Enumerates all key/value pairs in the specified hashtable.
1070 * @param h the hashtable
1071 * @throws ClassCastException if any of the property keys
1072 * is not of String type.
1074 private synchronized void enumerate(Hashtable h) {
1075 if (defaults != null) {
1076 defaults.enumerate(h);
1078 for (Enumeration e = keys() ; e.hasMoreElements() ;) {
1079 String key = (String)e.nextElement();
1080 h.put(key, get(key));
1085 * Enumerates all key/value pairs in the specified hashtable
1086 * and omits the property if the key or value is not a string.
1087 * @param h the hashtable
1089 private synchronized void enumerateStringProperties(Hashtable<String, String> h) {
1090 if (defaults != null) {
1091 defaults.enumerateStringProperties(h);
1093 for (Enumeration e = keys() ; e.hasMoreElements() ;) {
1094 Object k = e.nextElement();
1096 if (k instanceof String && v instanceof String) {
1097 h.put((String) k, (String) v);
1103 * Convert a nibble to a hex character
1104 * @param nibble the nibble to convert.
1106 private static char toHex(int nibble) {
1107 return hexDigit[(nibble & 0xF)];
1110 /** A table of hex digits */
1111 private static final char[] hexDigit = {
1112 '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'