Source Home >> Java Source 1.6.0 >> java.util.Properties V 0.09
  • 0001/*
  • 0002 * @(#)Properties.java 1.96 06/08/07
  • 0003 *
  • 0004 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
  • 0005 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  • 0006 */
  • 0007
  • 0008package java.util;
  • 0009
  • 0010import java.io.IOException;
  • 0011import java.io.PrintStream;
  • 0012import java.io.PrintWriter;
  • 0013import java.io.InputStream;
  • 0014import java.io.OutputStream;
  • 0015import java.io.Reader;
  • 0016import java.io.Writer;
  • 0017import java.io.OutputStreamWriter;
  • 0018import java.io.BufferedWriter;
  • 0019
  • 0020/**
  • 0021 * The <code>Properties</code> class represents a persistent set of
  • 0022 * properties. The <code>Properties</code> can be saved to a stream
  • 0023 * or loaded from a stream. Each key and its corresponding value in
  • 0024 * the property list is a string.
  • 0025 * <p>
  • 0026 * A property list can contain another property list as its
  • 0027 * "defaults"; this second property list is searched if
  • 0028 * the property key is not found in the original property list.
  • 0029 * <p>
  • 0030 * Because <code>Properties</code> inherits from <code>Hashtable</code>, the
  • 0031 * <code>put</code> and <code>putAll</code> methods can be applied to a
  • 0032 * <code>Properties</code> object. Their use is strongly discouraged as they
  • 0033 * allow the caller to insert entries whose keys or values are not
  • 0034 * <code>Strings</code>. The <code>setProperty</code> method should be used
  • 0035 * instead. If the <code>store</code> or <code>save</code> method is called
  • 0036 * on a "compromised" <code>Properties</code> object that contains a
  • 0037 * non-<code>String</code> key or value, the call will fail. Similarly,
  • 0038 * the call to the <code>propertyNames</code> or <code>list</code> method
  • 0039 * will fail if it is called on a "compromised" <code>Properties</code>
  • 0040 * object that contains a non-<code>String</code> key.
  • 0041 *
  • 0042 * <p>
  • 0043 * The {@link #load(java.io.Reader) load(Reader)} <tt>/</tt>
  • 0044 * {@link #store(java.io.Writer, java.lang.String) store(Writer, String)}
  • 0045 * methods load and store properties from and to a character based stream
  • 0046 * in a simple line-oriented format specified below.
  • 0047 *
  • 0048 * The {@link #load(java.io.InputStream) load(InputStream)} <tt>/</tt>
  • 0049 * {@link #store(java.io.OutputStream, java.lang.String) store(OutputStream, String)}
  • 0050 * methods work the same way as the load(Reader)/store(Writer, String) pair, except
  • 0051 * the input/output stream is encoded in ISO 8859-1 character encoding.
  • 0052 * Characters that cannot be directly represented in this encoding can be written using
  • 0053 * <a href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.3">Unicode escapes</a>
  • 0054 * ; only a single 'u' character is allowed in an escape
  • 0055 * sequence. The native2ascii tool can be used to convert property files to and
  • 0056 * from other character encodings.
  • 0057 *
  • 0058 * <p> The {@link #loadFromXML(InputStream)} and {@link
  • 0059 * #storeToXML(OutputStream, String, String)} methods load and store properties
  • 0060 * in a simple XML format. By default the UTF-8 character encoding is used,
  • 0061 * however a specific encoding may be specified if required. An XML properties
  • 0062 * document has the following DOCTYPE declaration:
  • 0063 *
  • 0064 * <pre>
  • 0065 * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
  • 0066 * </pre>
  • 0067 * Note that the system URI (http://java.sun.com/dtd/properties.dtd) is
  • 0068 * <i>not</i> accessed when exporting or importing properties; it merely
  • 0069 * serves as a string to uniquely identify the DTD, which is:
  • 0070 * <pre>
  • 0071 * <?xml version="1.0" encoding="UTF-8"?>
  • 0072 *
  • 0073 * <!-- DTD for properties -->
  • 0074 *
  • 0075 * <!ELEMENT properties ( comment?, entry* ) >
  • 0076 *
  • 0077 * <!ATTLIST properties version CDATA #FIXED "1.0">
  • 0078 *
  • 0079 * <!ELEMENT comment (#PCDATA) >
  • 0080 *
  • 0081 * <!ELEMENT entry (#PCDATA) >
  • 0082 *
  • 0083 * <!ATTLIST entry key CDATA #REQUIRED>
  • 0084 * </pre>
  • 0085 *
  • 0086 * @see <a href="../../../technotes/tools/solaris/native2ascii.html">native2ascii tool for Solaris</a>
  • 0087 * @see <a href="../../../technotes/tools/windows/native2ascii.html">native2ascii tool for Windows</a>
  • 0088 *
  • 0089 * <p>This class is thread-safe: multiple threads can share a single
  • 0090 * <tt>Properties</tt> object without the need for external synchronization.
  • 0091 *
  • 0092 * @author Arthur van Hoff
  • 0093 * @author Michael McCloskey
  • 0094 * @author Xueming Shen
  • 0095 * @version 1.96, 08/07/06
  • 0096 * @since JDK1.0
  • 0097 */
  • 0098public
  • 0099class Properties extends Hashtable<Object,Object> {
  • 0100 /**
  • 0101 * use serialVersionUID from JDK 1.1.X for interoperability
  • 0102 */
  • 0103 private static final long serialVersionUID = 4112578634029874840L;
  • 0104
  • 0105 /**
  • 0106 * A property list that contains default values for any keys not
  • 0107 * found in this property list.
  • 0108 *
  • 0109 * @serial
  • 0110 */
  • 0111 protected Properties defaults;
  • 0112
  • 0113 /**
  • 0114 * Creates an empty property list with no default values.
  • 0115 */
  • 0116 public Properties() {
  • 0117 this(null);
  • 0118 }
  • 0119
  • 0120 /**
  • 0121 * Creates an empty property list with the specified defaults.
  • 0122 *
  • 0123 * @param defaults the defaults.
  • 0124 */
  • 0125 public Properties(Properties defaults) {
  • 0126 this.defaults = defaults;
  • 0127 }
  • 0128
  • 0129 /**
  • 0130 * Calls the <tt>Hashtable</tt> method <code>put</code>. Provided for
  • 0131 * parallelism with the <tt>getProperty</tt> method. Enforces use of
  • 0132 * strings for property keys and values. The value returned is the
  • 0133 * result of the <tt>Hashtable</tt> call to <code>put</code>.
  • 0134 *
  • 0135 * @param key the key to be placed into this property list.
  • 0136 * @param value the value corresponding to <tt>key</tt>.
  • 0137 * @return the previous value of the specified key in this property
  • 0138 * list, or <code>null</code> if it did not have one.
  • 0139 * @see #getProperty
  • 0140 * @since 1.2
  • 0141 */
  • 0142 public synchronized Object setProperty(String key, String value) {
  • 0143 return put(key, value);
  • 0144 }
  • 0145
  • 0146
  • 0147 /**
  • 0148 * Reads a property list (key and element pairs) from the input
  • 0149 * character stream in a simple line-oriented format.
  • 0150 * <p>
  • 0151 * Properties are processed in terms of lines. There are two
  • 0152 * kinds of line, <i>natural lines</i> and <i>logical lines</i>.
  • 0153 * A natural line is defined as a line of
  • 0154 * characters that is terminated either by a set of line terminator
  • 0155 * characters (<code>\n</code> or <code>\r</code> or <code>\r\n</code>)
  • 0156 * or by the end of the stream. A natural line may be either a blank line,
  • 0157 * a comment line, or hold all or some of a key-element pair. A logical
  • 0158 * line holds all the data of a key-element pair, which may be spread
  • 0159 * out across several adjacent natural lines by escaping
  • 0160 * the line terminator sequence with a backslash character
  • 0161 * <code>\</code>. Note that a comment line cannot be extended
  • 0162 * in this manner; every natural line that is a comment must have
  • 0163 * its own comment indicator, as described below. Lines are read from
  • 0164 * input until the end of the stream is reached.
  • 0165 *
  • 0166 * <p>
  • 0167 * A natural line that contains only white space characters is
  • 0168 * considered blank and is ignored. A comment line has an ASCII
  • 0169 * <code>'#'</code> or <code>'!'</code> as its first non-white
  • 0170 * space character; comment lines are also ignored and do not
  • 0171 * encode key-element information. In addition to line
  • 0172 * terminators, this format considers the characters space
  • 0173 * (<code>' '</code>, <code>'\u0020'</code>), tab
  • 0174 * (<code>'\t'</code>, <code>'\u0009'</code>), and form feed
  • 0175 * (<code>'\f'</code>, <code>'\u000C'</code>) to be white
  • 0176 * space.
  • 0177 *
  • 0178 * <p>
  • 0179 * If a logical line is spread across several natural lines, the
  • 0180 * backslash escaping the line terminator sequence, the line
  • 0181 * terminator sequence, and any white space at the start of the
  • 0182 * following line have no affect on the key or element values.
  • 0183 * The remainder of the discussion of key and element parsing
  • 0184 * (when loading) will assume all the characters constituting
  • 0185 * the key and element appear on a single natural line after
  • 0186 * line continuation characters have been removed. Note that
  • 0187 * it is <i>not</i> sufficient to only examine the character
  • 0188 * preceding a line terminator sequence to decide if the line
  • 0189 * terminator is escaped; there must be an odd number of
  • 0190 * contiguous backslashes for the line terminator to be escaped.
  • 0191 * Since the input is processed from left to right, a
  • 0192 * non-zero even number of 2<i>n</i> contiguous backslashes
  • 0193 * before a line terminator (or elsewhere) encodes <i>n</i>
  • 0194 * backslashes after escape processing.
  • 0195 *
  • 0196 * <p>
  • 0197 * The key contains all of the characters in the line starting
  • 0198 * with the first non-white space character and up to, but not
  • 0199 * including, the first unescaped <code>'='</code>,
  • 0200 * <code>':'</code>, or white space character other than a line
  • 0201 * terminator. All of these key termination characters may be
  • 0202 * included in the key by escaping them with a preceding backslash
  • 0203 * character; for example,<p>
  • 0204 *
  • 0205 * <code>\:\=</code><p>
  • 0206 *
  • 0207 * would be the two-character key <code>":="</code>. Line
  • 0208 * terminator characters can be included using <code>\r</code> and
  • 0209 * <code>\n</code> escape sequences. Any white space after the
  • 0210 * key is skipped; if the first non-white space character after
  • 0211 * the key is <code>'='</code> or <code>':'</code>, then it is
  • 0212 * ignored and any white space characters after it are also
  • 0213 * skipped. All remaining characters on the line become part of
  • 0214 * the associated element string; if there are no remaining
  • 0215 * characters, the element is the empty string
  • 0216 * <code>""</code>. Once the raw character sequences
  • 0217 * constituting the key and element are identified, escape
  • 0218 * processing is performed as described above.
  • 0219 *
  • 0220 * <p>
  • 0221 * As an example, each of the following three lines specifies the key
  • 0222 * <code>"Truth"</code> and the associated element value
  • 0223 * <code>"Beauty"</code>:
  • 0224 * <p>
  • 0225 * <pre>
  • 0226 * Truth = Beauty
  • 0227 * Truth:Beauty
  • 0228 * Truth :Beauty
  • 0229 * </pre>
  • 0230 * As another example, the following three lines specify a single
  • 0231 * property:
  • 0232 * <p>
  • 0233 * <pre>
  • 0234 * fruits apple, banana, pear, \
  • 0235 * cantaloupe, watermelon, \
  • 0236 * kiwi, mango
  • 0237 * </pre>
  • 0238 * The key is <code>"fruits"</code> and the associated element is:
  • 0239 * <p>
  • 0240 * <pre>"apple, banana, pear, cantaloupe, watermelon, kiwi, mango"</pre>
  • 0241 * Note that a space appears before each <code>\</code> so that a space
  • 0242 * will appear after each comma in the final result; the <code>\</code>,
  • 0243 * line terminator, and leading white space on the continuation line are
  • 0244 * merely discarded and are <i>not</i> replaced by one or more other
  • 0245 * characters.
  • 0246 * <p>
  • 0247 * As a third example, the line:
  • 0248 * <p>
  • 0249 * <pre>cheeses
  • 0250 * </pre>
  • 0251 * specifies that the key is <code>"cheeses"</code> and the associated
  • 0252 * element is the empty string <code>""</code>.<p>
  • 0253 * <p>
  • 0254 *
  • 0255 * <a name="unicodeescapes"></a>
  • 0256 * Characters in keys and elements can be represented in escape
  • 0257 * sequences similar to those used for character and string literals
  • 0258 * (see <a
  • 0259 * href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.3">§3.3</a>
  • 0260 * and <a
  • 0261 * href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.10.6">§3.10.6</a>
  • 0262 * of the <i>Java Language Specification</i>).
  • 0263 *
  • 0264 * The differences from the character escape sequences and Unicode
  • 0265 * escapes used for characters and strings are:
  • 0266 *
  • 0267 * <ul>
  • 0268 * <li> Octal escapes are not recognized.
  • 0269 *
  • 0270 * <li> The character sequence <code>\b</code> does <i>not</i>
  • 0271 * represent a backspace character.
  • 0272 *
  • 0273 * <li> The method does not treat a backslash character,
  • 0274 * <code>\</code>, before a non-valid escape character as an
  • 0275 * error; the backslash is silently dropped. For example, in a
  • 0276 * Java string the sequence <code>"\z"</code> would cause a
  • 0277 * compile time error. In contrast, this method silently drops
  • 0278 * the backslash. Therefore, this method treats the two character
  • 0279 * sequence <code>"\b"</code> as equivalent to the single
  • 0280 * character <code>'b'</code>.
  • 0281 *
  • 0282 * <li> Escapes are not necessary for single and double quotes;
  • 0283 * however, by the rule above, single and double quote characters
  • 0284 * preceded by a backslash still yield single and double quote
  • 0285 * characters, respectively.
  • 0286 *
  • 0287 * <li> Only a single 'u' character is allowed in a Uniocde escape
  • 0288 * sequence.
  • 0289 *
  • 0290 * </ul>
  • 0291 * <p>
  • 0292 * The specified stream remains open after this method returns.
  • 0293 *
  • 0294 * @param reader the input character stream.
  • 0295 * @throws IOException if an error occurred when reading from the
  • 0296 * input stream.
  • 0297 * @throws IllegalArgumentException if a malformed Unicode escape
  • 0298 * appears in the input.
  • 0299 * @since 1.6
  • 0300 */
  • 0301 public synchronized void load(Reader reader) throws IOException {
  • 0302 load0(new LineReader(reader));
  • 0303 }
  • 0304
  • 0305 /**
  • 0306 * Reads a property list (key and element pairs) from the input
  • 0307 * byte stream. The input stream is in a simple line-oriented
  • 0308 * format as specified in
  • 0309 * {@link #load(java.io.Reader) load(Reader)} and is assumed to use
  • 0310 * the ISO 8859-1 character encoding; that is each byte is one Latin1
  • 0311 * character. Characters not in Latin1, and certain special characters,
  • 0312 * are represented in keys and elements using
  • 0313 * <a href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.3">Unicode escapes</a>.
  • 0314 * <p>
  • 0315 * The specified stream remains open after this method returns.
  • 0316 *
  • 0317 * @param inStream the input stream.
  • 0318 * @exception IOException if an error occurred when reading from the
  • 0319 * input stream.
  • 0320 * @throws IllegalArgumentException if the input stream contains a
  • 0321 * malformed Unicode escape sequence.
  • 0322 * @since 1.2
  • 0323 */
  • 0324 public synchronized void load(InputStream inStream) throws IOException {
  • 0325 load0(new LineReader(inStream));
  • 0326 }
  • 0327
  • 0328 private void load0 (LineReader lr) throws IOException {
  • 0329 char[] convtBuf = new char[1024];
  • 0330 int limit;
  • 0331 int keyLen;
  • 0332 int valueStart;
  • 0333 char c;
  • 0334 boolean hasSep;
  • 0335 boolean precedingBackslash;
  • 0336
  • 0337 while ((limit = lr.readLine()) >= 0) {
  • 0338 c = 0;
  • 0339 keyLen = 0;
  • 0340 valueStart = limit;
  • 0341 hasSep = false;
  • 0342
  • 0343 //System.out.println("line=<" + new String(lineBuf, 0, limit) + ">");
  • 0344 precedingBackslash = false;
  • 0345 while (keyLen < limit) {
  • 0346 c = lr.lineBuf[keyLen];
  • 0347 //need check if escaped.
  • 0348 if ((c == '=' || c == ':') && !precedingBackslash) {
  • 0349 valueStart = keyLen + 1;
  • 0350 hasSep = true;
  • 0351 break;
  • 0352 } else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) {
  • 0353 valueStart = keyLen + 1;
  • 0354 break;
  • 0355 }
  • 0356 if (c == '\\') {
  • 0357 precedingBackslash = !precedingBackslash;
  • 0358 } else {
  • 0359 precedingBackslash = false;
  • 0360 }
  • 0361 keyLen++;
  • 0362 }
  • 0363 while (valueStart < limit) {
  • 0364 c = lr.lineBuf[valueStart];
  • 0365 if (c != ' ' && c != '\t' && c != '\f') {
  • 0366 if (!hasSep && (c == '=' || c == ':')) {
  • 0367 hasSep = true;
  • 0368 } else {
  • 0369 break;
  • 0370 }
  • 0371 }
  • 0372 valueStart++;
  • 0373 }
  • 0374 String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
  • 0375 String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
  • 0376 put(key, value);
  • 0377 }
  • 0378 }
  • 0379
  • 0380 /* Read in a "logical line" from an InputStream/Reader, skip all comment
  • 0381 * and blank lines and filter out those leading whitespace characters
  • 0382 * (\u0020, \u0009 and \u000c) from the beginning of a "natural line".
  • 0383 * Method returns the char length of the "logical line" and stores
  • 0384 * the line in "lineBuf".
  • 0385 */
  • 0386 class LineReader {
  • 0387 public LineReader(InputStream inStream) {
  • 0388 this.inStream = inStream;
  • 0389 inByteBuf = new byte[8192];
  • 0390 }
  • 0391
  • 0392 public LineReader(Reader reader) {
  • 0393 this.reader = reader;
  • 0394 inCharBuf = new char[8192];
  • 0395 }
  • 0396
  • 0397 byte[] inByteBuf;
  • 0398 char[] inCharBuf;
  • 0399 char[] lineBuf = new char[1024];
  • 0400 int inLimit = 0;
  • 0401 int inOff = 0;
  • 0402 InputStream inStream;
  • 0403 Reader reader;
  • 0404
  • 0405 int readLine() throws IOException {
  • 0406 int len = 0;
  • 0407 char c = 0;
  • 0408
  • 0409 boolean skipWhiteSpace = true;
  • 0410 boolean isCommentLine = false;
  • 0411 boolean isNewLine = true;
  • 0412 boolean appendedLineBegin = false;
  • 0413 boolean precedingBackslash = false;
  • 0414 boolean skipLF = false;
  • 0415
  • 0416 while (true) {
  • 0417 if (inOff >= inLimit) {
  • 0418 inLimit = (inStream==null)?reader.read(inCharBuf)
  • 0419 :inStream.read(inByteBuf);
  • 0420 inOff = 0;
  • 0421 if (inLimit <= 0) {
  • 0422 if (len == 0 || isCommentLine) {
  • 0423 return -1;
  • 0424 }
  • 0425 return len;
  • 0426 }
  • 0427 }
  • 0428 if (inStream != null) {
  • 0429 //The line below is equivalent to calling a
  • 0430 //ISO8859-1 decoder.
  • 0431 c = (char) (0xff & inByteBuf[inOff++]);
  • 0432 } else {
  • 0433 c = inCharBuf[inOff++];
  • 0434 }
  • 0435 if (skipLF) {
  • 0436 skipLF = false;
  • 0437 if (c == '\n') {
  • 0438 continue;
  • 0439 }
  • 0440 }
  • 0441 if (skipWhiteSpace) {
  • 0442 if (c == ' ' || c == '\t' || c == '\f') {
  • 0443 continue;
  • 0444 }
  • 0445 if (!appendedLineBegin && (c == '\r' || c == '\n')) {
  • 0446 continue;
  • 0447 }
  • 0448 skipWhiteSpace = false;
  • 0449 appendedLineBegin = false;
  • 0450 }
  • 0451 if (isNewLine) {
  • 0452 isNewLine = false;
  • 0453 if (c == '#' || c == '!') {
  • 0454 isCommentLine = true;
  • 0455 continue;
  • 0456 }
  • 0457 }
  • 0458
  • 0459 if (c != '\n' && c != '\r') {
  • 0460 lineBuf[len++] = c;
  • 0461 if (len == lineBuf.length) {
  • 0462 int newLength = lineBuf.length * 2;
  • 0463 if (newLength < 0) {
  • 0464 newLength = Integer.MAX_VALUE;
  • 0465 }
  • 0466 char[] buf = new char[newLength];
  • 0467 System.arraycopy(lineBuf, 0, buf, 0, lineBuf.length);
  • 0468 lineBuf = buf;
  • 0469 }
  • 0470 //flip the preceding backslash flag
  • 0471 if (c == '\\') {
  • 0472 precedingBackslash = !precedingBackslash;
  • 0473 } else {
  • 0474 precedingBackslash = false;
  • 0475 }
  • 0476 }
  • 0477 else {
  • 0478 // reached EOL
  • 0479 if (isCommentLine || len == 0) {
  • 0480 isCommentLine = false;
  • 0481 isNewLine = true;
  • 0482 skipWhiteSpace = true;
  • 0483 len = 0;
  • 0484 continue;
  • 0485 }
  • 0486 if (inOff >= inLimit) {
  • 0487 inLimit = (inStream==null)
  • 0488 ?reader.read(inCharBuf)
  • 0489 :inStream.read(inByteBuf);
  • 0490 inOff = 0;
  • 0491 if (inLimit <= 0) {
  • 0492 return len;
  • 0493 }
  • 0494 }
  • 0495 if (precedingBackslash) {
  • 0496 len -= 1;
  • 0497 //skip the leading whitespace characters in following line
  • 0498 skipWhiteSpace = true;
  • 0499 appendedLineBegin = true;
  • 0500 precedingBackslash = false;
  • 0501 if (c == '\r') {
  • 0502 skipLF = true;
  • 0503 }
  • 0504 } else {
  • 0505 return len;
  • 0506 }
  • 0507 }
  • 0508 }
  • 0509 }
  • 0510 }
  • 0511
  • 0512 /*
  • 0513 * Converts encoded \uxxxx to unicode chars
  • 0514 * and changes special saved chars to their original forms
  • 0515 */
  • 0516 private String loadConvert (char[] in, int off, int len, char[] convtBuf) {
  • 0517 if (convtBuf.length < len) {
  • 0518 int newLen = len * 2;
  • 0519 if (newLen < 0) {
  • 0520 newLen = Integer.MAX_VALUE;
  • 0521 }
  • 0522 convtBuf = new char[newLen];
  • 0523 }
  • 0524 char aChar;
  • 0525 char[] out = convtBuf;
  • 0526 int outLen = 0;
  • 0527 int end = off + len;
  • 0528
  • 0529 while (off < end) {
  • 0530 aChar = in[off++];
  • 0531 if (aChar == '\\') {
  • 0532 aChar = in[off++];
  • 0533 if(aChar == 'u') {
  • 0534 // Read the xxxx
  • 0535 int value=0;
  • 0536 for (int i=0; i<4; i++) {
  • 0537 aChar = in[off++];
  • 0538 switch (aChar) {
  • 0539 case '0': case '1': case '2': case '3': case '4':
  • 0540 case '5': case '6': case '7': case '8': case '9':
  • 0541 value = (value << 4) + aChar - '0';
  • 0542 break;
  • 0543 case 'a': case 'b': case 'c':
  • 0544 case 'd': case 'e': case 'f':
  • 0545 value = (value << 4) + 10 + aChar - 'a';
  • 0546 break;
  • 0547 case 'A': case 'B': case 'C':
  • 0548 case 'D': case 'E': case 'F':
  • 0549 value = (value << 4) + 10 + aChar - 'A';
  • 0550 break;
  • 0551 default:
  • 0552 throw new IllegalArgumentException(
  • 0553 "Malformed \\uxxxx encoding.");
  • 0554 }
  • 0555 }
  • 0556 out[outLen++] = (char)value;
  • 0557 } else {
  • 0558 if (aChar == 't') aChar = '\t';
  • 0559 else if (aChar == 'r') aChar = '\r';
  • 0560 else if (aChar == 'n') aChar = '\n';
  • 0561 else if (aChar == 'f') aChar = '\f';
  • 0562 out[outLen++] = aChar;
  • 0563 }
  • 0564 } else {
  • 0565 out[outLen++] = (char)aChar;
  • 0566 }
  • 0567 }
  • 0568 return new String (out, 0, outLen);
  • 0569 }
  • 0570
  • 0571 /*
  • 0572 * Converts unicodes to encoded \uxxxx and escapes
  • 0573 * special characters with a preceding slash
  • 0574 */
  • 0575 private String saveConvert(String theString,
  • 0576 boolean escapeSpace,
  • 0577 boolean escapeUnicode) {
  • 0578 int len = theString.length();
  • 0579 int bufLen = len * 2;
  • 0580 if (bufLen < 0) {
  • 0581 bufLen = Integer.MAX_VALUE;
  • 0582 }
  • 0583 StringBuffer outBuffer = new StringBuffer(bufLen);
  • 0584
  • 0585 for(int x=0; x<len; x++) {
  • 0586 char aChar = theString.charAt(x);
  • 0587 // Handle common case first, selecting largest block that
  • 0588 // avoids the specials below
  • 0589 if ((aChar > 61) && (aChar < 127)) {
  • 0590 if (aChar == '\\') {
  • 0591 outBuffer.append('\\'); outBuffer.append('\\');
  • 0592 continue;
  • 0593 }
  • 0594 outBuffer.append(aChar);
  • 0595 continue;
  • 0596 }
  • 0597 switch(aChar) {
  • 0598 case ' ':
  • 0599 if (x == 0 || escapeSpace)
  • 0600 outBuffer.append('\\');
  • 0601 outBuffer.append(' ');
  • 0602 break;
  • 0603 case '\t':outBuffer.append('\\'); outBuffer.append('t');
  • 0604 break;
  • 0605 case '\n':outBuffer.append('\\'); outBuffer.append('n');
  • 0606 break;
  • 0607 case '\r':outBuffer.append('\\'); outBuffer.append('r');
  • 0608 break;
  • 0609 case '\f':outBuffer.append('\\'); outBuffer.append('f');
  • 0610 break;
  • 0611 case '=': // Fall through
  • 0612 case ':': // Fall through
  • 0613 case '#': // Fall through
  • 0614 case '!':
  • 0615 outBuffer.append('\\'); outBuffer.append(aChar);
  • 0616 break;
  • 0617 default:
  • 0618 if (((aChar < 0x0020) || (aChar > 0x007e)) & escapeUnicode ) {
  • 0619 outBuffer.append('\\');
  • 0620 outBuffer.append('u');
  • 0621 outBuffer.append(toHex((aChar >> 12) & 0xF));
  • 0622 outBuffer.append(toHex((aChar >> 8) & 0xF));
  • 0623 outBuffer.append(toHex((aChar >> 4) & 0xF));
  • 0624 outBuffer.append(toHex( aChar & 0xF));
  • 0625 } else {
  • 0626 outBuffer.append(aChar);
  • 0627 }
  • 0628 }
  • 0629 }
  • 0630 return outBuffer.toString();
  • 0631 }
  • 0632
  • 0633 private static void writeComments(BufferedWriter bw, String comments)
  • 0634 throws IOException {
  • 0635 bw.write("#");
  • 0636 int len = comments.length();
  • 0637 int current = 0;
  • 0638 int last = 0;
  • 0639 char[] uu = new char[6];
  • 0640 uu[0] = '\\';
  • 0641 uu[1] = 'u';
  • 0642 while (current < len) {
  • 0643 char c = comments.charAt(current);
  • 0644 if (c > '\u00ff' || c == '\n' || c == '\r') {
  • 0645 if (last != current)
  • 0646 bw.write(comments.substring(last, current));
  • 0647 if (c > '\u00ff') {
  • 0648 uu[2] = toHex((c >> 12) & 0xf);
  • 0649 uu[3] = toHex((c >> 8) & 0xf);
  • 0650 uu[4] = toHex((c >> 4) & 0xf);
  • 0651 uu[5] = toHex( c & 0xf);
  • 0652 bw.write(new String(uu));
  • 0653 } else {
  • 0654 bw.newLine();
  • 0655 if (c == '\r' &&
  • 0656 current != len - 1 &&
  • 0657 comments.charAt(current + 1) == '\n') {
  • 0658 current++;
  • 0659 }
  • 0660 if (current == len - 1 ||
  • 0661 (comments.charAt(current + 1) != '#' &&
  • 0662 comments.charAt(current + 1) != '!'))
  • 0663 bw.write("#");
  • 0664 }
  • 0665 last = current + 1;
  • 0666 }
  • 0667 current++;
  • 0668 }
  • 0669 if (last != current)
  • 0670 bw.write(comments.substring(last, current));
  • 0671 bw.newLine();
  • 0672 }
  • 0673
  • 0674 /**
  • 0675 * Calls the <code>store(OutputStream out, String comments)</code> method
  • 0676 * and suppresses IOExceptions that were thrown.
  • 0677 *
  • 0678 * @deprecated This method does not throw an IOException if an I/O error
  • 0679 * occurs while saving the property list. The preferred way to save a
  • 0680 * properties list is via the <code>store(OutputStream out,
  • 0681 * String comments)</code> method or the
  • 0682 * <code>storeToXML(OutputStream os, String comment)</code> method.
  • 0683 *
  • 0684 * @param out an output stream.
  • 0685 * @param comments a description of the property list.
  • 0686 * @exception ClassCastException if this <code>Properties</code> object
  • 0687 * contains any keys or values that are not
  • 0688 * <code>Strings</code>.
  • 0689 */
  • 0690 @Deprecated
  • 0691 public synchronized void save(OutputStream out, String comments) {
  • 0692 try {
  • 0693 store(out, comments);
  • 0694 } catch (IOException e) {
  • 0695 }
  • 0696 }
  • 0697
  • 0698 /**
  • 0699 * Writes this property list (key and element pairs) in this
  • 0700 * <code>Properties</code> table to the output character stream in a
  • 0701 * format suitable for using the {@link #load(java.io.Reader) load(Reader)}
  • 0702 * method.
  • 0703 * <p>
  • 0704 * Properties from the defaults table of this <code>Properties</code>
  • 0705 * table (if any) are <i>not</i> written out by this method.
  • 0706 * <p>
  • 0707 * If the comments argument is not null, then an ASCII <code>#</code>
  • 0708 * character, the comments string, and a line separator are first written
  • 0709 * to the output stream. Thus, the <code>comments</code> can serve as an
  • 0710 * identifying comment. Any one of a line feed ('\n'), a carriage
  • 0711 * return ('\r'), or a carriage return followed immediately by a line feed
  • 0712 * in comments is replaced by a line separator generated by the <code>Writer</code>
  • 0713 * and if the next character in comments is not character <code>#</code> or
  • 0714 * character <code>!</code> then an ASCII <code>#</code> is written out
  • 0715 * after that line separator.
  • 0716 * <p>
  • 0717 * Next, a comment line is always written, consisting of an ASCII
  • 0718 * <code>#</code> character, the current date and time (as if produced
  • 0719 * by the <code>toString</code> method of <code>Date</code> for the
  • 0720 * current time), and a line separator as generated by the <code>Writer</code>.
  • 0721 * <p>
  • 0722 * Then every entry in this <code>Properties</code> table is
  • 0723 * written out, one per line. For each entry the key string is
  • 0724 * written, then an ASCII <code>=</code>, then the associated
  • 0725 * element string. For the key, all space characters are
  • 0726 * written with a preceding <code>\</code> character. For the
  • 0727 * element, leading space characters, but not embedded or trailing
  • 0728 * space characters, are written with a preceding <code>\</code>
  • 0729 * character. The key and element characters <code>#</code>,
  • 0730 * <code>!</code>, <code>=</code>, and <code>:</code> are written
  • 0731 * with a preceding backslash to ensure that they are properly loaded.
  • 0732 * <p>
  • 0733 * After the entries have been written, the output stream is flushed.
  • 0734 * The output stream remains open after this method returns.
  • 0735 * <p>
  • 0736 *
  • 0737 * @param writer an output character stream writer.
  • 0738 * @param comments a description of the property list.
  • 0739 * @exception IOException if writing this property list to the specified
  • 0740 * output stream throws an <tt>IOException</tt>.
  • 0741 * @exception ClassCastException if this <code>Properties</code> object
  • 0742 * contains any keys or values that are not <code>Strings</code>.
  • 0743 * @exception NullPointerException if <code>writer</code> is null.
  • 0744 * @since 1.6
  • 0745 */
  • 0746 public void store(Writer writer, String comments)
  • 0747 throws IOException
  • 0748 {
  • 0749 store0((writer instanceof BufferedWriter)?(BufferedWriter)writer
  • 0750 : new BufferedWriter(writer),
  • 0751 comments,
  • 0752 false);
  • 0753 }
  • 0754
  • 0755 /**
  • 0756 * Writes this property list (key and element pairs) in this
  • 0757 * <code>Properties</code> table to the output stream in a format suitable
  • 0758 * for loading into a <code>Properties</code> table using the
  • 0759 * {@link #load(InputStream) load(InputStream)} method.
  • 0760 * <p>
  • 0761 * Properties from the defaults table of this <code>Properties</code>
  • 0762 * table (if any) are <i>not</i> written out by this method.
  • 0763 * <p>
  • 0764 * This method outputs the comments, properties keys and values in
  • 0765 * the same format as specified in
  • 0766 * {@link #store(java.io.Writer, java.lang.String) store(Writer)},
  • 0767 * with the following differences:
  • 0768 * <ul>
  • 0769 * <li>The stream is written using the ISO 8859-1 character encoding.
  • 0770 *
  • 0771 * <li>Characters not in Latin-1 in the comments are written as
  • 0772 * <code>\u</code><i>xxxx</i> for their appropriate unicode
  • 0773 * hexadecimal value <i>xxxx</i>.
  • 0774 *
  • 0775 * <li>Characters less than <code>\u0020</code> and characters greater
  • 0776 * than <code>\u007E</code> in property keys or values are written
  • 0777 * as <code>\u</code><i>xxxx</i> for the appropriate hexadecimal
  • 0778 * value <i>xxxx</i>.
  • 0779 * </ul>
  • 0780 * <p>
  • 0781 * After the entries have been written, the output stream is flushed.
  • 0782 * The output stream remains open after this method returns.
  • 0783 * <p>
  • 0784 * @param out an output stream.
  • 0785 * @param comments a description of the property list.
  • 0786 * @exception IOException if writing this property list to the specified
  • 0787 * output stream throws an <tt>IOException</tt>.
  • 0788 * @exception ClassCastException if this <code>Properties</code> object
  • 0789 * contains any keys or values that are not <code>Strings</code>.
  • 0790 * @exception NullPointerException if <code>out</code> is null.
  • 0791 * @since 1.2
  • 0792 */
  • 0793 public void store(OutputStream out, String comments)
  • 0794 throws IOException
  • 0795 {
  • 0796 store0(new BufferedWriter(new OutputStreamWriter(out, "8859_1")),
  • 0797 comments,
  • 0798 true);
  • 0799 }
  • 0800
  • 0801 private void store0(BufferedWriter bw, String comments, boolean escUnicode)
  • 0802 throws IOException
  • 0803 {
  • 0804 if (comments != null) {
  • 0805 writeComments(bw, comments);
  • 0806 }
  • 0807 bw.write("#" + new Date().toString());
  • 0808 bw.newLine();
  • 0809 synchronized (this) {
  • 0810 for (Enumeration e = keys(); e.hasMoreElements();) {
  • 0811 String key = (String)e.nextElement();
  • 0812 String val = (String)get(key);
  • 0813 key = saveConvert(key, true, escUnicode);
  • 0814 /* No need to escape embedded and trailing spaces for value, hence
  • 0815 * pass false to flag.
  • 0816 */
  • 0817 val = saveConvert(val, false, escUnicode);
  • 0818 bw.write(key + "=" + val);
  • 0819 bw.newLine();
  • 0820 }
  • 0821 }
  • 0822 bw.flush();
  • 0823 }
  • 0824
  • 0825 /**
  • 0826 * Loads all of the properties represented by the XML document on the
  • 0827 * specified input stream into this properties table.
  • 0828 *
  • 0829 * <p>The XML document must have the following DOCTYPE declaration:
  • 0830 * <pre>
  • 0831 * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
  • 0832 * </pre>
  • 0833 * Furthermore, the document must satisfy the properties DTD described
  • 0834 * above.
  • 0835 *
  • 0836 * <p>The specified stream is closed after this method returns.
  • 0837 *
  • 0838 * @param in the input stream from which to read the XML document.
  • 0839 * @throws IOException if reading from the specified input stream
  • 0840 * results in an <tt>IOException</tt>.
  • 0841 * @throws InvalidPropertiesFormatException Data on input stream does not
  • 0842 * constitute a valid XML document with the mandated document type.
  • 0843 * @throws NullPointerException if <code>in</code> is null.
  • 0844 * @see #storeToXML(OutputStream, String, String)
  • 0845 * @since 1.5
  • 0846 */
  • 0847 public synchronized void loadFromXML(InputStream in)
  • 0848 throws IOException, InvalidPropertiesFormatException
  • 0849 {
  • 0850 if (in == null)
  • 0851 throw new