1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/rt/emul/compact/src/main/java/java/util/Properties.java Thu Oct 03 15:40:35 2013 +0200
1.3 @@ -0,0 +1,1114 @@
1.4 +/*
1.5 + * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
1.7 + *
1.8 + * This code is free software; you can redistribute it and/or modify it
1.9 + * under the terms of the GNU General Public License version 2 only, as
1.10 + * published by the Free Software Foundation. Oracle designates this
1.11 + * particular file as subject to the "Classpath" exception as provided
1.12 + * by Oracle in the LICENSE file that accompanied this code.
1.13 + *
1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
1.17 + * version 2 for more details (a copy is included in the LICENSE file that
1.18 + * accompanied this code).
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License version
1.21 + * 2 along with this work; if not, write to the Free Software Foundation,
1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1.23 + *
1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
1.25 + * or visit www.oracle.com if you need additional information or have any
1.26 + * questions.
1.27 + */
1.28 +
1.29 +package java.util;
1.30 +
1.31 +import java.io.IOException;
1.32 +import java.io.PrintStream;
1.33 +import java.io.PrintWriter;
1.34 +import java.io.InputStream;
1.35 +import java.io.OutputStream;
1.36 +import java.io.Reader;
1.37 +import java.io.Writer;
1.38 +import java.io.OutputStreamWriter;
1.39 +import java.io.BufferedWriter;
1.40 +
1.41 +/**
1.42 + * The <code>Properties</code> class represents a persistent set of
1.43 + * properties. The <code>Properties</code> can be saved to a stream
1.44 + * or loaded from a stream. Each key and its corresponding value in
1.45 + * the property list is a string.
1.46 + * <p>
1.47 + * A property list can contain another property list as its
1.48 + * "defaults"; this second property list is searched if
1.49 + * the property key is not found in the original property list.
1.50 + * <p>
1.51 + * Because <code>Properties</code> inherits from <code>Hashtable</code>, the
1.52 + * <code>put</code> and <code>putAll</code> methods can be applied to a
1.53 + * <code>Properties</code> object. Their use is strongly discouraged as they
1.54 + * allow the caller to insert entries whose keys or values are not
1.55 + * <code>Strings</code>. The <code>setProperty</code> method should be used
1.56 + * instead. If the <code>store</code> or <code>save</code> method is called
1.57 + * on a "compromised" <code>Properties</code> object that contains a
1.58 + * non-<code>String</code> key or value, the call will fail. Similarly,
1.59 + * the call to the <code>propertyNames</code> or <code>list</code> method
1.60 + * will fail if it is called on a "compromised" <code>Properties</code>
1.61 + * object that contains a non-<code>String</code> key.
1.62 + *
1.63 + * <p>
1.64 + * The {@link #load(java.io.Reader) load(Reader)} <tt>/</tt>
1.65 + * {@link #store(java.io.Writer, java.lang.String) store(Writer, String)}
1.66 + * methods load and store properties from and to a character based stream
1.67 + * in a simple line-oriented format specified below.
1.68 + *
1.69 + * The {@link #load(java.io.InputStream) load(InputStream)} <tt>/</tt>
1.70 + * {@link #store(java.io.OutputStream, java.lang.String) store(OutputStream, String)}
1.71 + * methods work the same way as the load(Reader)/store(Writer, String) pair, except
1.72 + * the input/output stream is encoded in ISO 8859-1 character encoding.
1.73 + * Characters that cannot be directly represented in this encoding can be written using
1.74 + * Unicode escapes as defined in section 3.3 of
1.75 + * <cite>The Java™ Language Specification</cite>;
1.76 + * only a single 'u' character is allowed in an escape
1.77 + * sequence. The native2ascii tool can be used to convert property files to and
1.78 + * from other character encodings.
1.79 + *
1.80 + * <p> The {@link #loadFromXML(InputStream)} and {@link
1.81 + * #storeToXML(OutputStream, String, String)} methods load and store properties
1.82 + * in a simple XML format. By default the UTF-8 character encoding is used,
1.83 + * however a specific encoding may be specified if required. An XML properties
1.84 + * document has the following DOCTYPE declaration:
1.85 + *
1.86 + * <pre>
1.87 + * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
1.88 + * </pre>
1.89 + * Note that the system URI (http://java.sun.com/dtd/properties.dtd) is
1.90 + * <i>not</i> accessed when exporting or importing properties; it merely
1.91 + * serves as a string to uniquely identify the DTD, which is:
1.92 + * <pre>
1.93 + * <?xml version="1.0" encoding="UTF-8"?>
1.94 + *
1.95 + * <!-- DTD for properties -->
1.96 + *
1.97 + * <!ELEMENT properties ( comment?, entry* ) >
1.98 + *
1.99 + * <!ATTLIST properties version CDATA #FIXED "1.0">
1.100 + *
1.101 + * <!ELEMENT comment (#PCDATA) >
1.102 + *
1.103 + * <!ELEMENT entry (#PCDATA) >
1.104 + *
1.105 + * <!ATTLIST entry key CDATA #REQUIRED>
1.106 + * </pre>
1.107 + *
1.108 + * <p>This class is thread-safe: multiple threads can share a single
1.109 + * <tt>Properties</tt> object without the need for external synchronization.
1.110 + *
1.111 + * @see <a href="../../../technotes/tools/solaris/native2ascii.html">native2ascii tool for Solaris</a>
1.112 + * @see <a href="../../../technotes/tools/windows/native2ascii.html">native2ascii tool for Windows</a>
1.113 + *
1.114 + * @author Arthur van Hoff
1.115 + * @author Michael McCloskey
1.116 + * @author Xueming Shen
1.117 + * @since JDK1.0
1.118 + */
1.119 +public
1.120 +class Properties extends Hashtable<Object,Object> {
1.121 + /**
1.122 + * use serialVersionUID from JDK 1.1.X for interoperability
1.123 + */
1.124 + private static final long serialVersionUID = 4112578634029874840L;
1.125 +
1.126 + /**
1.127 + * A property list that contains default values for any keys not
1.128 + * found in this property list.
1.129 + *
1.130 + * @serial
1.131 + */
1.132 + protected Properties defaults;
1.133 +
1.134 + /**
1.135 + * Creates an empty property list with no default values.
1.136 + */
1.137 + public Properties() {
1.138 + this(null);
1.139 + }
1.140 +
1.141 + /**
1.142 + * Creates an empty property list with the specified defaults.
1.143 + *
1.144 + * @param defaults the defaults.
1.145 + */
1.146 + public Properties(Properties defaults) {
1.147 + this.defaults = defaults;
1.148 + }
1.149 +
1.150 + /**
1.151 + * Calls the <tt>Hashtable</tt> method <code>put</code>. Provided for
1.152 + * parallelism with the <tt>getProperty</tt> method. Enforces use of
1.153 + * strings for property keys and values. The value returned is the
1.154 + * result of the <tt>Hashtable</tt> call to <code>put</code>.
1.155 + *
1.156 + * @param key the key to be placed into this property list.
1.157 + * @param value the value corresponding to <tt>key</tt>.
1.158 + * @return the previous value of the specified key in this property
1.159 + * list, or <code>null</code> if it did not have one.
1.160 + * @see #getProperty
1.161 + * @since 1.2
1.162 + */
1.163 + public synchronized Object setProperty(String key, String value) {
1.164 + return put(key, value);
1.165 + }
1.166 +
1.167 +
1.168 + /**
1.169 + * Reads a property list (key and element pairs) from the input
1.170 + * character stream in a simple line-oriented format.
1.171 + * <p>
1.172 + * Properties are processed in terms of lines. There are two
1.173 + * kinds of line, <i>natural lines</i> and <i>logical lines</i>.
1.174 + * A natural line is defined as a line of
1.175 + * characters that is terminated either by a set of line terminator
1.176 + * characters (<code>\n</code> or <code>\r</code> or <code>\r\n</code>)
1.177 + * or by the end of the stream. A natural line may be either a blank line,
1.178 + * a comment line, or hold all or some of a key-element pair. A logical
1.179 + * line holds all the data of a key-element pair, which may be spread
1.180 + * out across several adjacent natural lines by escaping
1.181 + * the line terminator sequence with a backslash character
1.182 + * <code>\</code>. Note that a comment line cannot be extended
1.183 + * in this manner; every natural line that is a comment must have
1.184 + * its own comment indicator, as described below. Lines are read from
1.185 + * input until the end of the stream is reached.
1.186 + *
1.187 + * <p>
1.188 + * A natural line that contains only white space characters is
1.189 + * considered blank and is ignored. A comment line has an ASCII
1.190 + * <code>'#'</code> or <code>'!'</code> as its first non-white
1.191 + * space character; comment lines are also ignored and do not
1.192 + * encode key-element information. In addition to line
1.193 + * terminators, this format considers the characters space
1.194 + * (<code>' '</code>, <code>'\u0020'</code>), tab
1.195 + * (<code>'\t'</code>, <code>'\u0009'</code>), and form feed
1.196 + * (<code>'\f'</code>, <code>'\u000C'</code>) to be white
1.197 + * space.
1.198 + *
1.199 + * <p>
1.200 + * If a logical line is spread across several natural lines, the
1.201 + * backslash escaping the line terminator sequence, the line
1.202 + * terminator sequence, and any white space at the start of the
1.203 + * following line have no affect on the key or element values.
1.204 + * The remainder of the discussion of key and element parsing
1.205 + * (when loading) will assume all the characters constituting
1.206 + * the key and element appear on a single natural line after
1.207 + * line continuation characters have been removed. Note that
1.208 + * it is <i>not</i> sufficient to only examine the character
1.209 + * preceding a line terminator sequence to decide if the line
1.210 + * terminator is escaped; there must be an odd number of
1.211 + * contiguous backslashes for the line terminator to be escaped.
1.212 + * Since the input is processed from left to right, a
1.213 + * non-zero even number of 2<i>n</i> contiguous backslashes
1.214 + * before a line terminator (or elsewhere) encodes <i>n</i>
1.215 + * backslashes after escape processing.
1.216 + *
1.217 + * <p>
1.218 + * The key contains all of the characters in the line starting
1.219 + * with the first non-white space character and up to, but not
1.220 + * including, the first unescaped <code>'='</code>,
1.221 + * <code>':'</code>, or white space character other than a line
1.222 + * terminator. All of these key termination characters may be
1.223 + * included in the key by escaping them with a preceding backslash
1.224 + * character; for example,<p>
1.225 + *
1.226 + * <code>\:\=</code><p>
1.227 + *
1.228 + * would be the two-character key <code>":="</code>. Line
1.229 + * terminator characters can be included using <code>\r</code> and
1.230 + * <code>\n</code> escape sequences. Any white space after the
1.231 + * key is skipped; if the first non-white space character after
1.232 + * the key is <code>'='</code> or <code>':'</code>, then it is
1.233 + * ignored and any white space characters after it are also
1.234 + * skipped. All remaining characters on the line become part of
1.235 + * the associated element string; if there are no remaining
1.236 + * characters, the element is the empty string
1.237 + * <code>""</code>. Once the raw character sequences
1.238 + * constituting the key and element are identified, escape
1.239 + * processing is performed as described above.
1.240 + *
1.241 + * <p>
1.242 + * As an example, each of the following three lines specifies the key
1.243 + * <code>"Truth"</code> and the associated element value
1.244 + * <code>"Beauty"</code>:
1.245 + * <p>
1.246 + * <pre>
1.247 + * Truth = Beauty
1.248 + * Truth:Beauty
1.249 + * Truth :Beauty
1.250 + * </pre>
1.251 + * As another example, the following three lines specify a single
1.252 + * property:
1.253 + * <p>
1.254 + * <pre>
1.255 + * fruits apple, banana, pear, \
1.256 + * cantaloupe, watermelon, \
1.257 + * kiwi, mango
1.258 + * </pre>
1.259 + * The key is <code>"fruits"</code> and the associated element is:
1.260 + * <p>
1.261 + * <pre>"apple, banana, pear, cantaloupe, watermelon, kiwi, mango"</pre>
1.262 + * Note that a space appears before each <code>\</code> so that a space
1.263 + * will appear after each comma in the final result; the <code>\</code>,
1.264 + * line terminator, and leading white space on the continuation line are
1.265 + * merely discarded and are <i>not</i> replaced by one or more other
1.266 + * characters.
1.267 + * <p>
1.268 + * As a third example, the line:
1.269 + * <p>
1.270 + * <pre>cheeses
1.271 + * </pre>
1.272 + * specifies that the key is <code>"cheeses"</code> and the associated
1.273 + * element is the empty string <code>""</code>.<p>
1.274 + * <p>
1.275 + *
1.276 + * <a name="unicodeescapes"></a>
1.277 + * Characters in keys and elements can be represented in escape
1.278 + * sequences similar to those used for character and string literals
1.279 + * (see sections 3.3 and 3.10.6 of
1.280 + * <cite>The Java™ Language Specification</cite>).
1.281 + *
1.282 + * The differences from the character escape sequences and Unicode
1.283 + * escapes used for characters and strings are:
1.284 + *
1.285 + * <ul>
1.286 + * <li> Octal escapes are not recognized.
1.287 + *
1.288 + * <li> The character sequence <code>\b</code> does <i>not</i>
1.289 + * represent a backspace character.
1.290 + *
1.291 + * <li> The method does not treat a backslash character,
1.292 + * <code>\</code>, before a non-valid escape character as an
1.293 + * error; the backslash is silently dropped. For example, in a
1.294 + * Java string the sequence <code>"\z"</code> would cause a
1.295 + * compile time error. In contrast, this method silently drops
1.296 + * the backslash. Therefore, this method treats the two character
1.297 + * sequence <code>"\b"</code> as equivalent to the single
1.298 + * character <code>'b'</code>.
1.299 + *
1.300 + * <li> Escapes are not necessary for single and double quotes;
1.301 + * however, by the rule above, single and double quote characters
1.302 + * preceded by a backslash still yield single and double quote
1.303 + * characters, respectively.
1.304 + *
1.305 + * <li> Only a single 'u' character is allowed in a Uniocde escape
1.306 + * sequence.
1.307 + *
1.308 + * </ul>
1.309 + * <p>
1.310 + * The specified stream remains open after this method returns.
1.311 + *
1.312 + * @param reader the input character stream.
1.313 + * @throws IOException if an error occurred when reading from the
1.314 + * input stream.
1.315 + * @throws IllegalArgumentException if a malformed Unicode escape
1.316 + * appears in the input.
1.317 + * @since 1.6
1.318 + */
1.319 + public synchronized void load(Reader reader) throws IOException {
1.320 + load0(new LineReader(reader));
1.321 + }
1.322 +
1.323 + /**
1.324 + * Reads a property list (key and element pairs) from the input
1.325 + * byte stream. The input stream is in a simple line-oriented
1.326 + * format as specified in
1.327 + * {@link #load(java.io.Reader) load(Reader)} and is assumed to use
1.328 + * the ISO 8859-1 character encoding; that is each byte is one Latin1
1.329 + * character. Characters not in Latin1, and certain special characters,
1.330 + * are represented in keys and elements using Unicode escapes as defined in
1.331 + * section 3.3 of
1.332 + * <cite>The Java™ Language Specification</cite>.
1.333 + * <p>
1.334 + * The specified stream remains open after this method returns.
1.335 + *
1.336 + * @param inStream the input stream.
1.337 + * @exception IOException if an error occurred when reading from the
1.338 + * input stream.
1.339 + * @throws IllegalArgumentException if the input stream contains a
1.340 + * malformed Unicode escape sequence.
1.341 + * @since 1.2
1.342 + */
1.343 + public synchronized void load(InputStream inStream) throws IOException {
1.344 + load0(new LineReader(inStream));
1.345 + }
1.346 +
1.347 + private void load0 (LineReader lr) throws IOException {
1.348 + char[] convtBuf = new char[1024];
1.349 + int limit;
1.350 + int keyLen;
1.351 + int valueStart;
1.352 + char c;
1.353 + boolean hasSep;
1.354 + boolean precedingBackslash;
1.355 +
1.356 + while ((limit = lr.readLine()) >= 0) {
1.357 + c = 0;
1.358 + keyLen = 0;
1.359 + valueStart = limit;
1.360 + hasSep = false;
1.361 +
1.362 + //System.out.println("line=<" + new String(lineBuf, 0, limit) + ">");
1.363 + precedingBackslash = false;
1.364 + while (keyLen < limit) {
1.365 + c = lr.lineBuf[keyLen];
1.366 + //need check if escaped.
1.367 + if ((c == '=' || c == ':') && !precedingBackslash) {
1.368 + valueStart = keyLen + 1;
1.369 + hasSep = true;
1.370 + break;
1.371 + } else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) {
1.372 + valueStart = keyLen + 1;
1.373 + break;
1.374 + }
1.375 + if (c == '\\') {
1.376 + precedingBackslash = !precedingBackslash;
1.377 + } else {
1.378 + precedingBackslash = false;
1.379 + }
1.380 + keyLen++;
1.381 + }
1.382 + while (valueStart < limit) {
1.383 + c = lr.lineBuf[valueStart];
1.384 + if (c != ' ' && c != '\t' && c != '\f') {
1.385 + if (!hasSep && (c == '=' || c == ':')) {
1.386 + hasSep = true;
1.387 + } else {
1.388 + break;
1.389 + }
1.390 + }
1.391 + valueStart++;
1.392 + }
1.393 + String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
1.394 + String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
1.395 + put(key, value);
1.396 + }
1.397 + }
1.398 +
1.399 + /* Read in a "logical line" from an InputStream/Reader, skip all comment
1.400 + * and blank lines and filter out those leading whitespace characters
1.401 + * (\u0020, \u0009 and \u000c) from the beginning of a "natural line".
1.402 + * Method returns the char length of the "logical line" and stores
1.403 + * the line in "lineBuf".
1.404 + */
1.405 + class LineReader {
1.406 + public LineReader(InputStream inStream) {
1.407 + this.inStream = inStream;
1.408 + inByteBuf = new byte[8192];
1.409 + }
1.410 +
1.411 + public LineReader(Reader reader) {
1.412 + this.reader = reader;
1.413 + inCharBuf = new char[8192];
1.414 + }
1.415 +
1.416 + byte[] inByteBuf;
1.417 + char[] inCharBuf;
1.418 + char[] lineBuf = new char[1024];
1.419 + int inLimit = 0;
1.420 + int inOff = 0;
1.421 + InputStream inStream;
1.422 + Reader reader;
1.423 +
1.424 + int readLine() throws IOException {
1.425 + int len = 0;
1.426 + char c = 0;
1.427 +
1.428 + boolean skipWhiteSpace = true;
1.429 + boolean isCommentLine = false;
1.430 + boolean isNewLine = true;
1.431 + boolean appendedLineBegin = false;
1.432 + boolean precedingBackslash = false;
1.433 + boolean skipLF = false;
1.434 +
1.435 + while (true) {
1.436 + if (inOff >= inLimit) {
1.437 + inLimit = (inStream==null)?reader.read(inCharBuf)
1.438 + :inStream.read(inByteBuf);
1.439 + inOff = 0;
1.440 + if (inLimit <= 0) {
1.441 + if (len == 0 || isCommentLine) {
1.442 + return -1;
1.443 + }
1.444 + return len;
1.445 + }
1.446 + }
1.447 + if (inStream != null) {
1.448 + //The line below is equivalent to calling a
1.449 + //ISO8859-1 decoder.
1.450 + c = (char) (0xff & inByteBuf[inOff++]);
1.451 + } else {
1.452 + c = inCharBuf[inOff++];
1.453 + }
1.454 + if (skipLF) {
1.455 + skipLF = false;
1.456 + if (c == '\n') {
1.457 + continue;
1.458 + }
1.459 + }
1.460 + if (skipWhiteSpace) {
1.461 + if (c == ' ' || c == '\t' || c == '\f') {
1.462 + continue;
1.463 + }
1.464 + if (!appendedLineBegin && (c == '\r' || c == '\n')) {
1.465 + continue;
1.466 + }
1.467 + skipWhiteSpace = false;
1.468 + appendedLineBegin = false;
1.469 + }
1.470 + if (isNewLine) {
1.471 + isNewLine = false;
1.472 + if (c == '#' || c == '!') {
1.473 + isCommentLine = true;
1.474 + continue;
1.475 + }
1.476 + }
1.477 +
1.478 + if (c != '\n' && c != '\r') {
1.479 + lineBuf[len++] = c;
1.480 + if (len == lineBuf.length) {
1.481 + int newLength = lineBuf.length * 2;
1.482 + if (newLength < 0) {
1.483 + newLength = Integer.MAX_VALUE;
1.484 + }
1.485 + char[] buf = new char[newLength];
1.486 + System.arraycopy(lineBuf, 0, buf, 0, lineBuf.length);
1.487 + lineBuf = buf;
1.488 + }
1.489 + //flip the preceding backslash flag
1.490 + if (c == '\\') {
1.491 + precedingBackslash = !precedingBackslash;
1.492 + } else {
1.493 + precedingBackslash = false;
1.494 + }
1.495 + }
1.496 + else {
1.497 + // reached EOL
1.498 + if (isCommentLine || len == 0) {
1.499 + isCommentLine = false;
1.500 + isNewLine = true;
1.501 + skipWhiteSpace = true;
1.502 + len = 0;
1.503 + continue;
1.504 + }
1.505 + if (inOff >= inLimit) {
1.506 + inLimit = (inStream==null)
1.507 + ?reader.read(inCharBuf)
1.508 + :inStream.read(inByteBuf);
1.509 + inOff = 0;
1.510 + if (inLimit <= 0) {
1.511 + return len;
1.512 + }
1.513 + }
1.514 + if (precedingBackslash) {
1.515 + len -= 1;
1.516 + //skip the leading whitespace characters in following line
1.517 + skipWhiteSpace = true;
1.518 + appendedLineBegin = true;
1.519 + precedingBackslash = false;
1.520 + if (c == '\r') {
1.521 + skipLF = true;
1.522 + }
1.523 + } else {
1.524 + return len;
1.525 + }
1.526 + }
1.527 + }
1.528 + }
1.529 + }
1.530 +
1.531 + /*
1.532 + * Converts encoded \uxxxx to unicode chars
1.533 + * and changes special saved chars to their original forms
1.534 + */
1.535 + private String loadConvert (char[] in, int off, int len, char[] convtBuf) {
1.536 + if (convtBuf.length < len) {
1.537 + int newLen = len * 2;
1.538 + if (newLen < 0) {
1.539 + newLen = Integer.MAX_VALUE;
1.540 + }
1.541 + convtBuf = new char[newLen];
1.542 + }
1.543 + char aChar;
1.544 + char[] out = convtBuf;
1.545 + int outLen = 0;
1.546 + int end = off + len;
1.547 +
1.548 + while (off < end) {
1.549 + aChar = in[off++];
1.550 + if (aChar == '\\') {
1.551 + aChar = in[off++];
1.552 + if(aChar == 'u') {
1.553 + // Read the xxxx
1.554 + int value=0;
1.555 + for (int i=0; i<4; i++) {
1.556 + aChar = in[off++];
1.557 + switch (aChar) {
1.558 + case '0': case '1': case '2': case '3': case '4':
1.559 + case '5': case '6': case '7': case '8': case '9':
1.560 + value = (value << 4) + aChar - '0';
1.561 + break;
1.562 + case 'a': case 'b': case 'c':
1.563 + case 'd': case 'e': case 'f':
1.564 + value = (value << 4) + 10 + aChar - 'a';
1.565 + break;
1.566 + case 'A': case 'B': case 'C':
1.567 + case 'D': case 'E': case 'F':
1.568 + value = (value << 4) + 10 + aChar - 'A';
1.569 + break;
1.570 + default:
1.571 + throw new IllegalArgumentException(
1.572 + "Malformed \\uxxxx encoding.");
1.573 + }
1.574 + }
1.575 + out[outLen++] = (char)value;
1.576 + } else {
1.577 + if (aChar == 't') aChar = '\t';
1.578 + else if (aChar == 'r') aChar = '\r';
1.579 + else if (aChar == 'n') aChar = '\n';
1.580 + else if (aChar == 'f') aChar = '\f';
1.581 + out[outLen++] = aChar;
1.582 + }
1.583 + } else {
1.584 + out[outLen++] = aChar;
1.585 + }
1.586 + }
1.587 + return new String (out, 0, outLen);
1.588 + }
1.589 +
1.590 + /*
1.591 + * Converts unicodes to encoded \uxxxx and escapes
1.592 + * special characters with a preceding slash
1.593 + */
1.594 + private String saveConvert(String theString,
1.595 + boolean escapeSpace,
1.596 + boolean escapeUnicode) {
1.597 + int len = theString.length();
1.598 + int bufLen = len * 2;
1.599 + if (bufLen < 0) {
1.600 + bufLen = Integer.MAX_VALUE;
1.601 + }
1.602 + StringBuffer outBuffer = new StringBuffer(bufLen);
1.603 +
1.604 + for(int x=0; x<len; x++) {
1.605 + char aChar = theString.charAt(x);
1.606 + // Handle common case first, selecting largest block that
1.607 + // avoids the specials below
1.608 + if ((aChar > 61) && (aChar < 127)) {
1.609 + if (aChar == '\\') {
1.610 + outBuffer.append('\\'); outBuffer.append('\\');
1.611 + continue;
1.612 + }
1.613 + outBuffer.append(aChar);
1.614 + continue;
1.615 + }
1.616 + switch(aChar) {
1.617 + case ' ':
1.618 + if (x == 0 || escapeSpace)
1.619 + outBuffer.append('\\');
1.620 + outBuffer.append(' ');
1.621 + break;
1.622 + case '\t':outBuffer.append('\\'); outBuffer.append('t');
1.623 + break;
1.624 + case '\n':outBuffer.append('\\'); outBuffer.append('n');
1.625 + break;
1.626 + case '\r':outBuffer.append('\\'); outBuffer.append('r');
1.627 + break;
1.628 + case '\f':outBuffer.append('\\'); outBuffer.append('f');
1.629 + break;
1.630 + case '=': // Fall through
1.631 + case ':': // Fall through
1.632 + case '#': // Fall through
1.633 + case '!':
1.634 + outBuffer.append('\\'); outBuffer.append(aChar);
1.635 + break;
1.636 + default:
1.637 + if (((aChar < 0x0020) || (aChar > 0x007e)) & escapeUnicode ) {
1.638 + outBuffer.append('\\');
1.639 + outBuffer.append('u');
1.640 + outBuffer.append(toHex((aChar >> 12) & 0xF));
1.641 + outBuffer.append(toHex((aChar >> 8) & 0xF));
1.642 + outBuffer.append(toHex((aChar >> 4) & 0xF));
1.643 + outBuffer.append(toHex( aChar & 0xF));
1.644 + } else {
1.645 + outBuffer.append(aChar);
1.646 + }
1.647 + }
1.648 + }
1.649 + return outBuffer.toString();
1.650 + }
1.651 +
1.652 + private static void writeComments(BufferedWriter bw, String comments)
1.653 + throws IOException {
1.654 + bw.write("#");
1.655 + int len = comments.length();
1.656 + int current = 0;
1.657 + int last = 0;
1.658 + char[] uu = new char[6];
1.659 + uu[0] = '\\';
1.660 + uu[1] = 'u';
1.661 + while (current < len) {
1.662 + char c = comments.charAt(current);
1.663 + if (c > '\u00ff' || c == '\n' || c == '\r') {
1.664 + if (last != current)
1.665 + bw.write(comments.substring(last, current));
1.666 + if (c > '\u00ff') {
1.667 + uu[2] = toHex((c >> 12) & 0xf);
1.668 + uu[3] = toHex((c >> 8) & 0xf);
1.669 + uu[4] = toHex((c >> 4) & 0xf);
1.670 + uu[5] = toHex( c & 0xf);
1.671 + bw.write(new String(uu));
1.672 + } else {
1.673 + bw.newLine();
1.674 + if (c == '\r' &&
1.675 + current != len - 1 &&
1.676 + comments.charAt(current + 1) == '\n') {
1.677 + current++;
1.678 + }
1.679 + if (current == len - 1 ||
1.680 + (comments.charAt(current + 1) != '#' &&
1.681 + comments.charAt(current + 1) != '!'))
1.682 + bw.write("#");
1.683 + }
1.684 + last = current + 1;
1.685 + }
1.686 + current++;
1.687 + }
1.688 + if (last != current)
1.689 + bw.write(comments.substring(last, current));
1.690 + bw.newLine();
1.691 + }
1.692 +
1.693 + /**
1.694 + * Calls the <code>store(OutputStream out, String comments)</code> method
1.695 + * and suppresses IOExceptions that were thrown.
1.696 + *
1.697 + * @deprecated This method does not throw an IOException if an I/O error
1.698 + * occurs while saving the property list. The preferred way to save a
1.699 + * properties list is via the <code>store(OutputStream out,
1.700 + * String comments)</code> method or the
1.701 + * <code>storeToXML(OutputStream os, String comment)</code> method.
1.702 + *
1.703 + * @param out an output stream.
1.704 + * @param comments a description of the property list.
1.705 + * @exception ClassCastException if this <code>Properties</code> object
1.706 + * contains any keys or values that are not
1.707 + * <code>Strings</code>.
1.708 + */
1.709 + @Deprecated
1.710 + public void save(OutputStream out, String comments) {
1.711 + try {
1.712 + store(out, comments);
1.713 + } catch (IOException e) {
1.714 + }
1.715 + }
1.716 +
1.717 + /**
1.718 + * Writes this property list (key and element pairs) in this
1.719 + * <code>Properties</code> table to the output character stream in a
1.720 + * format suitable for using the {@link #load(java.io.Reader) load(Reader)}
1.721 + * method.
1.722 + * <p>
1.723 + * Properties from the defaults table of this <code>Properties</code>
1.724 + * table (if any) are <i>not</i> written out by this method.
1.725 + * <p>
1.726 + * If the comments argument is not null, then an ASCII <code>#</code>
1.727 + * character, the comments string, and a line separator are first written
1.728 + * to the output stream. Thus, the <code>comments</code> can serve as an
1.729 + * identifying comment. Any one of a line feed ('\n'), a carriage
1.730 + * return ('\r'), or a carriage return followed immediately by a line feed
1.731 + * in comments is replaced by a line separator generated by the <code>Writer</code>
1.732 + * and if the next character in comments is not character <code>#</code> or
1.733 + * character <code>!</code> then an ASCII <code>#</code> is written out
1.734 + * after that line separator.
1.735 + * <p>
1.736 + * Next, a comment line is always written, consisting of an ASCII
1.737 + * <code>#</code> character, the current date and time (as if produced
1.738 + * by the <code>toString</code> method of <code>Date</code> for the
1.739 + * current time), and a line separator as generated by the <code>Writer</code>.
1.740 + * <p>
1.741 + * Then every entry in this <code>Properties</code> table is
1.742 + * written out, one per line. For each entry the key string is
1.743 + * written, then an ASCII <code>=</code>, then the associated
1.744 + * element string. For the key, all space characters are
1.745 + * written with a preceding <code>\</code> character. For the
1.746 + * element, leading space characters, but not embedded or trailing
1.747 + * space characters, are written with a preceding <code>\</code>
1.748 + * character. The key and element characters <code>#</code>,
1.749 + * <code>!</code>, <code>=</code>, and <code>:</code> are written
1.750 + * with a preceding backslash to ensure that they are properly loaded.
1.751 + * <p>
1.752 + * After the entries have been written, the output stream is flushed.
1.753 + * The output stream remains open after this method returns.
1.754 + * <p>
1.755 + *
1.756 + * @param writer an output character stream writer.
1.757 + * @param comments a description of the property list.
1.758 + * @exception IOException if writing this property list to the specified
1.759 + * output stream throws an <tt>IOException</tt>.
1.760 + * @exception ClassCastException if this <code>Properties</code> object
1.761 + * contains any keys or values that are not <code>Strings</code>.
1.762 + * @exception NullPointerException if <code>writer</code> is null.
1.763 + * @since 1.6
1.764 + */
1.765 + public void store(Writer writer, String comments)
1.766 + throws IOException
1.767 + {
1.768 + store0((writer instanceof BufferedWriter)?(BufferedWriter)writer
1.769 + : new BufferedWriter(writer),
1.770 + comments,
1.771 + false);
1.772 + }
1.773 +
1.774 + /**
1.775 + * Writes this property list (key and element pairs) in this
1.776 + * <code>Properties</code> table to the output stream in a format suitable
1.777 + * for loading into a <code>Properties</code> table using the
1.778 + * {@link #load(InputStream) load(InputStream)} method.
1.779 + * <p>
1.780 + * Properties from the defaults table of this <code>Properties</code>
1.781 + * table (if any) are <i>not</i> written out by this method.
1.782 + * <p>
1.783 + * This method outputs the comments, properties keys and values in
1.784 + * the same format as specified in
1.785 + * {@link #store(java.io.Writer, java.lang.String) store(Writer)},
1.786 + * with the following differences:
1.787 + * <ul>
1.788 + * <li>The stream is written using the ISO 8859-1 character encoding.
1.789 + *
1.790 + * <li>Characters not in Latin-1 in the comments are written as
1.791 + * <code>\u</code><i>xxxx</i> for their appropriate unicode
1.792 + * hexadecimal value <i>xxxx</i>.
1.793 + *
1.794 + * <li>Characters less than <code>\u0020</code> and characters greater
1.795 + * than <code>\u007E</code> in property keys or values are written
1.796 + * as <code>\u</code><i>xxxx</i> for the appropriate hexadecimal
1.797 + * value <i>xxxx</i>.
1.798 + * </ul>
1.799 + * <p>
1.800 + * After the entries have been written, the output stream is flushed.
1.801 + * The output stream remains open after this method returns.
1.802 + * <p>
1.803 + * @param out an output stream.
1.804 + * @param comments a description of the property list.
1.805 + * @exception IOException if writing this property list to the specified
1.806 + * output stream throws an <tt>IOException</tt>.
1.807 + * @exception ClassCastException if this <code>Properties</code> object
1.808 + * contains any keys or values that are not <code>Strings</code>.
1.809 + * @exception NullPointerException if <code>out</code> is null.
1.810 + * @since 1.2
1.811 + */
1.812 + public void store(OutputStream out, String comments)
1.813 + throws IOException
1.814 + {
1.815 + store0(new BufferedWriter(new OutputStreamWriter(out, "8859_1")),
1.816 + comments,
1.817 + true);
1.818 + }
1.819 +
1.820 + private void store0(BufferedWriter bw, String comments, boolean escUnicode)
1.821 + throws IOException
1.822 + {
1.823 + if (comments != null) {
1.824 + writeComments(bw, comments);
1.825 + }
1.826 + bw.write("#" + new Date().toString());
1.827 + bw.newLine();
1.828 + synchronized (this) {
1.829 + for (Enumeration e = keys(); e.hasMoreElements();) {
1.830 + String key = (String)e.nextElement();
1.831 + String val = (String)get(key);
1.832 + key = saveConvert(key, true, escUnicode);
1.833 + /* No need to escape embedded and trailing spaces for value, hence
1.834 + * pass false to flag.
1.835 + */
1.836 + val = saveConvert(val, false, escUnicode);
1.837 + bw.write(key + "=" + val);
1.838 + bw.newLine();
1.839 + }
1.840 + }
1.841 + bw.flush();
1.842 + }
1.843 +
1.844 + /**
1.845 + * Loads all of the properties represented by the XML document on the
1.846 + * specified input stream into this properties table.
1.847 + *
1.848 + * <p>The XML document must have the following DOCTYPE declaration:
1.849 + * <pre>
1.850 + * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
1.851 + * </pre>
1.852 + * Furthermore, the document must satisfy the properties DTD described
1.853 + * above.
1.854 + *
1.855 + * <p>The specified stream is closed after this method returns.
1.856 + *
1.857 + * @param in the input stream from which to read the XML document.
1.858 + * @throws IOException if reading from the specified input stream
1.859 + * results in an <tt>IOException</tt>.
1.860 + * @throws InvalidPropertiesFormatException Data on input stream does not
1.861 + * constitute a valid XML document with the mandated document type.
1.862 + * @throws NullPointerException if <code>in</code> is null.
1.863 + * @see #storeToXML(OutputStream, String, String)
1.864 + * @since 1.5
1.865 + */
1.866 + public synchronized void loadFromXML(InputStream in)
1.867 + throws IOException, InvalidPropertiesFormatException
1.868 + {
1.869 + if (in == null)
1.870 + throw new NullPointerException();
1.871 + XMLUtils.load(this, in);
1.872 + in.close();
1.873 + }
1.874 +
1.875 + /**
1.876 + * Emits an XML document representing all of the properties contained
1.877 + * in this table.
1.878 + *
1.879 + * <p> An invocation of this method of the form <tt>props.storeToXML(os,
1.880 + * comment)</tt> behaves in exactly the same way as the invocation
1.881 + * <tt>props.storeToXML(os, comment, "UTF-8");</tt>.
1.882 + *
1.883 + * @param os the output stream on which to emit the XML document.
1.884 + * @param comment a description of the property list, or <code>null</code>
1.885 + * if no comment is desired.
1.886 + * @throws IOException if writing to the specified output stream
1.887 + * results in an <tt>IOException</tt>.
1.888 + * @throws NullPointerException if <code>os</code> is null.
1.889 + * @throws ClassCastException if this <code>Properties</code> object
1.890 + * contains any keys or values that are not
1.891 + * <code>Strings</code>.
1.892 + * @see #loadFromXML(InputStream)
1.893 + * @since 1.5
1.894 + */
1.895 + public void storeToXML(OutputStream os, String comment)
1.896 + throws IOException
1.897 + {
1.898 + if (os == null)
1.899 + throw new NullPointerException();
1.900 + storeToXML(os, comment, "UTF-8");
1.901 + }
1.902 +
1.903 + /**
1.904 + * Emits an XML document representing all of the properties contained
1.905 + * in this table, using the specified encoding.
1.906 + *
1.907 + * <p>The XML document will have the following DOCTYPE declaration:
1.908 + * <pre>
1.909 + * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
1.910 + * </pre>
1.911 + *
1.912 + *<p>If the specified comment is <code>null</code> then no comment
1.913 + * will be stored in the document.
1.914 + *
1.915 + * <p>The specified stream remains open after this method returns.
1.916 + *
1.917 + * @param os the output stream on which to emit the XML document.
1.918 + * @param comment a description of the property list, or <code>null</code>
1.919 + * if no comment is desired.
1.920 + * @param encoding the name of a supported
1.921 + * <a href="../lang/package-summary.html#charenc">
1.922 + * character encoding</a>
1.923 + *
1.924 + * @throws IOException if writing to the specified output stream
1.925 + * results in an <tt>IOException</tt>.
1.926 + * @throws NullPointerException if <code>os</code> is <code>null</code>,
1.927 + * or if <code>encoding</code> is <code>null</code>.
1.928 + * @throws ClassCastException if this <code>Properties</code> object
1.929 + * contains any keys or values that are not
1.930 + * <code>Strings</code>.
1.931 + * @see #loadFromXML(InputStream)
1.932 + * @since 1.5
1.933 + */
1.934 + public void storeToXML(OutputStream os, String comment, String encoding)
1.935 + throws IOException
1.936 + {
1.937 + if (os == null)
1.938 + throw new NullPointerException();
1.939 + XMLUtils.save(this, os, comment, encoding);
1.940 + }
1.941 +
1.942 + /**
1.943 + * Searches for the property with the specified key in this property list.
1.944 + * If the key is not found in this property list, the default property list,
1.945 + * and its defaults, recursively, are then checked. The method returns
1.946 + * <code>null</code> if the property is not found.
1.947 + *
1.948 + * @param key the property key.
1.949 + * @return the value in this property list with the specified key value.
1.950 + * @see #setProperty
1.951 + * @see #defaults
1.952 + */
1.953 + public String getProperty(String key) {
1.954 + Object oval = super.get(key);
1.955 + String sval = (oval instanceof String) ? (String)oval : null;
1.956 + return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval;
1.957 + }
1.958 +
1.959 + /**
1.960 + * Searches for the property with the specified key in this property list.
1.961 + * If the key is not found in this property list, the default property list,
1.962 + * and its defaults, recursively, are then checked. The method returns the
1.963 + * default value argument if the property is not found.
1.964 + *
1.965 + * @param key the hashtable key.
1.966 + * @param defaultValue a default value.
1.967 + *
1.968 + * @return the value in this property list with the specified key value.
1.969 + * @see #setProperty
1.970 + * @see #defaults
1.971 + */
1.972 + public String getProperty(String key, String defaultValue) {
1.973 + String val = getProperty(key);
1.974 + return (val == null) ? defaultValue : val;
1.975 + }
1.976 +
1.977 + /**
1.978 + * Returns an enumeration of all the keys in this property list,
1.979 + * including distinct keys in the default property list if a key
1.980 + * of the same name has not already been found from the main
1.981 + * properties list.
1.982 + *
1.983 + * @return an enumeration of all the keys in this property list, including
1.984 + * the keys in the default property list.
1.985 + * @throws ClassCastException if any key in this property list
1.986 + * is not a string.
1.987 + * @see java.util.Enumeration
1.988 + * @see java.util.Properties#defaults
1.989 + * @see #stringPropertyNames
1.990 + */
1.991 + public Enumeration<?> propertyNames() {
1.992 + Hashtable h = new Hashtable();
1.993 + enumerate(h);
1.994 + return h.keys();
1.995 + }
1.996 +
1.997 + /**
1.998 + * Returns a set of keys in this property list where
1.999 + * the key and its corresponding value are strings,
1.1000 + * including distinct keys in the default property list if a key
1.1001 + * of the same name has not already been found from the main
1.1002 + * properties list. Properties whose key or value is not
1.1003 + * of type <tt>String</tt> are omitted.
1.1004 + * <p>
1.1005 + * The returned set is not backed by the <tt>Properties</tt> object.
1.1006 + * Changes to this <tt>Properties</tt> are not reflected in the set,
1.1007 + * or vice versa.
1.1008 + *
1.1009 + * @return a set of keys in this property list where
1.1010 + * the key and its corresponding value are strings,
1.1011 + * including the keys in the default property list.
1.1012 + * @see java.util.Properties#defaults
1.1013 + * @since 1.6
1.1014 + */
1.1015 + public Set<String> stringPropertyNames() {
1.1016 + Hashtable<String, String> h = new Hashtable<>();
1.1017 + enumerateStringProperties(h);
1.1018 + return h.keySet();
1.1019 + }
1.1020 +
1.1021 + /**
1.1022 + * Prints this property list out to the specified output stream.
1.1023 + * This method is useful for debugging.
1.1024 + *
1.1025 + * @param out an output stream.
1.1026 + * @throws ClassCastException if any key in this property list
1.1027 + * is not a string.
1.1028 + */
1.1029 + public void list(PrintStream out) {
1.1030 + out.println("-- listing properties --");
1.1031 + Hashtable h = new Hashtable();
1.1032 + enumerate(h);
1.1033 + for (Enumeration e = h.keys() ; e.hasMoreElements() ;) {
1.1034 + String key = (String)e.nextElement();
1.1035 + String val = (String)h.get(key);
1.1036 + if (val.length() > 40) {
1.1037 + val = val.substring(0, 37) + "...";
1.1038 + }
1.1039 + out.println(key + "=" + val);
1.1040 + }
1.1041 + }
1.1042 +
1.1043 + /**
1.1044 + * Prints this property list out to the specified output stream.
1.1045 + * This method is useful for debugging.
1.1046 + *
1.1047 + * @param out an output stream.
1.1048 + * @throws ClassCastException if any key in this property list
1.1049 + * is not a string.
1.1050 + * @since JDK1.1
1.1051 + */
1.1052 + /*
1.1053 + * Rather than use an anonymous inner class to share common code, this
1.1054 + * method is duplicated in order to ensure that a non-1.1 compiler can
1.1055 + * compile this file.
1.1056 + */
1.1057 + public void list(PrintWriter out) {
1.1058 + out.println("-- listing properties --");
1.1059 + Hashtable h = new Hashtable();
1.1060 + enumerate(h);
1.1061 + for (Enumeration e = h.keys() ; e.hasMoreElements() ;) {
1.1062 + String key = (String)e.nextElement();
1.1063 + String val = (String)h.get(key);
1.1064 + if (val.length() > 40) {
1.1065 + val = val.substring(0, 37) + "...";
1.1066 + }
1.1067 + out.println(key + "=" + val);
1.1068 + }
1.1069 + }
1.1070 +
1.1071 + /**
1.1072 + * Enumerates all key/value pairs in the specified hashtable.
1.1073 + * @param h the hashtable
1.1074 + * @throws ClassCastException if any of the property keys
1.1075 + * is not of String type.
1.1076 + */
1.1077 + private synchronized void enumerate(Hashtable h) {
1.1078 + if (defaults != null) {
1.1079 + defaults.enumerate(h);
1.1080 + }
1.1081 + for (Enumeration e = keys() ; e.hasMoreElements() ;) {
1.1082 + String key = (String)e.nextElement();
1.1083 + h.put(key, get(key));
1.1084 + }
1.1085 + }
1.1086 +
1.1087 + /**
1.1088 + * Enumerates all key/value pairs in the specified hashtable
1.1089 + * and omits the property if the key or value is not a string.
1.1090 + * @param h the hashtable
1.1091 + */
1.1092 + private synchronized void enumerateStringProperties(Hashtable<String, String> h) {
1.1093 + if (defaults != null) {
1.1094 + defaults.enumerateStringProperties(h);
1.1095 + }
1.1096 + for (Enumeration e = keys() ; e.hasMoreElements() ;) {
1.1097 + Object k = e.nextElement();
1.1098 + Object v = get(k);
1.1099 + if (k instanceof String && v instanceof String) {
1.1100 + h.put((String) k, (String) v);
1.1101 + }
1.1102 + }
1.1103 + }
1.1104 +
1.1105 + /**
1.1106 + * Convert a nibble to a hex character
1.1107 + * @param nibble the nibble to convert.
1.1108 + */
1.1109 + private static char toHex(int nibble) {
1.1110 + return hexDigit[(nibble & 0xF)];
1.1111 + }
1.1112 +
1.1113 + /** A table of hex digits */
1.1114 + private static final char[] hexDigit = {
1.1115 + '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
1.1116 + };
1.1117 +}