Merge from mainline (gomp-merge-2005-02-26).
[official-gcc.git] / libjava / java / util / Properties.java
blob5e351056c7036ab700a131f96c603beffd97b022
1 /* Properties.java -- a set of persistent properties
2 Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package java.util;
41 import java.io.BufferedReader;
42 import java.io.IOException;
43 import java.io.InputStream;
44 import java.io.InputStreamReader;
45 import java.io.OutputStream;
46 import java.io.OutputStreamWriter;
47 import java.io.PrintStream;
48 import java.io.PrintWriter;
50 /**
51 * A set of persistent properties, which can be saved or loaded from a stream.
52 * A property list may also contain defaults, searched if the main list
53 * does not contain a property for a given key.
55 * An example of a properties file for the german language is given
56 * here. This extends the example given in ListResourceBundle.
57 * Create a file MyResource_de.properties with the following contents
58 * and put it in the CLASSPATH. (The character
59 * <code>\</code><code>u00e4</code> is the german umlaut)
62 <pre>s1=3
63 s2=MeineDisk
64 s3=3. M\<code></code>u00e4rz 96
65 s4=Die Diskette ''{1}'' enth\<code></code>u00e4lt {0} in {2}.
66 s5=0
67 s6=keine Dateien
68 s7=1
69 s8=eine Datei
70 s9=2
71 s10={0,number} Dateien
72 s11=Das Formatieren schlug fehl mit folgender Exception: {0}
73 s12=FEHLER
74 s13=Ergebnis
75 s14=Dialog
76 s15=Auswahlkriterium
77 s16=1,3</pre>
79 * <p>Although this is a sub class of a hash table, you should never
80 * insert anything other than strings to this property, or several
81 * methods, that need string keys and values, will fail. To ensure
82 * this, you should use the <code>get/setProperty</code> method instead
83 * of <code>get/put</code>.
85 * Properties are saved in ISO 8859-1 encoding, using Unicode escapes with
86 * a single <code>u</code> for any character which cannot be represented.
88 * @author Jochen Hoenicke
89 * @author Eric Blake (ebb9@email.byu.edu)
90 * @see PropertyResourceBundle
91 * @status updated to 1.4
93 public class Properties extends Hashtable
95 // WARNING: Properties is a CORE class in the bootstrap cycle. See the
96 // comments in vm/reference/java/lang/Runtime for implications of this fact.
98 /**
99 * The property list that contains default values for any keys not
100 * in this property list.
102 * @serial the default properties
104 protected Properties defaults;
107 * Compatible with JDK 1.0+.
109 private static final long serialVersionUID = 4112578634029874840L;
112 * Creates a new empty property list with no default values.
114 public Properties()
119 * Create a new empty property list with the specified default values.
121 * @param defaults a Properties object containing the default values
123 public Properties(Properties defaults)
125 this.defaults = defaults;
129 * Adds the given key/value pair to this properties. This calls
130 * the hashtable method put.
132 * @param key the key for this property
133 * @param value the value for this property
134 * @return The old value for the given key
135 * @see #getProperty(String)
136 * @since 1.2
138 public Object setProperty(String key, String value)
140 return put(key, value);
144 * Reads a property list from an input stream. The stream should
145 * have the following format: <br>
147 * An empty line or a line starting with <code>#</code> or
148 * <code>!</code> is ignored. An backslash (<code>\</code>) at the
149 * end of the line makes the line continueing on the next line
150 * (but make sure there is no whitespace after the backslash).
151 * Otherwise, each line describes a key/value pair. <br>
153 * The chars up to the first whitespace, = or : are the key. You
154 * can include this caracters in the key, if you precede them with
155 * a backslash (<code>\</code>). The key is followed by optional
156 * whitespaces, optionally one <code>=</code> or <code>:</code>,
157 * and optionally some more whitespaces. The rest of the line is
158 * the resource belonging to the key. <br>
160 * Escape sequences <code>\t, \n, \r, \\, \", \', \!, \#, \ </code>(a
161 * space), and unicode characters with the
162 * <code>\\u</code><em>xxxx</em> notation are detected, and
163 * converted to the corresponding single character. <br>
166 <pre># This is a comment
167 key = value
168 k\:5 \ a string starting with space and ending with newline\n
169 # This is a multiline specification; note that the value contains
170 # no white space.
171 weekdays: Sunday,Monday,Tuesday,Wednesday,\\
172 Thursday,Friday,Saturday
173 # The safest way to include a space at the end of a value:
174 label = Name:\\u0020</pre>
176 * @param in the input stream
177 * @throws IOException if an error occurred when reading the input
178 * @throws NullPointerException if in is null
180 public void load(InputStream inStream) throws IOException
182 // The spec says that the file must be encoded using ISO-8859-1.
183 BufferedReader reader =
184 new BufferedReader(new InputStreamReader(inStream, "ISO-8859-1"));
185 String line;
187 while ((line = reader.readLine()) != null)
189 char c = 0;
190 int pos = 0;
191 // Leading whitespaces must be deleted first.
192 while (pos < line.length()
193 && Character.isWhitespace(c = line.charAt(pos)))
194 pos++;
196 // If empty line or begins with a comment character, skip this line.
197 if ((line.length() - pos) == 0
198 || line.charAt(pos) == '#' || line.charAt(pos) == '!')
199 continue;
201 // The characters up to the next Whitespace, ':', or '='
202 // describe the key. But look for escape sequences.
203 StringBuffer key = new StringBuffer();
204 while (pos < line.length()
205 && ! Character.isWhitespace(c = line.charAt(pos++))
206 && c != '=' && c != ':')
208 if (c == '\\')
210 if (pos == line.length())
212 // The line continues on the next line.
213 line = reader.readLine();
214 pos = 0;
215 while (pos < line.length()
216 && Character.isWhitespace(c = line.charAt(pos)))
217 pos++;
219 else
221 c = line.charAt(pos++);
222 switch (c)
224 case 'n':
225 key.append('\n');
226 break;
227 case 't':
228 key.append('\t');
229 break;
230 case 'r':
231 key.append('\r');
232 break;
233 case 'u':
234 if (pos + 4 <= line.length())
236 char uni = (char) Integer.parseInt
237 (line.substring(pos, pos + 4), 16);
238 key.append(uni);
239 pos += 4;
240 } // else throw exception?
241 break;
242 default:
243 key.append(c);
244 break;
248 else
249 key.append(c);
252 boolean isDelim = (c == ':' || c == '=');
253 while (pos < line.length()
254 && Character.isWhitespace(c = line.charAt(pos)))
255 pos++;
257 if (! isDelim && (c == ':' || c == '='))
259 pos++;
260 while (pos < line.length()
261 && Character.isWhitespace(c = line.charAt(pos)))
262 pos++;
265 StringBuffer element = new StringBuffer(line.length() - pos);
266 while (pos < line.length())
268 c = line.charAt(pos++);
269 if (c == '\\')
271 if (pos == line.length())
273 // The line continues on the next line.
274 line = reader.readLine();
276 // We might have seen a backslash at the end of
277 // the file. The JDK ignores the backslash in
278 // this case, so we follow for compatibility.
279 if (line == null)
280 break;
282 pos = 0;
283 while (pos < line.length()
284 && Character.isWhitespace(c = line.charAt(pos)))
285 pos++;
286 element.ensureCapacity(line.length() - pos +
287 element.length());
289 else
291 c = line.charAt(pos++);
292 switch (c)
294 case 'n':
295 element.append('\n');
296 break;
297 case 't':
298 element.append('\t');
299 break;
300 case 'r':
301 element.append('\r');
302 break;
303 case 'u':
304 if (pos + 4 <= line.length())
306 char uni = (char) Integer.parseInt
307 (line.substring(pos, pos + 4), 16);
308 element.append(uni);
309 pos += 4;
310 } // else throw exception?
311 break;
312 default:
313 element.append(c);
314 break;
318 else
319 element.append(c);
321 put(key.toString(), element.toString());
326 * Calls <code>store(OutputStream out, String header)</code> and
327 * ignores the IOException that may be thrown.
329 * @param out the stream to write to
330 * @param header a description of the property list
331 * @throws ClassCastException if this property contains any key or
332 * value that are not strings
333 * @deprecated use {@link #store(OutputStream, String)} instead
335 public void save(OutputStream out, String header)
339 store(out, header);
341 catch (IOException ex)
347 * Writes the key/value pairs to the given output stream, in a format
348 * suitable for <code>load</code>.<br>
350 * If header is not null, this method writes a comment containing
351 * the header as first line to the stream. The next line (or first
352 * line if header is null) contains a comment with the current date.
353 * Afterwards the key/value pairs are written to the stream in the
354 * following format.<br>
356 * Each line has the form <code>key = value</code>. Newlines,
357 * Returns and tabs are written as <code>\n,\t,\r</code> resp.
358 * The characters <code>\, !, #, =</code> and <code>:</code> are
359 * preceeded by a backslash. Spaces are preceded with a backslash,
360 * if and only if they are at the beginning of the key. Characters
361 * that are not in the ascii range 33 to 127 are written in the
362 * <code>\</code><code>u</code>xxxx Form.<br>
364 * Following the listing, the output stream is flushed but left open.
366 * @param out the output stream
367 * @param header the header written in the first line, may be null
368 * @throws ClassCastException if this property contains any key or
369 * value that isn't a string
370 * @throws IOException if writing to the stream fails
371 * @throws NullPointerException if out is null
372 * @since 1.2
374 public void store(OutputStream out, String header) throws IOException
376 // The spec says that the file must be encoded using ISO-8859-1.
377 PrintWriter writer
378 = new PrintWriter(new OutputStreamWriter(out, "ISO-8859-1"));
379 if (header != null)
380 writer.println("#" + header);
381 writer.println ("#" + Calendar.getInstance ().getTime ());
383 Iterator iter = entrySet ().iterator ();
384 int i = size ();
385 StringBuffer s = new StringBuffer (); // Reuse the same buffer.
386 while (--i >= 0)
388 Map.Entry entry = (Map.Entry) iter.next ();
389 formatForOutput ((String) entry.getKey (), s, true);
390 s.append ('=');
391 formatForOutput ((String) entry.getValue (), s, false);
392 writer.println (s);
395 writer.flush ();
399 * Gets the property with the specified key in this property list.
400 * If the key is not found, the default property list is searched.
401 * If the property is not found in the default, null is returned.
403 * @param key The key for this property
404 * @return the value for the given key, or null if not found
405 * @throws ClassCastException if this property contains any key or
406 * value that isn't a string
407 * @see #defaults
408 * @see #setProperty(String, String)
409 * @see #getProperty(String, String)
411 public String getProperty(String key)
413 return getProperty(key, null);
417 * Gets the property with the specified key in this property list. If
418 * the key is not found, the default property list is searched. If the
419 * property is not found in the default, the specified defaultValue is
420 * returned.
422 * @param key The key for this property
423 * @param defaultValue A default value
424 * @return The value for the given key
425 * @throws ClassCastException if this property contains any key or
426 * value that isn't a string
427 * @see #defaults
428 * @see #setProperty(String, String)
430 public String getProperty(String key, String defaultValue)
432 Properties prop = this;
433 // Eliminate tail recursion.
436 String value = (String) prop.get(key);
437 if (value != null)
438 return value;
439 prop = prop.defaults;
441 while (prop != null);
442 return defaultValue;
446 * Returns an enumeration of all keys in this property list, including
447 * the keys in the default property list.
449 * @return an Enumeration of all defined keys
451 public Enumeration propertyNames()
453 // We make a new Set that holds all the keys, then return an enumeration
454 // for that. This prevents modifications from ruining the enumeration,
455 // as well as ignoring duplicates.
456 Properties prop = this;
457 Set s = new HashSet();
458 // Eliminate tail recursion.
461 s.addAll(prop.keySet());
462 prop = prop.defaults;
464 while (prop != null);
465 return Collections.enumeration(s);
469 * Prints the key/value pairs to the given print stream. This is
470 * mainly useful for debugging purposes.
472 * @param out the print stream, where the key/value pairs are written to
473 * @throws ClassCastException if this property contains a key or a
474 * value that isn't a string
475 * @see #list(PrintWriter)
477 public void list(PrintStream out)
479 PrintWriter writer = new PrintWriter (out);
480 list (writer);
484 * Prints the key/value pairs to the given print writer. This is
485 * mainly useful for debugging purposes.
487 * @param out the print writer where the key/value pairs are written to
488 * @throws ClassCastException if this property contains a key or a
489 * value that isn't a string
490 * @see #list(PrintStream)
491 * @since 1.1
493 public void list(PrintWriter out)
495 out.println ("-- listing properties --");
497 Iterator iter = entrySet ().iterator ();
498 int i = size ();
499 while (--i >= 0)
501 Map.Entry entry = (Map.Entry) iter.next ();
502 out.print ((String) entry.getKey () + "=");
504 // JDK 1.3/1.4 restrict the printed value, but not the key,
505 // to 40 characters, including the truncating ellipsis.
506 String s = (String ) entry.getValue ();
507 if (s != null && s.length () > 40)
508 out.println (s.substring (0, 37) + "...");
509 else
510 out.println (s);
512 out.flush ();
516 * Formats a key or value for output in a properties file.
517 * See store for a description of the format.
519 * @param str the string to format
520 * @param buffer the buffer to add it to
521 * @param key true if all ' ' must be escaped for the key, false if only
522 * leading spaces must be escaped for the value
523 * @see #store(OutputStream, String)
525 private void formatForOutput(String str, StringBuffer buffer, boolean key)
527 if (key)
529 buffer.setLength(0);
530 buffer.ensureCapacity(str.length());
532 else
533 buffer.ensureCapacity(buffer.length() + str.length());
534 boolean head = true;
535 int size = str.length();
536 for (int i = 0; i < size; i++)
538 char c = str.charAt(i);
539 switch (c)
541 case '\n':
542 buffer.append("\\n");
543 break;
544 case '\r':
545 buffer.append("\\r");
546 break;
547 case '\t':
548 buffer.append("\\t");
549 break;
550 case ' ':
551 buffer.append(head ? "\\ " : " ");
552 break;
553 case '\\':
554 case '!':
555 case '#':
556 case '=':
557 case ':':
558 buffer.append('\\').append(c);
559 break;
560 default:
561 if (c < ' ' || c > '~')
563 String hex = Integer.toHexString(c);
564 buffer.append("\\u0000".substring(0, 6 - hex.length()));
565 buffer.append(hex);
567 else
568 buffer.append(c);
570 if (c != ' ')
571 head = key;
574 } // class Properties