9733e4ee31
* java/io/InputStreamReader.java (read): If length is 0, return 0. Reset `wpos' and `wcount' when buffer has been filled and emptied. * java/util/Properties.java (save): Removed `FIXME' comment. (load): Invalid characters in \u now treated as terminators. Make sure to append character resulting from `\' handling. Cast to `char' when appending to key or value. (skip_ws): Inverted test for whitespace. From-SVN: r26862
381 lines
8.4 KiB
Java
381 lines
8.4 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 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 ()
|
|
{
|
|
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)
|
|
{
|
|
// 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;
|
|
|
|
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)
|
|
{
|
|
}
|
|
}
|
|
}
|