source: trunk/gcc/libjava/java/util/Properties.java

Last change on this file was 1392, checked in by bird, 21 years ago

This commit was generated by cvs2svn to compensate for changes in r1391,
which included commits to RCS files with non-trunk default branches.

  • Property cvs2svn:cvs-rev set to 1.1.1.2
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 18.8 KB
Line 
1/* Properties.java -- a set of persistent properties
2 Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
3
4This file is part of GNU Classpath.
5
6GNU Classpath is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11GNU Classpath is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Classpath; see the file COPYING. If not, write to the
18Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
1902111-1307 USA.
20
21Linking this library statically or dynamically with other modules is
22making a combined work based on this library. Thus, the terms and
23conditions of the GNU General Public License cover the whole
24combination.
25
26As a special exception, the copyright holders of this library give you
27permission to link this library with independent modules to produce an
28executable, regardless of the license terms of these independent
29modules, and to copy and distribute the resulting executable under
30terms of your choice, provided that you also meet, for each linked
31independent module, the terms and conditions of the license of that
32module. An independent module is a module which is not derived from
33or based on this library. If you modify this library, you may extend
34this exception to your version of the library, but you are not
35obligated to do so. If you do not wish to do so, delete this
36exception statement from your version. */
37
38
39package java.util;
40
41import java.io.IOException;
42import java.io.InputStream;
43import java.io.BufferedReader;
44import java.io.InputStreamReader;
45import java.io.OutputStream;
46import java.io.PrintWriter;
47import java.io.PrintStream;
48import java.io.OutputStreamWriter;
49
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.
54 *
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 &auml;)
60 *
61 *
62<pre>s1=3
63s2=MeineDisk
64s3=3. M\<code></code>u00e4rz 96
65s4=Die Diskette ''{1}'' enth\<code></code>u00e4lt {0} in {2}.
66s5=0
67s6=keine Dateien
68s7=1
69s8=eine Datei
70s9=2
71s10={0,number} Dateien
72s11=Das Formatieren schlug fehl mit folgender Exception: {0}
73s12=FEHLER
74s13=Ergebnis
75s14=Dialog
76s15=Auswahlkriterium
77s16=1,3</pre>
78 *
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>.
84 *
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.
87 *
88 * @author Jochen Hoenicke
89 * @author Eric Blake <ebb9@email.byu.edu>
90 * @see PropertyResourceBundle
91 * @status updated to 1.4
92 */
93public class Properties extends Hashtable
94{
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.
97
98 /**
99 * The property list that contains default values for any keys not
100 * in this property list.
101 *
102 * @serial the default properties
103 */
104 protected Properties defaults;
105
106 /**
107 * Compatible with JDK 1.0+.
108 */
109 private static final long serialVersionUID = 4112578634029874840L;
110
111 /**
112 * Creates a new empty property list with no default values.
113 */
114 public Properties()
115 {
116 }
117
118 /**
119 * Create a new empty property list with the specified default values.
120 *
121 * @param defaults a Properties object containing the default values
122 */
123 public Properties(Properties defaults)
124 {
125 this.defaults = defaults;
126 }
127
128 /**
129 * Adds the given key/value pair to this properties. This calls
130 * the hashtable method put.
131 *
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
137 */
138 public Object setProperty(String key, String value)
139 {
140 return put(key, value);
141 }
142
143 /**
144 * Reads a property list from an input stream. The stream should
145 * have the following format: <br>
146 *
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>
152 *
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>
159 *
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>
164 *
165 *
166<pre># This is a comment
167key = value
168k\: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.
171weekdays: Sunday,Monday,Tuesday,Wednesday,\\
172 Thursday,Friday,Saturday
173# The safest way to include a space at the end of a value:
174label = Name:\\u0020</pre>
175 *
176 * @param in the input stream
177 * @throws IOException if an error occurred when reading the input
178 * @throws NullPointerException if in is null
179 */
180 public void load(InputStream inStream) throws IOException
181 {
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;
186
187 while ((line = reader.readLine()) != null)
188 {
189 char c = 0;
190 int pos = 0;
191 // If empty line or begins with a comment character, skip this line.
192 if (line.length() == 0
193 || line.charAt(0) == '#' || line.charAt(0) == '!')
194 continue;
195
196 while (pos < line.length()
197 && Character.isWhitespace(c = line.charAt(pos)))
198 pos++;
199
200 // If line is empty skip this line.
201 if (pos == line.length())
202 continue;
203
204 // The characters up to the next Whitespace, ':', or '='
205 // describe the key. But look for escape sequences.
206 StringBuffer key = new StringBuffer();
207 while (pos < line.length()
208 && ! Character.isWhitespace(c = line.charAt(pos++))
209 && c != '=' && c != ':')
210 {
211 if (c == '\\')
212 {
213 if (pos == line.length())
214 {
215 // The line continues on the next line.
216 line = reader.readLine();
217 pos = 0;
218 while (pos < line.length()
219 && Character.isWhitespace(c = line.charAt(pos)))
220 pos++;
221 }
222 else
223 {
224 c = line.charAt(pos++);
225 switch (c)
226 {
227 case 'n':
228 key.append('\n');
229 break;
230 case 't':
231 key.append('\t');
232 break;
233 case 'r':
234 key.append('\r');
235 break;
236 case 'u':
237 if (pos + 4 <= line.length())
238 {
239 char uni = (char) Integer.parseInt
240 (line.substring(pos, pos + 4), 16);
241 key.append(uni);
242 pos += 4;
243 } // else throw exception?
244 break;
245 default:
246 key.append(c);
247 break;
248 }
249 }
250 }
251 else
252 key.append(c);
253 }
254
255 boolean isDelim = (c == ':' || c == '=');
256 while (pos < line.length()
257 && Character.isWhitespace(c = line.charAt(pos)))
258 pos++;
259
260 if (! isDelim && (c == ':' || c == '='))
261 {
262 pos++;
263 while (pos < line.length()
264 && Character.isWhitespace(c = line.charAt(pos)))
265 pos++;
266 }
267
268 StringBuffer element = new StringBuffer(line.length() - pos);
269 while (pos < line.length())
270 {
271 c = line.charAt(pos++);
272 if (c == '\\')
273 {
274 if (pos == line.length())
275 {
276 // The line continues on the next line.
277 line = reader.readLine();
278
279 // We might have seen a backslash at the end of
280 // the file. The JDK ignores the backslash in
281 // this case, so we follow for compatibility.
282 if (line == null)
283 break;
284
285 pos = 0;
286 while (pos < line.length()
287 && Character.isWhitespace(c = line.charAt(pos)))
288 pos++;
289 element.ensureCapacity(line.length() - pos +
290 element.length());
291 }
292 else
293 {
294 c = line.charAt(pos++);
295 switch (c)
296 {
297 case 'n':
298 element.append('\n');
299 break;
300 case 't':
301 element.append('\t');
302 break;
303 case 'r':
304 element.append('\r');
305 break;
306 case 'u':
307 if (pos + 4 <= line.length())
308 {
309 char uni = (char) Integer.parseInt
310 (line.substring(pos, pos + 4), 16);
311 element.append(uni);
312 pos += 4;
313 } // else throw exception?
314 break;
315 default:
316 element.append(c);
317 break;
318 }
319 }
320 }
321 else
322 element.append(c);
323 }
324 put(key.toString(), element.toString());
325 }
326 }
327
328 /**
329 * Calls <code>store(OutputStream out, String header)</code> and
330 * ignores the IOException that may be thrown.
331 *
332 * @param out the stream to write to
333 * @param header a description of the property list
334 * @throws ClassCastException if this property contains any key or
335 * value that are not strings
336 * @deprecated use {@link #store(OutputStream, String)} instead
337 */
338 public void save(OutputStream out, String header)
339 {
340 try
341 {
342 store(out, header);
343 }
344 catch (IOException ex)
345 {
346 }
347 }
348
349 /**
350 * Writes the key/value pairs to the given output stream, in a format
351 * suitable for <code>load</code>.<br>
352 *
353 * If header is not null, this method writes a comment containing
354 * the header as first line to the stream. The next line (or first
355 * line if header is null) contains a comment with the current date.
356 * Afterwards the key/value pairs are written to the stream in the
357 * following format.<br>
358 *
359 * Each line has the form <code>key = value</code>. Newlines,
360 * Returns and tabs are written as <code>\n,\t,\r</code> resp.
361 * The characters <code>\, !, #, =</code> and <code>:</code> are
362 * preceeded by a backslash. Spaces are preceded with a backslash,
363 * if and only if they are at the beginning of the key. Characters
364 * that are not in the ascii range 33 to 127 are written in the
365 * <code>\</code><code>u</code>xxxx Form.<br>
366 *
367 * Following the listing, the output stream is flushed but left open.
368 *
369 * @param out the output stream
370 * @param header the header written in the first line, may be null
371 * @throws ClassCastException if this property contains any key or
372 * value that isn't a string
373 * @throws IOException if writing to the stream fails
374 * @throws NullPointerException if out is null
375 * @since 1.2
376 */
377 public void store(OutputStream out, String header) throws IOException
378 {
379 // The spec says that the file must be encoded using ISO-8859-1.
380 PrintWriter writer
381 = new PrintWriter(new OutputStreamWriter(out, "ISO-8859-1"));
382 if (header != null)
383 writer.println("#" + header);
384 writer.println ("#" + Calendar.getInstance ().getTime ());
385
386 Iterator iter = entrySet ().iterator ();
387 int i = size ();
388 StringBuffer s = new StringBuffer (); // Reuse the same buffer.
389 while (--i >= 0)
390 {
391 Map.Entry entry = (Map.Entry) iter.next ();
392 formatForOutput ((String) entry.getKey (), s, true);
393 s.append ('=');
394 formatForOutput ((String) entry.getValue (), s, false);
395 writer.println (s);
396 }
397
398 writer.flush ();
399 }
400
401 /**
402 * Gets the property with the specified key in this property list.
403 * If the key is not found, the default property list is searched.
404 * If the property is not found in the default, null is returned.
405 *
406 * @param key The key for this property
407 * @return the value for the given key, or null if not found
408 * @throws ClassCastException if this property contains any key or
409 * value that isn't a string
410 * @see #defaults
411 * @see #setProperty(String, String)
412 * @see #getProperty(String, String)
413 */
414 public String getProperty(String key)
415 {
416 return getProperty(key, null);
417 }
418
419 /**
420 * Gets the property with the specified key in this property list. If
421 * the key is not found, the default property list is searched. If the
422 * property is not found in the default, the specified defaultValue is
423 * returned.
424 *
425 * @param key The key for this property
426 * @param defaultValue A default value
427 * @return The value for the given key
428 * @throws ClassCastException if this property contains any key or
429 * value that isn't a string
430 * @see #defaults
431 * @see #setProperty(String, String)
432 */
433 public String getProperty(String key, String defaultValue)
434 {
435 Properties prop = this;
436 // Eliminate tail recursion.
437 do
438 {
439 String value = (String) prop.get(key);
440 if (value != null)
441 return value;
442 prop = prop.defaults;
443 }
444 while (prop != null);
445 return defaultValue;
446 }
447
448 /**
449 * Returns an enumeration of all keys in this property list, including
450 * the keys in the default property list.
451 *
452 * @return an Enumeration of all defined keys
453 */
454 public Enumeration propertyNames()
455 {
456 // We make a new Set that holds all the keys, then return an enumeration
457 // for that. This prevents modifications from ruining the enumeration,
458 // as well as ignoring duplicates.
459 Properties prop = this;
460 Set s = new HashSet();
461 // Eliminate tail recursion.
462 do
463 {
464 s.addAll(prop.keySet());
465 prop = prop.defaults;
466 }
467 while (prop != null);
468 return Collections.enumeration(s);
469 }
470
471 /**
472 * Prints the key/value pairs to the given print stream. This is
473 * mainly useful for debugging purposes.
474 *
475 * @param out the print stream, where the key/value pairs are written to
476 * @throws ClassCastException if this property contains a key or a
477 * value that isn't a string
478 * @see #list(PrintWriter)
479 */
480 public void list(PrintStream out)
481 {
482 PrintWriter writer = new PrintWriter (out);
483 list (writer);
484 }
485
486 /**
487 * Prints the key/value pairs to the given print writer. This is
488 * mainly useful for debugging purposes.
489 *
490 * @param out the print writer where the key/value pairs are written to
491 * @throws ClassCastException if this property contains a key or a
492 * value that isn't a string
493 * @see #list(PrintStream)
494 * @since 1.1
495 */
496 public void list(PrintWriter out)
497 {
498 out.println ("-- listing properties --");
499
500 Iterator iter = entrySet ().iterator ();
501 int i = size ();
502 while (--i >= 0)
503 {
504 Map.Entry entry = (Map.Entry) iter.next ();
505 out.print ((String) entry.getKey () + "=");
506
507 // JDK 1.3/1.4 restrict the printed value, but not the key,
508 // to 40 characters, including the truncating ellipsis.
509 String s = (String ) entry.getValue ();
510 if (s != null && s.length () > 40)
511 out.println (s.substring (0, 37) + "...");
512 else
513 out.println (s);
514 }
515 out.flush ();
516 }
517
518 /**
519 * Formats a key or value for output in a properties file.
520 * See store for a description of the format.
521 *
522 * @param str the string to format
523 * @param buffer the buffer to add it to
524 * @param key true if all ' ' must be escaped for the key, false if only
525 * leading spaces must be escaped for the value
526 * @see #store(OutputStream, String)
527 */
528 private void formatForOutput(String str, StringBuffer buffer, boolean key)
529 {
530 if (key)
531 {
532 buffer.setLength(0);
533 buffer.ensureCapacity(str.length());
534 }
535 else
536 buffer.ensureCapacity(buffer.length() + str.length());
537 boolean head = true;
538 int size = str.length();
539 for (int i = 0; i < size; i++)
540 {
541 char c = str.charAt(i);
542 switch (c)
543 {
544 case '\n':
545 buffer.append("\\n");
546 break;
547 case '\r':
548 buffer.append("\\r");
549 break;
550 case '\t':
551 buffer.append("\\t");
552 break;
553 case ' ':
554 buffer.append(head ? "\\ " : " ");
555 break;
556 case '\\':
557 case '!':
558 case '#':
559 case '=':
560 case ':':
561 buffer.append('\\').append(c);
562 break;
563 default:
564 if (c < ' ' || c > '~')
565 {
566 String hex = Integer.toHexString(c);
567 buffer.append("\\u0000".substring(0, 6 - hex.length()));
568 buffer.append(hex);
569 }
570 else
571 buffer.append(c);
572 }
573 if (c != ' ')
574 head = key;
575 }
576 }
577} // class Properties
Note: See TracBrowser for help on using the repository browser.