History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/java/util/Properties.java (1 of 18352 files). Show all

Unified · Split

java/pkg/java/util/Properties.java+1114−0
@@ -0,0 +1,1114 @@
1+/*
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.
4+ *
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.
10+ *
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).
16+ *
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.
20+ *
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
23+ * questions.
24+ */
25+
26+package java.util;
27+
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;
37+
38+/**
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.
43+ * <p>
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.
47+ * <p>
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.
59+ *
60+ * <p>
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.
65+ *
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&trade; 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.
76+ *
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:
82+ *
83+ * <pre>
84+ * &lt;!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"&gt;
85+ * </pre>
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:
89+ * <pre>
90+ * &lt;?xml version="1.0" encoding="UTF-8"?&gt;
91+ *
92+ * &lt;!-- DTD for properties --&gt;
93+ *
94+ * &lt;!ELEMENT properties ( comment?, entry* ) &gt;
95+ *
96+ * &lt;!ATTLIST properties version CDATA #FIXED "1.0"&gt;
97+ *
98+ * &lt;!ELEMENT comment (#PCDATA) &gt;
99+ *
100+ * &lt;!ELEMENT entry (#PCDATA) &gt;
101+ *
102+ * &lt;!ATTLIST entry key CDATA #REQUIRED&gt;
103+ * </pre>
104+ *
105+ * <p>This class is thread-safe: multiple threads can share a single
106+ * <tt>Properties</tt> object without the need for external synchronization.
107+ *
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>
110+ *
111+ * @author Arthur van Hoff
112+ * @author Michael McCloskey
113+ * @author Xueming Shen
114+ * @since JDK1.0
115+ */
116+public
117+class Properties extends Hashtable<Object,Object> {
118+ /**
119+ * use serialVersionUID from JDK 1.1.X for interoperability
120+ */
121+ private static final long serialVersionUID = 4112578634029874840L;
122+
123+ /**
124+ * A property list that contains default values for any keys not
125+ * found in this property list.
126+ *
127+ * @serial
128+ */
129+ protected Properties defaults;
130+
131+ /**
132+ * Creates an empty property list with no default values.
133+ */
134+ public Properties() {
135+ this(null);
136+ }
137+
138+ /**
139+ * Creates an empty property list with the specified defaults.
140+ *
141+ * @param defaults the defaults.
142+ */
143+ public Properties(Properties defaults) {
144+ this.defaults = defaults;
145+ }
146+
147+ /**
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>.
152+ *
153+ * @param key the key to be placed into this property list.
154+ * @param value the value corresponding to <tt>key</tt>.
155+ * @return the previous value of the specified key in this property
156+ * list, or <code>null</code> if it did not have one.
157+ * @see #getProperty
158+ * @since 1.2
159+ */
160+ public synchronized Object setProperty(String key, String value) {
161+ return put(key, value);
162+ }
163+
164+
165+ /**
166+ * Reads a property list (key and element pairs) from the input
167+ * character stream in a simple line-oriented format.
168+ * <p>
169+ * Properties are processed in terms of lines. There are two
170+ * kinds of line, <i>natural lines</i> and <i>logical lines</i>.
171+ * A natural line is defined as a line of
172+ * characters that is terminated either by a set of line terminator
173+ * characters (<code>\n</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.
183+ *
184+ * <p>
185+ * A natural line that contains only white space characters is
186+ * considered blank and is ignored. A comment line has an ASCII
187+ * <code>'#'</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>'&#92;u0020'</code>), tab
192+ * (<code>'\t'</code>, <code>'&#92;u0009'</code>), and form feed
193+ * (<code>'\f'</code>, <code>'&#92;u000C'</code>) to be white
194+ * space.
195+ *
196+ * <p>
197+ * If a logical line is spread across several natural lines, the
198+ * backslash escaping the line terminator sequence, the line
199+ * terminator sequence, and any white space at the start of the
200+ * following line have no affect on the key or element values.
201+ * The remainder of the discussion of key and element parsing
202+ * (when loading) will assume all the characters constituting
203+ * the key and element appear on a single natural line after
204+ * line continuation characters have been removed. Note that
205+ * it is <i>not</i> sufficient to only examine the character
206+ * preceding a line terminator sequence to decide if the line
207+ * terminator is escaped; there must be an odd number of
208+ * contiguous backslashes for the line terminator to be escaped.
209+ * Since the input is processed from left to right, a
210+ * non-zero even number of 2<i>n</i> contiguous backslashes
211+ * before a line terminator (or elsewhere) encodes <i>n</i>
212+ * backslashes after escape processing.
213+ *
214+ * <p>
215+ * The key contains all of the characters in the line starting
216+ * with the first non-white space character and up to, but not
217+ * including, the first unescaped <code>'='</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>
222+ *
223+ * <code>\:\=</code><p>
224+ *
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>&quot;&quot;</code>. Once the raw character sequences
235+ * constituting the key and element are identified, escape
236+ * processing is performed as described above.
237+ *
238+ * <p>
239+ * As an example, each of the following three lines specifies the key
240+ * <code>"Truth"</code> and the associated element value
241+ * <code>"Beauty"</code>:
242+ * <p>
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+ * <p>
251+ * <pre>
252+ * fruits apple, banana, pear, \
253+ * cantaloupe, watermelon, \
254+ * kiwi, mango
255+ * </pre>
256+ * The key is <code>"fruits"</code> and the associated element is:
257+ * <p>
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
263+ * characters.
264+ * <p>
265+ * As a third example, the line:
266+ * <p>
267+ * <pre>cheeses
268+ * </pre>
269+ * specifies that the key is <code>"cheeses"</code> and the associated
270+ * element is the empty string <code>""</code>.<p>
271+ * <p>
272+ *
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&trade; Language Specification</cite>).
278+ *
279+ * The differences from the character escape sequences and Unicode
280+ * escapes used for characters and strings are:
281+ *
282+ * <ul>
283+ * <li> Octal escapes are not recognized.
284+ *
285+ * <li> The character sequence <code>\b</code> does <i>not</i>
286+ * represent a backspace character.
287+ *
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>.
296+ *
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.
301+ *
302+ * <li> Only a single 'u' character is allowed in a Uniocde escape
303+ * sequence.
304+ *
305+ * </ul>
306+ * <p>
307+ * The specified stream remains open after this method returns.
308+ *
309+ * @param reader the input character stream.
310+ * @throws IOException if an error occurred when reading from the
311+ * input stream.
312+ * @throws IllegalArgumentException if a malformed Unicode escape
313+ * appears in the input.
314+ * @since 1.6
315+ */
316+ public synchronized void load(Reader reader) throws IOException {
317+ load0(new LineReader(reader));
318+ }
319+
320+ /**
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
328+ * section 3.3 of
329+ * <cite>The Java&trade; Language Specification</cite>.
330+ * <p>
331+ * The specified stream remains open after this method returns.
332+ *
333+ * @param inStream the input stream.
334+ * @exception IOException if an error occurred when reading from the
335+ * input stream.
336+ * @throws IllegalArgumentException if the input stream contains a
337+ * malformed Unicode escape sequence.
338+ * @since 1.2
339+ */
340+ public synchronized void load(InputStream inStream) throws IOException {
341+ load0(new LineReader(inStream));
342+ }
343+
344+ private void load0 (LineReader lr) throws IOException {
345+ char[] convtBuf = new char[1024];
346+ int limit;
347+ int keyLen;
348+ int valueStart;
349+ char c;
350+ boolean hasSep;
351+ boolean precedingBackslash;
352+
353+ while ((limit = lr.readLine()) >= 0) {
354+ c = 0;
355+ keyLen = 0;
356+ valueStart = limit;
357+ hasSep = false;
358+
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;
366+ hasSep = true;
367+ break;
368+ } else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) {
369+ valueStart = keyLen + 1;
370+ break;
371+ }
372+ if (c == '\\') {
373+ precedingBackslash = !precedingBackslash;
374+ } else {
375+ precedingBackslash = false;
376+ }
377+ keyLen++;
378+ }
379+ while (valueStart < limit) {
380+ c = lr.lineBuf[valueStart];
381+ if (c != ' ' && c != '\t' && c != '\f') {
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 {
403+ public LineReader(InputStream inStream) {
404+ this.inStream = inStream;
405+ inByteBuf = new byte[8192];
406+ }
407+
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+
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+ return len;
442+ }
443+ }
444+ if (inStream != null) {
445+ //The line below is equivalent to calling a
446+ //ISO8859-1 decoder.
447+ c = (char) (0xff & inByteBuf[inOff++]);
448+ } else {
449+ c = inCharBuf[inOff++];
450+ }
451+ if (skipLF) {
452+ skipLF = false;
453+ if (c == '\n') {
454+ continue;
455+ }
456+ }
457+ if (skipWhiteSpace) {
458+ if (c == ' ' || c == '\t' || c == '\f') {
459+ continue;
460+ }
461+ if (!appendedLineBegin && (c == '\r' || c == '\n')) {
462+ continue;
463+ }
464+ skipWhiteSpace = false;
465+ appendedLineBegin = false;
466+ }
467+ if (isNewLine) {
468+ isNewLine = false;
469+ if (c == '#' || c == '!') {
470+ isCommentLine = true;
471+ continue;
472+ }
473+ }
474+
475+ if (c != '\n' && c != '\r') {
476+ lineBuf[len++] = c;
477+ if (len == lineBuf.length) {
478+ int newLength = lineBuf.length * 2;
479+ if (newLength < 0) {
480+ newLength = Integer.MAX_VALUE;
481+ }
482+ char[] buf = new char[newLength];
483+ System.arraycopy(lineBuf, 0, buf, 0, lineBuf.length);
484+ lineBuf = buf;
485+ }
486+ //flip the preceding backslash flag
487+ if (c == '\\') {
488+ precedingBackslash = !precedingBackslash;
489+ } else {
490+ precedingBackslash = false;
491+ }
492+ }
493+ else {
494+ // reached EOL
495+ if (isCommentLine || len == 0) {
496+ isCommentLine = false;
497+ isNewLine = true;
498+ skipWhiteSpace = true;
499+ len = 0;
500+ continue;
501+ }
502+ if (inOff >= inLimit) {
503+ inLimit = (inStream==null)
504+ ?reader.read(inCharBuf)
505+ :inStream.read(inByteBuf);
506+ inOff = 0;
507+ if (inLimit <= 0) {
508+ return len;
509+ }
510+ }
511+ if (precedingBackslash) {
512+ len -= 1;
513+ //skip the leading whitespace characters in following line
514+ skipWhiteSpace = true;
515+ appendedLineBegin = true;
516+ precedingBackslash = false;
517+ if (c == '\r') {
518+ skipLF = true;
519+ }
520+ } else {
521+ return len;
522+ }
523+ }
524+ }
525+ }
526+ }
527+
528+ /*
529+ * Converts encoded &#92;uxxxx to unicode chars
530+ * and changes special saved chars to their original forms
531+ */
532+ private String loadConvert (char[] in, int off, int len, char[] convtBuf) {
533+ if (convtBuf.length < len) {
534+ int newLen = len * 2;
535+ if (newLen < 0) {
536+ newLen = Integer.MAX_VALUE;
537+ }
538+ convtBuf = new char[newLen];
539+ }
540+ char aChar;
541+ char[] out = convtBuf;
542+ int outLen = 0;
543+ int end = off + len;
544+
545+ while (off < end) {
546+ aChar = in[off++];
547+ if (aChar == '\\') {
548+ aChar = in[off++];
549+ if(aChar == 'u') {
550+ // Read the xxxx
551+ int value=0;
552+ for (int i=0; i<4; i++) {
553+ aChar = in[off++];
554+ switch (aChar) {
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';
558+ break;
559+ case 'a': case 'b': case 'c':
560+ case 'd': case 'e': case 'f':
561+ value = (value << 4) + 10 + aChar - 'a';
562+ break;
563+ case 'A': case 'B': case 'C':
564+ case 'D': case 'E': case 'F':
565+ value = (value << 4) + 10 + aChar - 'A';
566+ break;
567+ default:
568+ throw new IllegalArgumentException(
569+ "Malformed \\uxxxx encoding.");
570+ }
571+ }
572+ out[outLen++] = (char)value;
573+ } else {
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;
579+ }
580+ } else {
581+ out[outLen++] = aChar;
582+ }
583+ }
584+ return new String (out, 0, outLen);
585+ }
586+
587+ /*
588+ * Converts unicodes to encoded &#92;uxxxx and escapes
589+ * special characters with a preceding slash
590+ */
591+ private String saveConvert(String theString,
592+ boolean escapeSpace,
593+ boolean escapeUnicode) {
594+ int len = theString.length();
595+ int bufLen = len * 2;
596+ if (bufLen < 0) {
597+ bufLen = Integer.MAX_VALUE;
598+ }
599+ StringBuffer outBuffer = new StringBuffer(bufLen);
600+
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)) {
606+ if (aChar == '\\') {
607+ outBuffer.append('\\'); outBuffer.append('\\');
608+ continue;
609+ }
610+ outBuffer.append(aChar);
611+ continue;
612+ }
613+ switch(aChar) {
614+ case ' ':
615+ if (x == 0 || escapeSpace)
616+ outBuffer.append('\\');
617+ outBuffer.append(' ');
618+ break;
619+ case '\t':outBuffer.append('\\'); outBuffer.append('t');
620+ break;
621+ case '\n':outBuffer.append('\\'); outBuffer.append('n');
622+ break;
623+ case '\r':outBuffer.append('\\'); outBuffer.append('r');
624+ break;
625+ case '\f':outBuffer.append('\\'); outBuffer.append('f');
626+ break;
627+ case '=': // Fall through
628+ case ':': // Fall through
629+ case '#': // Fall through
630+ case '!':
631+ outBuffer.append('\\'); outBuffer.append(aChar);
632+ break;
633+ default:
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));
641+ } else {
642+ outBuffer.append(aChar);
643+ }
644+ }
645+ }
646+ return outBuffer.toString();
647+ }
648+
649+ private static void writeComments(BufferedWriter bw, String comments)
650+ throws IOException {
651+ bw.write("#");
652+ int len = comments.length();
653+ int current = 0;
654+ int last = 0;
655+ char[] uu = new char[6];
656+ uu[0] = '\\';
657+ uu[1] = 'u';
658+ while (current < len) {
659+ char c = comments.charAt(current);
660+ if (c > '\u00ff' || c == '\n' || c == '\r') {
661+ if (last != current)
662+ bw.write(comments.substring(last, current));
663+ if (c > '\u00ff') {
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));
669+ } else {
670+ bw.newLine();
671+ if (c == '\r' &&
672+ current != len - 1 &&
673+ comments.charAt(current + 1) == '\n') {
674+ current++;
675+ }
676+ if (current == len - 1 ||
677+ (comments.charAt(current + 1) != '#' &&
678+ comments.charAt(current + 1) != '!'))
679+ bw.write("#");
680+ }
681+ last = current + 1;
682+ }
683+ current++;
684+ }
685+ if (last != current)
686+ bw.write(comments.substring(last, current));
687+ bw.newLine();
688+ }
689+
690+ /**
691+ * Calls the <code>store(OutputStream out, String comments)</code> method
692+ * and suppresses IOExceptions that were thrown.
693+ *
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.
699+ *
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>.
705+ */
706+ @Deprecated
707+ public void save(OutputStream out, String comments) {
708+ try {
709+ store(out, comments);
710+ } catch (IOException e) {
711+ }
712+ }
713+
714+ /**
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)}
718+ * method.
719+ * <p>
720+ * Properties from the defaults table of this <code>Properties</code>
721+ * table (if any) are <i>not</i> written out by this method.
722+ * <p>
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.
732+ * <p>
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>.
737+ * <p>
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.
748+ * <p>
749+ * After the entries have been written, the output stream is flushed.
750+ * The output stream remains open after this method returns.
751+ * <p>
752+ *
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.
760+ * @since 1.6
761+ */
762+ public void store(Writer writer, String comments)
763+ throws IOException
764+ {
765+ store0((writer instanceof BufferedWriter)?(BufferedWriter)writer
766+ : new BufferedWriter(writer),
767+ comments,
768+ false);
769+ }
770+
771+ /**
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.
776+ * <p>
777+ * Properties from the defaults table of this <code>Properties</code>
778+ * table (if any) are <i>not</i> written out by this method.
779+ * <p>
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:
784+ * <ul>
785+ * <li>The stream is written using the ISO 8859-1 character encoding.
786+ *
787+ * <li>Characters not in Latin-1 in the comments are written as
788+ * <code>&#92;u</code><i>xxxx</i> for their appropriate unicode
789+ * hexadecimal value <i>xxxx</i>.
790+ *
791+ * <li>Characters less than <code>&#92;u0020</code> and characters greater
792+ * than <code>&#92;u007E</code> in property keys or values are written
793+ * as <code>&#92;u</code><i>xxxx</i> for the appropriate hexadecimal
794+ * value <i>xxxx</i>.
795+ * </ul>
796+ * <p>
797+ * After the entries have been written, the output stream is flushed.
798+ * The output stream remains open after this method returns.
799+ * <p>
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.
807+ * @since 1.2
808+ */
809+ public void store(OutputStream out, String comments)
810+ throws IOException
811+ {
812+ store0(new BufferedWriter(new OutputStreamWriter(out, "8859_1")),
813+ comments,
814+ true);
815+ }
816+
817+ private void store0(BufferedWriter bw, String comments, boolean escUnicode)
818+ throws IOException
819+ {
820+ if (comments != null) {
821+ writeComments(bw, comments);
822+ }
823+ bw.write("#" + new Date().toString());
824+ bw.newLine();
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.
832+ */
833+ val = saveConvert(val, false, escUnicode);
834+ bw.write(key + "=" + val);
835+ bw.newLine();
836+ }
837+ }
838+ bw.flush();
839+ }
840+
841+ /**
842+ * Loads all of the properties represented by the XML document on the
843+ * specified input stream into this properties table.
844+ *
845+ * <p>The XML document must have the following DOCTYPE declaration:
846+ * <pre>
847+ * &lt;!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"&gt;
848+ * </pre>
849+ * Furthermore, the document must satisfy the properties DTD described
850+ * above.
851+ *
852+ * <p>The specified stream is closed after this method returns.
853+ *
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)
861+ * @since 1.5
862+ */
863+ public synchronized void loadFromXML(InputStream in)
864+ throws IOException, InvalidPropertiesFormatException
865+ {
866+ if (in == null)
867+ throw new NullPointerException();
868+ XMLUtils.load(this, in);
869+ in.close();
870+ }
871+
872+ /**
873+ * Emits an XML document representing all of the properties contained
874+ * in this table.
875+ *
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>.
879+ *
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)
890+ * @since 1.5
891+ */
892+ public void storeToXML(OutputStream os, String comment)
893+ throws IOException
894+ {
895+ if (os == null)
896+ throw new NullPointerException();
897+ storeToXML(os, comment, "UTF-8");
898+ }
899+
900+ /**
901+ * Emits an XML document representing all of the properties contained
902+ * in this table, using the specified encoding.
903+ *
904+ * <p>The XML document will have the following DOCTYPE declaration:
905+ * <pre>
906+ * &lt;!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"&gt;
907+ * </pre>
908+ *
909+ *<p>If the specified comment is <code>null</code> then no comment
910+ * will be stored in the document.
911+ *
912+ * <p>The specified stream remains open after this method returns.
913+ *
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>
920+ *
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)
929+ * @since 1.5
930+ */
931+ public void storeToXML(OutputStream os, String comment, String encoding)
932+ throws IOException
933+ {
934+ if (os == null)
935+ throw new NullPointerException();
936+ XMLUtils.save(this, os, comment, encoding);
937+ }
938+
939+ /**
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.
944+ *
945+ * @param key the property key.
946+ * @return the value in this property list with the specified key value.
947+ * @see #setProperty
948+ * @see #defaults
949+ */
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;
954+ }
955+
956+ /**
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.
961+ *
962+ * @param key the hashtable key.
963+ * @param defaultValue a default value.
964+ *
965+ * @return the value in this property list with the specified key value.
966+ * @see #setProperty
967+ * @see #defaults
968+ */
969+ public String getProperty(String key, String defaultValue) {
970+ String val = getProperty(key);
971+ return (val == null) ? defaultValue : val;
972+ }
973+
974+ /**
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
978+ * properties list.
979+ *
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
983+ * is not a string.
984+ * @see java.util.Enumeration
985+ * @see java.util.Properties#defaults
986+ * @see #stringPropertyNames
987+ */
988+ public Enumeration<?> propertyNames() {
989+ Hashtable h = new Hashtable();
990+ enumerate(h);
991+ return h.keys();
992+ }
993+
994+ /**
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.
1001+ * <p>
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,
1004+ * or vice versa.
1005+ *
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
1010+ * @since 1.6
1011+ */
1012+ public Set<String> stringPropertyNames() {
1013+ Hashtable<String, String> h = new Hashtable<>();
1014+ enumerateStringProperties(h);
1015+ return h.keySet();
1016+ }
1017+
1018+ /**
1019+ * Prints this property list out to the specified output stream.
1020+ * This method is useful for debugging.
1021+ *
1022+ * @param out an output stream.
1023+ * @throws ClassCastException if any key in this property list
1024+ * is not a string.
1025+ */
1026+ public void list(PrintStream out) {
1027+ out.println("-- listing properties --");
1028+ Hashtable h = new Hashtable();
1029+ enumerate(h);
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) + "...";
1035+ }
1036+ out.println(key + "=" + val);
1037+ }
1038+ }
1039+
1040+ /**
1041+ * Prints this property list out to the specified output stream.
1042+ * This method is useful for debugging.
1043+ *
1044+ * @param out an output stream.
1045+ * @throws ClassCastException if any key in this property list
1046+ * is not a string.
1047+ * @since JDK1.1
1048+ */
1049+ /*
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.
1053+ */
1054+ public void list(PrintWriter out) {
1055+ out.println("-- listing properties --");
1056+ Hashtable h = new Hashtable();
1057+ enumerate(h);
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) + "...";
1063+ }
1064+ out.println(key + "=" + val);
1065+ }
1066+ }
1067+
1068+ /**
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.
1073+ */
1074+ private synchronized void enumerate(Hashtable h) {
1075+ if (defaults != null) {
1076+ defaults.enumerate(h);
1077+ }
1078+ for (Enumeration e = keys() ; e.hasMoreElements() ;) {
1079+ String key = (String)e.nextElement();
1080+ h.put(key, get(key));
1081+ }
1082+ }
1083+
1084+ /**
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
1088+ */
1089+ private synchronized void enumerateStringProperties(Hashtable<String, String> h) {
1090+ if (defaults != null) {
1091+ defaults.enumerateStringProperties(h);
1092+ }
1093+ for (Enumeration e = keys() ; e.hasMoreElements() ;) {
1094+ Object k = e.nextElement();
1095+ Object v = get(k);
1096+ if (k instanceof String && v instanceof String) {
1097+ h.put((String) k, (String) v);
1098+ }
1099+ }
1100+ }
1101+
1102+ /**
1103+ * Convert a nibble to a hex character
1104+ * @param nibble the nibble to convert.
1105+ */
1106+ private static char toHex(int nibble) {
1107+ return hexDigit[(nibble & 0xF)];
1108+ }
1109+
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'
1113+ };
1114+}