Commit 43f8e390 by Tom Tromey Committed by Tom Tromey

* java/util/Properties.java: Merged with Classpath version.

From-SVN: r36775
parent 20d431c9
2000-10-07 Tom Tromey <tromey@cygnus.com>
* java/util/Properties.java: Merged with Classpath version.
2000-10-05 Tom Tromey <tromey@cygnus.com> 2000-10-05 Tom Tromey <tromey@cygnus.com>
* java/lang/reflect/natField.cc (BooleanClass): Don't define. * java/lang/reflect/natField.cc (BooleanClass): Don't define.
......
// 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 You should have received a copy of the GNU General Public License
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for along with GNU Classpath; see the file COPYING. If not, write to the
details. */ Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.
package java.util; As a special exception, if you link this library with other files to
produce an executable, this library does not by itself cause the
import java.io.BufferedWriter; resulting executable to be covered by the GNU General Public License.
import java.io.IOException; This exception does not however invalidate any other reasons why the
import java.io.InputStream; executable file might be covered by the GNU General Public License. */
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.
*/
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) /**
{ * An example of a properties file for the german language is given
return getProperty (propName, null); * 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) /**
{ * Create a new empty property list with the specified default values.
String r = (String) get (propName); * @param defaults a Properties object containing the default values.
if (r == null) */
{ public Properties(Properties defaults) {
if (defaults != null) this.defaults = defaults;
r = defaults.getProperty(propName, defVal);
else
r = defVal;
}
return r;
} }
public Object setProperty (String key, String value) /**
{ * Reads a property list from an input stream. The stream should
return put (key, value); * have the following format: <br>
} *
* An empty line or a line starting with <code>#</code> or
public void list (PrintStream out) * <code>!</code> is ignored. An backslash (<code>\</code>) at the
{ * end of the line makes the line continueing on the next line
Enumeration e = propertyNames (); * (but make sure there is no whitespace after the backslash).
while (e.hasMoreElements()) * Otherwise, each line describes a key/value pair. <br>
{ *
String key = (String) e.nextElement(); * The chars up to the first whitespace, = or : are the key. You
String value = getProperty(key); * can include this caracters in the key, if you precede them with
if (value != null) * a backslash (<code>\</code>). The key is followed by optional
{ * whitespaces, optionally one <code>=</code> or <code>:</code>,
if (value.length() > 40) * and optionally some more whitespaces. The rest of the line is
{ * the resource belonging to the key. <br>
// JDK compatibility. *
value = value.substring(0, 37) + "..."; * Escape sequences <code>\t, \n, \r, \\, \", \', \!, \#, \ </code>(a
} * space), and unicode characters with the
out.print(key); * <code>\</code><code>u</code>xxxx notation are detected, and
out.print("="); * converted to the corresponding single character. <br>
out.println(value); *
} * <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) /**
{ * Calls <code>store(OutputStream out, String header)</code> and
Enumeration e = propertyNames (); * ignores the IOException that may be thrown.
while (e.hasMoreElements()) * @deprecated use store instead.
{ * @exception ClassCastException if this property contains any key or
String key = (String) e.nextElement(); * value that isn't a string.
String value = getProperty(key); */
if (value != null) public void save(OutputStream out, String header) {
{ try {
if (value.length() > 40) store(out,header);
{ } catch (IOException ex) {
// JDK compatibility. }
value = value.substring(0, 37) + "...";
}
writer.print(key);
writer.print("=");
writer.println(value);
}
}
} }
private final boolean skip_ws (PushbackReader reader) throws IOException /**
{ * Writes the key/value pairs to the given output stream. <br>
while (true) *
{ * If header is not null, this method writes a comment containing
int c = reader.read(); * the header as first line to the stream. The next line (or first
if (c == -1) * line if header is null) contains a comment with the current date.
return false; * Afterwards the key/value pairs are written to the stream in the
// FIXME: we use our own definition of whitespace. * following format. <br>
// Character.isWhitespace includes newlines, which we don't *
// want. Character.isSpaceChar doesn't include \t. * Each line has the form <code>key = value</code>. Newlines,
if (c != ' ' && c != '\t') * Returns and tabs are written as <code>\n,\t,\r</code> resp.
{ * The characters <code>\, !, #, =</code> and <code>:</code> are
reader.unread(c); * preceeded by a backslash. Spaces are preceded with a backslash,
return true; * 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 * Adds the given key/value pair to this properties. This calls
// means that the whole line should be ignored. The spec doesn't * the hashtable method put.
// specifically address this, but this interpretation seems valid. * @param key the key for this property
public synchronized void load (InputStream in) throws IOException * @param value the value for this property
{ * @return The old value for the given key.
PushbackReader reader = new PushbackReader (new InputStreamReader (in)); * @since JDK1.2 */
public Object setProperty(String key, String value) {
StringBuffer key = new StringBuffer (); return put(key,value);
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());
}
} }
public Properties () /**
{ * Gets the property with the specified key in this property list.
defaults = null; * 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) /**
{ * Gets the property with the specified key in this property list. If
defaults = defs; * 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) private final void addHashEntries (Hashtable base) {
{ if (defaults != null)
if (defaults != null) defaults.addHashEntries(base);
defaults.addHashEntries(base); Enumeration keys = keys ();
Enumeration keys = keys (); while (keys.hasMoreElements())
while (keys.hasMoreElements()) base.put(keys.nextElement(), base);
base.put(keys.nextElement(), base);
} }
public Enumeration propertyNames () /**
{ * Returns an enumeration of all keys in this property list, including
// We make a new Hashtable that holds all the keys. Then we * the keys in the default property list.
// return an enumeration for this hash. We do this because we */
// don't want modifications to be reflected in the enumeration public Enumeration propertyNames () {
// (per JCL), and because there doesn't seem to be a // We make a new Hashtable that holds all the keys. Then we
// particularly better way to ensure that duplicates are // return an enumeration for this hash. We do this because we
// ignored. // don't want modifications to be reflected in the enumeration
Hashtable t = new Hashtable (); // (per JCL), and because there doesn't seem to be a
addHashEntries (t); // particularly better way to ensure that duplicates are
return t.keys(); // ignored.
Hashtable t = new Hashtable ();
addHashEntries (t);
return t.keys();
} }
public synchronized void save (OutputStream out, String comment) /**
{ * Formats a key/value pair for output in a properties file.
try * See store for a description of the format.
{ * @param key the key.
store (out, comment); * @param value the value.
} * @see #store
catch (IOException _) */
{ 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;
public synchronized void store (OutputStream out, String comment) for (int i=0; i< key.length(); i++) {
throws IOException char c = key.charAt(i);
{ switch (c) {
// Use a buffer because writing a single string through case '\n':
// OutputStreamWriter is fairly expensive. result.append("\\n");
BufferedWriter output break;
= new BufferedWriter (new OutputStreamWriter (out)); case '\r':
String newline = System.getProperty("line.separator"); result.append("\\r");
break;
if (comment != null) case '\t':
{ result.append("\\t");
// We just lose if COMMENT contains a newline. This is break;
// what JDK 1.1 does. case '\\':
output.write("#"); result.append("\\\\");
output.write(comment); break;
output.write(newline); case '!':
} result.append("\\!");
output.write("# "); break;
output.write(new Date().toString()); case '#':
output.write(newline); result.append("\\#");
break;
Enumeration keys = keys (); case '=':
while (keys.hasMoreElements()) result.append("\\=");
{ break;
String key = (String) keys.nextElement(); case ':':
String value = (String) get (key); result.append("\\:");
break;
// FIXME: JCL says that the key can contain many Unicode case ' ':
// characters. But it also doesn't say we should encode result.append("\\ ");
// it in any way. break;
// FIXME: if key contains ':', '=', or whitespace, must default:
// quote it here. Note that JDK 1.1 does not do this. if (c < 32 || c > '~') {
output.write(key); String hex = Integer.toHexString(c);
output.write("="); result.append("\\u0000".substring(0, 6-hex.length()));
result.append(hex);
boolean leading = true; } else
for (int i = 0; i < value.length(); ++i) result.append(c);
{ }
boolean new_lead = false; if (c != 32)
char c = value.charAt(i); head = false;
switch (c) }
{ result.append('=');
case '\n': head=true;
output.write("\\n"); for (int i=0; i< value.length(); i++) {
break; char c = value.charAt(i);
case '\r': switch (c) {
output.write("\\r"); case '\n':
break; result.append("\\n");
case '\t': break;
output.write("\\t"); case '\r':
break; result.append("\\r");
case '\\': break;
output.write("\\\\"); case '\t':
break; result.append("\\t");
break;
case '#': case '\\':
case '!': result.append("\\\\");
case '=': break;
case ':': case '!':
output.write("\\"); result.append("\\!");
output.write(c); break;
break; case '#':
result.append("\\#");
case ' ': break;
new_lead = leading; case ' ':
if (leading) result.append(head ? "\\ ": " ");
output.write("\\"); break;
output.write(c); default:
break; 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') * Writes the key/value pairs to the given print stream. They are
{ * written in the way, described in the method store.
output.write("\\u"); * @param out the stream, where the key/value pairs are written to.
output.write(Character.forDigit(c >>> 12, 16)); * @exception ClassCastException if this property contains any key or
output.write(Character.forDigit((c >>> 8) & 0xff, * value that isn't a string.
16)); * @see #store
output.write(Character.forDigit((c >>> 4) & 0xff, */
16)); public void list(PrintStream out) {
output.write(Character.forDigit(c & 0xff, 16)); Enumeration keys = keys();
} Enumeration elts = elements();
else while (keys.hasMoreElements()) {
output.write(c); String key = (String) keys.nextElement();
} String elt = (String) elts.nextElement();
leading = new_lead; String output = formatForOutput(key,elt);
} out.println(output);
output.write(newline); }
} }
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);
}
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment