gcc/libjava/java/util/Properties.java
Tom Tromey ee9dd3721b Initial revision
From-SVN: r26263
1999-04-07 14:42:40 +00:00

377 lines
8.2 KiB
Java

// Properties - Property list representation.
/* Copyright (C) 1998, 1999 Cygnus Solutions
This file is part of libgcj.
This software is copyrighted work licensed under the terms of the
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
details. */
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.1.
*/
public class Properties extends Hashtable
{
protected Properties defaults;
public String getProperty (String propName)
{
return getProperty (propName, 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;
}
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);
}
}
}
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);
}
}
}
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;
}
}
}
// 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 definitino of whitespace.
if (c == ' ' || c == '\t' || c == ':' || c == '=')
break;
first_char = false;
key.append(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);
// FIXME: what to do here? We call it an
// error.
if (d == -1)
throw new IOException ();
c <<= 4;
c |= d;
}
break;
default:
// Nothing.
}
}
else
value.append(c);
}
put (key.toString(), value.toString());
}
}
public Properties ()
{
defaults = null;
}
public Properties (Properties defs)
{
defaults = defs;
}
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();
}
public synchronized void save (OutputStream out, String comment)
{
// 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");
try
{
if (comment != null)
{
// FIXME: what if COMMENT contains newlines?
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.
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;
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);
}
output.flush();
}
catch (IOException ignore)
{
}
}
}