OSDN Git Service

* All files: Updated copyright to reflect Cygnus purchase.
[pf3gnuchains/gcc-fork.git] / libjava / java / util / Properties.java
1 // Properties - Property list representation.
2
3 /* Copyright (C) 1998, 1999  Red Hat, Inc.
4
5    This file is part of libgcj.
6
7 This software is copyrighted work licensed under the terms of the
8 Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
9 details.  */
10
11 package java.util;
12
13 import java.io.BufferedWriter;
14 import java.io.IOException;
15 import java.io.InputStream;
16 import java.io.InputStreamReader;
17 import java.io.OutputStream;
18 import java.io.OutputStreamWriter;
19 import java.io.PrintStream;
20 import java.io.PrintWriter;
21 import java.io.PushbackReader;
22
23 /**
24  * @author Tom Tromey <tromey@cygnus.com>
25  * @date October 26, 1998.
26  */
27
28 /* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
29  * Status: Complete to JDK 1.1.
30  */
31
32 public class Properties extends Hashtable
33 {
34   protected Properties defaults;
35
36   public String getProperty (String propName)
37     {
38       return getProperty (propName, null);
39     }
40
41   public String getProperty (String propName, String defVal)
42     {
43       String r = (String) get (propName);
44       if (r == null)
45         {
46           if (defaults != null)
47             r = defaults.getProperty(propName, defVal);
48           else
49             r = defVal;
50         }
51       return r;
52     }
53
54   public void list (PrintStream out)
55     {
56       Enumeration e = propertyNames ();
57       while (e.hasMoreElements())
58         {
59           String key = (String) e.nextElement();
60           String value = getProperty(key);
61           if (value != null)
62             {
63               if (value.length() > 40)
64                 {
65                   // JDK compatibility.
66                   value = value.substring(0, 37) + "...";
67                 }
68               out.print(key);
69               out.print("=");
70               out.println(value);
71             }
72         }
73     }
74
75   public void list (PrintWriter writer)
76     {
77       Enumeration e = propertyNames ();
78       while (e.hasMoreElements())
79         {
80           String key = (String) e.nextElement();
81           String value = getProperty(key);
82           if (value != null)
83             {
84               if (value.length() > 40)
85                 {
86                   // JDK compatibility.
87                   value = value.substring(0, 37) + "...";
88                 }
89               writer.print(key);
90               writer.print("=");
91               writer.println(value);
92             }
93         }
94     }
95
96   private final boolean skip_ws (PushbackReader reader) throws IOException
97     {
98       while (true)
99         {
100           int c = reader.read();
101           if (c == -1)
102             return false;
103           // FIXME: we use our own definition of whitespace.
104           // Character.isWhitespace includes newlines, which we don't
105           // want.  Character.isSpaceChar doesn't include \t.
106           if (c != ' ' && c != '\t')
107             {
108               reader.unread(c);
109               return true;
110             }
111         }
112     }
113
114   // Note: this method needs to be rewritten for JDK 1.2.
115   // We rather arbitrarily decide that an EOF in the middle of a line
116   // means that the whole line should be ignored.  The spec doesn't
117   // specifically address this, but this interpretation seems valid.
118   public synchronized void load (InputStream in) throws IOException
119     {
120       PushbackReader reader = new PushbackReader (new InputStreamReader (in));
121
122       StringBuffer key = new StringBuffer ();
123       StringBuffer value = new StringBuffer ();
124
125     nextLine:
126       while (true)
127         {
128           key.setLength(0);
129           value.setLength(0);
130
131           // Skip leading whitespace.
132           if (! skip_ws (reader))
133             return;
134
135           // Read key until key terminator.
136           boolean first_char = true;
137           int c;
138           while (true)
139             {
140               c = reader.read();
141               if (c == -1)
142                 return;
143               if (c == '\\')
144                 {
145                   first_char = false;
146                   c = reader.read();
147                   if (c == -1)
148                     return;
149                 }
150
151               // If we found a comment, just read to end of line and
152               // then keep going.
153               if (first_char == true && (c == '#' || c == '!'))
154                 {
155                   while (c != -1 && c != '\r' && c != '\n')
156                     c = reader.read();
157                   if (c == -1)
158                     return;
159                   continue nextLine;
160                 }
161
162               if (c == '\r' || c == '\n')
163                 {
164                   if (first_char)
165                     continue nextLine;
166                   reader.unread(c);
167                   break;
168                 }
169               // FIXME: again, our own definition of whitespace.
170               if (c == ' ' || c == '\t' || c == ':' || c == '=')
171                 break;
172
173               first_char = false;
174               key.append((char) c);
175             }
176
177           // Found end of key.  Skip whitespace.  If the terminator
178           // was whitespace, also skip a single instance of a "real"
179           // terminator, and then more whitespace.
180           if (! skip_ws (reader))
181             return;
182           if (c != ':' && c != '=')
183             {
184               c = reader.read();
185               if (c == -1)
186                 return;
187               if (c == ':' || c == '=')
188                 {
189                   // Skip more whitespace.
190                   if (! skip_ws (reader))
191                     return;
192                 }
193               else
194                 reader.unread(c);
195             }
196
197           // Now read the value.
198           while (true)
199             {
200               c = reader.read();
201               if (c == -1)
202                 return;
203               if (c == '\r' || c == '\n')
204                 break;
205               if (c == '\\')
206                 {
207                   c = reader.read();
208                   switch (c)
209                     {
210                     case -1:
211                       return;
212                     case 't':
213                       c = '\t';
214                       break;
215                     case 'r':
216                       c = '\r';
217                       break;
218                     case 'n':
219                       c = '\n';
220                       break;
221                     case 'u':
222                       c = 0;
223                       for (int i = 0; i < 4; ++i)
224                         {
225                           int x = reader.read();
226                           if (x == -1)
227                             return;
228                           int d = Character.digit((char) x, 16);
229                           // We follow JDK here: invalid characters
230                           // are treated as terminators.
231                           if (d == -1)
232                             {
233                               value.append((char) c);
234                               c = x;
235                               break;
236                             }
237                           c <<= 4;
238                           c |= d;
239                         }
240                       break;
241                     default:
242                       // Nothing.
243                     }
244                 }
245               value.append((char) c);
246             }
247
248           put (key.toString(), value.toString());
249         }
250     }
251
252   public Properties ()
253     {
254       defaults = null;
255     }
256
257   public Properties (Properties defs)
258     {
259       defaults = defs;
260     }
261
262   private final void addHashEntries (Hashtable base)
263     {
264       if (defaults != null)
265         defaults.addHashEntries(base);
266       Enumeration keys = keys ();
267       while (keys.hasMoreElements())
268         base.put(keys.nextElement(), base);
269     }
270
271   public Enumeration propertyNames ()
272     {
273       // We make a new Hashtable that holds all the keys.  Then we
274       // return an enumeration for this hash.  We do this because we
275       // don't want modifications to be reflected in the enumeration
276       // (per JCL), and because there doesn't seem to be a
277       // particularly better way to ensure that duplicates are
278       // ignored.
279       Hashtable t = new Hashtable ();
280       addHashEntries (t);
281       return t.keys();
282     }
283
284   public synchronized void save (OutputStream out, String comment)
285     {
286       // Use a buffer because writing a single string through
287       // OutputStreamWriter is fairly expensive.
288       BufferedWriter output
289         = new BufferedWriter (new OutputStreamWriter (out));
290       String newline = System.getProperty("line.separator");
291
292       try
293         {
294           if (comment != null)
295             {
296               // We just lose if COMMENT contains a newline.  This is
297               // what JDK 1.1 does.
298               output.write("#");
299               output.write(comment);
300               output.write(newline);
301             }
302           output.write("# ");
303           output.write(new Date().toString());
304           output.write(newline);
305
306           Enumeration keys = keys ();
307           while (keys.hasMoreElements())
308             {
309               String key = (String) keys.nextElement();
310               String value = (String) get (key);
311
312               // FIXME: JCL says that the key can contain many Unicode
313               // characters.  But it also doesn't say we should encode
314               // it in any way.
315               // FIXME: if key contains ':', '=', or whitespace, must
316               // quote it here.  Note that JDK 1.1 does not do this.
317               output.write(key);
318               output.write("=");
319
320               boolean leading = true;
321               for (int i = 0; i < value.length(); ++i)
322                 {
323                   boolean new_lead = false;
324                   char c = value.charAt(i);
325                   switch (c)
326                     {
327                     case '\n':
328                       output.write("\\n");
329                       break;
330                     case '\r':
331                       output.write("\\r");
332                       break;
333                     case '\t':
334                       output.write("\\t");
335                       break;
336                     case '\\':
337                       output.write("\\\\");
338                       break;
339
340                     case '#':
341                     case '!':
342                     case '=':
343                     case ':':
344                       output.write("\\");
345                       output.write(c);
346                       break;
347
348                     case ' ':
349                       new_lead = leading;
350                       if (leading)
351                         output.write("\\");
352                       output.write(c);
353                       break;
354
355                     default:
356                       if (c < '\u0020' || c > '\u007e')
357                         {
358                           output.write("\\u");
359                           output.write(Character.forDigit(c >>> 12, 16));
360                           output.write(Character.forDigit((c >>> 8) & 0xff,
361                                                           16));
362                           output.write(Character.forDigit((c >>> 4) & 0xff,
363                                                           16));
364                           output.write(Character.forDigit(c & 0xff, 16));
365                         }
366                       else
367                         output.write(c);
368                     }
369                   leading = new_lead;
370                 }
371               output.write(newline);
372             }
373
374           output.flush();
375         }
376       catch (IOException ignore)
377         {
378         }
379     }
380 }