changes to MGC classlibrary
[IRC.git] / Robust / src / ClassLibrary / MGC / gnu / Properties.java
1 /* Properties.java -- a set of persistent properties
2    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005  Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
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.
10
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.
15
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., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
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.
25
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. */
37
38
39 /**
40  * A set of persistent properties, which can be saved or loaded from a stream.
41  * A property list may also contain defaults, searched if the main list
42  * does not contain a property for a given key.
43  *
44  * An example of a properties file for the german language is given
45  * here.  This extends the example given in ListResourceBundle.
46  * Create a file MyResource_de.properties with the following contents
47  * and put it in the CLASSPATH.  (The character
48  * <code>\</code><code>u00e4</code> is the german umlaut)
49  *
50  * 
51 <pre>s1=3
52 s2=MeineDisk
53 s3=3. M\<code></code>u00e4rz 96
54 s4=Die Diskette ''{1}'' enth\<code></code>u00e4lt {0} in {2}.
55 s5=0
56 s6=keine Dateien
57 s7=1
58 s8=eine Datei
59 s9=2
60 s10={0,number} Dateien
61 s11=Das Formatieren schlug fehl mit folgender Exception: {0}
62 s12=FEHLER
63 s13=Ergebnis
64 s14=Dialog
65 s15=Auswahlkriterium
66 s16=1,3</pre>
67  *
68  * <p>Although this is a sub class of a hash table, you should never
69  * insert anything other than strings to this property, or several
70  * methods, that need string keys and values, will fail.  To ensure
71  * this, you should use the <code>get/setProperty</code> method instead
72  * of <code>get/put</code>.
73  *
74  * Properties are saved in ISO 8859-1 encoding, using Unicode escapes with
75  * a single <code>u</code> for any character which cannot be represented.
76  *
77  * @author Jochen Hoenicke
78  * @author Eric Blake (ebb9@email.byu.edu)
79  * @see PropertyResourceBundle
80  * @status updated to 1.4
81  */
82 public class Properties //extends Hashtable//<Object, Object>
83 {
84   // WARNING: Properties is a CORE class in the bootstrap cycle. See the
85   // comments in vm/reference/java/lang/Runtime for implications of this fact.
86
87   /**
88    * The property list that contains default values for any keys not
89    * in this property list.
90    *
91    * @serial the default properties
92    */
93   Hashtable proptbl;
94   
95   protected Properties defaults;
96
97   /**
98    * Compatible with JDK 1.0+.
99    */
100   private static final long serialVersionUID = 4112578634029874840L;
101
102   /**
103    * Creates a new empty property list with no default values.
104    */
105   public Properties()
106   {
107     proptbl = new Hashtable();
108   }
109
110   /**
111    * Create a new empty property list with the specified default values.
112    *
113    * @param defaults a Properties object containing the default values
114    */
115   public Properties(Properties defaults)
116   {
117     proptbl = new Hashtable();
118     this.defaults = defaults;
119   }
120
121   /**
122    * Adds the given key/value pair to this properties.  This calls
123    * the hashtable method put.
124    *
125    * @param key the key for this property
126    * @param value the value for this property
127    * @return The old value for the given key
128    * @see #getProperty(String)
129    * @since 1.2
130    */
131   public Object setProperty(String key, String value)
132   {
133     return proptbl.put(key, value);
134   }
135   
136   public Object put(String key, String value)
137   {
138     return proptbl.put(key, value);
139   }
140
141   /**
142    * Reads a property list from a character stream.  The stream should
143    * have the following format: <br>
144    *
145    * An empty line or a line starting with <code>#</code> or
146    * <code>!</code> is ignored.  An backslash (<code>\</code>) at the
147    * end of the line makes the line continueing on the next line
148    * (but make sure there is no whitespace after the backslash).
149    * Otherwise, each line describes a key/value pair. <br>
150    *
151    * The chars up to the first whitespace, = or : are the key.  You
152    * can include this caracters in the key, if you precede them with
153    * a backslash (<code>\</code>). The key is followed by optional
154    * whitespaces, optionally one <code>=</code> or <code>:</code>,
155    * and optionally some more whitespaces.  The rest of the line is
156    * the resource belonging to the key. <br>
157    *
158    * Escape sequences <code>\t, \n, \r, \\, \", \', \!, \#, \ </code>(a
159    * space), and unicode characters with the
160    * <code>\\u</code><em>xxxx</em> notation are detected, and
161    * converted to the corresponding single character. <br>
162    *
163    * 
164 <pre># This is a comment
165 key     = value
166 k\:5      \ a string starting with space and ending with newline\n
167 # This is a multiline specification; note that the value contains
168 # no white space.
169 weekdays: Sunday,Monday,Tuesday,Wednesday,\\
170           Thursday,Friday,Saturday
171 # The safest way to include a space at the end of a value:
172 label   = Name:\\u0020</pre>
173    *
174    * @param inReader the input {@link java.io.Reader}.
175    * @throws IOException if an error occurred when reading the input
176    * @throws NullPointerException if in is null
177    * @since 1.6
178    */
179   /*public void load(Reader inReader) throws IOException
180   {
181     BufferedReader reader = new BufferedReader(inReader);
182     String line;
183
184     while ((line = reader.readLine()) != null)
185       {
186         char c = 0;
187         int pos = 0;
188         // Leading whitespaces must be deleted first.
189         while (pos < line.length()
190                && Character.isWhitespace(c = line.charAt(pos)))
191           pos++;
192
193         // If empty line or begins with a comment character, skip this line.
194         if ((line.length() - pos) == 0
195             || line.charAt(pos) == '#' || line.charAt(pos) == '!')
196           continue;
197
198         // The characters up to the next Whitespace, ':', or '='
199         // describe the key.  But look for escape sequences.
200         // Try to short-circuit when there is no escape char.
201         int start = pos;
202         boolean needsEscape = line.indexOf('\\', pos) != -1;
203         CPStringBuilder key = needsEscape ? new CPStringBuilder() : null;
204         while (pos < line.length()
205                && ! Character.isWhitespace(c = line.charAt(pos++))
206                && c != '=' && c != ':')
207           {
208             if (needsEscape && c == '\\')
209               {
210                 if (pos == line.length())
211                   {
212                     // The line continues on the next line.  If there
213                     // is no next line, just treat it as a key with an
214                     // empty value.
215                     line = reader.readLine();
216                     if (line == null)
217                       line = "";
218                     pos = 0;
219                     while (pos < line.length()
220                            && Character.isWhitespace(c = line.charAt(pos)))
221                       pos++;
222                   }
223                 else
224                   {
225                     c = line.charAt(pos++);
226                     switch (c)
227                       {
228                       case 'n':
229                         key.append('\n');
230                         break;
231                       case 't':
232                         key.append('\t');
233                         break;
234                       case 'r':
235                         key.append('\r');
236                         break;
237                       case 'u':
238                         if (pos + 4 <= line.length())
239                           {
240                             char uni = (char) Integer.parseInt
241                               (line.substring(pos, pos + 4), 16);
242                             key.append(uni);
243                             pos += 4;
244                           }        // else throw exception?
245                         break;
246                       default:
247                         key.append(c);
248                         break;
249                       }
250                   }
251               }
252             else if (needsEscape)
253               key.append(c);
254           }
255
256         boolean isDelim = (c == ':' || c == '=');
257
258         String keyString;
259         if (needsEscape)
260           keyString = key.toString();
261         else if (isDelim || Character.isWhitespace(c))
262           keyString = line.substring(start, pos - 1);
263         else
264           keyString = line.substring(start, pos);
265
266         while (pos < line.length()
267                && Character.isWhitespace(c = line.charAt(pos)))
268           pos++;
269
270         if (! isDelim && (c == ':' || c == '='))
271           {
272             pos++;
273             while (pos < line.length()
274                    && Character.isWhitespace(c = line.charAt(pos)))
275               pos++;
276           }
277
278         // Short-circuit if no escape chars found.
279         if (!needsEscape)
280           {
281             put(keyString, line.substring(pos));
282             continue;
283           }
284
285         // Escape char found so iterate through the rest of the line.
286         StringBuilder element = new StringBuilder(line.length() - pos);
287         while (pos < line.length())
288           {
289             c = line.charAt(pos++);
290             if (c == '\\')
291               {
292                 if (pos == line.length())
293                   {
294                     // The line continues on the next line.
295                     line = reader.readLine();
296
297                     // We might have seen a backslash at the end of
298                     // the file.  The JDK ignores the backslash in
299                     // this case, so we follow for compatibility.
300                     if (line == null)
301                       break;
302
303                     pos = 0;
304                     while (pos < line.length()
305                            && Character.isWhitespace(c = line.charAt(pos)))
306                       pos++;
307                     element.ensureCapacity(line.length() - pos +
308                                            element.length());
309                   }
310                 else
311                   {
312                     c = line.charAt(pos++);
313                     switch (c)
314                       {
315                       case 'n':
316                         element.append('\n');
317                         break;
318                       case 't':
319                         element.append('\t');
320                         break;
321                       case 'r':
322                         element.append('\r');
323                         break;
324                       case 'u':
325                         if (pos + 4 <= line.length())
326                           {
327                             char uni = (char) Integer.parseInt
328                               (line.substring(pos, pos + 4), 16);
329                             element.append(uni);
330                             pos += 4;
331                           }        // else throw exception?
332                         break;
333                       default:
334                         element.append(c);
335                         break;
336                       }
337                   }
338               }
339             else
340               element.append(c);
341           }
342         put(keyString, element.toString());
343       }
344   }*/
345
346   /**
347    * Reads a property list from the supplied input stream.
348    * This method has the same functionality as {@link #load(Reader)}
349    * but the character encoding is assumed to be ISO-8859-1.
350    * Unicode characters not within the Latin1 set supplied by
351    * ISO-8859-1 should be escaped using '\\uXXXX' where XXXX
352    * is the UTF-16 code unit in hexadecimal.
353    *
354    * @param inStream the byte stream to read the property list from.
355    * @throws IOException if an I/O error occurs.
356    * @see #load(Reader)
357    * @since 1.2
358    */
359   public void load(InputStream inStream) throws IOException
360   {
361     //load(new InputStreamReader(inStream, "ISO-8859-1"));
362     System.println("Unimplemented Properties.load(InputStream) invoked");
363   }
364
365   /**
366    * Calls <code>store(OutputStream out, String header)</code> and
367    * ignores the IOException that may be thrown.
368    *
369    * @param out the stream to write to
370    * @param header a description of the property list
371    * @throws ClassCastException if this property contains any key or
372    *         value that are not strings
373    * @deprecated use {@link #store(OutputStream, String)} instead
374    */
375   //@Deprecated
376   /*public void save(OutputStream out, String header)
377   {
378     try
379       {
380         store(out, header);
381       }
382     catch (IOException ex)
383       {
384       }
385   }*/
386
387   /**
388    * Writes the key/value pairs to the given output stream, in a format
389    * suitable for <code>load</code>.<br>
390    *
391    * If header is not null, this method writes a comment containing
392    * the header as first line to the stream.  The next line (or first
393    * line if header is null) contains a comment with the current date.
394    * Afterwards the key/value pairs are written to the stream in the
395    * following format.<br>
396    *
397    * Each line has the form <code>key = value</code>.  Newlines,
398    * Returns and tabs are written as <code>\n,\t,\r</code> resp.
399    * The characters <code>\, !, #, =</code> and <code>:</code> are
400    * preceeded by a backslash.  Spaces are preceded with a backslash,
401    * if and only if they are at the beginning of the key.  Characters
402    * that are not in the ascii range 33 to 127 are written in the
403    * <code>\</code><code>u</code>xxxx Form.<br>
404    *
405    * Following the listing, the output stream is flushed but left open.
406    *
407    * @param out the output stream
408    * @param header the header written in the first line, may be null
409    * @throws ClassCastException if this property contains any key or
410    *         value that isn't a string
411    * @throws IOException if writing to the stream fails
412    * @throws NullPointerException if out is null
413    * @since 1.2
414    */
415   public void store(OutputStream out, String header)// throws IOException
416   {
417     // The spec says that the file must be encoded using ISO-8859-1.
418     /*PrintWriter writer
419       = new PrintWriter(new OutputStreamWriter(out, "ISO-8859-1"));
420     if (header != null)
421       writer.println("#" + header);
422     writer.println ("#" + Calendar.getInstance ().getTime ());
423     
424     Iterator iter = entrySet ().iterator ();
425     int i = size ();
426     CPStringBuilder s = new CPStringBuilder (); // Reuse the same buffer.
427     while (--i >= 0)
428       {
429         Map.Entry entry = (Map.Entry) iter.next ();
430         formatForOutput ((String) entry.getKey (), s, true);
431         s.append ('=');
432         formatForOutput ((String) entry.getValue (), s, false);
433         writer.println (s);
434       }
435
436     writer.flush ();*/
437     System.println("Unimplemented Properties.store() invoked");
438   }
439
440   /**
441    * Gets the property with the specified key in this property list.
442    * If the key is not found, the default property list is searched.
443    * If the property is not found in the default, null is returned.
444    *
445    * @param key The key for this property
446    * @return the value for the given key, or null if not found
447    * @throws ClassCastException if this property contains any key or
448    *         value that isn't a string
449    * @see #defaults
450    * @see #setProperty(String, String)
451    * @see #getProperty(String, String)
452    */
453   public String getProperty(String key)
454   {
455     Hashtable tbl = this.proptbl;
456     // Eliminate tail recursion.
457     Properties prop = this.defaults;
458     while (prop != null)
459       {
460         String value = (String) tbl.get(key);
461         if (value != null)
462           return value;
463         tbl = prop.proptbl;
464         prop = prop.defaults;
465       }
466     String value = (String) tbl.get(key);
467     if (value != null)
468       return value;
469     else 
470       return null;
471   }
472
473   /**
474    * Gets the property with the specified key in this property list.  If
475    * the key is not found, the default property list is searched.  If the
476    * property is not found in the default, the specified defaultValue is
477    * returned.
478    *
479    * @param key The key for this property
480    * @param defaultValue A default value
481    * @return The value for the given key
482    * @throws ClassCastException if this property contains any key or
483    *         value that isn't a string
484    * @see #defaults
485    * @see #setProperty(String, String)
486    */
487   public String getProperty(String key, String defaultValue)
488   {
489     String prop = getProperty(key);
490     if (prop == null)
491       prop = defaultValue;
492     return prop;
493   }
494
495   /**
496    * Returns an enumeration of all keys in this property list, including
497    * the keys in the default property list.
498    *
499    * @return an Enumeration of all defined keys
500    */
501   public Enumeration/*<?>*/ propertyNames()
502   {
503     // We make a new Set that holds all the keys, then return an enumeration
504     // for that. This prevents modifications from ruining the enumeration,
505     // as well as ignoring duplicates.
506     Properties prop = this.defaults;
507     Object[] tarray = keySet().toArray();
508     HashSet s = new HashSet();
509     for(int i = 0; i < tarray.length; i++) {
510       s.add(tarray[i]);
511     }
512     // Eliminate tail recursion.
513     while (prop != null)
514       {
515         tarray = prop.keySet().toArray();
516         for(int i = 0; i < tarray.length; i++) {
517           s.add(tarray[i]);
518         }
519         prop = prop.defaults;
520       }
521     return new Enumeration(); //Collections.enumeration(s);
522   }
523   
524   public Set keySet() {
525     HashMapIterator it = (HashMapIterator)this.proptbl.iterator(0);
526     Set keys = new Vector();
527     while(it.hasNext()) {
528       keys.add(it.next());
529     }
530     return keys;
531   }
532
533   /**
534    * Prints the key/value pairs to the given print stream.  This is 
535    * mainly useful for debugging purposes.
536    *
537    * @param out the print stream, where the key/value pairs are written to
538    * @throws ClassCastException if this property contains a key or a
539    *         value that isn't a string
540    * @see #list(PrintWriter)
541    */
542   /*public void list(PrintStream out)
543   {
544     PrintWriter writer = new PrintWriter (out);
545     list (writer);
546   }*/
547
548   /**
549    * Prints the key/value pairs to the given print writer.  This is
550    * mainly useful for debugging purposes.
551    *
552    * @param out the print writer where the key/value pairs are written to
553    * @throws ClassCastException if this property contains a key or a
554    *         value that isn't a string
555    * @see #list(PrintStream)
556    * @since 1.1
557    */
558   /*public void list(PrintWriter out)
559   {
560     out.println ("-- listing properties --");
561
562     Iterator iter = entrySet ().iterator ();
563     int i = size ();
564     while (--i >= 0)
565       {
566         Map.Entry entry = (Map.Entry) iter.next ();
567         out.print ((String) entry.getKey () + "=");
568
569         // JDK 1.3/1.4 restrict the printed value, but not the key,
570         // to 40 characters, including the truncating ellipsis.
571         String s = (String ) entry.getValue ();
572         if (s != null && s.length () > 40)
573           out.println (s.substring (0, 37) + "...");
574         else
575           out.println (s);
576       }
577     out.flush ();
578   }*/
579
580   /**
581    * Formats a key or value for output in a properties file.
582    * See store for a description of the format.
583    *
584    * @param str the string to format
585    * @param buffer the buffer to add it to
586    * @param key true if all ' ' must be escaped for the key, false if only
587    *        leading spaces must be escaped for the value
588    * @see #store(OutputStream, String)
589    */
590   /*private void formatForOutput(String str, CPStringBuilder buffer, boolean key)
591   {
592     if (key)
593       {
594         buffer.setLength(0);
595         buffer.ensureCapacity(str.length());
596       }
597     else
598       buffer.ensureCapacity(buffer.length() + str.length());
599     boolean head = true;
600     int size = str.length();
601     for (int i = 0; i < size; i++)
602       {
603         char c = str.charAt(i);
604         switch (c)
605           {
606           case '\n':
607             buffer.append("\\n");
608             break;
609           case '\r':
610             buffer.append("\\r");
611             break;
612           case '\t':
613             buffer.append("\\t");
614             break;
615           case ' ':
616             buffer.append(head ? "\\ " : " ");
617             break;
618           case '\\':
619           case '!':
620           case '#':
621           case '=':
622           case ':':
623             buffer.append('\\').append(c);
624             break;
625           default:
626             if (c < ' ' || c > '~')
627               {
628                 String hex = Integer.toHexString(c);
629                 buffer.append("\\u0000".substring(0, 6 - hex.length()));
630                 buffer.append(hex);
631               }
632             else
633               buffer.append(c);
634           }
635         if (c != ' ')
636           head = key;
637       }
638   }*/
639
640   /**
641    * <p>
642    * Encodes the properties as an XML file using the UTF-8 encoding.
643    * The format of the XML file matches the DTD
644    * <a href="http://java.sun.com/dtd/properties.dtd">
645    * http://java.sun.com/dtd/properties.dtd</a>.
646    * </p>
647    * <p>
648    * Invoking this method provides the same behaviour as invoking
649    * <code>storeToXML(os, comment, "UTF-8")</code>.
650    * </p>
651    * 
652    * @param os the stream to output to.
653    * @param comment a comment to include at the top of the XML file, or
654    *                <code>null</code> if one is not required.
655    * @throws IOException if the serialization fails.
656    * @throws NullPointerException if <code>os</code> is null.
657    * @since 1.5
658    */
659   /*public void storeToXML(OutputStream os, String comment)
660     throws IOException
661   {
662     storeToXML(os, comment, "UTF-8");
663   }*/
664
665   /**
666    * <p>
667    * Encodes the properties as an XML file using the supplied encoding.
668    * The format of the XML file matches the DTD
669    * <a href="http://java.sun.com/dtd/properties.dtd">
670    * http://java.sun.com/dtd/properties.dtd</a>.
671    * </p>
672    * 
673    * @param os the stream to output to.
674    * @param comment a comment to include at the top of the XML file, or
675    *                <code>null</code> if one is not required.
676    * @param encoding the encoding to use for the XML output.
677    * @throws IOException if the serialization fails.
678    * @throws NullPointerException if <code>os</code> or <code>encoding</code>
679    *                              is null.
680    * @since 1.5
681    */
682   /*public void storeToXML(OutputStream os, String comment, String encoding)
683     throws IOException
684   {
685     if (os == null)
686       throw new NullPointerException("Null output stream supplied.");
687     if (encoding == null)
688       throw new NullPointerException("Null encoding supplied.");
689     try
690       {
691         DOMImplementationRegistry registry = 
692           DOMImplementationRegistry.newInstance();
693         DOMImplementation domImpl = registry.getDOMImplementation("LS 3.0");
694         DocumentType doctype =
695           domImpl.createDocumentType("properties", null,
696                                      "http://java.sun.com/dtd/properties.dtd");
697         Document doc = domImpl.createDocument(null, "properties", doctype);
698         Element root = doc.getDocumentElement();
699         if (comment != null)
700           {
701             Element commentElement = doc.createElement("comment");
702             commentElement.appendChild(doc.createTextNode(comment));
703             root.appendChild(commentElement);
704           }
705         Iterator iterator = entrySet().iterator();
706         while (iterator.hasNext())
707           {
708             Map.Entry entry = (Map.Entry) iterator.next();
709             Element entryElement = doc.createElement("entry");
710             entryElement.setAttribute("key", (String) entry.getKey());
711             entryElement.appendChild(doc.createTextNode((String)
712                                                         entry.getValue()));
713             root.appendChild(entryElement);
714           }
715         DOMImplementationLS loadAndSave = (DOMImplementationLS) domImpl;
716         LSSerializer serializer = loadAndSave.createLSSerializer();
717         LSOutput output = loadAndSave.createLSOutput();
718         output.setByteStream(os);
719         output.setEncoding(encoding);
720         serializer.write(doc, output);
721       }
722     catch (ClassNotFoundException e)
723       {
724         throw (IOException) 
725           new IOException("The XML classes could not be found.").initCause(e);
726       }
727     catch (InstantiationException e)
728       {
729         throw (IOException)
730           new IOException("The XML classes could not be instantiated.")
731           .initCause(e);
732       }
733     catch (IllegalAccessException e)
734       {
735         throw (IOException)
736           new IOException("The XML classes could not be accessed.")
737           .initCause(e);
738       }
739   }*/
740
741   /**
742    * <p>
743    * Decodes the contents of the supplied <code>InputStream</code> as
744    * an XML file, which represents a set of properties.  The format of
745    * the XML file must match the DTD
746    * <a href="http://java.sun.com/dtd/properties.dtd">
747    * http://java.sun.com/dtd/properties.dtd</a>.
748    * </p>
749    *
750    * @param in the input stream from which to receive the XML data.
751    * @throws IOException if an I/O error occurs in reading the input data.
752    * @throws InvalidPropertiesFormatException if the input data does not
753    *                                          constitute an XML properties
754    *                                          file.
755    * @throws NullPointerException if <code>in</code> is null.
756    * @since 1.5
757    */
758   /*public void loadFromXML(InputStream in)
759     throws IOException, InvalidPropertiesFormatException
760   {
761     if (in == null)
762       throw new NullPointerException("Null input stream supplied.");
763     try
764       {
765         XMLInputFactory factory = XMLInputFactory.newInstance();
766         // Don't resolve external entity references
767         factory.setProperty("javax.xml.stream.isSupportingExternalEntities",
768                             Boolean.FALSE);
769         XMLStreamReader reader = factory.createXMLStreamReader(in);
770         String name, key = null;
771         CPStringBuilder buf = null;
772         while (reader.hasNext())
773           {
774             switch (reader.next())
775               {
776               case XMLStreamConstants.START_ELEMENT:
777                 name = reader.getLocalName();
778                 if (buf == null && "entry".equals(name))
779                   {
780                     key = reader.getAttributeValue(null, "key");
781                     if (key == null)
782                       {
783                         String msg = "missing 'key' attribute";
784                         throw new InvalidPropertiesFormatException(msg);
785                       }
786                     buf = new CPStringBuilder();
787                   }
788                 else if (!"properties".equals(name) && !"comment".equals(name))
789                   {
790                     String msg = "unexpected element name '" + name + "'";
791                     throw new InvalidPropertiesFormatException(msg);
792                   }
793                 break;
794               case XMLStreamConstants.END_ELEMENT:
795                 name = reader.getLocalName();
796                 if (buf != null && "entry".equals(name))
797                   {
798                     put(key, buf.toString());
799                     buf = null;
800                   }
801                 else if (!"properties".equals(name) && !"comment".equals(name))
802                   {
803                     String msg = "unexpected element name '" + name + "'";
804                     throw new InvalidPropertiesFormatException(msg);
805                   }
806                 break;
807               case XMLStreamConstants.CHARACTERS:
808               case XMLStreamConstants.SPACE:
809               case XMLStreamConstants.CDATA:
810                 if (buf != null)
811                   buf.append(reader.getText());
812                 break;
813               }
814           }
815         reader.close();
816       }
817     catch (XMLStreamException e)
818       {
819         throw (InvalidPropertiesFormatException)
820           new InvalidPropertiesFormatException("Error in parsing XML.").
821           initCause(e);
822       }
823   }*/
824
825 } // class Properties