OSDN Git Service

* java/util/Properties.java: Merged with Classpath version.
authortromey <tromey@138bc75d-0d04-0410-961f-82ee72b054a4>
Sat, 7 Oct 2000 18:13:11 +0000 (18:13 +0000)
committertromey <tromey@138bc75d-0d04-0410-961f-82ee72b054a4>
Sat, 7 Oct 2000 18:13:11 +0000 (18:13 +0000)
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@36775 138bc75d-0d04-0410-961f-82ee72b054a4

libjava/ChangeLog
libjava/java/util/Properties.java

index b93bb7d..9bb268d 100644 (file)
@@ -1,3 +1,7 @@
+2000-10-07  Tom Tromey  <tromey@cygnus.com>
+
+       * java/util/Properties.java: Merged with Classpath version.
+
 2000-10-05  Tom Tromey  <tromey@cygnus.com>
 
        * java/lang/reflect/natField.cc (BooleanClass): Don't define.
index 6360b99..3f51d47 100644 (file)
-// Properties - Property list representation.
+/* java.util.Properties
+   Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
 
-/* Copyright (C) 1998, 1999, 2000  Free Software Foundation
+This file is part of GNU Classpath.
 
-   This file is part of libgcj.
+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.
 
-This software is copyrighted work licensed under the terms of the
-Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
-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.
 
-package java.util;
-
-import java.io.BufferedWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintStream;
-import java.io.PrintWriter;
-import java.io.PushbackReader;
-
-/**
- * @author Tom Tromey <tromey@cygnus.com>
- * @date October 26, 1998.
- */
-
-/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
- * Status: Complete to JDK 1.2.
- */
+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. */
 
-public class Properties extends Hashtable
-{
-  protected Properties defaults;
 
-  private static final long serialVersionUID = 4112578634029874840L;
+package java.util;
+import java.io.*;
 
-  public String getProperty (String propName)
-    {
-      return getProperty (propName, null);
+/**
+ * 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 &auml;)
+ * 
+ * <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;
     }
-
-  public String getProperty (String propName, String defVal)
-    {
-      String r = (String) get (propName);
-      if (r == null)
-       {
-         if (defaults != null)
-           r = defaults.getProperty(propName, defVal);
-         else
-           r = defVal;
-       }
-      return r;
+    
+    /**
+     * 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;
     }
 
-  public Object setProperty (String key, String value)
-  {
-    return put (key, value);
-  }
-
-  public void list (PrintStream out)
-    {
-      Enumeration e = propertyNames ();
-      while (e.hasMoreElements())
-       {
-         String key = (String) e.nextElement();
-         String value = getProperty(key);
-         if (value != null)
-           {
-             if (value.length() > 40)
-               {
-                 // JDK compatibility.
-                 value = value.substring(0, 37) + "...";
-               }
-             out.print(key);
-             out.print("=");
-             out.println(value);
-           }
-       }
+    /**
+     * 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 occured when reading
+     * from the input.  */
+    public void load(InputStream inStream) throws IOException {
+        BufferedReader reader = 
+            new BufferedReader(new InputStreamReader(inStream));
+        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 characaters 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);
+                            } // 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);
+                            } // else throw exception?
+                            break;
+                       default:
+                            element.append(c);
+                            break;
+                        }
+                    }
+                } else 
+                    element.append(c);
+            }
+            put(key.toString(), element.toString());
+        }
     }
 
-  public void list (PrintWriter writer)
-    {
-      Enumeration e = propertyNames ();
-      while (e.hasMoreElements())
-       {
-         String key = (String) e.nextElement();
-         String value = getProperty(key);
-         if (value != null)
-           {
-             if (value.length() > 40)
-               {
-                 // JDK compatibility.
-                 value = value.substring(0, 37) + "...";
-               }
-             writer.print(key);
-             writer.print("=");
-             writer.println(value);
-           }
-       }
+    /**
+     * 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) {
+        }
     }
-
-  private final boolean skip_ws (PushbackReader reader) throws IOException
-    {
-      while (true)
-       {
-         int c = reader.read();
-         if (c == -1)
-           return false;
-         // FIXME: we use our own definition of whitespace.
-         // Character.isWhitespace includes newlines, which we don't
-         // want.  Character.isSpaceChar doesn't include \t.
-         if (c != ' ' && c != '\t')
-           {
-             reader.unread(c);
-             return true;
-           }
-       }
+    
+    /**
+     * 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 {
+        PrintWriter writer = new PrintWriter(out);
+        if (header != null)
+            writer.println("#"+header);
+        writer.println("#"+new Date().toString());
+        list(writer);
+       writer.flush();
     }
-
-  // Note: this method needs to be rewritten for JDK 1.2.
-  // We rather arbitrarily decide that an EOF in the middle of a line
-  // means that the whole line should be ignored.  The spec doesn't
-  // specifically address this, but this interpretation seems valid.
-  public synchronized void load (InputStream in) throws IOException
-    {
-      PushbackReader reader = new PushbackReader (new InputStreamReader (in));
-
-      StringBuffer key = new StringBuffer ();
-      StringBuffer value = new StringBuffer ();
-
-    nextLine:
-      while (true)
-       {
-         key.setLength(0);
-         value.setLength(0);
-
-         // Skip leading whitespace.
-         if (! skip_ws (reader))
-           return;
-
-         // Read key until key terminator.
-         boolean first_char = true;
-         int c;
-         while (true)
-           {
-             c = reader.read();
-             if (c == -1)
-               return;
-             if (c == '\\')
-               {
-                 first_char = false;
-                 c = reader.read();
-                 if (c == -1)
-                   return;
-               }
-
-             // If we found a comment, just read to end of line and
-             // then keep going.
-             if (first_char == true && (c == '#' || c == '!'))
-               {
-                 while (c != -1 && c != '\r' && c != '\n')
-                   c = reader.read();
-                 if (c == -1)
-                   return;
-                 continue nextLine;
-               }
-
-             if (c == '\r' || c == '\n')
-               {
-                 if (first_char)
-                   continue nextLine;
-                 reader.unread(c);
-                 break;
-               }
-             // FIXME: again, our own definition of whitespace.
-             if (c == ' ' || c == '\t' || c == ':' || c == '=')
-               break;
-
-             first_char = false;
-             key.append((char) c);
-           }
-
-         // Found end of key.  Skip whitespace.  If the terminator
-         // was whitespace, also skip a single instance of a "real"
-         // terminator, and then more whitespace.
-         if (! skip_ws (reader))
-           return;
-         if (c != ':' && c != '=')
-           {
-             c = reader.read();
-             if (c == -1)
-               return;
-             if (c == ':' || c == '=')
-               {
-                 // Skip more whitespace.
-                 if (! skip_ws (reader))
-                   return;
-               }
-             else
-               reader.unread(c);
-           }
-
-         // Now read the value.
-         while (true)
-           {
-             c = reader.read();
-             if (c == -1)
-               return;
-             if (c == '\r' || c == '\n')
-               break;
-             if (c == '\\')
-               {
-                 c = reader.read();
-                 switch (c)
-                   {
-                   case -1:
-                     return;
-                   case 't':
-                     c = '\t';
-                     break;
-                   case 'r':
-                     c = '\r';
-                     break;
-                   case 'n':
-                     c = '\n';
-                     break;
-                   case 'u':
-                     c = 0;
-                     for (int i = 0; i < 4; ++i)
-                       {
-                         int x = reader.read();
-                         if (x == -1)
-                           return;
-                         int d = Character.digit((char) x, 16);
-                         // We follow JDK here: invalid characters
-                         // are treated as terminators.
-                         if (d == -1)
-                           {
-                             value.append((char) c);
-                             c = x;
-                             break;
-                           }
-                         c <<= 4;
-                         c |= d;
-                       }
-                     break;
-                   default:
-                     // Nothing.
-                   }
-               }
-             value.append((char) c);
-           }
-
-         put (key.toString(), value.toString());
-       }
+    
+    /**
+     * 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);
     }
 
-  public Properties ()
-    {
-      defaults = 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, 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);
     }
 
-  public Properties (Properties defs)
-    {
-      defaults = defs;
+    /**
+     * 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);
+    private final void addHashEntries (Hashtable base) {
+        if (defaults != null)
+           defaults.addHashEntries(base);
+       Enumeration keys = keys ();
+       while (keys.hasMoreElements())
+           base.put(keys.nextElement(), base);
     }
 
-  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();
+    /**
+     * 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();
     }
 
-  public synchronized void save (OutputStream out, String comment)
-  {
-    try
-      {
-       store (out, comment);
-      }
-    catch (IOException _)
-      {
-      }
-  }
-
-  public synchronized void store (OutputStream out, String comment)
-    throws IOException
-  {
-      // Use a buffer because writing a single string through
-      // OutputStreamWriter is fairly expensive.
-      BufferedWriter output
-       = new BufferedWriter (new OutputStreamWriter (out));
-      String newline = System.getProperty("line.separator");
-
-      if (comment != null)
-       {
-         // We just lose if COMMENT contains a newline.  This is
-         // what JDK 1.1 does.
-         output.write("#");
-         output.write(comment);
-         output.write(newline);
-       }
-      output.write("# ");
-      output.write(new Date().toString());
-      output.write(newline);
-
-      Enumeration keys = keys ();
-      while (keys.hasMoreElements())
-       {
-         String key = (String) keys.nextElement();
-         String value = (String) get (key);
-
-         // FIXME: JCL says that the key can contain many Unicode
-         // characters.  But it also doesn't say we should encode
-         // it in any way.
-         // FIXME: if key contains ':', '=', or whitespace, must
-         // quote it here.  Note that JDK 1.1 does not do this.
-         output.write(key);
-         output.write("=");
-
-         boolean leading = true;
-         for (int i = 0; i < value.length(); ++i)
-           {
-             boolean new_lead = false;
-             char c = value.charAt(i);
-             switch (c)
-               {
-               case '\n':
-                 output.write("\\n");
-                 break;
-               case '\r':
-                 output.write("\\r");
-                 break;
-               case '\t':
-                 output.write("\\t");
-                 break;
-               case '\\':
-                 output.write("\\\\");
-                 break;
-
-               case '#':
-               case '!':
-               case '=':
-               case ':':
-                 output.write("\\");
-                 output.write(c);
-                 break;
-
-               case ' ':
-                 new_lead = leading;
-                 if (leading)
-                   output.write("\\");
-                 output.write(c);
-                 break;
+    /**
+     * 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();
+    }
 
-               default:
-                 if (c < '\u0020' || c > '\u007e')
-                   {
-                     output.write("\\u");
-                     output.write(Character.forDigit(c >>> 12, 16));
-                     output.write(Character.forDigit((c >>> 8) & 0xff,
-                                                     16));
-                     output.write(Character.forDigit((c >>> 4) & 0xff,
-                                                     16));
-                     output.write(Character.forDigit(c & 0xff, 16));
-                   }
-                 else
-                   output.write(c);
-               }
-             leading = new_lead;
-           }
-         output.write(newline);
-       }
+    /**
+     * 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);
+        }
+    }
 
-      output.flush();
-  }
+    /**
+     * 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);
+        }
+    }
 }