| /* java.util.Properties | |
| Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| 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. | |
| 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. | |
| 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. */ | |
| package java.util; | |
| import java.io.*; | |
| /** | |
| * 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 ä) | |
| * | |
| * <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; | |
| } | |
| /** | |
| * 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; | |
| } | |
| /** | |
| * 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 occurred when reading | |
| * from the input. */ | |
| public void load(InputStream inStream) throws IOException | |
| { | |
| // The spec says that the file must be encoded using ISO-8859-1. | |
| BufferedReader reader = | |
| new BufferedReader(new InputStreamReader(inStream, "ISO-8859-1")); | |
| 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 characters 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); | |
| pos += 4; | |
| } // 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); | |
| pos += 4; | |
| } // else throw exception? | |
| break; | |
| default: | |
| element.append(c); | |
| break; | |
| } | |
| } | |
| } | |
| else | |
| element.append(c); | |
| } | |
| put(key.toString(), element.toString()); | |
| } | |
| } | |
| /** | |
| * 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) | |
| { | |
| } | |
| } | |
| /** | |
| * 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 | |
| { | |
| // The spec says that the file must be encoded using ISO-8859-1. | |
| PrintWriter writer | |
| = new PrintWriter(new OutputStreamWriter (out, "ISO-8859-1")); | |
| if (header != null) | |
| writer.println("#" + header); | |
| writer.println("#" + new Date().toString()); | |
| list(writer); | |
| writer.flush(); | |
| } | |
| /** | |
| * 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); | |
| } | |
| /** | |
| * 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); | |
| } | |
| /** | |
| * 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); | |
| } | |
| /** | |
| * 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(); | |
| } | |
| /** | |
| * 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(); | |
| } | |
| /** | |
| * 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); | |
| } | |
| } | |
| /** | |
| * 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); | |
| } | |
| } | |
| } |