aboutsummaryrefslogtreecommitdiff
path: root/libjava
diff options
context:
space:
mode:
authorTom Tromey <tromey@cygnus.com>2000-10-07 18:13:11 +0000
committerTom Tromey <tromey@gcc.gnu.org>2000-10-07 18:13:11 +0000
commit43f8e3900215533a7dd823c7a0791934a8ed08fa (patch)
tree938b28fd02a65d5451ffc1ebbfc93580af509b0e /libjava
parent20d431c9315f6aeadce38eab68cf2afb8a10cdcf (diff)
downloadgcc-43f8e3900215533a7dd823c7a0791934a8ed08fa.zip
gcc-43f8e3900215533a7dd823c7a0791934a8ed08fa.tar.gz
gcc-43f8e3900215533a7dd823c7a0791934a8ed08fa.tar.bz2
* java/util/Properties.java: Merged with Classpath version.
From-SVN: r36775
Diffstat (limited to 'libjava')
-rw-r--r--libjava/ChangeLog4
-rw-r--r--libjava/java/util/Properties.java827
2 files changed, 465 insertions, 366 deletions
diff --git a/libjava/ChangeLog b/libjava/ChangeLog
index b93bb7d..9bb268d 100644
--- a/libjava/ChangeLog
+++ b/libjava/ChangeLog
@@ -1,3 +1,7 @@
+2000-10-07 Tom Tromey <tromey@cygnus.com>
+
+ * java/util/Properties.java: Merged with Classpath version.
+
2000-10-05 Tom Tromey <tromey@cygnus.com>
* java/lang/reflect/natField.cc (BooleanClass): Don't define.
diff --git a/libjava/java/util/Properties.java b/libjava/java/util/Properties.java
index 6360b99..3f51d47 100644
--- a/libjava/java/util/Properties.java
+++ b/libjava/java/util/Properties.java
@@ -1,393 +1,488 @@
-// Properties - Property list representation.
+/* java.util.Properties
+ Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
-/* Copyright (C) 1998, 1999, 2000 Free Software Foundation
+This file is part of GNU Classpath.
- This file is part of libgcj.
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
-This software is copyrighted work licensed under the terms of the
-Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
-details. */
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
-package java.util;
-
-import java.io.BufferedWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintStream;
-import java.io.PrintWriter;
-import java.io.PushbackReader;
-
-/**
- * @author Tom Tromey <tromey@cygnus.com>
- * @date October 26, 1998.
- */
-
-/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
- * Status: Complete to JDK 1.2.
- */
+As a special exception, if you link this library with other files to
+produce an executable, this library does not by itself cause the
+resulting executable to be covered by the GNU General Public License.
+This exception does not however invalidate any other reasons why the
+executable file might be covered by the GNU General Public License. */
-public class Properties extends Hashtable
-{
- protected Properties defaults;
- private static final long serialVersionUID = 4112578634029874840L;
+package java.util;
+import java.io.*;
- public String getProperty (String propName)
- {
- return getProperty (propName, null);
+/**
+ * An example of a properties file for the german language is given
+ * here. This extends the example given in ListResourceBundle.
+ * Create a file MyResource_de.properties with the following contents
+ * and put it in the CLASSPATH. (The character
+ * <code>\</code><code>u00e4</code> is the german &auml;)
+ *
+ * <pre>
+ * s1=3
+ * s2=MeineDisk
+ * s3=3. M\<code></code>u00e4rz 96
+ * s4=Die Diskette ''{1}'' enth\<code></code>u00e4lt {0} in {2}.
+ * s5=0
+ * s6=keine Dateien
+ * s7=1
+ * s8=eine Datei
+ * s9=2
+ * s10={0,number} Dateien
+ * s11=Das Formatieren schlug fehl mit folgender Exception: {0}
+ * s12=FEHLER
+ * s13=Ergebnis
+ * s14=Dialog
+ * s15=Auswahlkriterium
+ * s16=1,3
+ * </pre>
+ *
+ * Although this is a sub class of a hash table, you should never
+ * insert anything other than strings to this property, or several
+ * methods, that need string keys and values, will fail. To ensure
+ * this, you should use the <code>get/setProperty</code> method instead
+ * of <code>get/put</code>.
+ *
+ * @see PropertyResourceBundle
+ * @author Jochen Hoenicke */
+public class Properties extends Hashtable {
+ /**
+ * The property list that contains default values for any keys not
+ * in this property list.
+ */
+ protected Properties defaults;
+
+ private static final long serialVersionUID = 4112578634029874840L;
+
+ /**
+ * Creates a new empty property list.
+ */
+ public Properties() {
+ this.defaults = null;
}
-
- public String getProperty (String propName, String defVal)
- {
- String r = (String) get (propName);
- if (r == null)
- {
- if (defaults != null)
- r = defaults.getProperty(propName, defVal);
- else
- r = defVal;
- }
- return r;
+
+ /**
+ * Create a new empty property list with the specified default values.
+ * @param defaults a Properties object containing the default values.
+ */
+ public Properties(Properties defaults) {
+ this.defaults = defaults;
}
- public Object setProperty (String key, String value)
- {
- return put (key, value);
- }
-
- public void list (PrintStream out)
- {
- Enumeration e = propertyNames ();
- while (e.hasMoreElements())
- {
- String key = (String) e.nextElement();
- String value = getProperty(key);
- if (value != null)
- {
- if (value.length() > 40)
- {
- // JDK compatibility.
- value = value.substring(0, 37) + "...";
- }
- out.print(key);
- out.print("=");
- out.println(value);
- }
- }
+ /**
+ * Reads a property list from an input stream. The stream should
+ * have the following format: <br>
+ *
+ * An empty line or a line starting with <code>#</code> or
+ * <code>!</code> is ignored. An backslash (<code>\</code>) at the
+ * end of the line makes the line continueing on the next line
+ * (but make sure there is no whitespace after the backslash).
+ * Otherwise, each line describes a key/value pair. <br>
+ *
+ * The chars up to the first whitespace, = or : are the key. You
+ * can include this caracters in the key, if you precede them with
+ * a backslash (<code>\</code>). The key is followed by optional
+ * whitespaces, optionally one <code>=</code> or <code>:</code>,
+ * and optionally some more whitespaces. The rest of the line is
+ * the resource belonging to the key. <br>
+ *
+ * Escape sequences <code>\t, \n, \r, \\, \", \', \!, \#, \ </code>(a
+ * space), and unicode characters with the
+ * <code>\</code><code>u</code>xxxx notation are detected, and
+ * converted to the corresponding single character. <br>
+ *
+ * <pre>
+ * # This is a comment
+ * key = value
+ * k\:5 \ a string starting with space and ending with newline\n
+ * # This is a multiline specification; note that the value contains
+ * # no white space.
+ * weekdays: Sunday,Monday,Tuesday,Wednesday,\
+ * Thursday,Friday,Saturday
+ * # The safest way to include a space at the end of a value:
+ * label = Name:\<code></code>u0020
+ * </pre>
+ *
+ * @param in the input stream
+ * @exception IOException if an error occured when reading
+ * from the input. */
+ public void load(InputStream inStream) throws IOException {
+ BufferedReader reader =
+ new BufferedReader(new InputStreamReader(inStream));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ char c = 0;
+ int pos = 0;
+ while (pos < line.length()
+ && Character.isWhitespace(c = line.charAt(pos)))
+ pos++;
+
+ // If line is empty or begins with a comment character,
+ // skip this line.
+ if (pos == line.length() || c == '#' || c == '!')
+ continue;
+
+ // The characaters up to the next Whitespace, ':', or '='
+ // describe the key. But look for escape sequences.
+ StringBuffer key = new StringBuffer();
+ while (pos < line.length()
+ && !Character.isWhitespace(c = line.charAt(pos++))
+ && c != '=' && c != ':') {
+ if (c == '\\') {
+ if (pos == line.length()) {
+ // The line continues on the next line.
+ line = reader.readLine();
+ pos = 0;
+ while (pos < line.length()
+ && Character.isWhitespace(c = line.charAt(pos)))
+ pos++;
+ } else {
+ c = line.charAt(pos++);
+ switch (c) {
+ case 'n':
+ key.append('\n');
+ break;
+ case 't':
+ key.append('\t');
+ break;
+ case 'r':
+ key.append('\r');
+ break;
+ case 'u':
+ if (pos+4 <= line.length()) {
+ char uni = (char) Integer.parseInt
+ (line.substring(pos, pos+4), 16);
+ key.append(uni);
+ } // else throw exception?
+ break;
+ default:
+ key.append(c);
+ break;
+ }
+ }
+ } else
+ key.append(c);
+ }
+
+ boolean isDelim = (c == ':' || c == '=');
+ while (pos < line.length()
+ && Character.isWhitespace(c = line.charAt(pos)))
+ pos++;
+
+ if (!isDelim && (c == ':' || c == '=')) {
+ pos++;
+ while (pos < line.length()
+ && Character.isWhitespace(c = line.charAt(pos)))
+ pos++;
+ }
+
+ StringBuffer element = new StringBuffer(line.length()-pos);
+ while (pos < line.length()) {
+ c = line.charAt(pos++);
+ if (c == '\\') {
+ if (pos == line.length()) {
+ // The line continues on the next line.
+ line = reader.readLine();
+ pos = 0;
+ while (pos < line.length()
+ && Character.isWhitespace(c = line.charAt(pos)))
+ pos++;
+ element.ensureCapacity(line.length()-pos+element.length());
+ } else {
+ c = line.charAt(pos++);
+ switch (c) {
+ case 'n':
+ element.append('\n');
+ break;
+ case 't':
+ element.append('\t');
+ break;
+ case 'r':
+ element.append('\r');
+ break;
+ case 'u':
+ if (pos+4 <= line.length()) {
+ char uni = (char) Integer.parseInt
+ (line.substring(pos, pos+4), 16);
+ element.append(uni);
+ } // else throw exception?
+ break;
+ default:
+ element.append(c);
+ break;
+ }
+ }
+ } else
+ element.append(c);
+ }
+ put(key.toString(), element.toString());
+ }
}
- public void list (PrintWriter writer)
- {
- Enumeration e = propertyNames ();
- while (e.hasMoreElements())
- {
- String key = (String) e.nextElement();
- String value = getProperty(key);
- if (value != null)
- {
- if (value.length() > 40)
- {
- // JDK compatibility.
- value = value.substring(0, 37) + "...";
- }
- writer.print(key);
- writer.print("=");
- writer.println(value);
- }
- }
+ /**
+ * Calls <code>store(OutputStream out, String header)</code> and
+ * ignores the IOException that may be thrown.
+ * @deprecated use store instead.
+ * @exception ClassCastException if this property contains any key or
+ * value that isn't a string.
+ */
+ public void save(OutputStream out, String header) {
+ try {
+ store(out,header);
+ } catch (IOException ex) {
+ }
}
-
- private final boolean skip_ws (PushbackReader reader) throws IOException
- {
- while (true)
- {
- int c = reader.read();
- if (c == -1)
- return false;
- // FIXME: we use our own definition of whitespace.
- // Character.isWhitespace includes newlines, which we don't
- // want. Character.isSpaceChar doesn't include \t.
- if (c != ' ' && c != '\t')
- {
- reader.unread(c);
- return true;
- }
- }
+
+ /**
+ * Writes the key/value pairs to the given output stream. <br>
+ *
+ * If header is not null, this method writes a comment containing
+ * the header as first line to the stream. The next line (or first
+ * line if header is null) contains a comment with the current date.
+ * Afterwards the key/value pairs are written to the stream in the
+ * following format. <br>
+ *
+ * Each line has the form <code>key = value</code>. Newlines,
+ * Returns and tabs are written as <code>\n,\t,\r</code> resp.
+ * The characters <code>\, !, #, =</code> and <code>:</code> are
+ * preceeded by a backslash. Spaces are preceded with a backslash,
+ * if and only if they are at the beginning of the key. Characters
+ * that are not in the ascii range 33 to 127 are written in the
+ * <code>\</code><code>u</code>xxxx Form.
+ *
+ * @param out the output stream
+ * @param header the header written in the first line, may be null.
+ * @exception ClassCastException if this property contains any key or
+ * value that isn't a string.
+ */
+ public void store(OutputStream out, String header) throws IOException {
+ PrintWriter writer = new PrintWriter(out);
+ if (header != null)
+ writer.println("#"+header);
+ writer.println("#"+new Date().toString());
+ list(writer);
+ writer.flush();
}
-
- // Note: this method needs to be rewritten for JDK 1.2.
- // We rather arbitrarily decide that an EOF in the middle of a line
- // means that the whole line should be ignored. The spec doesn't
- // specifically address this, but this interpretation seems valid.
- public synchronized void load (InputStream in) throws IOException
- {
- PushbackReader reader = new PushbackReader (new InputStreamReader (in));
-
- StringBuffer key = new StringBuffer ();
- StringBuffer value = new StringBuffer ();
-
- nextLine:
- while (true)
- {
- key.setLength(0);
- value.setLength(0);
-
- // Skip leading whitespace.
- if (! skip_ws (reader))
- return;
-
- // Read key until key terminator.
- boolean first_char = true;
- int c;
- while (true)
- {
- c = reader.read();
- if (c == -1)
- return;
- if (c == '\\')
- {
- first_char = false;
- c = reader.read();
- if (c == -1)
- return;
- }
-
- // If we found a comment, just read to end of line and
- // then keep going.
- if (first_char == true && (c == '#' || c == '!'))
- {
- while (c != -1 && c != '\r' && c != '\n')
- c = reader.read();
- if (c == -1)
- return;
- continue nextLine;
- }
-
- if (c == '\r' || c == '\n')
- {
- if (first_char)
- continue nextLine;
- reader.unread(c);
- break;
- }
- // FIXME: again, our own definition of whitespace.
- if (c == ' ' || c == '\t' || c == ':' || c == '=')
- break;
-
- first_char = false;
- key.append((char) c);
- }
-
- // Found end of key. Skip whitespace. If the terminator
- // was whitespace, also skip a single instance of a "real"
- // terminator, and then more whitespace.
- if (! skip_ws (reader))
- return;
- if (c != ':' && c != '=')
- {
- c = reader.read();
- if (c == -1)
- return;
- if (c == ':' || c == '=')
- {
- // Skip more whitespace.
- if (! skip_ws (reader))
- return;
- }
- else
- reader.unread(c);
- }
-
- // Now read the value.
- while (true)
- {
- c = reader.read();
- if (c == -1)
- return;
- if (c == '\r' || c == '\n')
- break;
- if (c == '\\')
- {
- c = reader.read();
- switch (c)
- {
- case -1:
- return;
- case 't':
- c = '\t';
- break;
- case 'r':
- c = '\r';
- break;
- case 'n':
- c = '\n';
- break;
- case 'u':
- c = 0;
- for (int i = 0; i < 4; ++i)
- {
- int x = reader.read();
- if (x == -1)
- return;
- int d = Character.digit((char) x, 16);
- // We follow JDK here: invalid characters
- // are treated as terminators.
- if (d == -1)
- {
- value.append((char) c);
- c = x;
- break;
- }
- c <<= 4;
- c |= d;
- }
- break;
- default:
- // Nothing.
- }
- }
- value.append((char) c);
- }
-
- put (key.toString(), value.toString());
- }
+
+ /**
+ * Adds the given key/value pair to this properties. This calls
+ * the hashtable method put.
+ * @param key the key for this property
+ * @param value the value for this property
+ * @return The old value for the given key.
+ * @since JDK1.2 */
+ public Object setProperty(String key, String value) {
+ return put(key,value);
}
- public Properties ()
- {
- defaults = null;
+ /**
+ * Gets the property with the specified key in this property list.
+ * If the key is not found, the default property list is searched.
+ * If the property is not found in default or the default of
+ * default, null is returned.
+ * @param key The key for this property.
+ * @param defaulValue A default value
+ * @return The value for the given key, or null if not found.
+ * @exception ClassCastException if this property contains any key or
+ * value that isn't a string.
+ */
+ public String getProperty(String key) {
+ return getProperty(key, null);
}
- public Properties (Properties defs)
- {
- defaults = defs;
+ /**
+ * Gets the property with the specified key in this property list. If
+ * the key is not found, the default property list is searched. If the
+ * property is not found in default or the default of default, the
+ * specified defaultValue is returned.
+ * @param key The key for this property.
+ * @param defaulValue A default value
+ * @return The value for the given key.
+ * @exception ClassCastException if this property contains any key or
+ * value that isn't a string.
+ */
+ public String getProperty(String key, String defaultValue) {
+ Properties prop = this;
+ // Eliminate tail recursion.
+ do {
+ String value = (String) prop.get(key);
+ if (value != null)
+ return value;
+ prop = prop.defaults;
+ } while (prop != null);
+ return defaultValue;
}
- private final void addHashEntries (Hashtable base)
- {
- if (defaults != null)
- defaults.addHashEntries(base);
- Enumeration keys = keys ();
- while (keys.hasMoreElements())
- base.put(keys.nextElement(), base);
+ private final void addHashEntries (Hashtable base) {
+ if (defaults != null)
+ defaults.addHashEntries(base);
+ Enumeration keys = keys ();
+ while (keys.hasMoreElements())
+ base.put(keys.nextElement(), base);
}
- public Enumeration propertyNames ()
- {
- // We make a new Hashtable that holds all the keys. Then we
- // return an enumeration for this hash. We do this because we
- // don't want modifications to be reflected in the enumeration
- // (per JCL), and because there doesn't seem to be a
- // particularly better way to ensure that duplicates are
- // ignored.
- Hashtable t = new Hashtable ();
- addHashEntries (t);
- return t.keys();
+ /**
+ * Returns an enumeration of all keys in this property list, including
+ * the keys in the default property list.
+ */
+ public Enumeration propertyNames () {
+ // We make a new Hashtable that holds all the keys. Then we
+ // return an enumeration for this hash. We do this because we
+ // don't want modifications to be reflected in the enumeration
+ // (per JCL), and because there doesn't seem to be a
+ // particularly better way to ensure that duplicates are
+ // ignored.
+ Hashtable t = new Hashtable ();
+ addHashEntries (t);
+ return t.keys();
}
- public synchronized void save (OutputStream out, String comment)
- {
- try
- {
- store (out, comment);
- }
- catch (IOException _)
- {
- }
- }
-
- public synchronized void store (OutputStream out, String comment)
- throws IOException
- {
- // Use a buffer because writing a single string through
- // OutputStreamWriter is fairly expensive.
- BufferedWriter output
- = new BufferedWriter (new OutputStreamWriter (out));
- String newline = System.getProperty("line.separator");
-
- if (comment != null)
- {
- // We just lose if COMMENT contains a newline. This is
- // what JDK 1.1 does.
- output.write("#");
- output.write(comment);
- output.write(newline);
- }
- output.write("# ");
- output.write(new Date().toString());
- output.write(newline);
-
- Enumeration keys = keys ();
- while (keys.hasMoreElements())
- {
- String key = (String) keys.nextElement();
- String value = (String) get (key);
-
- // FIXME: JCL says that the key can contain many Unicode
- // characters. But it also doesn't say we should encode
- // it in any way.
- // FIXME: if key contains ':', '=', or whitespace, must
- // quote it here. Note that JDK 1.1 does not do this.
- output.write(key);
- output.write("=");
-
- boolean leading = true;
- for (int i = 0; i < value.length(); ++i)
- {
- boolean new_lead = false;
- char c = value.charAt(i);
- switch (c)
- {
- case '\n':
- output.write("\\n");
- break;
- case '\r':
- output.write("\\r");
- break;
- case '\t':
- output.write("\\t");
- break;
- case '\\':
- output.write("\\\\");
- break;
-
- case '#':
- case '!':
- case '=':
- case ':':
- output.write("\\");
- output.write(c);
- break;
-
- case ' ':
- new_lead = leading;
- if (leading)
- output.write("\\");
- output.write(c);
- break;
+ /**
+ * Formats a key/value pair for output in a properties file.
+ * See store for a description of the format.
+ * @param key the key.
+ * @param value the value.
+ * @see #store
+ */
+ private String formatForOutput(String key, String value) {
+ // This is a simple approximation of the expected line size.
+ StringBuffer result = new StringBuffer(key.length()+value.length()+16);
+ boolean head = true;
+ for (int i=0; i< key.length(); i++) {
+ char c = key.charAt(i);
+ switch (c) {
+ case '\n':
+ result.append("\\n");
+ break;
+ case '\r':
+ result.append("\\r");
+ break;
+ case '\t':
+ result.append("\\t");
+ break;
+ case '\\':
+ result.append("\\\\");
+ break;
+ case '!':
+ result.append("\\!");
+ break;
+ case '#':
+ result.append("\\#");
+ break;
+ case '=':
+ result.append("\\=");
+ break;
+ case ':':
+ result.append("\\:");
+ break;
+ case ' ':
+ result.append("\\ ");
+ break;
+ default:
+ if (c < 32 || c > '~') {
+ String hex = Integer.toHexString(c);
+ result.append("\\u0000".substring(0, 6-hex.length()));
+ result.append(hex);
+ } else
+ result.append(c);
+ }
+ if (c != 32)
+ head = false;
+ }
+ result.append('=');
+ head=true;
+ for (int i=0; i< value.length(); i++) {
+ char c = value.charAt(i);
+ switch (c) {
+ case '\n':
+ result.append("\\n");
+ break;
+ case '\r':
+ result.append("\\r");
+ break;
+ case '\t':
+ result.append("\\t");
+ break;
+ case '\\':
+ result.append("\\\\");
+ break;
+ case '!':
+ result.append("\\!");
+ break;
+ case '#':
+ result.append("\\#");
+ break;
+ case ' ':
+ result.append(head ? "\\ ": " ");
+ break;
+ default:
+ if (c < 32 || c > '~') {
+ String hex = Integer.toHexString(c);
+ result.append("\\u0000".substring(0, 6-hex.length()));
+ result.append(hex);
+ } else
+ result.append(c);
+ }
+ if (c != 32)
+ head = false;
+ }
+ return result.toString();
+ }
- default:
- if (c < '\u0020' || c > '\u007e')
- {
- output.write("\\u");
- output.write(Character.forDigit(c >>> 12, 16));
- output.write(Character.forDigit((c >>> 8) & 0xff,
- 16));
- output.write(Character.forDigit((c >>> 4) & 0xff,
- 16));
- output.write(Character.forDigit(c & 0xff, 16));
- }
- else
- output.write(c);
- }
- leading = new_lead;
- }
- output.write(newline);
- }
+ /**
+ * Writes the key/value pairs to the given print stream. They are
+ * written in the way, described in the method store.
+ * @param out the stream, where the key/value pairs are written to.
+ * @exception ClassCastException if this property contains any key or
+ * value that isn't a string.
+ * @see #store
+ */
+ public void list(PrintStream out) {
+ Enumeration keys = keys();
+ Enumeration elts = elements();
+ while (keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ String elt = (String) elts.nextElement();
+ String output = formatForOutput(key,elt);
+ out.println(output);
+ }
+ }
- output.flush();
- }
+ /**
+ * Writes the key/value pairs to the given print writer. They are
+ * written in the way, described in the method store.
+ * @param out the writer, where the key/value pairs are written to.
+ * @exception ClassCastException if this property contains any key or
+ * value that isn't a string.
+ * @see #store
+ * @see #list(java.io.PrintStream)
+ * @since JDK1.1
+ */
+ public void list(PrintWriter out) {
+ Enumeration keys = keys();
+ Enumeration elts = elements();
+ while (keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ String elt = (String) elts.nextElement();
+ String output = formatForOutput(key,elt);
+ out.println(output);
+ }
+ }
}