OSDN Git Service

* gcj/javaprims.h: Updated class declaration list.
authortromey <tromey@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 18 Jun 2002 15:40:16 +0000 (15:40 +0000)
committertromey <tromey@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 18 Jun 2002 15:40:16 +0000 (15:40 +0000)
* Makefile.in: Rebuilt.
* Makefile.am (core_java_source_files): Added
PropertyPermissionCollection.java.
* java/lang/Thread.java (group, name): Now package-private.
* java/lang/ThreadGroup.java: Re-merge with Classpath.
* java/util/AbstractList.java: Likewise.
* java/util/AbstractMap.java: Likewise.
* java/util/Calendar.java: Likewise.
* java/util/Collections.java: Likewise.
* java/util/HashMap.java: Likewise.
* java/util/Hashtable.java: Likewise.
* java/util/LinkedHashMap.java: Likewise.
* java/util/LinkedList.java: Likewise.
* java/util/List.java: Likewise.
* java/util/ListResourceBundle.java: Likewise.
* java/util/Map.java: Likewise.
* java/util/Observable.java: Likewise.
* java/util/Properties.java: Likewise.
* java/util/PropertyPermission.java: Likewise.
* java/util/PropertyPermissionCollection.java: Likewise.
* java/util/PropertyResourceBundle.java: Likewise.
* java/util/Random.java: Likewise.
* java/util/SimpleTimeZone.java: Likewise.
* java/util/StringTokenizer.java: Likewise.
* java/util/TimerTask.java: Likewise.
* java/util/TreeMap.java: Likewise.
* java/util/WeakHashMap.java: Likewise.
* java/util/jar/Attributes.java: Likewise.
* java/util/jar/JarException.java: Likewise.
* java/util/jar/Manifest.java: Likewise.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@54743 138bc75d-0d04-0410-961f-82ee72b054a4

31 files changed:
libjava/ChangeLog
libjava/Makefile.am
libjava/Makefile.in
libjava/gcj/javaprims.h
libjava/java/lang/Thread.java
libjava/java/lang/ThreadGroup.java
libjava/java/util/AbstractList.java
libjava/java/util/AbstractMap.java
libjava/java/util/Calendar.java
libjava/java/util/Collections.java
libjava/java/util/HashMap.java
libjava/java/util/Hashtable.java
libjava/java/util/LinkedHashMap.java
libjava/java/util/LinkedList.java
libjava/java/util/List.java
libjava/java/util/ListResourceBundle.java
libjava/java/util/Map.java
libjava/java/util/Observable.java
libjava/java/util/Properties.java
libjava/java/util/PropertyPermission.java
libjava/java/util/PropertyPermissionCollection.java [new file with mode: 0644]
libjava/java/util/PropertyResourceBundle.java
libjava/java/util/Random.java
libjava/java/util/SimpleTimeZone.java
libjava/java/util/StringTokenizer.java
libjava/java/util/TimerTask.java
libjava/java/util/TreeMap.java
libjava/java/util/WeakHashMap.java
libjava/java/util/jar/Attributes.java
libjava/java/util/jar/JarException.java
libjava/java/util/jar/Manifest.java

index 04542a7..d189b02 100644 (file)
@@ -1,3 +1,37 @@
+2002-06-18  Tom Tromey  <tromey@redhat.com>
+
+       * gcj/javaprims.h: Updated class declaration list.
+       * Makefile.in: Rebuilt.
+       * Makefile.am (core_java_source_files): Added
+       PropertyPermissionCollection.java.
+       * java/lang/Thread.java (group, name): Now package-private.
+       * java/lang/ThreadGroup.java: Re-merge with Classpath.
+       * java/util/AbstractList.java: Likewise.
+       * java/util/AbstractMap.java: Likewise.
+       * java/util/Calendar.java: Likewise.
+       * java/util/Collections.java: Likewise.
+       * java/util/HashMap.java: Likewise.
+       * java/util/Hashtable.java: Likewise.
+       * java/util/LinkedHashMap.java: Likewise.
+       * java/util/LinkedList.java: Likewise.
+       * java/util/List.java: Likewise.
+       * java/util/ListResourceBundle.java: Likewise.
+       * java/util/Map.java: Likewise.
+       * java/util/Observable.java: Likewise.
+       * java/util/Properties.java: Likewise.
+       * java/util/PropertyPermission.java: Likewise.
+       * java/util/PropertyPermissionCollection.java: Likewise.
+       * java/util/PropertyResourceBundle.java: Likewise.
+       * java/util/Random.java: Likewise.
+       * java/util/SimpleTimeZone.java: Likewise.
+       * java/util/StringTokenizer.java: Likewise.
+       * java/util/TimerTask.java: Likewise.
+       * java/util/TreeMap.java: Likewise.
+       * java/util/WeakHashMap.java: Likewise.
+       * java/util/jar/Attributes.java: Likewise.
+       * java/util/jar/JarException.java: Likewise.
+       * java/util/jar/Manifest.java: Likewise.
+
 2002-06-17  Tom Tromey  <tromey@redhat.com>
 
        * gcj/javaprims.h: Updated class declaration list.
index 3ccd548..86bae70 100644 (file)
@@ -1220,6 +1220,7 @@ java/util/Observable.java \
 java/util/Observer.java        \
 java/util/Properties.java \
 java/util/PropertyPermission.java \
+java/util/PropertyPermissionCollection.java \
 java/util/PropertyResourceBundle.java \
 java/util/Random.java \
 java/util/RandomAccess.java \
index 222746d..e281b10 100644 (file)
@@ -987,6 +987,7 @@ java/util/Observable.java \
 java/util/Observer.java        \
 java/util/Properties.java \
 java/util/PropertyPermission.java \
+java/util/PropertyPermissionCollection.java \
 java/util/PropertyResourceBundle.java \
 java/util/Random.java \
 java/util/RandomAccess.java \
@@ -2550,6 +2551,7 @@ DEP_FILES =  .deps/$(srcdir)/$(CONVERT_DIR)/gen-from-JIS.P \
 .deps/java/util/NoSuchElementException.P .deps/java/util/Observable.P \
 .deps/java/util/Observer.P .deps/java/util/Properties.P \
 .deps/java/util/PropertyPermission.P \
+.deps/java/util/PropertyPermissionCollection.P \
 .deps/java/util/PropertyResourceBundle.P .deps/java/util/Random.P \
 .deps/java/util/RandomAccess.P .deps/java/util/ResourceBundle.P \
 .deps/java/util/Set.P .deps/java/util/SimpleTimeZone.P \
index 1031503..26bbee8 100644 (file)
@@ -243,6 +243,7 @@ extern "Java"
       class AbstractCollection;
       class AbstractList;
       class AbstractMap;
+      class AbstractMap$BasicMapEntry;
       class AbstractSequentialList;
       class AbstractSet;
       class ArrayList;
@@ -317,11 +318,13 @@ extern "Java"
       class Map$Entry;
       class Map$Map;
       class MissingResourceException;
+      class MyResources;
       class NoSuchElementException;
       class Observable;
       class Observer;
       class Properties;
       class PropertyPermission;
+      class PropertyPermissionCollection;
       class PropertyResourceBundle;
       class Random;
       class RandomAccess;
index 92f74f6..af5b95f 100644 (file)
@@ -301,8 +301,8 @@ public class Thread implements Runnable
   public static native void yield ();
 
   // Private data.
-  private ThreadGroup group;
-  private String name;
+  ThreadGroup group;
+  String name;
   private Runnable runnable;
   private int priority;
   private boolean daemon_flag;
index ae65f85..80f62b6 100644 (file)
@@ -1,5 +1,5 @@
-/* java.lang.ThreadGroup
-   Copyright (C) 1998, 2000, 2001 Free Software Foundation, Inc.
+/* ThreadGroup -- a group of Threads
+   Copyright (C) 1998, 2000, 2001, 2002 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -7,7 +7,7 @@ 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
@@ -34,166 +34,213 @@ or based on this library.  If you modify this library, you may extend
 this exception to your version of the library, but you are not
 obligated to do so.  If you do not wish to do so, delete this
 exception statement from your version. */
+
 package java.lang;
 
 import java.util.Vector;
-import java.util.Enumeration;
 
-/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
- * "The Java Language Specification", ISBN 0-201-63451-1
- * plus online API docs for JDK 1.2 from http://www.javasoft.com.
- * Status:  Complete for 1.2.  Some parts from the JDK 1.0 spec only are
- * not implemented. 
- */
 /**
- * ThreadGroup allows you to group Threads together.  There is a
- * hierarchy of ThreadGroups, and only the initial ThreadGroup has
- * no parent.  A Thread may access information about its own
- * ThreadGroup, but not its parents or others outside the tree.
+ * ThreadGroup allows you to group Threads together.  There is a hierarchy
+ * of ThreadGroups, and only the initial ThreadGroup has no parent.  A Thread
+ * may access information about its own ThreadGroup, but not its parents or
+ * others outside the tree.
  *
  * @author John Keiser
  * @author Tom Tromey
  * @author Bryce McKinlay
- * @version 1.2.0
- * @since JDK1.0
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @see Thread
+ * @since 1.0
+ * @status updated to 1.4
  */
-
 public class ThreadGroup
 {
-  /* The Initial, top-level ThreadGroup. */
+  /** The Initial, top-level ThreadGroup. */
   static ThreadGroup root = new ThreadGroup();
-  /* This flag is set if an uncaught exception occurs. The runtime should 
-  check this and exit with an error status if it is set. */
-  static boolean had_uncaught_exception = false;
 
-  private ThreadGroup parent;
-  private String name;
-  private Vector threads = new Vector();
+  /**
+   * This flag is set if an uncaught exception occurs. The runtime should
+   * check this and exit with an error status if it is set.
+   */
+  static boolean had_uncaught_exception;
+
+  /** The parent thread group. */
+  private final ThreadGroup parent;
+
+  /** The group name, non-null. */
+  final String name;
+
+  /** The threads in the group. */
+  private final Vector threads = new Vector();
+
+  /** Child thread groups, or null when this group is destroyed. */
   private Vector groups = new Vector();
+
+  /** If all threads in the group are daemons. */
   private boolean daemon_flag = false;
-  private int maxpri = Thread.MAX_PRIORITY;
 
+  /** The maximum group priority. */
+  private int maxpri;
+
+  /**
+   * Hidden constructor to build the root node.
+   */
   private ThreadGroup()
   {
-    name = "main";    
+    name = "main";
+    parent = null;
+    maxpri = Thread.MAX_PRIORITY;
   }
 
-  /** Create a new ThreadGroup using the given name and the
-   *  current thread's ThreadGroup as a parent.
-   *  @param name the name to use for the ThreadGroup.
+  /**
+   * Create a new ThreadGroup using the given name and the current thread's
+   * ThreadGroup as a parent. There may be a security check,
+   * <code>checkAccess</code>.
+   *
+   * @param name the name to use for the ThreadGroup
+   * @throws SecurityException if the current thread cannot create a group
+   * @see #checkAccess()
    */
   public ThreadGroup(String name)
   {
-    this (Thread.currentThread().getThreadGroup(), name);
+    this(Thread.currentThread().group, name);
   }
 
-  /** Create a new ThreadGroup using the given name and
-   *  parent group.
-   *  @param name the name to use for the ThreadGroup.
-   *  @param parent the ThreadGroup to use as a parent.
-   *  @exception NullPointerException if parent is null.
-   *  @exception SecurityException if you cannot change
-   *             the intended parent group.
+  /**
+   * Create a new ThreadGroup using the given name and parent group. The new
+   * group inherits the maximum priority and daemon status of its parent
+   * group. There may be a security check, <code>checkAccess</code>.
+   *
+   * @param name the name to use for the ThreadGroup
+   * @param parent the ThreadGroup to use as a parent
+   * @throws NullPointerException if parent is null
+   * @throws SecurityException if the current thread cannot create a group
+   * @throws IllegalThreadStateException if the parent is destroyed
+   * @see #checkAccess()
    */
   public ThreadGroup(ThreadGroup parent, String name)
   {
     parent.checkAccess();
     this.parent = parent;
-    if (parent.isDestroyed())
-      throw new IllegalArgumentException ();
     this.name = name;
     maxpri = parent.maxpri;
     daemon_flag = parent.daemon_flag;
-    parent.addGroup(this);
+    synchronized (parent)
+      {
+        if (parent.groups == null)
+          throw new IllegalThreadStateException();
+        parent.groups.add(this);
+      }
   }
 
-  /** Get the name of this ThreadGroup.
-   *  @return the name of this ThreadGroup.
+  /**
+   * Get the name of this ThreadGroup.
+   *
+   * @return the name of this ThreadGroup
    */
   public final String getName()
   {
     return name;
   }
 
-  /** Get the parent of this ThreadGroup.
-   *  @return the parent of this ThreadGroup.
+  /**
+   * Get the parent of this ThreadGroup. If the parent is not null, there
+   * may be a security check, <code>checkAccess</code>.
+   *
+   * @return the parent of this ThreadGroup
+   * @throws SecurityException if permission is denied
    */
   public final ThreadGroup getParent()
   {
+    if (parent != null)
+      parent.checkAccess();
     return parent;
   }
 
-  /** Set the maximum priority for Threads in this ThreadGroup. setMaxPriority
-   *  can only be used to reduce the current maximum. If maxpri
-   *  is greater than the current Maximum, the current value is not changed.
-   *  Calling this does not effect threads already in this ThreadGroup.
-   *  @param maxpri the new maximum priority for this ThreadGroup.
-   *  @exception SecurityException if you cannoy modify this ThreadGroup.
-   */
-  public final synchronized void setMaxPriority(int maxpri)
-  {
-    checkAccess();
-    if (maxpri < this.maxpri
-        && maxpri >= Thread.MIN_PRIORITY
-       && maxpri <= Thread.MAX_PRIORITY)
-      {
-       this.maxpri = maxpri;        
-      }  
-  }
-
-  /** Get the maximum priority of Threads in this ThreadGroup.
-   *  @return the maximum priority of Threads in this ThreadGroup.
+  /**
+   * Get the maximum priority of Threads in this ThreadGroup. Threads created
+   * after this call in this group may not exceed this priority.
+   *
+   * @return the maximum priority of Threads in this ThreadGroup
    */
   public final int getMaxPriority()
   {
     return maxpri;
   }
 
-  /** Set whether this ThreadGroup is a daemon group.  A daemon
-   *  group will be destroyed when its last thread is stopped and
-   *  its last thread group is destroyed.
-   *  @specnote The Java API docs indicate that the group is destroyed
-   *           when either of those happen, but that doesn't make
-   *           sense.
-   *  @param daemon whether this ThreadGroup should be a daemon group.
-   *  @exception SecurityException if you cannoy modify this ThreadGroup.
+  /**
+   * Tell whether this ThreadGroup is a daemon group.  A daemon group will
+   * be automatically destroyed when its last thread is stopped and
+   * its last thread group is destroyed.
+   *
+   * @return whether this ThreadGroup is a daemon group
    */
-  public final void setDaemon (boolean daemon)
-  {
-    checkAccess();
-    daemon_flag = daemon;
-  }
-   
-  /** Tell whether this ThreadGroup is a daemon group.  A daemon
-    * group will be destroyed when its last thread is stopped and
-    * its last thread group is destroyed.
-    * @specnote The Java API docs indicate that the group is destroyed
-    *          when either of those happen, but that doesn't make
-    *          sense.
-    * @return whether this ThreadGroup is a daemon group.
-    */
   public final boolean isDaemon()
   {
     return daemon_flag;
   }
 
-  /** Tell whether this ThreadGroup has been destroyed or not.
-    * @return whether this ThreadGroup has been destroyed or not.
-    */
+  /**
+   * Tell whether this ThreadGroup has been destroyed or not.
+   *
+   * @return whether this ThreadGroup has been destroyed or not
+   * @since 1.1
+   */
   public synchronized boolean isDestroyed()
   {
-    return parent == null && this != root;
+    return groups == null;
   }
 
-  /** Check whether this ThreadGroup is an ancestor of the
-    * specified ThreadGroup, or if they are the same.
-    *
-    * @param g the group to test on.
-    * @return whether this ThreadGroup is a parent of the
-    *        specified group.
-    */
+  /**
+   * Set whether this ThreadGroup is a daemon group.  A daemon group will be
+   * destroyed when its last thread is stopped and its last thread group is
+   * destroyed. There may be a security check, <code>checkAccess</code>.
+   *
+   * @param daemon whether this ThreadGroup should be a daemon group
+   * @throws SecurityException if you cannot modify this ThreadGroup
+   * @see #checkAccess()
+   */
+  public final void setDaemon(boolean daemon)
+  {
+    checkAccess();
+    daemon_flag = daemon;
+  }
+
+  /**
+   * Set the maximum priority for Threads in this ThreadGroup. setMaxPriority
+   * can only be used to reduce the current maximum. If maxpri is greater
+   * than the current Maximum of the parent group, the current value is not
+   * changed. Otherwise, all groups which belong to this have their priority
+   * adjusted as well. Calling this does not affect threads already in this
+   * ThreadGroup. There may be a security check, <code>checkAccess</code>.
+   *
+   * @param maxpri the new maximum priority for this ThreadGroup
+   * @throws SecurityException if you cannot modify this ThreadGroup
+   * @see #getMaxPriority()
+   * @see #checkAccess()
+   */
+  public final synchronized void setMaxPriority(int maxpri)
+  {
+    checkAccess();
+    if (maxpri < Thread.MIN_PRIORITY || maxpri > Thread.MAX_PRIORITY)
+      return;
+    if (parent != null && maxpri > parent.maxpri)
+      maxpri = parent.maxpri;
+    this.maxpri = maxpri;
+    if (groups == null)
+      return;
+    int i = groups.size();
+    while (--i >= 0)
+      ((ThreadGroup) groups.get(i)).setMaxPriority(maxpri);
+  }
+
+  /**
+   * Check whether this ThreadGroup is an ancestor of the specified
+   * ThreadGroup, or if they are the same.
+   *
+   * @param g the group to test on
+   * @return whether this ThreadGroup is a parent of the specified group
+   */
   public final boolean parentOf(ThreadGroup tg)
   {
     while (tg != null)
@@ -205,423 +252,497 @@ public class ThreadGroup
     return false;
   }
 
-  /** Return the total number of active threads in this ThreadGroup
-    * and all its descendants.<P>
-    *
-    * This cannot return an exact number, since the status of threads
-    * may change after they were counted.  But it should be pretty
-    * close.<P>
-    *
-    * @return the number of active threads in this ThreadGroup and
-    *        its descendants.
-    * @specnote it isn't clear what the definition of an "Active" thread is.
-    *           Current JDKs regard a thread as active if has been
-    *           started and not finished.  We implement this behaviour.
-    *           There is a JDC bug, <A HREF="http://developer.java.sun.com/developer/bugParade/bugs/4089701.html">
-    *           4089701</A>, regarding this issue.
-    *           
-    */
-  public synchronized int activeCount()
+  /**
+   * Find out if the current Thread can modify this ThreadGroup. This passes
+   * the check on to <code>SecurityManager.checkAccess(this)</code>.
+   *
+   * @throws SecurityException if the current Thread cannot modify this
+   *         ThreadGroup
+   * @see SecurityManager#checkAccess(ThreadGroup)
+   */
+  public final void checkAccess()
   {
-    int total = 0;
-    for (int i = 0; i < threads.size(); ++i)
-      {
-       if (((Thread) threads.elementAt(i)).isAlive ())
-         ++total;
-      }
-
-    for (int i=0; i < groups.size(); i++)
-      {
-        ThreadGroup g = (ThreadGroup) groups.elementAt(i);
-        total += g.activeCount();
-      }
-    return total;
+    // Bypass System.getSecurityManager, for bootstrap efficiency.
+    SecurityManager sm = Runtime.securityManager;
+    if (sm != null)
+      sm.checkAccess(this);
   }
 
-  /** Get the number of active groups in this ThreadGroup.  This group
-    * itself is not included in the count.
-    * @specnote it is unclear what exactly constitutes an
-    *          active ThreadGroup.  Currently we assume that
-    *          all sub-groups are active, per current JDKs.
-    * @return the number of active groups in this ThreadGroup.
-    */
-  public synchronized int activeGroupCount()
+  /**
+   * Return an estimate of the total number of active threads in this
+   * ThreadGroup and all its descendants. This cannot return an exact number,
+   * since the status of threads may change after they were counted; but it
+   * should be pretty close. Based on a JDC bug,
+   * <a href="http://developer.java.sun.com/developer/bugParade/bugs/4089701.html">
+   * 4089701</a>, we take active to mean isAlive().
+   *
+   * @return count of active threads in this ThreadGroup and its descendants
+   */
+  public int activeCount()
   {
-    int total = groups.size();
-    for (int i=0; i < groups.size(); i++)
-      {
-       ThreadGroup g = (ThreadGroup) groups.elementAt(i);
-       total += g.activeGroupCount();
-      }
+    int total = 0;
+    if (groups == null)
+      return total;
+    int i = threads.size();
+    while (--i >= 0)
+      if (((Thread) threads.get(i)).isAlive())
+        total++;
+    i = groups.size();
+    while (--i >= 0)
+      total += ((ThreadGroup) groups.get(i)).activeCount();
     return total;
   }
 
-  /** Copy all of the active Threads from this ThreadGroup and
-    * its descendants into the specified array.  If the array is
-    * not big enough to hold all the Threads, extra Threads will
-    * simply not be copied.
-    *
-    * @param threads the array to put the threads into.
-    * @return the number of threads put into the array.
-    */
-  public int enumerate(Thread[] threads)
-  {
-    return enumerate(threads, 0, true);
-  }
-
-  /** Copy all of the active Threads from this ThreadGroup and,
-    * if desired, from its descendants, into the specified array.
-    * If the array is not big enough to hold all the Threads,
-    * extra Threads will simply not be copied.
-    *
-    * @param threads the array to put the threads into.
-    * @param useDescendants whether to count Threads in this
-    *       ThreadGroup's descendants or not.
-    * @return the number of threads put into the array.
-    */
-  public int enumerate(Thread[] threads, boolean useDescendants)
-  {
-    return enumerate(threads, 0, useDescendants);
-  }
-
-  // This actually implements enumerate.
-  private synchronized int enumerate(Thread[] list, int next_index, 
-                                    boolean recurse)
-  {
-    Enumeration e = threads.elements();
-    while (e.hasMoreElements() && next_index < list.length)
-      {
-       Thread t = (Thread) e.nextElement();
-       if (t.isAlive ())
-         list[next_index++] = t;
-      }
-    if (recurse && next_index != list.length)
-      {
-       e = groups.elements();
-       while (e.hasMoreElements() && next_index < list.length)
-         {
-           ThreadGroup g = (ThreadGroup) e.nextElement();
-           next_index = g.enumerate(list, next_index, true);
-         }
-      }
-    return next_index;
-  }
-
-  /** Copy all active ThreadGroups that are descendants of this
-    * ThreadGroup into the specified array.  If the array is not
-    * large enough to hold all active ThreadGroups, extra
-    * ThreadGroups simply will not be copied.
-    *
-    * @param groups the array to put the ThreadGroups into.
-    * @return the number of ThreadGroups copied into the array.
-    */
-  public int enumerate(ThreadGroup[] groups)
-  {
-    return enumerate(groups, 0, true);
-  }
-
-  /** Copy all active ThreadGroups that are children of this
-    * ThreadGroup into the specified array, and if desired, also
-    * copy all active descendants into the array.  If the array
-    * is not large enough to hold all active ThreadGroups, extra
-    * ThreadGroups simply will not be copied.
-    *
-    * @param groups the array to put the ThreadGroups into.
-    * @param recurse whether to include all descendants
-    *       of this ThreadGroup's children in determining
-    *       activeness.
-    * @return the number of ThreadGroups copied into the array.
-    */
-  public int enumerate(ThreadGroup[] groups, boolean recurse)
+  /**
+   * Copy all of the active Threads from this ThreadGroup and its descendants
+   * into the specified array.  If the array is not big enough to hold all
+   * the Threads, extra Threads will simply not be copied. There may be a
+   * security check, <code>checkAccess</code>.
+   *
+   * @param array the array to put the threads into
+   * @return the number of threads put into the array
+   * @throws SecurityException if permission was denied
+   * @throws NullPointerException if array is null
+   * @throws ArrayStoreException if a thread does not fit in the array
+   * @see #activeCount()
+   * @see #checkAccess()
+   * @see #enumerate(Thread[], boolean)
+   */
+  public int enumerate(Thread[] array)
+  {
+    return enumerate(array, 0, true);
+  }
+
+  /**
+   * Copy all of the active Threads from this ThreadGroup and, if desired,
+   * from its descendants, into the specified array. If the array is not big
+   * enough to hold all the Threads, extra Threads will simply not be copied.
+   * There may be a security check, <code>checkAccess</code>.
+   *
+   * @param array the array to put the threads into
+   * @param recurse whether to recurse into descendent ThreadGroups
+   * @return the number of threads put into the array
+   * @throws SecurityException if permission was denied
+   * @throws NullPointerException if array is null
+   * @throws ArrayStoreException if a thread does not fit in the array
+   * @see #activeCount()
+   * @see #checkAccess()
+   */
+  public int enumerate(Thread[] array, boolean recurse)
   {
-    return enumerate(groups, 0, recurse);
+    return enumerate(array, 0, recurse);
   }
 
-  // This actually implements enumerate.
-  private synchronized int enumerate (ThreadGroup[] list, int next_index, 
-                                     boolean recurse)
+  /**
+   * Get the number of active groups in this ThreadGroup.  This group itself
+   * is not included in the count. A sub-group is active if it has not been
+   * destroyed. This cannot return an exact number, since the status of
+   * threads may change after they were counted; but it should be pretty close.
+   *
+   * @return the number of active groups in this ThreadGroup
+   */
+  public int activeGroupCount()
   {
-    Enumeration e = groups.elements();
-    while (e.hasMoreElements() && next_index < list.length)
-      {
-       ThreadGroup g = (ThreadGroup) e.nextElement();
-       list[next_index++] = g;
-       if (recurse && next_index != list.length)
-         next_index = g.enumerate(list, next_index, true);
-      }
-    return next_index;
+    if (groups == null)
+      return 0;
+    int total = groups.size();
+    int i = total;
+    while (--i >= 0)
+      total += ((ThreadGroup) groups.get(i)).activeGroupCount();
+    return total;
   }
 
-  /** Interrupt all Threads in this ThreadGroup and its sub-groups.
-    * @exception SecurityException if you cannot modify this
-    *           ThreadGroup or any of its Threads or children
-    *           ThreadGroups.
-    * @since JDK1.2
-    */
-  public final synchronized void interrupt()
+  /**
+   * Copy all active ThreadGroups that are descendants of this ThreadGroup
+   * into the specified array.  If the array is not large enough to hold all
+   * active ThreadGroups, extra ThreadGroups simply will not be copied. There
+   * may be a security check, <code>checkAccess</code>.
+   *
+   * @param array the array to put the ThreadGroups into
+   * @return the number of ThreadGroups copied into the array
+   * @throws SecurityException if permission was denied
+   * @throws NullPointerException if array is null
+   * @throws ArrayStoreException if a group does not fit in the array
+   * @see #activeCount()
+   * @see #checkAccess()
+   * @see #enumerate(ThreadGroup[], boolean)
+   */
+  public int enumerate(ThreadGroup[] array)
+  {
+    return enumerate(array, 0, true);
+  }
+
+  /**
+   * Copy all active ThreadGroups that are children of this ThreadGroup into
+   * the specified array, and if desired, also all descendents.  If the array
+   * is not large enough to hold all active ThreadGroups, extra ThreadGroups
+   * simply will not be copied. There may be a security check,
+   * <code>checkAccess</code>.
+   *
+   * @param array the array to put the ThreadGroups into
+   * @param recurse whether to recurse into descendent ThreadGroups
+   * @return the number of ThreadGroups copied into the array
+   * @throws SecurityException if permission was denied
+   * @throws NullPointerException if array is null
+   * @throws ArrayStoreException if a group does not fit in the array
+   * @see #activeCount()
+   * @see #checkAccess()
+   */
+  public int enumerate(ThreadGroup[] array, boolean recurse)
+  {
+    return enumerate(array, 0, recurse);
+  }
+
+  /**
+   * Stop all Threads in this ThreadGroup and its descendants.
+   *
+   * <p>This is inherently unsafe, as it can interrupt synchronized blocks and
+   * leave data in bad states.  Hence, there is a security check:
+   * <code>checkAccess()</code>, followed by further checks on each thread
+   * being stopped.
+   *
+   * @throws SecurityException if permission is denied
+   * @see #checkAccess()
+   * @see Thread#stop(Throwable)
+   * @deprecated unsafe operation, try not to use
+   */
+  public final synchronized void stop()
   {
     checkAccess();
-    for (int i=0; i < threads.size(); i++)
-      {
-        Thread t = (Thread) threads.elementAt(i);
-        t.interrupt();
-      }
-    for (int i=0; i < groups.size(); i++)
-      {
-        ThreadGroup tg = (ThreadGroup) groups.elementAt(i);
-        tg.interrupt();
-      }
-  }
-
-  /** Stop all Threads in this ThreadGroup and its descendants.
-    * @exception SecurityException if you cannot modify this
-    *           ThreadGroup or any of its Threads or children
-    *           ThreadGroups.
-    * @deprecated This method calls Thread.stop(), which is dangerous.
-    */
-  public final synchronized void stop()
+    if (groups == null)
+      return;
+    int i = threads.size();
+    while (--i >= 0)
+      ((Thread) threads.get(i)).stop();
+    i = groups.size();
+    while (--i >= 0)
+      ((ThreadGroup) groups.get(i)).stop();
+  }
+
+  /**
+   * Interrupt all Threads in this ThreadGroup and its sub-groups. There may
+   * be a security check, <code>checkAccess</code>.
+   *
+   * @throws SecurityException if permission is denied
+   * @see #checkAccess()
+   * @see Thread#interrupt()
+   * @since 1.2
+   */
+  public final synchronized void interrupt()
   {
     checkAccess();
-    for (int i=0; i<threads.size(); i++)
-      {
-        Thread t = (Thread) threads.elementAt(i);
-       t.stop();
-      }
-    for (int i=0; i < groups.size(); i++)
-      {
-        ThreadGroup tg = (ThreadGroup) groups.elementAt(i);
-        tg.stop();
-      }
-  }
-
-  /** Suspend all Threads in this ThreadGroup and its descendants.
-    * @exception SecurityException if you cannot modify this
-    *           ThreadGroup or any of its Threads or children
-    *           ThreadGroups.
-    * @deprecated This method calls Thread.suspend(), which is dangerous.
-    */
+    if (groups == null)
+      return;
+    int i = threads.size();
+    while (--i >= 0)
+      ((Thread) threads.get(i)).interrupt();
+    i = groups.size();
+    while (--i >= 0)
+      ((ThreadGroup) groups.get(i)).interrupt();
+  }
+
+  /**
+   * Suspend all Threads in this ThreadGroup and its descendants.
+   *
+   * <p>This is inherently unsafe, as suspended threads still hold locks,
+   * which can lead to deadlock.  Hence, there is a security check:
+   * <code>checkAccess()</code>, followed by further checks on each thread
+   * being suspended.
+   *
+   * @throws SecurityException if permission is denied
+   * @see #checkAccess()
+   * @see Thread#suspend()
+   * @deprecated unsafe operation, try not to use
+   */
   public final synchronized void suspend()
   {
     checkAccess();
-    for (int i=0; i<threads.size(); i++)
-      {
-        Thread t = (Thread) threads.elementAt(i);
-        t.suspend();
-      }
-    for (int i=0; i < groups.size(); i++)
-      {
-        ThreadGroup tg = (ThreadGroup) groups.elementAt(i);
-        tg.suspend();
-      }
-  }
-
-  /** Resume all Threads in this ThreadGroup and its descendants.
-    * @exception SecurityException if you cannot modify this
-    *           ThreadGroup or any of its Threads or children
-    *           ThreadGroups.
-    * @deprecated This method relies on Thread.suspend(), which is dangerous.
-    */
+    if (groups == null)
+      return;
+    int i = threads.size();
+    while (--i >= 0)
+      ((Thread) threads.get(i)).suspend();
+    i = groups.size();
+    while (--i >= 0)
+      ((ThreadGroup) groups.get(i)).suspend();
+  }
+
+  /**
+   * Resume all suspended Threads in this ThreadGroup and its descendants.
+   * To mirror suspend(), there is a security check:
+   * <code>checkAccess()</code>, followed by further checks on each thread
+   * being resumed.
+   *
+   * @throws SecurityException if permission is denied
+   * @see #checkAccess()
+   * @see Thread#suspend()
+   * @deprecated pointless, since suspend is deprecated
+   */
   public final synchronized void resume()
   {
     checkAccess();
-    for (int i=0; i < threads.size(); i++)
-      {
-        Thread t = (Thread) threads.elementAt(i);
-       t.resume();
-      }
-    for (int i=0; i < groups.size(); i++)
-      {
-        ThreadGroup tg = (ThreadGroup) groups.elementAt(i);
-        tg.resume();
-      }
-  }
-
-  // This is a helper that is used to implement the destroy method.
-  private synchronized void checkDestroy ()
-  {
-    if (! threads.isEmpty())
-      throw new IllegalThreadStateException ("ThreadGroup has threads");
-    for (int i=0; i < groups.size(); i++)
-      {
-        ThreadGroup tg = (ThreadGroup) groups.elementAt(i);
-       tg.checkDestroy();
-      }
-  }
-
-  /** Destroy this ThreadGroup.  There can be no Threads in it,
-    * and none of its descendants (sub-groups) may have Threads in them.
-    * All its descendants will be destroyed as well.
-    * @exception IllegalThreadStateException if the ThreadGroup or
-    *           its descendants have Threads remaining in them, or
-    *           if the ThreadGroup in question is already destroyed.
-    * @exception SecurityException if you cannot modify this
-    *           ThreadGroup or any of its descendants.
-    */
+    if (groups == null)
+      return;
+    int i = threads.size();
+    while (--i >= 0)
+      ((Thread) threads.get(i)).resume();
+    i = groups.size();
+    while (--i >= 0)
+      ((ThreadGroup) groups.get(i)).resume();
+  }
+
+  /**
+   * Destroy this ThreadGroup.  The group must be empty, meaning that all
+   * threads and sub-groups have completed execution. Daemon groups are
+   * destroyed automatically. There may be a security check,
+   * <code>checkAccess</code>.
+   *
+   * @throws IllegalThreadStateException if the ThreadGroup is not empty, or
+   *         was previously destroyed
+   * @throws SecurityException if permission is denied
+   * @see #checkAccess()
+   */
   public final synchronized void destroy()
   {
     checkAccess();
-    if (isDestroyed())
-      throw new IllegalThreadStateException("Already destroyed.");
-    checkDestroy ();
+    if (! threads.isEmpty() || groups == null)
+      throw new IllegalThreadStateException();
+    int i = groups.size();
+    while (--i >= 0)
+      ((ThreadGroup) groups.get(i)).destroy();
+    groups = null;
     if (parent != null)
       parent.removeGroup(this);
-    parent = null;
-
-    for (int i=0; i < groups.size(); i++)
-      {
-        ThreadGroup tg = (ThreadGroup) groups.elementAt(i);
-       tg.destroy();
-      }
   }
-  
-  /** Print out information about this ThreadGroup to System.out.
-    */
+
+  /**
+   * Print out information about this ThreadGroup to System.out. This is
+   * meant for debugging purposes. <b>WARNING:</b> This method is not secure,
+   * and can print the name of threads to standard out even when you cannot
+   * otherwise get at such threads.
+   */
   public void list()
   {
     list("");
   }
 
-  private synchronized void list(String indentation)
-  {
-    System.out.print(indentation);
-    System.out.println(toString ());
-    String sub = indentation + "    ";
-    for (int i=0; i < threads.size(); i++)
-      {
-        Thread t = (Thread) threads.elementAt(i);
-       System.out.print(sub);
-       System.out.println(t.toString());
-      }
-    for (int i=0; i < groups.size(); i++)
-      {
-        ThreadGroup tg = (ThreadGroup) groups.elementAt(i);
-       tg.list(sub);
-      }
-  }
-
-  /** When a Thread in this ThreadGroup does not catch an exception,
-    * this method of the ThreadGroup is called.<P>
-    *
-    * ThreadGroup's implementation does the following:<BR>
-    * <OL>
-    * <LI>If there is a parent ThreadGroup, call uncaughtException()
-    *    in the parent.</LI>
-    * <LI>If the Throwable passed is a ThreadDeath, don't do
-    *    anything.</LI>
-    * <LI>Otherwise, call <CODE>exception.printStackTrace().</CODE></LI>
-    * </OL>
-    *
-    * @param thread the thread that exited.
-    * @param exception the uncaught exception.
-    */
+  /**
+   * When a Thread in this ThreadGroup does not catch an exception, the
+   * virtual machine calls this method. The default implementation simply
+   * passes the call to the parent; then in top ThreadGroup, it will
+   * ignore ThreadDeath and print the stack trace of any other throwable.
+   * Override this method if you want to handle the exception in a different
+   * manner.
+   *
+   * @param thread the thread that exited
+   * @param exception the uncaught exception
+   * @throws NullPointerException if t is null
+   * @see ThreadDeath
+   * @see System#err
+   * @see Throwable#printStackTrace()
+   */
   public void uncaughtException(Thread thread, Throwable t)
   {
     if (parent != null)
-      parent.uncaughtException (thread, t);
+      parent.uncaughtException(thread, t);
     else if (! (t instanceof ThreadDeath))
       {
-        if (thread != null)
-          System.err.print ("Exception in thread \""
-                           + thread.getName() + "\" ");
-       try
-         {
-           t.printStackTrace(System.err);
-         }
-       catch (Throwable x)
-         {
-           // This means that something is badly screwed up with the runtime,
-           // or perhaps someone is messing with the SecurityManager. In any
-           // case, try to deal with it gracefully.
-           System.err.println(t);
-           System.err.println("*** Got " + x.toString() + 
-                              " while trying to print stack trace");
-         }
-       had_uncaught_exception = true;
+        if (t == null)
+          throw new NullPointerException();
+        had_uncaught_exception = true;
+        try
+          {
+            if (thread != null)
+              System.err.print("Exception in thread \"" + thread.name + "\" ");
+            t.printStackTrace(System.err);
+          }
+        catch (Throwable x)
+          {
+            // This means that something is badly screwed up with the runtime,
+            // or perhaps someone overloaded the Throwable.printStackTrace to
+            // die. In any case, try to deal with it gracefully.
+            try
+              {
+                System.err.println(t);
+                System.err.println("*** Got " + x
+                                   + " while trying to print stack trace.");
+              }
+            catch (Throwable x2)
+              {
+                // Here, someone may have overloaded t.toString() or
+                // x.toString() to die. Give up all hope; we can't even chain
+                // the exception, because the chain would likewise die.
+                System.err.println("*** Catastrophic failure while handling "
+                                   + "uncaught exception.");
+                throw new InternalError();
+              }
+          }
       }
   }
 
-  /** Tell the VM whether it may suspend Threads in low memory
-    * situations.
-    * @deprecated This method is unimplemented, because it would rely on
-    *            suspend(), which is deprecated. There is no way for a Java
-    *            program to determine whether this has any effect whatsoever,
-    *            so we don't need it.
-    * @return false
-    */
+  /**
+   * Originally intended to tell the VM whether it may suspend Threads in
+   * low memory situations, this method was never implemented by Sun, and
+   * is hence a no-op.
+   *
+   * @param allow whether to allow low-memory thread suspension; ignored
+   * @return false
+   * @since 1.1
+   * @deprecated pointless, since suspend is deprecated
+   */
   public boolean allowThreadSuspension(boolean allow)
   {
     return false;
   }
 
-  /** Get a human-readable representation of this ThreadGroup.
-    * @return a String representing this ThreadGroup.
-    * @specnote Language Spec and Class Libraries book disagree a bit here.
-    *          We follow the Spec, but add "ThreadGroup" per the book.  We
-    *          include "java.lang" based on the list() example in the Class
-    *          Libraries book.
-    */
-  public String toString ()
+  /**
+   * Return a human-readable String representing this ThreadGroup. The format
+   * of the string is:<br>
+   * <code>getClass().getName() + "[name=" + getName() + ",maxpri="
+   * + getMaxPriority() + ']'</code>.
+   *
+   * @return a human-readable String representing this ThreadGroup
+   */
+  public String toString()
+  {
+    return getClass().getName() + "[name=" + name + ",maxpri=" + maxpri + ']';
+  }
+
+  /**
+   * Implements enumerate.
+   *
+   * @param list the array to put the threads into
+   * @param next the next open slot in the array
+   * @param recurse whether to recurse into descendent ThreadGroups
+   * @return the number of threads put into the array
+   * @throws SecurityException if permission was denied
+   * @throws NullPointerException if list is null
+   * @throws ArrayStoreException if a thread does not fit in the array
+   * @see #enumerate(Thread[])
+   * @see #enumerate(Thread[], boolean)
+   */
+  private int enumerate(Thread[] list, int next, boolean recurse)
   {
-    return "java.lang.ThreadGroup[name=" + name + 
-           ",maxpri=" + maxpri + "]";
-  }
-
-  /** Find out if the current Thread can modify this ThreadGroup.
-    * Calls the current SecurityManager's checkAccess() method to
-    * find out.  If there is none, it assumes everything's OK.
-    * @exception SecurityException if the current Thread cannot
-    *           modify this ThreadGroup.
-    */
-  public final void checkAccess()
+    checkAccess();
+    if (groups == null)
+      return next;
+    int i = threads.size();
+    while (--i >= 0 && next < list.length)
+      {
+        Thread t = (Thread) threads.get(i);
+        if (t.isAlive())
+          list[next++] = t;
+      }
+    if (recurse)
+      {
+        i = groups.size();
+        while (--i >= 0 && next < list.length)
+          {
+            ThreadGroup g = (ThreadGroup) groups.get(i);
+            next = g.enumerate(list, next, true);
+          }
+      }
+    return next;
+  }
+
+  /**
+   * Implements enumerate.
+   *
+   * @param list the array to put the groups into
+   * @param next the next open slot in the array
+   * @param recurse whether to recurse into descendent ThreadGroups
+   * @return the number of groups put into the array
+   * @throws SecurityException if permission was denied
+   * @throws NullPointerException if list is null
+   * @throws ArrayStoreException if a group does not fit in the array
+   * @see #enumerate(ThreadGroup[])
+   * @see #enumerate(ThreadGroup[], boolean)
+   */
+  private int enumerate(ThreadGroup[] list, int next, boolean recurse)
   {
-    SecurityManager sm = System.getSecurityManager();
-    if (sm != null)
-      sm.checkAccess(this);
+    checkAccess();
+    if (groups == null)
+      return next;
+    int i = groups.size();
+    while (--i >= 0 && next < list.length)
+      {
+        ThreadGroup g = (ThreadGroup) groups.get(i);
+        list[next++] = g;
+        if (recurse && next != list.length)
+          next = g.enumerate(list, next, true);
+      }
+    return next;
   }
 
-  // This is called to add a Thread to our internal list.
+  /**
+   * Implements list.
+   *
+   * @param indentation the current level of indentation
+   * @see #list()
+   */
+  private void list(String indentation)
+  {
+    if (groups == null)
+      return;
+    System.out.print(indentation + this);
+    indentation += "    ";
+    int i = threads.size();
+    while (--i >= 0)
+      System.out.println(indentation + threads.get(i));
+    i = groups.size();
+    while (--i >= 0)
+      ((ThreadGroup) groups.get(i)).list(indentation);
+  }
+
+  /**
+   * Add a thread to the group. Called by Thread constructors.
+   *
+   * @param t the thread to add, non-null
+   * @throws IllegalThreadStateException if the group is destroyed
+   */
   final synchronized void addThread(Thread t)
   {
-    if (isDestroyed())
-      throw new IllegalThreadStateException ("ThreadGroup is destroyed");
-  
-    threads.addElement(t);
+    if (groups == null)
+      throw new IllegalThreadStateException("ThreadGroup is destroyed");
+    threads.add(t);
   }
 
-  // This is called to remove a Thread from our internal list.
+  /**
+   * Called by the VM to remove a thread that has died.
+   *
+   * @param t the thread to remove, non-null
+   * @XXX A ThreadListener to call this might be nice.
+   */
   final synchronized void removeThread(Thread t)
   {
-    if (isDestroyed())
-      throw new IllegalThreadStateException ();
-  
-    threads.removeElement(t);
+    if (groups == null)
+      return;
+    threads.remove(t);
     // Daemon groups are automatically destroyed when all their threads die.
     if (daemon_flag && groups.size() == 0 && threads.size() == 0)
       {
-       // We inline destroy to avoid the access check.
-       if (parent != null)
-         parent.removeGroup(this);
-       parent = null;
+        // We inline destroy to avoid the access check.
+        groups = null;
+        if (parent != null)
+          parent.removeGroup(this);
       }
   }
 
-  // This is called to add a ThreadGroup to our internal list.
-  final synchronized void addGroup(ThreadGroup g)
-  {
-    groups.addElement(g);
-  }
-
-  // This is called to remove a ThreadGroup from our internal list.
+  /**
+   * Called when a group is destroyed, to remove it from its parent.
+   *
+   * @param g the destroyed group, non-null
+   */
   final synchronized void removeGroup(ThreadGroup g)
   {
-    groups.removeElement(g);
+    groups.remove(g);
     // Daemon groups are automatically destroyed when all their threads die.
     if (daemon_flag && groups.size() == 0 && threads.size() == 0)
       {
-       // We inline destroy to avoid the access check.
-       if (parent != null)
-         parent.removeGroup(this);
-       parent = null;
+        // We inline destroy to avoid the access check.
+        groups = null;
+        if (parent != null)
+          parent.removeGroup(this);
       }
   }
-}
+} // class ThreadGroup
index 1c2d1de..be0bc30 100644 (file)
@@ -1,5 +1,5 @@
 /* AbstractList.java -- Abstract implementation of most of List
-   Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -237,20 +237,22 @@ public abstract class AbstractList extends AbstractCollection implements List
   }
 
   /**
-   * Obtain a hash code for this list. In order to obey the general contract of
-   * the hashCode method of class Object, this value is calculated as follows:
-   * <pre>
-   *   hashCode = 1;
-   *   Iterator i = list.iterator();
-   *   while (i.hasNext())
-   *     {
-   *       Object obj = i.next();
-   *       hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode());
-   *     }
-   * </pre>
+   * Obtains a hash code for this list. In order to obey the general
+   * contract of the hashCode method of class Object, this value is
+   * calculated as follows:
+   * 
+<pre>hashCode = 1;
+Iterator i = list.iterator();
+while (i.hasNext())
+{
+  Object obj = i.next();
+  hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode());
+}</pre>
+   *
    * This ensures that the general contract of Object.hashCode() is adhered to.
    *
    * @return the hash code of this list
+   *
    * @see Object#hashCode()
    * @see #equals(Object)
    */
@@ -611,19 +613,21 @@ public abstract class AbstractList extends AbstractCollection implements List
 
 /**
  * This class follows the implementation requirements set forth in
- * {@link AbstractList#subList(int, int)}. Some compilers have problems
- * with AbstractList.this.modCount if this class is nested in AbstractList,
- * even though the JLS defines that to be legal, so we make it a top-level
- * class.
+ * {@link AbstractList#subList(int, int)}. It matches Sun's implementation
+ * by using a non-public top-level class in the same package.
  *
  * @author Original author unknown
  * @author Eric Blake <ebb9@email.byu.edu>
  */
 class SubList extends AbstractList
 {
-  private final AbstractList backingList;
-  private final int offset;
-  private int size;
+  // Package visible, for use by iterator.
+  /** The original list. */
+  final AbstractList backingList;
+  /** The index of the first element of the sublist. */
+  final int offset;
+  /** The size of the sublist. */
+  int size;
 
   /**
    * Construct the sublist.
@@ -647,8 +651,8 @@ class SubList extends AbstractList
    * @throws ConcurrentModificationException if the backing list has been
    *         modified externally to this sublist
    */
-  // This will get inlined, since it is private.
-  private void checkMod()
+  // This can be inlined. Package visible, for use by iterator.
+  void checkMod()
   {
     if (modCount != backingList.modCount)
       throw new ConcurrentModificationException();
index 555d055..11c8f5b 100644 (file)
@@ -1,5 +1,5 @@
 /* AbstractMap.java -- Abstract implementation of most of Map
-   Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -518,4 +518,130 @@ public abstract class AbstractMap implements Map
   {
     return o == null ? 0 : o.hashCode();
   }
+
+  /**
+   * A class which implements Map.Entry. It is shared by HashMap, TreeMap,
+   * Hashtable, and Collections. It is not specified by the JDK, but makes
+   * life much easier.
+   *
+   * @author Jon Zeppieri
+   * @author Eric Blake <ebb9@email.byu.edu>
+   */
+  static class BasicMapEntry implements Map.Entry
+  {    // XXX - FIXME Use fully qualified implements as gcj 3.1 workaround.
+    /**
+     * The key. Package visible for direct manipulation.
+     */
+    Object key;
+
+    /**
+     * The value. Package visible for direct manipulation.
+     */
+    Object value;
+
+    /**
+     * Basic constructor initializes the fields.
+     * @param newKey the key
+     * @param newValue the value
+     */
+    BasicMapEntry(Object newKey, Object newValue)
+    {
+      key = newKey;
+      value = newValue;
+    }
+
+    /**
+     * Compares the specified object with this entry. Returns true only if
+     * the object is a mapping of identical key and value. In other words,
+     * this must be:
+     * 
+<pre>(o instanceof Map.Entry) &&
+(getKey() == null ? ((HashMap) o).getKey() == null
+                  : getKey().equals(((HashMap) o).getKey())) &&
+(getValue() == null ? ((HashMap) o).getValue() == null
+                    : getValue().equals(((HashMap) o).getValue()))</pre>
+     *
+     * @param o the object to compare
+     *
+     * @return <code>true</code> if it is equal
+     */
+    public final boolean equals(Object o)
+    {
+      if (! (o instanceof Map.Entry))
+        return false;
+      // Optimize for our own entries.
+      if (o instanceof BasicMapEntry)
+        {
+          BasicMapEntry e = (BasicMapEntry) o;
+          return (AbstractMap.equals(key, e.key)
+                  && AbstractMap.equals(value, e.value));
+        }
+      Map.Entry e = (Map.Entry) o;
+      return (AbstractMap.equals(key, e.getKey())
+              && AbstractMap.equals(value, e.getValue()));
+    }
+
+    /**
+     * Get the key corresponding to this entry.
+     *
+     * @return the key
+     */
+    public final Object getKey()
+    {
+      return key;
+    }
+
+    /**
+     * Get the value corresponding to this entry. If you already called
+     * Iterator.remove(), the behavior undefined, but in this case it works.
+     *
+     * @return the value
+     */
+    public final Object getValue()
+    {
+      return value;
+    }
+
+    /**
+     * Returns the hash code of the entry.  This is defined as the exclusive-or
+     * of the hashcodes of the key and value (using 0 for null). In other
+     * words, this must be:
+     * 
+<pre>(getKey() == null ? 0 : getKey().hashCode())
+^ (getValue() == null ? 0 : getValue().hashCode())</pre>
+     *
+     * @return the hash code
+     */
+    public final int hashCode()
+    {
+      return (AbstractMap.hashCode(key) ^ AbstractMap.hashCode(value));
+    }
+
+    /**
+     * Replaces the value with the specified object. This writes through
+     * to the map, unless you have already called Iterator.remove(). It
+     * may be overridden to restrict a null value.
+     *
+     * @param newVal the new value to store
+     * @return the old value
+     * @throws NullPointerException if the map forbids null values
+     */
+    public Object setValue(Object newVal)
+    {
+      Object r = value;
+      value = newVal;
+      return r;
+    }
+
+    /**
+     * This provides a string representation of the entry. It is of the form
+     * "key=value", where string concatenation is used on key and value.
+     *
+     * @return the string representation
+     */
+    public final String toString()
+    {
+      return key + "=" + value;
+    }
+  } // class BasicMapEntry
 }
index 8271779..999f105 100644 (file)
@@ -73,13 +73,12 @@ import java.io.*;
  * and for the first line all fields are set, that line is used to
  * compute the day. <br>
  *
- * <pre>
- * month + day_of_month
- * month + week_of_month + day_of_week
- * month + day_of_week_of_month + day_of_week
- * day_of_year
- * day_of_week + week_of_year
- * </pre>
+ * 
+<pre>month + day_of_month
+month + week_of_month + day_of_week
+month + day_of_week_of_month + day_of_week
+day_of_year
+day_of_week + week_of_year</pre>
  * 
  * The hour_of_day-field takes precedence over the ampm and
  * hour_of_ampm fields. <br>
index 9c10a4b..815afcc 100644 (file)
@@ -1,5 +1,5 @@
 /* Collections.java -- Utility class with methods to operate on collections
-   Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -60,7 +60,6 @@ import java.io.Serializable;
  * modify the set.
  *
  * @author Original author unknown
- * @author Bryce McKinlay
  * @author Eric Blake <ebb9@email.byu.edu>
  * @see Collection
  * @see Set
@@ -108,24 +107,6 @@ public class Collections
    */
   public static final Set EMPTY_SET = new EmptySet();
 
-  private static final Iterator EMPTY_ITERATOR = new Iterator()
-    {
-      public boolean hasNext()
-      {
-       return false;
-      }
-
-      public Object next()
-      {
-       throw new NoSuchElementException();
-      }
-
-      public void remove()
-      {
-       throw new UnsupportedOperationException();
-      }
-    };
-
   /**
    * The implementation of {@link #EMPTY_SET}. This class name is required
    * for compatibility with Sun's JDK serializability.
@@ -158,9 +139,94 @@ public class Collections
     /**
      * Returns an iterator that does not iterate.
      */
+    // This is really cheating! I think it's perfectly valid, though.
     public Iterator iterator()
     {
-      return EMPTY_ITERATOR;
+      return EMPTY_LIST.iterator();
+    }
+
+    // The remaining methods are optional, but provide a performance
+    // advantage by not allocating unnecessary iterators in AbstractSet.
+    /**
+     * The empty set never contains anything.
+     */
+    public boolean contains(Object o)
+    {
+      return false;
+    }
+
+    /**
+     * This is true only if the given collection is also empty.
+     */
+    public boolean containsAll(Collection c)
+    {
+      return c.isEmpty();
+    }
+
+    /**
+     * Equal only if the other set is empty.
+     */
+    public boolean equals(Object o)
+    {
+      return o instanceof Set && ((Set) o).isEmpty();
+    }
+
+    /**
+     * The hashcode is always 0.
+     */
+    public int hashCode()
+    {
+      return 0;
+    }
+
+    /**
+     * Always succeeds with false result.
+     */
+    public boolean remove(Object o)
+    {
+      return false;
+    }
+
+    /**
+     * Always succeeds with false result.
+     */
+    public boolean removeAll(Collection c)
+    {
+      return false;
+    }
+
+    /**
+     * Always succeeds with false result.
+     */
+    public boolean retainAll(Collection c)
+    {
+      return false;
+    }
+
+    /**
+     * The array is always empty.
+     */
+    public Object[] toArray()
+    {
+      return new Object[0];
+    }
+
+    /**
+     * We don't even need to use reflection!
+     */
+    public Object[] toArray(Object[] a)
+    {
+      if (a.length > 0)
+        a[0] = null;
+      return a;
+    }
+
+    /**
+     * The string never changes.
+     */
+    public String toString()
+    {
+      return "[]";
     }
   } // class EmptySet
 
@@ -207,15 +273,106 @@ public class Collections
     {
       throw new IndexOutOfBoundsException();
     }
-    
+
+    // The remaining methods are optional, but provide a performance
+    // advantage by not allocating unnecessary iterators in AbstractList.
     /**
-     * Returns an iterator that does not iterate. Optional, but avoids 
-     * allocation of an iterator in AbstractList.
+     * Never contains anything.
      */
-    public Iterator iterator()
+    public boolean contains(Object o)
     {
-      return EMPTY_ITERATOR;
-    }    
+      return false;
+    }
+
+    /**
+     * This is true only if the given collection is also empty.
+     */
+    public boolean containsAll(Collection c)
+    {
+      return c.isEmpty();
+    }
+
+    /**
+     * Equal only if the other set is empty.
+     */
+    public boolean equals(Object o)
+    {
+      return o instanceof List && ((List) o).isEmpty();
+    }
+
+    /**
+     * The hashcode is always 1.
+     */
+    public int hashCode()
+    {
+      return 1;
+    }
+
+    /**
+     * Returns -1.
+     */
+    public int indexOf(Object o)
+    {
+      return -1;
+    }
+
+    /**
+     * Returns -1.
+     */
+    public int lastIndexOf(Object o)
+    {
+      return -1;
+    }
+
+    /**
+     * Always succeeds with false result.
+     */
+    public boolean remove(Object o)
+    {
+      return false;
+    }
+
+    /**
+     * Always succeeds with false result.
+     */
+    public boolean removeAll(Collection c)
+    {
+      return false;
+    }
+
+    /**
+     * Always succeeds with false result.
+     */
+    public boolean retainAll(Collection c)
+    {
+      return false;
+    }
+
+    /**
+     * The array is always empty.
+     */
+    public Object[] toArray()
+    {
+      return new Object[0];
+    }
+
+    /**
+     * We don't even need to use reflection!
+     */
+    public Object[] toArray(Object[] a)
+    {
+      if (a.length > 0)
+        a[0] = null;
+      return a;
+    }
+
+    /**
+     * The string never changes.
+     */
+    public String toString()
+    {
+      return "[]";
+    }
   } // class EmptyList
 
   /**
@@ -253,6 +410,64 @@ public class Collections
       return EMPTY_SET;
     }
 
+    // The remaining methods are optional, but provide a performance
+    // advantage by not allocating unnecessary iterators in AbstractMap.
+    /**
+     * No entries!
+     */
+    public boolean containsKey(Object key)
+    {
+      return false;
+    }
+
+    /**
+     * No entries!
+     */
+    public boolean containsValue(Object value)
+    {
+      return false;
+    }
+
+    /**
+     * Equal to all empty maps.
+     */
+    public boolean equals(Object o)
+    {
+      return o instanceof Map && ((Map) o).isEmpty();
+    }
+
+    /**
+     * No mappings, so this returns null.
+     */
+    public Object get(Object o)
+    {
+      return null;
+    }
+
+    /**
+     * The hashcode is always 0.
+     */
+    public int hashCode()
+    {
+      return 0;
+    }
+
+    /**
+     * No entries.
+     */
+    public Set keySet()
+    {
+      return EMPTY_SET;
+    }
+
+    /**
+     * Remove always succeeds, with null result.
+     */
+    public Object remove(Object o)
+    {
+      return null;
+    }
+
     /**
      * Size is always 0.
      */
@@ -269,8 +484,17 @@ public class Collections
     {
       return EMPTY_SET;
     }
+
+    /**
+     * The string never changes.
+     */
+    public String toString()
+    {
+      return "[]";
+    }
   } // class EmptyMap
 
+\f
   /**
    * Compare two objects with or without a Comparator. If c is null, uses the
    * natural ordering. Slightly slower than doing it inline if the JVM isn't
@@ -502,7 +726,7 @@ public class Collections
   }
 
   /**
-   * Returns an array list holding the elements visited by a given
+   * Returns an ArrayList holding the elements visited by a given
    * Enumeration. This method exists for interoperability between legacy
    * APIs and the new Collection API.
    *
@@ -511,9 +735,9 @@ public class Collections
    * @see ArrayList
    * @since 1.4
    */
-  public static List list(Enumeration e)
+  public static ArrayList list(Enumeration e)
   {
-    List l = new ArrayList();
+    ArrayList l = new ArrayList();
     while (e.hasMoreElements())
       l.add(e.nextElement());
     return l;
@@ -1353,7 +1577,7 @@ public class Collections
     public Set entrySet()
     {
       if (entries == null)
-        entries = singleton(new BasicMapEntry(k, v)
+        entries = singleton(new AbstractMap.BasicMapEntry(k, v)
         {
           public Object setValue(Object o)
           {
@@ -1500,6 +1724,7 @@ public class Collections
     l.set(i, l.set(j, l.get(i)));
   }
 
+\f
   /**
    * Returns a synchronized (thread-safe) collection wrapper backed by the
    * given collection. Notice that element access through the iterators
@@ -2682,6 +2907,7 @@ public class Collections
     }
   } // class SynchronizedSortedSet
 
+\f
   /**
    * Returns an unmodifiable view of the given collection. This allows
    * "read-only" access, although changes in the backing collection show up
index 9451744..a78eb9a 100644 (file)
@@ -1,6 +1,6 @@
 /* HashMap.java -- a class providing a basic hashtable data structure,
    mapping Object --> Object
-   Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -162,7 +162,7 @@ public class HashMap extends AbstractMap
    *
    * @author Eric Blake <ebb9@email.byu.edu>
    */
-  static class HashEntry extends BasicMapEntry
+  static class HashEntry extends AbstractMap.BasicMapEntry
   {
     /**
      * The next entry in the linked list. Package visible for use by subclass.
@@ -373,9 +373,9 @@ public class HashMap extends AbstractMap
       {
         Map.Entry e = (Map.Entry) itr.next();
         // Optimize in case the Entry is one of our own.
-        if (e instanceof BasicMapEntry)
+        if (e instanceof AbstractMap.BasicMapEntry)
           {
-            BasicMapEntry entry = (BasicMapEntry) e;
+            AbstractMap.BasicMapEntry entry = (AbstractMap.BasicMapEntry) e;
             put(entry.key, entry.value);
           }
         else
@@ -647,7 +647,8 @@ public class HashMap extends AbstractMap
    * @return the matching entry, if found, or null
    * @see #entrySet()
    */
-  private HashEntry getEntry(Object o)
+  // Package visible, for use in nested classes.
+  HashEntry getEntry(Object o)
   {
     if (!(o instanceof Map.Entry))
       return null;
@@ -710,14 +711,13 @@ public class HashMap extends AbstractMap
   }
 
   /**
-   * Increases the size of the HashMap and rehashes all keys to new array
-   * indices; this is called when the addition of a new value would cause
-   * size() > threshold. Note that the existing Entry objects are reused in
-   * the new hash table.
-   * <p>
+   * Increases the size of the HashMap and rehashes all keys to new
+   * array indices; this is called when the addition of a new value
+   * would cause size() &gt; threshold. Note that the existing Entry
+   * objects are reused in the new hash table.
    *
-   * This is not specified, but the new size is twice the current size plus
-   * one; this number is not always prime, unfortunately.
+   * <p>This is not specified, but the new size is twice the current size
+   * plus one; this number is not always prime, unfortunately.
    */
   private void rehash()
   {
index 71ec4b8..a94143b 100644 (file)
@@ -1,6 +1,6 @@
 /* Hashtable.java -- a class providing a basic hashtable data structure,
    mapping Object --> Object
-   Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -102,6 +102,9 @@ import java.io.ObjectOutputStream;
 public class Hashtable extends Dictionary
   implements Map, Cloneable, Serializable
 {
+  // WARNING: Hashtable is a CORE class in the bootstrap cycle. See the
+  // comments in vm/reference/java/lang/Runtime for implications of this fact.
+
   /** Default number of buckets. This is the value the JDK 1.3 uses. Some
    * early documentation specified this value as 101. That is incorrect.
    */
@@ -176,7 +179,7 @@ public class Hashtable extends Dictionary
    * pair. A Hashtable Entry is identical to a HashMap Entry, except that
    * `null' is not allowed for keys and values.
    */
-  private static final class HashEntry extends BasicMapEntry
+  private static final class HashEntry extends AbstractMap.BasicMapEntry
   {
     /** The next entry in the linked list. */
     HashEntry next;
@@ -340,9 +343,9 @@ public class Hashtable extends Dictionary
    *
    * @param value the value to search for in this Hashtable
    * @return true if at least one key maps to the value
-   * @throws NullPointerException if <code>value</code> is null
    * @see #contains(Object)
    * @see #containsKey(Object)
+   * @throws NullPointerException if <code>value</code> is null
    * @since 1.2
    */
   public boolean containsValue(Object value)
@@ -361,7 +364,7 @@ public class Hashtable extends Dictionary
     // Must throw on null argument even if the table is empty
     if (value == null)
       throw new NullPointerException();
-
     return false;
   }
 
@@ -511,9 +514,9 @@ public class Hashtable extends Dictionary
       {
         Map.Entry e = (Map.Entry) itr.next();
         // Optimize in case the Entry is one of our own.
-        if (e instanceof BasicMapEntry)
+        if (e instanceof AbstractMap.BasicMapEntry)
           {
-            BasicMapEntry entry = (BasicMapEntry) e;
+            AbstractMap.BasicMapEntry entry = (AbstractMap.BasicMapEntry) e;
             put(entry.key, entry.value);
           }
         else
@@ -763,9 +766,9 @@ public class Hashtable extends Dictionary
   /**
    * Returns true if this Hashtable equals the supplied Object <code>o</code>.
    * As specified by Map, this is:
-   * <pre>
+   * <code>
    * (o instanceof Map) && entrySet().equals(((Map) o).entrySet());
-   * </pre>
+   * </code>
    *
    * @param o the object to compare to
    * @return true if o is an equal map
@@ -812,7 +815,10 @@ public class Hashtable extends Dictionary
    */
   private int hash(Object key)
   {
-    return Math.abs(key.hashCode() % buckets.length);
+    // Note: Inline Math.abs here, for less method overhead, and to avoid
+    // a bootstrap dependency, since Math relies on native methods.
+    int hash = key.hashCode() % buckets.length;
+    return hash < 0 ? -hash : hash;
   }
 
   /**
@@ -823,7 +829,8 @@ public class Hashtable extends Dictionary
    * @return the matching entry, if found, or null
    * @see #entrySet()
    */
-  private HashEntry getEntry(Object o)
+  // Package visible, for use in nested classes.
+  HashEntry getEntry(Object o)
   {
     if (! (o instanceof Map.Entry))
       return null;
@@ -869,7 +876,7 @@ public class Hashtable extends Dictionary
   /**
    * Increases the size of the Hashtable and rehashes all keys to new array
    * indices; this is called when the addition of a new value would cause
-   * size() > threshold. Note that the existing Entry objects are reused in
+   * size() &gt; threshold. Note that the existing Entry objects are reused in
    * the new hash table.
    * <p>
    *
@@ -1139,4 +1146,4 @@ public class Hashtable extends Dictionary
       return type == VALUES ? e.value : e.key;
     }
   } // class Enumerator
-}
+} // class Hashtable
index 0709bdf..2716ac1 100644 (file)
@@ -71,7 +71,7 @@ package java.util;
  * <p>
  *
  * Under ideal circumstances (no collisions), LinkedHashMap offers O(1) 
- * performance on most operations (<pre>containsValue()</pre> is,
+ * performance on most operations (<code>containsValue()</code> is,
  * of course, O(n)).  In the worst case (all keys map to the same 
  * hash code -- very unlikely), most operations are O(n).
  * <p>
@@ -87,7 +87,7 @@ package java.util;
  * {@link ConcurrentModificationException} rather than exhibit
  * non-deterministic behavior.
  *
- * @author Eric Blake <ebb9@email.byu.edu>
+ * @author Eric Blake (ebb9@email.byu.edu)
  * @see Object#hashCode()
  * @see Collection
  * @see Map
@@ -256,8 +256,9 @@ public class LinkedHashMap extends HashMap
    * @param initialCapacity the initial capacity (>=0)
    * @param loadFactor the load factor (>0, not NaN)
    * @param accessOrder true for access-order, false for insertion-order
-   * @throws IllegalArgumentException if (initialCapacity < 0) ||
-   *                                     ! (loadFactor > 0.0)
+   *
+   * @throws IllegalArgumentException if (initialCapacity &lt; 0) ||
+   *                                     ! (loadFactor &gt; 0.0)
    */
   public LinkedHashMap(int initialCapacity, float loadFactor,
                        boolean accessOrder)
@@ -277,11 +278,12 @@ public class LinkedHashMap extends HashMap
   }
 
   /**
-   * Returns true if this HashMap contains a value <pre>o</pre>, such that
-   * <pre>o.equals(value)</pre>.
+   * Returns <code>true</code> if this HashMap contains a value
+   * <code>o</code>, such that <code>o.equals(value)</code>.
    *
    * @param value the value to search for in this HashMap
-   * @return true if at least one key maps to the value
+   *
+   * @return <code>true</code> if at least one key maps to the value
    */
   public boolean containsValue(Object value)
   {
@@ -297,7 +299,7 @@ public class LinkedHashMap extends HashMap
 
   /**
    * Return the value in this Map associated with the supplied key,
-   * or <pre>null</pre> if the key maps to nothing.  If this is an
+   * or <code>null</code> if the key maps to nothing.  If this is an
    * access-ordered Map and the key is found, this performs structural
    * modification, moving the key to the newest end of the list. NOTE:
    * Since the value could also be null, you must use containsKey to
@@ -350,14 +352,14 @@ public class LinkedHashMap extends HashMap
    * <p>
    *
    * For example, to keep the Map limited to 100 entries, override as follows:
-   * <pre>
-   * private static final int MAX_ENTRIES = 100;
-   *
-   * protected boolean removeEldestEntry(Map.Entry eldest)
-   * {
-   *   return size() > MAX_ENTRIES;
-   * }
-   * </pre><p>
+   * 
+<pre>private static final int MAX_ENTRIES = 100;
+
+protected boolean removeEldestEntry(Map.Entry eldest)
+{
+  return size() &gt; MAX_ENTRIES;
+}
+</pre><p>
    *
    * Typically, this method does not modify the map, but just uses the
    * return value as an indication to <code>put</code> whether to proceed.
@@ -376,6 +378,7 @@ public class LinkedHashMap extends HashMap
    *        returns true. For an access-order map, this is the least
    *        recently accessed; for an insertion-order map, this is the
    *        earliest element inserted.
+   *
    * @return true if <code>eldest</code> should be removed
    */
   protected boolean removeEldestEntry(Map.Entry eldest)
@@ -467,8 +470,10 @@ public class LinkedHashMap extends HashMap
       
       /**
        * Removes from the backing HashMap the last element which was fetched
-       * with the <pre>next()</pre> method.
+       * with the <code>next()</code> method.
+       *
        * @throws ConcurrentModificationException if the HashMap was modified
+       *
        * @throws IllegalStateException if called when there is no last element
        */
       public void remove()
index d87e95a..c891f82 100644 (file)
@@ -1,5 +1,5 @@
 /* LinkedList.java -- Linked list implementation of the List interface
-   Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -125,12 +125,13 @@ public class LinkedList extends AbstractSequentialList
    * entry in the list is obtained in constant time, which is a very desirable
    * property.
    * For speed and flexibility, range checking is not done in this method:
-   * Incorrect values will be returned if (n < 0) or (n >= size).
+   * Incorrect values will be returned if (n &lt; 0) or (n &gt;= size).
    *
    * @param n the number of the entry to get
    * @return the entry at position n
    */
-  private Entry getEntry(int n)
+  // Package visible for use in nested classes.
+  Entry getEntry(int n)
   {
     Entry e;
     if (n < size / 2)
@@ -156,7 +157,8 @@ public class LinkedList extends AbstractSequentialList
    *
    * @param e the entry to remove
    */
-  private void removeEntry(Entry e)
+  // Package visible for use in nested classes.
+  void removeEntry(Entry e)
   {
     modCount++;
     size--;
index 0d8df5d..22a6b83 100644 (file)
@@ -201,7 +201,7 @@ public interface List extends Collection
    * @see Object#equals(Object)
    * @see #hashCode()
    */
- /* boolean equals(Object o);*/
+  boolean equals(Object o);
 
   /**
    * Get the element at a given index in this list.
@@ -213,18 +213,20 @@ public interface List extends Collection
   Object get(int index);
 
   /**
-   * Obtain a hash code for this list. In order to obey the general contract of
-   * the hashCode method of class Object, this value is calculated as follows:
-   * <pre>
-   *   hashCode = 1;
-   *   Iterator i = list.iterator();
-   *   while (i.hasNext())
-   *     {
-   *       Object obj = i.next();
-   *       hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode());
-   *     }
-   * </pre>
-   * This ensures that the general contract of Object.hashCode() is adhered to.
+   * Obtains a hash code for this list. In order to obey the general
+   * contract of the hashCode method of class Object, this value is
+   * calculated as follows:
+   * 
+<p><pre>hashCode = 1;
+Iterator i = list.iterator();
+while (i.hasNext())
+{
+  Object obj = i.next();
+  hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode());
+}</pre>
+   *
+   * <p>This ensures that the general contract of Object.hashCode()
+   * is adhered to.
    *
    * @return the hash code of this list
    * @see Object#hashCode()
index 45508c7..b7b32c2 100644 (file)
@@ -1,5 +1,5 @@
-/* java.util.ListResourceBundle
-   Copyright (C) 1998, 1999, 2001 Free Software Foundation, Inc.
+/* ListResourceBundle -- a resource bundle build around a list
+   Copyright (C) 1998, 1999, 2001, 2002 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -39,73 +39,102 @@ exception statement from your version. */
 package java.util;
 
 /**
- * A <code>ListResouceBundle</code> provides an easy way, to create
- * your own resource bundle.  It is an abstract class that you can
- * subclass.  You should then overwrite the getContents method, that
- * provides a key/value list.
- * <br>
- * The key/value list is a two dimensional list of Object.  The first
- * dimension ranges over the resources. The second dimension ranges
- * from zero (key) to one (value).  The keys must be of type String.
- * <br>
- * XXX Example!
+ * A <code>ListResouceBundle</code> provides an easy way, to create your own
+ * resource bundle. It is an abstract class that you can subclass. You should
+ * then overwrite the getContents method, that provides a key/value list.
  *
+ * <p>The key/value list is a two dimensional list of Object.  The first
+ * dimension ranges over the resources. The second dimension ranges from
+ * zero (key) to one (value). The keys must be of type String, and they are
+ * case-sensitive. For example:
+ *
+<br><pre>public class MyResources
+  extends ListResourceBundle
+{
+  public Object[][] getContents()
+  {
+    return contents;
+  }
+
+  static final Object[][] contents =
+  {
+    // LOCALIZED STRINGS
+    {"s1", "The disk \"{1}\" contains {0}."},  // MessageFormat pattern
+    {"s2", "1"},                       // location of {0} in pattern
+    {"s3", "My Disk"},                 // sample disk name
+    {"s4", "no files"},                // first ChoiceFormat choice
+    {"s5", "one file"},                // second ChoiceFormat choice
+    {"s6", "{0,number} files"}         // third ChoiceFormat choice
+    {"s7", "3 Mar 96"},                // sample date
+    {"s8", new Dimension(1,5)}         // real object, not just string
+    // END OF LOCALIZED MATERIAL
+  };
+}</pre>
+ *
+ * @author Jochen Hoenicke
+ * @author Eric Blake <ebb9@email.byu.edu>
  * @see Locale
  * @see PropertyResourceBundle
- * @author Jochen Hoenicke */
+ * @since 1.1
+ * @status updated to 1.4
+ */
 public abstract class ListResourceBundle extends ResourceBundle
 {
   /**
-   * The constructor.  It does nothing special.
+   * The constructor. It does nothing special.
    */
   public ListResourceBundle()
   {
   }
 
   /**
-   * Gets the key/value list.  You must override this method.
-   * @return a two dimensional list of Objects.  The first dimension
-   * ranges over the objects, and the second dimension ranges from
-   * zero (key) to one (value).  
-   */
-  protected abstract Object[][] getContents();
-
-  /**
-   * Override this method to provide the resource for a keys.  This gets
-   * called by <code>getObject</code>.
-   * @param key The key of the resource.
-   * @return The resource for the key or null if it doesn't exists.
+   * Gets a resource for a given key. This is called by <code>getObject</code>.
+   *
+   * @param key the key of the resource
+   * @return the resource for the key, or null if it doesn't exist
    */
   public final Object handleGetObject(String key)
   {
     Object[][] contents = getContents();
-    for (int i = 0; i < contents.length; i++)
-      {
-       if (key.equals(contents[i][0]))
-         return contents[i][1];
-      }
+    int i = contents.length;
+    while (--i >= 0)
+      if (key.equals(contents[i][0]))
+        return contents[i][1];
     return null;
   }
 
   /**
    * This method should return all keys for which a resource exists.
-   * @return An enumeration of the keys.
+   *
+   * @return an enumeration of the keys
    */
   public Enumeration getKeys()
   {
+    // We make a new Set that holds all the keys, then return an enumeration
+    // for that. This prevents modifications from ruining the enumeration,
+    // as well as ignoring duplicates.
     final Object[][] contents = getContents();
-    
-    return new Enumeration()
-    {
-      int i = 0;
-      public boolean hasMoreElements()
-      {
-       return i < contents.length;
-      }
-      public Object nextElement()
+    Set s = new HashSet();
+    int i = contents.length;
+    while (--i >= 0)
+      s.add(contents[i][0]);
+    ResourceBundle bundle = parent;
+    // Eliminate tail recursion.
+    while (bundle != null)
       {
-       return contents[i++][0];
+        Enumeration e = bundle.getKeys();
+        while (e.hasMoreElements())
+          s.add(e.nextElement());
+        bundle = bundle.parent;
       }
-    };
+    return Collections.enumeration(s);
   }
-}
+
+  /**
+   * Gets the key/value list. You must override this method, and should not
+   * provide duplicate keys or null entries.
+   *
+   * @return a two dimensional list of String key / Object resouce pairs
+   */
+  protected abstract Object[][] getContents();
+} // class ListResourceBundle
index 5918a41..01f9b23 100644 (file)
@@ -296,14 +296,14 @@ public interface Map
      */
     public Object setValue(Object value);
 
+
     /**
-     * Returns the hash code of the entry.  This is defined as the exclusive-or
-     * of the hashcodes of the key and value (using 0 for null). In other
-     * words, this must be:
-     * <pre>
-     *  (getKey() == null ? 0 : getKey().hashCode()) ^
-     *  (getValue() == null ? 0 : getValue().hashCode())
-     * </pre>
+     * Returns the hash code of the entry.  This is defined as the
+     * exclusive-or of the hashcodes of the key and value (using 0 for
+     * <code>null</code>). In other words, this must be:
+     * 
+<p><pre>(getKey() == null ? 0 : getKey().hashCode())
+^ (getValue() == null ? 0 : getValue().hashCode())</pre>
      *
      * @return the hash code
      */
@@ -313,16 +313,16 @@ public interface Map
      * Compares the specified object with this entry. Returns true only if
      * the object is a mapping of identical key and value. In other words,
      * this must be:
-     * <pre>
-     * (o instanceof Map.Entry)
-     * && (getKey() == null ? ((HashMap) o).getKey() == null
-     *                      : getKey().equals(((HashMap) o).getKey()))
-     * && (getValue() == null ? ((HashMap) o).getValue() == null
-     *                        : getValue().equals(((HashMap) o).getValue()))
-     * </pre>
+     * 
+<p><pre>(o instanceof Map.Entry)
+&& (getKey() == null ? ((HashMap) o).getKey() == null
+                     : getKey().equals(((HashMap) o).getKey()))
+&& (getValue() == null ? ((HashMap) o).getValue() == null
+                       : getValue().equals(((HashMap) o).getValue()))</pre>
      *
      * @param o the object to compare
-     * @return true if it is equal
+     *
+     * @return <code>true</code> if it is equal
      */
     public boolean equals(Object o);
   }
index 4760713..7616f51 100644 (file)
@@ -1,5 +1,5 @@
-/* java.util.Observable
-   Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+/* Observable.java -- an object to be observed
+   Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -38,55 +38,53 @@ exception statement from your version. */
 
 package java.util;
 
-/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
- * "The Java Language Specification", ISBN 0-201-63451-1
- * plus online API docs for JDK 1.2 beta from http://www.javasoft.com.
- * Status:  Believed complete and correct.
- */
-
 /**
+ * This class represents an object which is observable.  Other objects may
+ * register their intent to be notified when this object changes; and when
+ * this object does change, it will trigger the <code>update</code> method
+ * of each observer.
+ *
+ * Note that the <code>notifyObservers()</code> method of this class is
+ * unrelated to the <code>notify()</code> of Object.
+ *
  * @author Warren Levy <warrenl@cygnus.com>
- * @date September 2, 1998.
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @see Observer
+ * @status updated to 1.4
  */
 public class Observable
 {
-  /** tracks whether this object has changed */
+  /** Tracks whether this object has changed. */
   private boolean changed;
 
-  /* list of the Observers registered as interested in this Observable */
-  private Vector observers;
-
-  /* TBD: This might be better implemented as an Observer[]
-   * but that would mean writing more code rather than making use of
-   * the existing Vector class (this also implies a larger text code
-   * space in resulting executables).  The tradeoff is one of speed
-   * (manipulating the Observer[] directly) vs. size/reuse.  In the future,
-   * we may decide to make the tradeoff and reimplement with an Observer[].
-   */
+  /* List of the Observers registered as interested in this Observable. */
+  private LinkedHashSet observers;
 
   /**
    * Constructs an Observable with zero Observers.
    */
   public Observable()
   {
-    changed = false;
-    observers = new Vector();
+    observers = new LinkedHashSet();
   }
 
   /**
    * Adds an Observer. If the observer was already added this method does
    * nothing.
    *
-   * @param observer Observer to add.
+   * @param observer Observer to add
+   * @throws NullPointerException if observer is null
    */
   public synchronized void addObserver(Observer observer)
   {
-    if (!observers.contains(observer))
-      observers.addElement(observer);
+    observers.add(observer);
   }
 
   /**
-   * Reset this Observable's state to unchanged.
+   * Reset this Observable's state to unchanged. This is called automatically
+   * by <code>notifyObservers</code> once all observers have been notified.
+   *
+   * @see #notifyObservers()
    */
   protected synchronized void clearChanged()
   {
@@ -94,7 +92,9 @@ public class Observable
   }
 
   /**
-   * @return Number of Observers for this Observable.
+   * Returns the number of observers for this object.
+   *
+   * @return number of Observers for this
    */
   public synchronized int countObservers()
   {
@@ -104,11 +104,11 @@ public class Observable
   /**
    * Deletes an Observer of this Observable.
    *
-   * @param victim Observer to delete.
+   * @param victim Observer to delete
    */
   public synchronized void deleteObserver(Observer victim)
   {
-    observers.removeElement(victim);
+    observers.remove(victim);
   }
 
   /**
@@ -116,11 +116,14 @@ public class Observable
    */
   public synchronized void deleteObservers()
   {
-    observers.removeAllElements();
+    observers.clear();
   }
 
   /**
-   * @return Whether or not this Observable has changed.
+   * True if <code>setChanged</code> has been called more recently than
+   * <code>clearChanged</code>.
+   *
+   * @return whether or not this Observable has changed
    */
   public synchronized boolean hasChanged()
   {
@@ -129,7 +132,10 @@ public class Observable
 
   /**
    * If the Observable has actually changed then tell all Observers about it,
-   * then resets state to unchanged.
+   * then reset state to unchanged.
+   *
+   * @see #notifyObservers(Object)
+   * @see Observer#update(Observable, Object)
    */
   public void notifyObservers()
   {
@@ -138,21 +144,29 @@ public class Observable
 
   /**
    * If the Observable has actually changed then tell all Observers about it,
-   * then resets state to unchanged. 
-   * Note that though the order of notification is unspecified in subclasses,
-   * in Observable it is in the order of registration.
+   * then reset state to unchanged. Note that though the order of
+   * notification is unspecified in subclasses, in Observable it is in the
+   * order of registration.
    *
-   * @param obj Arguement to Observer's update method.
+   * @param obj argument to Observer's update method
+   * @see Observer#update(Observable, Object)
    */
   public void notifyObservers(Object obj)
   {
-    if (!hasChanged())
+    if (! hasChanged())
       return;
-    Vector ob1 = (Vector) observers.clone();
-
-    for (int i = 0; i < ob1.size(); i++)
-      ((Observer) ob1.elementAt(i)).update(this, obj);
-
+    // Create clone inside monitor, as that is relatively fast and still
+    // important to keep threadsafe, but update observers outside of the
+    // lock since update() can call arbitrary code.
+    Set s;
+    synchronized (this)
+      {
+        s = (Set) observers.clone();
+      }
+    int i = s.size();
+    Iterator iter = s.iterator();
+    while (--i >= 0)
+      ((Observer) iter.next()).update(this, obj);
     clearChanged();
   }
 
index fa3a754..adad250 100644 (file)
@@ -1,5 +1,5 @@
-/* java.util.Properties
-   Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+/* Properties.java -- a set of persistent properties
+   Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -37,64 +37,88 @@ exception statement from your version. */
 
 
 package java.util;
-import java.io.*;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.PrintStream;
+import java.io.OutputStreamWriter;
 
 /**
+ * A set of persistent properties, which can be saved or loaded from a stream.
+ * A property list may also contain defaults, searched if the main list
+ * does not contain a property for a given key.
+ *
  * 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>
+<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
+ * <p>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
+ * Properties are saved in ISO 8859-1 encoding, using Unicode escapes with
+ * a single <code>u</code> for any character which cannot be represented.
+ *
  * @author Jochen Hoenicke
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @see PropertyResourceBundle
+ * @status updated to 1.4
  */
 public class Properties extends Hashtable
 {
+  // WARNING: Properties is a CORE class in the bootstrap cycle. See the
+  // comments in vm/reference/java/lang/Runtime for implications of this fact.
+
   /**
    * The property list that contains default values for any keys not
-   * in this property list.  
+   * in this property list.
+   *
+   * @serial the default properties
    */
   protected Properties defaults;
 
+  /**
+   * Compatible with JDK 1.0+.
+   */
   private static final long serialVersionUID = 4112578634029874840L;
 
   /**
-   * Creates a new empty property list.
+   * Creates a new empty property list with no default values.
    */
   public Properties()
   {
-    this.defaults = null;
   }
 
   /**
    * Create a new empty property list with the specified default values.
-   * @param defaults a Properties object containing the default values.
+   *
+   * @param defaults a Properties object containing the default values
    */
   public Properties(Properties defaults)
   {
@@ -102,6 +126,21 @@ public class Properties extends Hashtable
   }
 
   /**
+   * 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
+   * @see #getProperty(String)
+   * @since 1.2
+   */
+  public Object setProperty(String key, String value)
+  {
+    return put(key, value);
+  }
+
+  /**
    * Reads a property list from an input stream.  The stream should
    * have the following format: <br>
    *
@@ -120,173 +159,176 @@ public class Properties extends Hashtable
    *
    * Escape sequences <code>\t, \n, \r, \\, \", \', \!, \#, \ </code>(a
    * space), and unicode characters with the
-   * <code>\</code><code>u</code>xxxx notation are detected, and 
+   * <code>\\u</code><em>xxxx</em> 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>
+   * 
+<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:\\u0020</pre>
    *
    * @param in the input stream
-   * @exception IOException if an error occurred when reading
-   * from the input.  */
+   * @throws IOException if an error occurred when reading the input
+   * @throws NullPointerException if in is null
+   */
   public void load(InputStream inStream) throws IOException
   {
     // The spec says that the file must be encoded using ISO-8859-1.
     BufferedReader reader =
       new BufferedReader(new InputStreamReader(inStream, "ISO-8859-1"));
     String line;
-    
+
     while ((line = reader.readLine()) != null)
       {
-       char c = 0;
-       int pos = 0;
-       while (pos < line.length()
-              && Character.isWhitespace(c = line.charAt(pos)))
-         pos++;
+        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;
+        // If line is empty or begins with a comment character,
+        // skip this line.
+        if (pos == line.length() || c == '#' || c == '!')
+          continue;
 
-       // The characters 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);
-                           pos += 4;
-                         }     // else throw exception?
-                       break;
-                     default:
-                       key.append(c);
-                       break;
-                     }
-                 }
-             }
-           else
-             key.append(c);
-         }
+        // The characters 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);
+                            pos += 4;
+                          }        // 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++;
+        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++;
-         }
+        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);
-                           pos += 4;
-                         }     // else throw exception?
-                       break;
-                     default:
-                       element.append(c);
-                       break;
-                     }
-                 }
-             }
-           else
-             element.append(c);
-         }
-       put(key.toString(), element.toString());
+        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);
+                            pos += 4;
+                          }        // else throw exception?
+                        break;
+                      default:
+                        element.append(c);
+                        break;
+                      }
+                  }
+              }
+            else
+              element.append(c);
+          }
+        put(key.toString(), element.toString());
       }
   }
 
   /**
    * 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.
+   *
+   * @param out the stream to write to
+   * @param header a description of the property list
+   * @throws ClassCastException if this property contains any key or
+   *         value that are not strings
+   * @deprecated use {@link #store(OutputStream, String)} instead
    */
   public void save(OutputStream out, String header)
   {
     try
       {
-       store(out, header);
+        store(out, header);
       }
     catch (IOException ex)
       {
@@ -294,13 +336,14 @@ public class Properties extends Hashtable
   }
 
   /**
-   * Writes the key/value pairs to the given output stream. <br>
+   * Writes the key/value pairs to the given output stream, in a format
+   * suitable for <code>load</code>.<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>
+   * 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.
@@ -308,47 +351,42 @@ public class Properties extends Hashtable
    * 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.
+   * <code>\</code><code>u</code>xxxx Form.<br>
+   *
+   * Following the listing, the output stream is flushed but left open.
    *
    * @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.
+   * @param header the header written in the first line, may be null
+   * @throws ClassCastException if this property contains any key or
+   *         value that isn't a string
+   * @throws IOException if writing to the stream fails
+   * @throws NullPointerException if out is null
+   * @since 1.2
    */
   public void store(OutputStream out, String header) throws IOException
   {
     // The spec says that the file must be encoded using ISO-8859-1.
     PrintWriter writer
-      = new PrintWriter(new OutputStreamWriter (out, "ISO-8859-1"));
+      = new PrintWriter(new OutputStreamWriter(out, "ISO-8859-1"));
     if (header != null)
       writer.println("#" + header);
-    writer.println("#" + new Date().toString());
+    writer.println("#" + new Date());
     list(writer);
     writer.flush();
   }
 
   /**
-   * 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);
-  }
-
-  /**
    * 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.
+   * If the property is not found in the default, null is returned.
+   *
+   * @param key The key for this property
+   * @return the value for the given key, or null if not found
+   * @throws ClassCastException if this property contains any key or
+   *         value that isn't a string
+   * @see #defaults
+   * @see #setProperty(String, String)
+   * @see #getProperty(String, String)
    */
   public String getProperty(String key)
   {
@@ -358,13 +396,16 @@ public class Properties extends Hashtable
   /**
    * 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.
+   * property is not found in the default, the specified defaultValue is
+   * returned.
+   *
+   * @param key The key for this property
+   * @param defaultValue A default value
+   * @return The value for the given key
+   * @throws ClassCastException if this property contains any key or
+   *         value that isn't a string
+   * @see #defaults
+   * @see #setProperty(String, String)
    */
   public String getProperty(String key, String defaultValue)
   {
@@ -372,184 +413,145 @@ public class Properties extends Hashtable
     // Eliminate tail recursion.
     do
       {
-       String value = (String) prop.get(key);
-       if (value != null)
-         return value;
-       prop = prop.defaults;
+        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);
-  }
-
   /**
    * Returns an enumeration of all keys in this property list, including
    * the keys in the default property list.
+   *
+   * @return an Enumeration of all defined keys
    */
   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();
+    // We make a new Set that holds all the keys, then return an enumeration
+    // for that. This prevents modifications from ruining the enumeration,
+    // as well as ignoring duplicates.
+    Properties prop = this;
+    Set s = new HashSet();
+    // Eliminate tail recursion.
+    do
+      {
+        s.addAll(prop.keySet());
+        prop = prop.defaults;
+      }
+    while (prop != null);
+    return Collections.enumeration(s);
   }
 
   /**
-   * 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
+   * Writes the key/value pairs to the given print stream.  They are
+   * written in the way described in the method store. This does not visit
+   * the keys in the default properties.
+   *
+   * @param out the stream, where the key/value pairs are written to
+   * @throws ClassCastException if this property contains any key or
+   *         value that isn't a string
+   * @see #store(OutputStream, String)
    */
-  private String formatForOutput(String key, String value)
+  public void list(PrintStream out)
   {
-    // 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++)
+    Iterator iter = entrySet().iterator();
+    int i = size();
+    StringBuffer s = new StringBuffer(); // Reuse the same buffer.
+    while (--i >= 0)
       {
-       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;
+        Map.Entry entry = (Map.Entry) iter.next();
+        formatForOutput((String) entry.getKey(), s, true);
+        s.append('=');
+        formatForOutput((String) entry.getValue(), s, false);
+        out.println(s);
       }
-    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();
   }
 
   /**
-   * Writes the key/value pairs to the given print stream.  They are
+   * Writes the key/value pairs to the given print writer.  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
+   *
+   * @param out the writer, where the key/value pairs are written to
+   * @throws ClassCastException if this property contains any key or
+   *         value that isn't a string
+   * @see #store(OutputStream, String)
+   * @see #list(PrintStream)
+   * @since 1.1
    */
-  public void list(PrintStream out)
+  public void list(PrintWriter out)
   {
-    Enumeration keys = keys();
-    Enumeration elts = elements();
-    while (keys.hasMoreElements())
+    Iterator iter = entrySet().iterator();
+    int i = size();
+    StringBuffer s = new StringBuffer(); // Reuse the same buffer.
+    while (--i >= 0)
       {
-       String key = (String) keys.nextElement();
-       String elt = (String) elts.nextElement();
-       String output = formatForOutput(key, elt);
-       out.println(output);
+        Map.Entry entry = (Map.Entry) iter.next();
+        formatForOutput((String) entry.getKey(), s, true);
+        s.append('=');
+        formatForOutput((String) entry.getValue(), s, false);
+        out.println(s);
       }
   }
 
   /**
-   * 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
+   * Formats a key or value for output in a properties file.
+   * See store for a description of the format.
+   *
+   * @param str the string to format
+   * @param buffer the buffer to add it to
+   * @param key true if all ' ' must be escaped for the key, false if only
+   *        leading spaces must be escaped for the value
+   * @see #store(OutputStream, String)
    */
-  public void list(PrintWriter out)
+  private void formatForOutput(String str, StringBuffer buffer, boolean key)
   {
-    Enumeration keys = keys();
-    Enumeration elts = elements();
-    while (keys.hasMoreElements())
+    if (key)
+      {
+        buffer.setLength(0);
+        buffer.ensureCapacity(str.length());
+      }
+    else
+      buffer.ensureCapacity(buffer.length() + str.length());
+    boolean head = true;
+    int size = str.length();
+    for (int i = 0; i < size; i++)
       {
-       String key = (String) keys.nextElement();
-       String elt = (String) elts.nextElement();
-       String output = formatForOutput(key, elt);
-       out.println(output);
+        char c = str.charAt(i);
+        switch (c)
+          {
+          case '\n':
+            buffer.append("\\n");
+            break;
+          case '\r':
+            buffer.append("\\r");
+            break;
+          case '\t':
+            buffer.append("\\t");
+            break;
+          case ' ':
+            buffer.append(head ? "\\ " : " ");
+            break;
+          case '\\':
+          case '!':
+          case '#':
+          case '=':
+          case ':':
+            buffer.append('\\').append(c);
+          default:
+            if (c < ' ' || c > '~')
+              {
+                String hex = Integer.toHexString(c);
+                buffer.append("\\u0000".substring(0, 6 - hex.length()));
+                buffer.append(hex);
+              }
+            else
+              buffer.append(c);
+          }
+        if (c != ' ')
+          head = key;
       }
   }
-}
+} // class Properties
index ec9673f..bb03e45 100644 (file)
@@ -1,5 +1,5 @@
-/* java.util.PropertyPermission
-   Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+/* PropertyPermission.java -- permission to get and set System properties
+   Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -37,6 +37,7 @@ exception statement from your version. */
 
 
 package java.util;
+
 import java.security.Permission;
 import java.security.BasicPermission;
 import java.security.PermissionCollection;
@@ -49,79 +50,127 @@ import java.io.IOException;
  * This class represents the permission to access and modify a property.<br>
  *
  * The name is the name of the property, e.g. xxx.  You can also
- * use an asterisk "*" as described in BasicPermission <br>
+ * use an asterisk "*" as described in BasicPermission.<br>
  *
- * The action string is a comma-separated list if keywords.  There are
+ * The action string is a comma-separated list of keywords.  There are
  * two possible actions:
  * <dl>
- * <dt>read</dt> 
+ * <dt>read</dt>
  * <dd>Allows to read the property via <code>System.getProperty</code>.</dd>
- * <dt>write</dt> 
+ * <dt>write</dt>
  * <dd>Allows to write the property via <code>System.setProperty</code>.</dd>
  * </dl>
- * 
+ *
  * The action string is case insensitive (it is converted to lower case).
  *
  * @see Permission
  * @see BasicPermission
- * @author Jochen Hoenicke 
+ * @see SecurityManager
+ * @author Jochen Hoenicke
+ * @since 1.2
+ * @status updated to 1.4
  */
 public final class PropertyPermission extends BasicPermission
 {
   /**
-   * @serialField action String
-   *   The action string.
+   * PropertyPermission uses a more efficient representation than the
+   * serialized form; this documents the difference.
+   *
+   * @serialField action String the action string
    */
   private static final ObjectStreamField[] serialPersistentFields =
   {
     new ObjectStreamField("action", String.class)
   };
 
+  /**
+   * Compatible with JDK 1.2+.
+   */
   private static final long serialVersionUID = 885438825399942851L;
 
+  /** Permission to read. */
   private static final int READ = 1;
+  /** Permission to write. */
   private static final int WRITE = 2;
-  private transient int actions;
 
+  /** The set of actions permitted. */
+  // Package visible for use by PropertyPermissionCollection.
+  transient int actions;
+
+  /**
+   * The String forms of the actions permitted.
+   */
   private static final String actionStrings[] =
   {
     "", "read", "write", "read,write"
   };
 
   /**
-   * Constructs a PropertyPermission witha he specified property.  Possible
-   * actions are read and write.
-   * @param name the name of the property.
-   * @param actions the action string.
-   * @exception IllegalArgumentException if name string contains an
-   * illegal wildcard or actions string contains an illegal action
+   * Constructs a PropertyPermission with the specified property.  Possible
+   * actions are read and write, comma-separated and case-insensitive.
+   *
+   * @param name the name of the property
+   * @param actions the action string
+   * @throws NullPointerException if name is null
+   * @throws IllegalArgumentException if name string contains an
+   *         illegal wildcard or actions string contains an illegal action
+   *         (this includes a null actions string)
    */
   public PropertyPermission(String name, String actions)
   {
     super(name);
+    if (actions == null)
+      throw new IllegalArgumentException();
     setActions(actions.toLowerCase());
   }
 
   /**
    * Parse the action string and convert actions from external to internal
    * form.  This will set the internal actions field.
-   * @param actions the action string.
-   * @exception IllegalArgumentException if actions string contains an
-   * illegal action */
-  private void setActions(String actions)
+   *
+   * @param str the action string
+   * @throws IllegalArgumentException if actions string contains an
+   *         illegal action
+   */
+  private void setActions(String str)
   {
-    this.actions = 0;
-    StringTokenizer actionTokenizer = new StringTokenizer(actions, ",");
-    while (actionTokenizer.hasMoreElements())
-      {
-       String anAction = actionTokenizer.nextToken();
-       if ("read".equals(anAction))
-         this.actions |= READ;
-       else if ("write".equals(anAction))
-         this.actions |= WRITE;
-       else
-         throw new IllegalArgumentException("illegal action " + anAction);
-      }
+    if ("read".equals(str))
+      actions = READ;
+    else if ("write".equals(str))
+      actions = WRITE;
+    else if ("read,write".equals(str) || "write,read".equals(str))
+      actions = READ | WRITE;
+    else
+      throw new IllegalArgumentException("illegal action " + str);
+  }
+
+  /**
+   * Reads an object from the stream. This converts the external to the
+   * internal representation.
+   *
+   * @param s the stream to read from
+   * @throws IOException if the stream fails
+   * @throws ClassNotFoundException if reserialization fails
+   */
+  private void readObject(ObjectInputStream s)
+    throws IOException, ClassNotFoundException
+  {
+    ObjectInputStream.GetField fields = s.readFields();
+    setActions((String) fields.get("actions", null));
+  }
+
+  /**
+   * Writes an object to the stream. This converts the internal to the
+   * external representation.
+   *
+   * @param s the stram to write to
+   * @throws IOException if the stream fails
+   */
+  private void writeObject(ObjectOutputStream s) throws IOException
+  {
+    ObjectOutputStream.PutField fields = s.putFields();
+    fields.put("actions", getActions());
+    s.writeFields();
   }
 
   /**
@@ -129,134 +178,70 @@ public final class PropertyPermission extends BasicPermission
    * the following conditions are true:
    * <ul>
    * <li> p is a PropertyPermission </li>
-   * <li> this.getName() implies p.getName(),  
+   * <li> this.getName() implies p.getName(),
    *  e.g. <code>java.*</code> implies <code>java.home</code> </li>
    * <li> this.getActions is a subset of p.getActions </li>
    * </ul>
+   *
+   * @param p the permission to check
+   * @return true if this permission implies p
    */
   public boolean implies(Permission p)
   {
-    if (!(p instanceof PropertyPermission))
-      return false;
-
-    // We have to check the actions.
-    PropertyPermission pp = (PropertyPermission) p;
-    if ((pp.actions & ~actions) != 0)
-      return false;
-
-    // BasicPermission checks for name.
-    if (!super.implies(p))
-      return false;
-
-    return true;
-  }
-
-  /**
-   * Returns the action string.  Note that this may differ from the string
-   * given at the constructor:  The actions are converted to lowercase and
-   * may be reordered.
-   */
-  public String getActions()
-  {
-    return actionStrings[actions];
+    // BasicPermission checks for name and type.
+    if (super.implies(p))
+      {
+        // We have to check the actions.
+        PropertyPermission pp = (PropertyPermission) p;
+        return (pp.actions & ~actions) == 0;
+      }
+    return false;
   }
 
   /**
    * Check to see whether this object is the same as another
-   * PropertyPermission object.
+   * PropertyPermission object; this is true if it has the same name and
+   * actions.
    *
-   * @param obj The other object
+   * @param obj the other object
+   * @return true if the two are equivalent
    */
-  public boolean equals (Object obj)
+  public boolean equals(Object obj)
   {
-    if (! (obj instanceof PropertyPermission))
-      return false;
-    PropertyPermission p = (PropertyPermission) obj;
-    return actions == p.actions && super.equals (p);
+    return super.equals(obj) && actions == ((PropertyPermission) obj).actions;
   }
 
   /**
-   * Reads an object from the stream. This converts the external to the
-   * internal representation.
+   * Returns the hash code for this permission.  It is equivalent to
+   * <code>getName().hashCode()</code>.
+   *
+   * @return the hash code
    */
-  private void readObject(ObjectInputStream s)
-    throws IOException, ClassNotFoundException
+  public int hashCode()
   {
-    ObjectInputStream.GetField fields = s.readFields();
-    setActions((String) fields.get("actions", null));
+    return super.hashCode();
   }
 
   /**
-   * Writes an object to the stream. This converts the internal to the
-   * external representation.
+   * Returns the action string.  Note that this may differ from the string
+   * given at the constructor:  The actions are converted to lowercase and
+   * may be reordered.
+   *
+   * @return one of "read", "write", or "read,write"
    */
-  private void writeObject(ObjectOutputStream s) throws IOException
+  public String getActions()
   {
-    ObjectOutputStream.PutField fields = s.putFields();
-    fields.put("actions", getActions());
-    s.writeFields();
+    return actionStrings[actions];
   }
 
   /**
    * Returns a permission collection suitable to take
    * PropertyPermission objects.
-   * @return a new empty PermissionCollection.  
+   *
+   * @return a new empty PermissionCollection
    */
   public PermissionCollection newPermissionCollection()
   {
-    return new PermissionCollection()
-    {
-      Hashtable permissions = new Hashtable();
-      int allActions = 0;
-
-      public void add(Permission permission)
-      {
-       if (isReadOnly())
-         throw new IllegalStateException("readonly");
-
-       // also check that permission is of correct type.
-       PropertyPermission pp = (PropertyPermission) permission;
-       String name = pp.getName();
-       if (name.equals("*"))
-         allActions |= pp.actions;
-       permissions.put(name, pp);
-      }
-
-      public boolean implies(Permission permission)
-      {
-       if (!(permission instanceof PropertyPermission))
-         return false;
-
-       PropertyPermission toImply = (PropertyPermission) permission;
-       if ((toImply.actions & ~allActions) == 0)
-         return true;
-
-       String name = toImply.getName();
-       if (name.equals("*"))
-         return false;
-
-       int prefixLength = name.length();
-       if (name.endsWith("*"))
-         prefixLength -= 2;
-
-       while (true)
-         {
-           PropertyPermission forName =
-             (PropertyPermission) permissions.get(name);
-           if (forName != null && (toImply.actions & ~forName.actions) == 0)
-             return true;
-
-           prefixLength = name.lastIndexOf('.', prefixLength);
-           if (prefixLength < 0)
-             return false;
-           name = name.substring(0, prefixLength + 1) + '*';
-         }
-      }
-
-      public Enumeration elements()
-      {
-       return permissions.elements();
-      }
-    };
+    return new PropertyPermissionCollection();
   }
 }
diff --git a/libjava/java/util/PropertyPermissionCollection.java b/libjava/java/util/PropertyPermissionCollection.java
new file mode 100644 (file)
index 0000000..8f9c71d
--- /dev/null
@@ -0,0 +1,164 @@
+/* PropertyPermissionCollection.java -- a collection of PropertyPermissions
+   Copyright (C) 2002 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+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.
+
+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.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util;
+
+import java.security.Permission;
+import java.security.PermissionCollection;
+
+/**
+ * This class provides the implementation for
+ * <code>PropertyPermission.newPermissionCollection()</code>. It only accepts
+ * PropertyPermissions, and correctly implements <code>implies</code>. It
+ * is synchronized, as specified in the superclass.
+ *
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @status an undocumented class, but this matches Sun's serialization
+ */
+class PropertyPermissionCollection extends PermissionCollection
+{
+  /**
+   * Compatible with JDK 1.4.
+   */
+  private static final long serialVersionUID = 7015263904581634791L;
+
+  /**
+   * The permissions.
+   *
+   * @serial the table of permissions in the collection
+   */
+  private final Hashtable permissions = new Hashtable();
+
+  /**
+   * A flag to detect if "*" is in the collection.
+   *
+   * @serial true if "*" is in the collection 
+   */
+  private boolean all_allowed;
+
+  /**
+   * Adds a PropertyPermission to this collection.
+   *
+   * @param permission the permission to add
+   * @throws IllegalArgumentException if permission is not a PropertyPermission
+   * @throws SecurityException if collection is read-only
+   */
+  public void add(Permission permission)
+  {
+    if (isReadOnly())
+      throw new SecurityException("readonly");
+    if (! (permission instanceof PropertyPermission))
+      throw new IllegalArgumentException();
+    PropertyPermission pp = (PropertyPermission) permission;
+    String name = pp.getName();
+    if (name.equals("*"))
+        all_allowed = true;
+    PropertyPermission old = (PropertyPermission) permissions.get(name);
+    if (old != null)
+      {
+        if ((pp.actions | old.actions) == old.actions)
+          pp = old; // Old implies pp.
+        else if ((pp.actions | old.actions) != pp.actions)
+          // Here pp doesn't imply old; the only case left is both actions.
+          pp = new PropertyPermission(name, "read,write");
+      }
+    permissions.put(name, pp);
+  }
+
+  /**
+   * Returns true if this collection implies the given permission. This even
+   * returns true for this case:
+   * <p>
+<pre>collection.add(new PropertyPermission("a.*", "read"));
+collection.add(new PropertyPermission("a.b.*", "write"));
+collection.implies(new PropertyPermission("a.b.c", "read,write"));</pre>
+   *
+   * @param permission the permission to check
+   * @return true if it is implied by this
+   */
+  public boolean implies(Permission permission)
+  {
+    if (! (permission instanceof PropertyPermission))
+      return false;
+    PropertyPermission toImply = (PropertyPermission) permission;
+    int actions = toImply.actions;
+
+    if (all_allowed)
+      {
+        int all_actions = ((PropertyPermission) permissions.get("*")).actions;
+        actions &= ~all_actions;
+        if (actions == 0)
+          return true;
+      }
+
+    String name = toImply.getName();
+    if (name.equals("*"))
+      return false;
+
+    int prefixLength = name.length();
+    if (name.endsWith("*"))
+      prefixLength -= 2;
+
+    while (true)
+      {
+        PropertyPermission forName =
+          (PropertyPermission) permissions.get(name);
+        if (forName != null)
+          {
+            actions &= ~forName.actions;
+            if (actions == 0)
+              return true;
+          }
+
+        prefixLength = name.lastIndexOf('.', prefixLength);
+        if (prefixLength < 0)
+          return false;
+        name = name.substring(0, prefixLength + 1) + '*';
+      }
+  }
+
+  /**
+   * Enumerate over the collection.
+   *
+   * @return an enumeration of the collection contents
+   */
+  public Enumeration elements()
+  {
+    return permissions.elements();
+  }
+}
index 55fb413..a3173cf 100644 (file)
@@ -1,5 +1,5 @@
-/* java.util.PropertyResourceBundle
-   Copyright (C) 1998, 1999, 2001 Free Software Foundation, Inc.
+/* PropertyResourceBundle -- a resource bundle built from a Property file
+   Copyright (C) 1998, 1999, 2001, 2002 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -38,11 +38,13 @@ exception statement from your version. */
 
 package java.util;
 
+import java.io.IOException;
+import java.io.InputStream;
 import gnu.java.util.DoubleEnumeration;
 
 /**
  * This class is a concrete <code>ResourceBundle</code> that gets it
- * resources from a property file.  This implies that the resources are
+ * resources from a property file. This implies that the resources are
  * strings. For more information about resource bundles see the class
  * <code>ResourceBundle</code>.
  *
@@ -52,75 +54,68 @@ import gnu.java.util.DoubleEnumeration;
  * file.
  *
  * If there is also a class for this resource and the same locale, the
- * class does win.
- *
- * The properties file should have the name of the resource bundle,
- * appended with the locale (e.g. <code>_de</code) and the extension
- * <code>.properties</code>.  The file should have the same format
+ * class will be chosen. The properties file should have the name of the
+ * resource bundle, appended with the locale (e.g. <code>_de</code) and the
+ * extension <code>.properties</code>. The file should have the same format
  * as for <code>Properties.load()</code>
  *
- * XXX- move this to properties.
- * The file should have the following
- * format: An empty line or a line starting with <code>#</code> is
- * ignored.  An backslash (<code>\</code>) at the end of the line
- * makes the line continueing on the next line.  Otherwise, each line
- * describes a key/value pair.  The chars up to the first whitespace,
- * = or : are the key.  The key is followed by one or more
- * whitespaces, <code>=</code> or <code>:</code>.  The rest of the
- * line is the resource belonging to the key.  You can give unicode
- * characters with the <code>\\uxxxx</code> notation, where
- * <code>xxxx</code> is the hex encoding of the 16 bit unicode char
- * number.
- *
  * An example of a properties file for the german language is given
- * here.  This extends the example given in ListResourceBundle.
+ * 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 char <code>\u00e4<char> is the 
+ * and put it in the CLASSPATH. (The char <code>\u00e4<char> is the
  * german &auml;)
- * 
- * <pre>
- * s1=3
- * s2=MeineDisk
- * s3=3. M\u00e4rz 96
- * s4=Die Diskette ''{1}'' enth\u00e4lt {0} in {2}.
- * s5=0
- * s6=keine Dateien
- * s7=1
- * s8=eine Datei
- * s9=2
- * s10={0,number} Dateien
- * s11=Die Formatierung warf eine Exception: {0}
- * s12=FEHLER
- * s13=Ergebnis
- * s14=Dialog
- * s15=Auswahlkriterium
- * s16=1,3
- * </pre>
  *
+ *
+<pre>
+s1=3
+s2=MeineDisk
+s3=3. M\u00e4rz 96
+s4=Die Diskette ''{1}'' enth\u00e4lt {0} in {2}.
+s5=0
+s6=keine Dateien
+s7=1
+s8=eine Datei
+s9=2
+s10={0,number} Dateien
+s11=Die Formatierung warf eine Exception: {0}
+s12=FEHLER
+s13=Ergebnis
+s14=Dialog
+s15=Auswahlkriterium
+s16=1,3
+</pre>
+ *
+ * @author Jochen Hoenicke
  * @see ResourceBundle
  * @see ListResourceBundle
  * @see Properties#load()
- * @author Jochen Hoenicke */
+ * @since 1.1
+ * @status updated to 1.4
+ */
 public class PropertyResourceBundle extends ResourceBundle
 {
-  Properties properties;
+  /** The properties file this bundle is based on. */
+  private Properties properties;
 
   /**
    * Creates a new property resource bundle.
-   * @param stream An input stream, where the resources are read from.
+   *
+   * @param stream an input stream, where the resources are read from
+   * @throws NullPointerException if stream is null
+   * @throws IOException if reading the stream fails
    */
-  public PropertyResourceBundle(java.io.InputStream stream)
-    throws java.io.IOException
+  public PropertyResourceBundle(InputStream stream) throws IOException
   {
     properties = new Properties();
     properties.load(stream);
   }
 
   /**
-   * Called by <code>getObject</code> when a resource is needed.  This
+   * Called by <code>getObject</code> when a resource is needed. This
    * returns the resource given by the key.
-   * @param key The key of the resource.
-   * @return The resource for the key or null if it doesn't exists.
+   *
+   * @param key the key of the resource
+   * @return the resource for the key, or null if it doesn't exist
    */
   public Object handleGetObject(String key)
   {
@@ -129,16 +124,30 @@ public class PropertyResourceBundle extends ResourceBundle
 
   /**
    * This method should return all keys for which a resource exists.
-   * @return An enumeration of the keys.
+   *
+   * @return an enumeration of the keys
    */
   public Enumeration getKeys()
   {
-    // We must also return the keys of our parent.
-    if (parent != null)
+    if (parent == null)
+      return properties.propertyNames();
+    // We make a new Set that holds all the keys, then return an enumeration
+    // for that. This prevents modifications from ruining the enumeration,
+    // as well as ignoring duplicates.
+    Set s = new HashSet();
+    Enumeration e = properties.propertyNames();
+    while (e.hasMoreElements())
+      s.add(e.nextElement());
+    ResourceBundle bundle = parent;
+    // Eliminate tail recursion.
+    do
       {
-       return new DoubleEnumeration(properties.propertyNames(),
-                                    parent.getKeys());
+        e = bundle.getKeys();
+        while (e.hasMoreElements())
+          s.add(e.nextElement());
+        bundle = bundle.parent;
       }
-    return properties.propertyNames();
+    while (bundle != null);
+    return Collections.enumeration(s);
   }
-}
+} // class PropertyResourceBundle
index 1365acd..500a02d 100644 (file)
@@ -1,5 +1,5 @@
-/* java.util.Random
-   Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+/* Random.java -- a pseudo-random number generator
+   Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -38,13 +38,16 @@ exception statement from your version. */
 
 package java.util;
 
+import java.io.Serializable;
+
 /**
  * This class generates pseudorandom numbers.  It uses the same
  * algorithm as the original JDK-class, so that your programs behave
  * exactly the same way, if started with the same seed.
  *
  * The algorithm is described in <em>The Art of Computer Programming,
- * Volume 2</em> by Donald Knuth in Section 3.2.1.
+ * Volume 2</em> by Donald Knuth in Section 3.2.1.  It is a 48-bit seed,
+ * linear congruential formula.
  *
  * If two instances of this class are created with the same seed and
  * the same calls to these classes are made, they behave exactly the
@@ -57,7 +60,7 @@ package java.util;
  * <code>setSeed(long)</code> method.  In that case the above
  * paragraph doesn't apply to you.
  *
- * This class shouldn't be used for security sensitive purposes (like 
+ * This class shouldn't be used for security sensitive purposes (like
  * generating passwords or encryption keys.  See <code>SecureRandom</code>
  * in package <code>java.security</code> for this purpose.
  *
@@ -66,51 +69,65 @@ package java.util;
  *
  * @see java.security.SecureRandom
  * @see Math#random()
- * @author Jochen Hoenicke */
-public class Random implements java.io.Serializable
+ * @author Jochen Hoenicke
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @status updated to 1.4
+ */
+public class Random implements Serializable
 {
   /**
    * True if the next nextGaussian is available.  This is used by
    * nextGaussian, which generates two gaussian numbers by one call,
-   * and returns the second on the second call.  
-   * @see #nextGaussian.  */
+   * and returns the second on the second call.
+   *
+   * @serial whether nextNextGaussian is available
+   * @see #nextGaussian()
+   * @see #nextNextGaussian
+   */
   private boolean haveNextNextGaussian;
+
   /**
-   * The next nextGaussian if available.  This is used by nextGaussian,
+   * The next nextGaussian, when available.  This is used by nextGaussian,
    * which generates two gaussian numbers by one call, and returns the
    * second on the second call.
-   * @see #nextGaussian.
+   *
+   * @serial the second gaussian of a pair
+   * @see #nextGaussian()
+   * @see #haveNextNextGaussian
    */
   private double nextNextGaussian;
+
   /**
    * The seed.  This is the number set by setSeed and which is used
    * in next.
-   * @see #next
+   *
+   * @serial the internal state of this generator
+   * @see #next()
    */
   private long seed;
 
+  /**
+   * Compatible with JDK 1.0+.
+   */
   private static final long serialVersionUID = 3905348978240129619L;
 
   /**
    * Creates a new pseudorandom number generator.  The seed is initialized
-   * to the current time as follows.
-   * <pre>
-   * setSeed(System.currentTimeMillis());
-   * </pre>
+   * to the current time, as if by
+   * <code>setSeed(System.currentTimeMillis());</code>.
+   *
    * @see System#currentTimeMillis()
    */
   public Random()
   {
-    setSeed(System.currentTimeMillis());
+    this(System.currentTimeMillis());
   }
 
   /**
    * Creates a new pseudorandom number generator, starting with the
-   * specified seed. This does:
-   * <pre>
-   * setSeed(seed);
-   * </pre>
-   * @param seed the initial seed.
+   * specified seed, using <code>setSeed(seed);</code>.
+   *
+   * @param seed the initial seed
    */
   public Random(long seed)
   {
@@ -122,12 +139,14 @@ public class Random implements java.io.Serializable
    * above, two instances of the same random class, starting with the
    * same seed, should produce the same results, if the same methods
    * are called.  The implementation for java.util.Random is:
-   * <pre>
-   * public synchronized void setSeed(long seed) {
-   *     this.seed = (seed ^ 0x5DEECE66DL) & ((1L << 48) - 1);
-   *     haveNextNextGaussian = false;
-   * }
-   * </pre>
+   *
+<pre>public synchronized void setSeed(long seed)
+{
+  this.seed = (seed ^ 0x5DEECE66DL) & ((1L &lt;&lt; 48) - 1);
+  haveNextNextGaussian = false;
+}</pre>
+   *
+   * @param seed the new seed
    */
   public synchronized void setSeed(long seed)
   {
@@ -140,20 +159,18 @@ public class Random implements java.io.Serializable
    * an int value whose <code>bits</code> low order bits are
    * independent chosen random bits (0 and 1 are equally likely).
    * The implementation for java.util.Random is:
-   * <pre>
-   * protected synchronized int next(int bits) {
-   *     seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
-   *     return (int) (seed >>> (48 - bits));
-   * }
-   * </pre>
-   * @param bits the number of random bits to generate.  Must be in range
-   * 1..32.
-   * @return the next pseudorandom value.
-   * @since JDK1.1
+   *
+<pre>protected synchronized int next(int bits)
+{
+  seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L &lt;&lt; 48) - 1);
+  return (int) (seed &gt;&gt;&gt; (48 - bits));
+}</pre>
+   *
+   * @param bits the number of random bits to generate, in the range 1..32
+   * @return the next pseudorandom value
+   * @since 1.1
    */
   protected synchronized int next(int bits)
-    /*{ require { 1 <= bits && bits <=32 :: 
-       "bits "+bits+" not in range [1..32]" } } */
   {
     seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
     return (int) (seed >>> (48 - bits));
@@ -163,42 +180,45 @@ public class Random implements java.io.Serializable
    * Fills an array of bytes with random numbers.  All possible values
    * are (approximately) equally likely.
    * The JDK documentation gives no implementation, but it seems to be:
-   * <pre>
-   * public void nextBytes(byte[] bytes) {
-   *     for (int i=0; i< bytes.length; i+=4) {
-   *         int random = next(32);
-   *         for (int j=0; i+j< bytes.length && j<4; j++)
-   *             bytes[i+j] = (byte) (random & 0xff)
-   *             random >>= 8;
-   *         }
-   *     }
-   * }
-   * </pre>
-   * @param bytes The byte array that should be filled.
-   * @since JDK1.1
+   *
+<pre>public void nextBytes(byte[] bytes)
+{
+  for (int i = 0; i &lt; bytes.length; i += 4)
+  {
+    int random = next(32);
+    for (int j = 0; i + j &lt; bytes.length && j &lt; 4; j++)
+    {
+      bytes[i+j] = (byte) (random & 0xff)
+      random &gt;&gt;= 8;
+    }
+  }
+}</pre>
+   *
+   * @param bytes the byte array that should be filled
+   * @throws NullPointerException if bytes is null
+   * @since 1.1
    */
   public void nextBytes(byte[] bytes)
-    /*{ require { bytes != null :: "bytes is null"; } } */
   {
     int random;
-    /* Do a little bit unrolling of the above algorithm. */
+    // Do a little bit unrolling of the above algorithm.
     int max = bytes.length & ~0x3;
     for (int i = 0; i < max; i += 4)
       {
-       random = next(32);
-       bytes[i] = (byte) random;
-       bytes[i + 1] = (byte) (random >> 8);
-       bytes[i + 2] = (byte) (random >> 16);
-       bytes[i + 3] = (byte) (random >> 24);
+        random = next(32);
+        bytes[i] = (byte) random;
+        bytes[i + 1] = (byte) (random >> 8);
+        bytes[i + 2] = (byte) (random >> 16);
+        bytes[i + 3] = (byte) (random >> 24);
       }
     if (max < bytes.length)
       {
-       random = next(32);
-       for (int j = max; j < bytes.length; j++)
-         {
-           bytes[j] = (byte) random;
-           random >>= 8;
-         }
+        random = next(32);
+        for (int j = max; j < bytes.length; j++)
+          {
+            bytes[j] = (byte) random;
+            random >>= 8;
+          }
       }
   }
 
@@ -207,13 +227,14 @@ public class Random implements java.io.Serializable
    * an int value whose 32 bits are independent chosen random bits
    * (0 and 1 are equally likely).  The implementation for
    * java.util.Random is:
-   * <pre>
-   * public int nextInt() {
-   *     return next(32);
-   * }
-   * </pre>
+   * 
+<pre>public int nextInt()
+{
+  return next(32);
+}</pre>
    *
-   * @return the next pseudorandom value.  */
+   * @return the next pseudorandom value
+   */
   public int nextInt()
   {
     return next(32);
@@ -225,51 +246,58 @@ public class Random implements java.io.Serializable
    * each value has the same likelihodd (1/<code>n</code>).
    * (0 and 1 are equally likely).  The implementation for
    * java.util.Random is:
-   * <pre>
-   * public int nextInt(int n) {
-   *     if (n<=0)
-   *         throw new IllegalArgumentException("n must be positive");
-   *     if ((n & -n) == n)  // i.e., n is a power of 2
-   *         return (int)((n * (long)next(31)) >> 31);
-   *     int bits, val;
-   *     do {
-   *         bits = next(32);
-   *         val = bits % n;
-   *     } while(bits - val + (n-1) < 0);
-   *     return val;
-   * }
-   * </pre>
-   * This algorithm would return every value with exactly the same 
+   * 
+<pre>
+public int nextInt(int n)
+{
+  if (n &lt;= 0)
+    throw new IllegalArgumentException("n must be positive");
+
+  if ((n & -n) == n)  // i.e., n is a power of 2
+    return (int)((n * (long) next(31)) &gt;&gt; 31);
+
+  int bits, val;
+  do
+  {
+    bits = next(32);
+    val = bits % n;
+  }
+  while(bits - val + (n-1) &lt; 0);
+
+  return val;
+}</pre>
+   *   
+   * <p>This algorithm would return every value with exactly the same
    * probability, if the next()-method would be a perfect random number
    * generator.
-   * 
+   *
    * The loop at the bottom only accepts a value, if the random
    * number was between 0 and the highest number less then 1<<31,
    * which is divisible by n.  The probability for this is high for small
    * n, and the worst case is 1/2 (for n=(1<<30)+1).
    *
-   * The special treatment for n = power of 2, selects the high bits of 
+   * The special treatment for n = power of 2, selects the high bits of
    * the random number (the loop at the bottom would select the low order
    * bits).  This is done, because the low order bits of linear congruential
-   * number generators (like the one used in this class) are known to be 
+   * number generators (like the one used in this class) are known to be
    * ``less random'' than the high order bits.
    *
-   * @param n the upper bound.
-   * @exception IllegalArgumentException if the given upper bound is negative
-   * @return the next pseudorandom value.  
+   * @param n the upper bound
+   * @throws IllegalArgumentException if the given upper bound is negative
+   * @return the next pseudorandom value
+   * @since 1.2
    */
   public int nextInt(int n)
-    /*{ require { n > 0 :: "n must be positive"; } } */
   {
     if (n <= 0)
       throw new IllegalArgumentException("n must be positive");
-    if ((n & -n) == n)         // i.e., n is a power of 2
+    if ((n & -n) == n) // i.e., n is a power of 2
       return (int) ((n * (long) next(31)) >> 31);
     int bits, val;
     do
       {
-       bits = next(32);
-       val = bits % n;
+        bits = next(32);
+        val = bits % n;
       }
     while (bits - val + (n - 1) < 0);
     return val;
@@ -279,12 +307,13 @@ public class Random implements java.io.Serializable
    * Generates the next pseudorandom long number.  All bits of this
    * long are independently chosen and 0 and 1 have equal likelihood.
    * The implementation for java.util.Random is:
-   * <pre>
-   * public long nextLong() {
-   *     return ((long)next(32) << 32) + next(32);
-   * }
-   * </pre>
-   * @return the next pseudorandom value.  
+   *
+<pre>public long nextLong()
+{
+  return ((long) next(32) &lt;&lt; 32) + next(32);
+}</pre>
+   *
+   * @return the next pseudorandom value
    */
   public long nextLong()
   {
@@ -294,12 +323,14 @@ public class Random implements java.io.Serializable
   /**
    * Generates the next pseudorandom boolean.  True and false have
    * the same probability.  The implementation is:
-   * <pre>
-   * public boolean nextBoolean() {
-   *     return next(1) != 0;
-   * }
-   * </pre>
-   * @return the next pseudorandom boolean.
+   * 
+<pre>public boolean nextBoolean()
+{
+  return next(1) != 0;
+}</pre>
+   *
+   * @return the next pseudorandom boolean
+   * @since 1.2
    */
   public boolean nextBoolean()
   {
@@ -308,83 +339,91 @@ public class Random implements java.io.Serializable
 
   /**
    * Generates the next pseudorandom float uniformly distributed
-   * between 0.0f (inclusive) and 1.0 (exclusive).  The
+   * between 0.0f (inclusive) and 1.0f (exclusive).  The
    * implementation is as follows.
-   * <pre>
-   * public float nextFloat() {
-   *     return next(24) / ((float)(1 << 24));
-   * }
-   * </pre>
-   * @return the next pseudorandom float.  */
+   * 
+<pre>public float nextFloat()
+{
+  return next(24) / ((float)(1 &lt;&lt; 24));
+}</pre>
+   *
+   * @return the next pseudorandom float
+   */
   public float nextFloat()
   {
-    return next(24) / ((float) (1 << 24));
+    return next(24) / (float) (1 << 24);
   }
 
   /**
    * Generates the next pseudorandom double uniformly distributed
-   * between 0.0f (inclusive) and 1.0 (exclusive).  The
+   * between 0.0 (inclusive) and 1.0 (exclusive).  The
    * implementation is as follows.
-   * <pre>
-   * public double nextDouble() {
-   *     return (((long)next(26) << 27) + next(27)) / (double)(1 << 53);
-   * }
-   * </pre>
-   * @return the next pseudorandom double.  */
+   *
+<pre>public double nextDouble()
+{
+  return (((long) next(26) &lt;&lt; 27) + next(27)) / (double)(1L &lt;&lt; 53);
+}</pre>
+   *
+   * @return the next pseudorandom double
+   */
   public double nextDouble()
   {
     return (((long) next(26) << 27) + next(27)) / (double) (1L << 53);
   }
 
   /**
-   * Generates the next pseudorandom, Gaussian (normally) distributed 
+   * Generates the next pseudorandom, Gaussian (normally) distributed
    * double value, with mean 0.0 and standard deviation 1.0.
    * The algorithm is as follows.
-   * <pre>
-   * public synchronized double nextGaussian() {
-   *     if (haveNextNextGaussian) {
-   *         haveNextNextGaussian = false;
-   *         return nextNextGaussian;
-   *     } else {
-   *         double v1, v2, s;
-   *         do {
-   *             v1 = 2 * nextDouble() - 1; // between -1.0 and 1.0
-   *             v2 = 2 * nextDouble() - 1; // between -1.0 and 1.0
-   *             s = v1 * v1 + v2 * v2;
-   *         } while (s >= 1);
-   *         double norm = Math.sqrt(-2 * Math.log(s)/s);
-   *         nextNextGaussian = v2 * norm;
-   *         haveNextNextGaussian = true;
-   *         return v1 * norm;
-   *     }
-   * }
-   * </pre>
-   * This is described in section 3.4.1 of <em>The Art of Computer
+   * 
+<pre>public synchronized double nextGaussian()
+{
+  if (haveNextNextGaussian)
+  {
+    haveNextNextGaussian = false;
+    return nextNextGaussian;
+  }
+  else
+  {
+    double v1, v2, s;
+    do
+    {
+      v1 = 2 * nextDouble() - 1; // between -1.0 and 1.0
+      v2 = 2 * nextDouble() - 1; // between -1.0 and 1.0
+      s = v1 * v1 + v2 * v2;
+    }
+    while (s >= 1);
+
+    double norm = Math.sqrt(-2 * Math.log(s) / s);
+    nextNextGaussian = v2 * norm;
+    haveNextNextGaussian = true;
+    return v1 * norm;
+  }
+}</pre>
+   *
+   * <p>This is described in section 3.4.1 of <em>The Art of Computer
    * Programming, Volume 2</em> by Donald Knuth.
    *
-   * @return the next pseudorandom Gaussian distributed double.  
+   * @return the next pseudorandom Gaussian distributed double
    */
   public synchronized double nextGaussian()
   {
     if (haveNextNextGaussian)
       {
-       haveNextNextGaussian = false;
-       return nextNextGaussian;
+        haveNextNextGaussian = false;
+        return nextNextGaussian;
       }
-    else
+    double v1, v2, s;
+    do
       {
-       double v1, v2, s;
-       do
-         {
-           v1 = 2 * nextDouble() - 1;  // between -1.0 and 1.0
-           v2 = 2 * nextDouble() - 1;  // between -1.0 and 1.0
-           s = v1 * v1 + v2 * v2;
-         }
-       while (s >= 1);
-       double norm = Math.sqrt(-2 * Math.log(s) / s);
-       nextNextGaussian = v2 * norm;
-       haveNextNextGaussian = true;
-       return v1 * norm;
+        v1 = 2 * nextDouble() - 1; // Between -1.0 and 1.0.
+        v2 = 2 * nextDouble() - 1; // Between -1.0 and 1.0.
+        s = v1 * v1 + v2 * v2;
       }
+    while (s >= 1);
+    double norm = Math.sqrt(-2 * Math.log(s) / s);
+    nextNextGaussian = v2 * norm;
+    haveNextNextGaussian = true;
+    return v1 * norm;
   }
 }
index 91d2d08..c2e2560 100644 (file)
@@ -52,7 +52,8 @@ import java.text.DateFormatSymbols;
  *
  * @see Calendar
  * @see GregorianCalender 
- * @author Jochen Hoenicke */
+ * @author Jochen Hoenicke
+ */
 public class SimpleTimeZone extends TimeZone
 {
   /**
@@ -436,9 +437,9 @@ public class SimpleTimeZone extends TimeZone
    * In the standard JDK the results given by this method may result in
    * inaccurate results at the end of February or the beginning of March.
    * To avoid this, you should use Calendar instead:
-   * <pre>
-   * offset = cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET);
-   * </pre>
+   * <code>offset = cal.get(Calendar.ZONE_OFFSET)
+   * + cal.get(Calendar.DST_OFFSET);</code>
+   *
    * You could also use in
    *
    * This version doesn't suffer this inaccuracy.
index 707ddd4..e7fc6fd 100644 (file)
@@ -1,5 +1,5 @@
-/* java.util.StringTokenizer
-   Copyright (C) 1998, 1999, 2001 Free Software Foundation, Inc.
+/* StringTokenizer -- breaks a String into tokens
+   Copyright (C) 1998, 1999, 2001, 2002 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -39,75 +39,79 @@ exception statement from your version. */
 package java.util;
 
 /**
- * This class splits a string into tokens.  The caller can set on which 
+ * This class splits a string into tokens.  The caller can set on which
  * delimiters the string should be split and if the delimiters should be
- * returned.
+ * returned. This is much simpler than {@link java.io.StreamTokenizer}.
  *
- * You may change the delimiter set on the fly by calling
+ * <p>You may change the delimiter set on the fly by calling
  * nextToken(String).  But the semantic is quite difficult; it even
  * depends on calling <code>hasMoreTokens()</code>.  You should call
  * <code>hasMoreTokens()</code> before, otherwise the old delimiters
- * after the last token are returned.
+ * after the last token are candidates for being returned.
  *
- * If you want to get the delimiters, you have to use the three argument
+ * <p>If you want to get the delimiters, you have to use the three argument
  * constructor.  The delimiters are returned as token consisting of a
- * single character.  
+ * single character.
  *
  * @author Jochen Hoenicke
  * @author Warren Levy <warrenl@cygnus.com>
+ * @see java.io.StreamTokenizer
+ * @status updated to 1.4
  */
 public class StringTokenizer implements Enumeration
 {
+  // WARNING: StringTokenizer is a CORE class in the bootstrap cycle. See the
+  // comments in vm/reference/java/lang/Runtime for implications of this fact.
+
   /**
    * The position in the str, where we currently are.
    */
   private int pos;
+
   /**
    * The string that should be split into tokens.
    */
-  private String str;
+  private final String str;
+
+  /**
+   * The length of the string.
+   */
+  private final int len;
+
   /**
    * The string containing the delimiter characters.
    */
   private String delim;
+
   /**
    * Tells, if we should return the delimiters.
    */
-  private boolean retDelims;
-
-  /*{ 
-     invariant {
-     pos >= 0 :: "position is negative";
-     pos <= str.length() :: "position is out of string";
-     str != null :: "String is null";
-     delim != null :: "Delimiters are null";
-     }
-     } */
+  private final boolean retDelims;
 
   /**
    * Creates a new StringTokenizer for the string <code>str</code>,
-   * that should split on the default delimiter set (space, tap,
+   * that should split on the default delimiter set (space, tab,
    * newline, return and formfeed), and which doesn't return the
    * delimiters.
-   * @param str The string to split.
+   *
+   * @param str The string to split
+   * @throws NullPointerException if str is null
    */
   public StringTokenizer(String str)
-    /*{ require { str != null :: "str must not be null"; } } */
   {
     this(str, " \t\n\r\f", false);
   }
 
   /**
-   * Create a new StringTokenizer, that splits the given string on 
+   * Create a new StringTokenizer, that splits the given string on
    * the given delimiter characters.  It doesn't return the delimiter
    * characters.
    *
-   * @param str The string to split.
-   * @param delim A string containing all delimiter characters.
+   * @param str the string to split
+   * @param delim a string containing all delimiter characters
+   * @throws NullPointerException if either argument is null
    */
   public StringTokenizer(String str, String delim)
-    /*{ require { str != null :: "str must not be null";
-       delim != null :: "delim must not be null"; } } */
   {
     this(str, delim, false);
   }
@@ -119,34 +123,34 @@ public class StringTokenizer implements Enumeration
    * characters are returned as tokens of their own.  The delimiter
    * tokens always consist of a single character.
    *
-   * @param str The string to split.
-   * @param delim A string containing all delimiter characters.
-   * @param returnDelims Tells, if you want to get the delimiters.
+   * @param str the string to split
+   * @param delim a string containing all delimiter characters
+   * @param returnDelims tells, if you want to get the delimiters
+   * @throws NullPointerException if str or delim is null
    */
   public StringTokenizer(String str, String delim, boolean returnDelims)
-    /*{ require { str != null :: "str must not be null";
-       delim != null :: "delim must not be null"; } } */
   {
+    len = str.length();
     this.str = str;
-    this.delim = delim;
+    // The toString() hack causes the NullPointerException.
+    this.delim = delim.toString();
     this.retDelims = returnDelims;
     this.pos = 0;
   }
 
   /**
    * Tells if there are more tokens.
-   * @return True, if the next call of nextToken() succeeds, false otherwise.
+   *
+   * @return true if the next call of nextToken() will succeed
    */
   public boolean hasMoreTokens()
   {
-    if (!retDelims)
+    if (! retDelims)
       {
-       while (pos < str.length() && delim.indexOf(str.charAt(pos)) > -1)
-         {
-           pos++;
-         }
+        while (pos < len && delim.indexOf(str.charAt(pos)) >= 0)
+          pos++;
       }
-    return pos < str.length();
+    return pos < len;
   }
 
   /**
@@ -154,13 +158,13 @@ public class StringTokenizer implements Enumeration
    * <code>delim</code>.  The change of the delimiter set is
    * permanent, ie. the next call of nextToken(), uses the same
    * delimiter set.
-   * @param delim a string containing the new delimiter characters.
-   * @return the next token with respect to the new delimiter characters.
-   * @exception NoSuchElementException if there are no more tokens.
+   *
+   * @param delim a string containing the new delimiter characters
+   * @return the next token with respect to the new delimiter characters
+   * @throws NoSuchElementException if there are no more tokens
+   * @throws NullPointerException if delim is null
    */
   public String nextToken(String delim) throws NoSuchElementException
-    /*{ require { hasMoreTokens() :: "no more Tokens available";
-       ensure { $return != null && $return.length() > 0; } } */
   {
     this.delim = delim;
     return nextToken();
@@ -168,32 +172,24 @@ public class StringTokenizer implements Enumeration
 
   /**
    * Returns the nextToken of the string.
-   * @param delim a string containing the new delimiter characters.
-   * @return the next token with respect to the new delimiter characters.
-   * @exception NoSuchElementException if there are no more tokens.
+   *
+   * @return the next token with respect to the current delimiter characters
+   * @throws NoSuchElementException if there are no more tokens
    */
   public String nextToken() throws NoSuchElementException
-    /*{ require { hasMoreTokens() :: "no more Tokens available";
-       ensure { $return != null && $return.length() > 0; } } */
   {
-    if (pos < str.length() && delim.indexOf(str.charAt(pos)) > -1)
+    if (pos < len && delim.indexOf(str.charAt(pos)) >= 0)
       {
-       if (retDelims)
-         return str.substring(pos, ++pos);
-
-       while (++pos < str.length() && delim.indexOf(str.charAt(pos)) > -1)
-         {
-           /* empty */
-         }
+        if (retDelims)
+          return str.substring(pos, ++pos);
+        while (++pos < len && delim.indexOf(str.charAt(pos)) >= 0);
       }
-    if (pos < str.length())
+    if (pos < len)
       {
-       int start = pos;
-       while (++pos < str.length() && delim.indexOf(str.charAt(pos)) == -1)
-         {
-           /* empty */
-         }
-       return str.substring(start, pos);
+        int start = pos;
+        while (++pos < len && delim.indexOf(str.charAt(pos)) < 0);
+
+        return str.substring(start, pos);
       }
     throw new NoSuchElementException();
   }
@@ -201,9 +197,9 @@ public class StringTokenizer implements Enumeration
   /**
    * This does the same as hasMoreTokens. This is the
    * <code>Enumeration</code interface method.
-   * @return True, if the next call of nextElement() succeeds, false
-   * otherwise.  
-   * @see #hasMoreTokens
+   *
+   * @return true, if the next call of nextElement() will succeed
+   * @see #hasMoreTokens()
    */
   public boolean hasMoreElements()
   {
@@ -213,9 +209,10 @@ public class StringTokenizer implements Enumeration
   /**
    * This does the same as nextTokens. This is the
    * <code>Enumeration</code interface method.
-   * @return the next token with respect to the new delimiter characters.
-   * @exception NoSuchElementException if there are no more tokens.
-   * @see #nextToken
+   *
+   * @return the next token with respect to the current delimiter characters
+   * @throws NoSuchElementException if there are no more tokens
+   * @see #nextToken()
    */
   public Object nextElement() throws NoSuchElementException
   {
@@ -225,49 +222,47 @@ public class StringTokenizer implements Enumeration
   /**
    * This counts the number of remaining tokens in the string, with
    * respect to the current delimiter set.
-   * @return the number of times <code>nextTokens()</code> will
-   * succeed.  
-   * @see #nextToken
+   *
+   * @return the number of times <code>nextTokens()</code> will succeed
+   * @see #nextToken()
    */
   public int countTokens()
   {
     int count = 0;
     int delimiterCount = 0;
-    boolean tokenFound = false;                // Set when a non-delimiter is found
+    boolean tokenFound = false; // Set when a non-delimiter is found
     int tmpPos = pos;
 
     // Note for efficiency, we count up the delimiters rather than check
     // retDelims every time we encounter one.  That way, we can
     // just do the conditional once at the end of the method
-    while (tmpPos < str.length())
+    while (tmpPos < len)
       {
-       if (delim.indexOf(str.charAt(tmpPos++)) > -1)
-         {
-           if (tokenFound)
-             {
-               // Got to the end of a token
-               count++;
-               tokenFound = false;
-             }
-
-           delimiterCount++;           // Increment for this delimiter
-         }
-       else
-         {
-           tokenFound = true;
-
-           // Get to the end of the token
-           while (tmpPos < str.length()
-                  && delim.indexOf(str.charAt(tmpPos)) == -1)
-             ++tmpPos;
-         }
+        if (delim.indexOf(str.charAt(tmpPos++)) >= 0)
+          {
+            if (tokenFound)
+              {
+                // Got to the end of a token
+                count++;
+                tokenFound = false;
+              }
+            delimiterCount++; // Increment for this delimiter
+          }
+        else
+          {
+            tokenFound = true;
+            // Get to the end of the token
+            while (tmpPos < len
+                   && delim.indexOf(str.charAt(tmpPos)) < 0)
+              ++tmpPos;
+          }
       }
 
-    // Make sure to count the last token 
+    // Make sure to count the last token
     if (tokenFound)
       count++;
 
     // if counting delmiters add them into the token count
     return retDelims ? count + delimiterCount : count;
   }
-}
+} // class StringTokenizer
index cb841e5..1b32b67 100644 (file)
@@ -44,11 +44,11 @@ package java.util;
  * it should have been scheduled and cancel itself when no longer needed.
  * <p>
  * Example:
- * <code>
+ * <pre>
  *  Timer timer = new Timer();
  *  TimerTask task = new TimerTask() {
  *      public void run() {
- *      if (this.scheduledExecutionTime() < System.currentTimeMillis() + 500)
+ *      if (this.scheduledExecutionTime() &lt; System.currentTimeMillis() + 500)
  *          // Do something
  *      else
  *          // Complain: We are more then half a second late!
@@ -56,7 +56,7 @@ package java.util;
  *          this.cancel(); // This was our last execution
  *  };
  *  timer.scheduleAtFixedRate(task, 1000, 1000); // schedule every second
- * </code>
+ * </pre>
  * <p>
  * Note that a TimerTask object is a one shot object and can only given once
  * to a Timer. (The Timer will use the TimerTask object for bookkeeping,
index 8799682..dfa9bc6 100644 (file)
@@ -1,6 +1,6 @@
 /* TreeMap.java -- a class providing a basic Red-Black Tree data structure,
    mapping Object --> Object
-   Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -162,7 +162,7 @@ public class TreeMap extends AbstractMap
    *
    * @author Eric Blake <ebb9@email.byu.edu>
    */
-  private static final class Node extends BasicMapEntry
+  private static final class Node extends AbstractMap.BasicMapEntry
   {
     // All fields package visible for use by nested classes.
     /** The color of this node. */
@@ -623,8 +623,10 @@ public class TreeMap extends AbstractMap
     Node n = getNode(key);
     if (n == nil)
       return null;
+    // Note: removeNode can alter the contents of n, so save value now.
+    Object result = n.value;
     removeNode(n);
-    return n.value;
+    return result;
   }
 
   /**
@@ -1768,7 +1770,7 @@ public class TreeMap extends AbstractMap
             SubMap.this.clear();
           }
         };
-      return this.keys;
+      return this.values;
     }
   } // class SubMap  
 } // class TreeMap
index 8c55ae0..3431ac9 100644 (file)
@@ -1,4 +1,4 @@
-/* java.util.WeakHashMap -- a hashtable that keeps only weak references
+/* WeakHashMap -- a hashtable that keeps only weak references
    to its keys, allowing the virtual machine to reclaim them
    Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
 
@@ -43,41 +43,41 @@ import java.lang.ref.WeakReference;
 import java.lang.ref.ReferenceQueue;
 
 /**
- * A weak hash map has only weak references to the key.  This means
- * that it allows the key to be garbage collected if they are not used
- * otherwise.  If this happens, the weak hash map will eventually
- * remove the whole entry from this map. <br>
+ * A weak hash map has only weak references to the key. This means that it
+ * allows the key to be garbage collected if it is not used otherwise. If
+ * this happens, the entry will eventually disappear from the map,
+ * asynchronously.
  *
- * A weak hash map makes most sense, if the keys doesn't override the
- * <code>equals</code>-method: If there is no other reference to the
+ * <p>A weak hash map makes most sense when the keys doesn't override the
+ * <code>equals</code> method: If there is no other reference to the
  * key nobody can ever look up the key in this table and so the entry
- * can be removed.  This table also works, if the <code>equals</code>
- * method is overloaded, e.g. with Strings as keys, but you should be
- * prepared that some entries disappear spontaneously. <br>
+ * can be removed.  This table also works when the <code>equals</code>
+ * method is overloaded, such as String keys, but you should be prepared
+ * to deal with some entries disappearing spontaneously.
  *
- * You should also be prepared that this hash map behaves very
- * strange: The size of this map may spontaneously shrink (even if you
- * use a synchronized map and synchronize it); it behaves as if
- * another thread removes entries from this table without
- * synchronizations.  The entry set returned by <code>entrySet</code>
+ * <p>Other strange behaviors to be aware of: The size of this map may
+ * spontaneously shrink (even if you use a synchronized map and synchronize
+ * it); it behaves as if another thread removes entries from this table
+ * without synchronization.  The entry set returned by <code>entrySet</code>
  * has similar phenomenons: The size may spontaneously shrink, or an
- * entry, that was in the set before, suddenly disappears. <br>
+ * entry, that was in the set before, suddenly disappears.
  *
- * A weak hash map is not meant for caches; use a normal map, with
- * soft references as values instead, or try {@link LinkedHashMap}.  <br>
+ * <p>A weak hash map is not meant for caches; use a normal map, with
+ * soft references as values instead, or try {@link LinkedHashMap}.
  *
- * The weak hash map supports null values and null keys.  The null key
- * is never deleted from the map (except explictly of course).
- * The performance of the methods are similar to that of a hash map. <br>
+ * <p>The weak hash map supports null values and null keys.  The null key
+ * is never deleted from the map (except explictly of course). The
+ * performance of the methods are similar to that of a hash map.
  *
- * The value objects are strongly referenced by this table.  So if a
+ * <p>The value objects are strongly referenced by this table.  So if a
  * value object maintains a strong reference to the key (either direct
  * or indirect) the key will never be removed from this map.  According
  * to Sun, this problem may be fixed in a future release.  It is not
  * possible to do it with the jdk 1.2 reference model, though.
  *
  * @author Jochen Hoenicke
- * @author Eric Blake <ebb9@email.byu.edu>
+ * @author Eric Blake (ebb9@email.byu.edu)
+ *
  * @see HashMap
  * @see WeakReference
  * @see LinkedHashMap
@@ -86,6 +86,9 @@ import java.lang.ref.ReferenceQueue;
  */
 public class WeakHashMap extends AbstractMap implements Map
 {
+  // WARNING: WeakHashMap is a CORE class in the bootstrap cycle. See the
+  // comments in vm/reference/java/lang/Runtime for implications of this fact.
+
   /**
    * The default capacity for an instance of HashMap.
    * Sun's documentation mildly suggests that this (11) is the correct
@@ -148,7 +151,7 @@ public class WeakHashMap extends AbstractMap implements Map
   /**
    * The rounded product of the capacity (i.e. number of buckets) and
    * the load factor. When the number of elements exceeds the
-   * threshold, the HashMap calls <pre>rehash()</pre>.
+   * threshold, the HashMap calls <code>rehash()</code>.
    */
   private int threshold;
 
@@ -364,11 +367,11 @@ public class WeakHashMap extends AbstractMap implements Map
 
     /**
      * The slot of this entry. This should be
-     * <pre>
-     *  Math.abs(key.hashCode() % buckets.length)
-     * </pre>
+     * <code>Math.abs(key.hashCode() % buckets.length)</code>.
+     *
      * But since the key may be silently removed we have to remember
      * the slot number.
+     *
      * If this bucket was removed the slot is -1.  This marker will
      * prevent the bucket from being removed twice.
      */
@@ -503,9 +506,10 @@ public class WeakHashMap extends AbstractMap implements Map
   private final WeakEntrySet theEntrySet;
 
   /**
-   * The hash buckets.  These are linked lists.
+   * The hash buckets.  These are linked lists. Package visible for use in
+   * nested classes.
    */
-  private WeakBucket[] buckets;
+  WeakBucket[] buckets;
 
   /**
    * Creates a new weak hash map with default load factor and default
@@ -676,10 +680,12 @@ public class WeakHashMap extends AbstractMap implements Map
 
   /**
    * Removes a bucket from this hash map, if it wasn't removed before
-   * (e.g. one time through rehashing and one time through reference queue)
+   * (e.g. one time through rehashing and one time through reference queue).
+   * Package visible for use in nested classes.
+   *
    * @param bucket the bucket to remove.
    */
-  private void internalRemove(WeakBucket bucket)
+  void internalRemove(WeakBucket bucket)
   {
     int slot = bucket.slot;
     if (slot == -1)
@@ -870,4 +876,4 @@ public class WeakHashMap extends AbstractMap implements Map
     cleanQueue();
     return super.values();
   }
-}
+} // class WeakHashMap
index 46d4ada..0bf27bd 100644 (file)
@@ -430,7 +430,7 @@ public class Attributes implements Cloneable, Map
    * @returns the old value of the attribute name or null if it didn't exist
    * yet
    */
-  private String putValue(Name name, String value)
+  String putValue(Name name, String value)
   {
     return (String) put(name, value);
   }
index c8c0fba..54291c6 100644 (file)
@@ -1,5 +1,5 @@
-/* Attributes.java -- exception thrown to indicate an problem with a jar file
-   Copyright (C) 2000 Free Software Foundation, Inc.
+/* JarException.java -- thrown to indicate an problem with a jar file
+   Copyright (C) 2000, 2002 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -41,35 +41,34 @@ import java.util.zip.ZipException;
 
 /**
  * This exception is thrown to indicate an problem with a jar file.
- * It can be constructed with or without a descriptive message of the problem.
- * <p>
  * Note that none of the methods in the java.util.jar package actually declare
  * to throw this exception, most just declare that they throw an IOException
  * which is super class of JarException.
- * 
- * @since 1.2
+ *
  * @author Mark Wielaard (mark@klomp.org)
+ * @since 1.2
  */
-
 public class JarException extends ZipException
 {
-  // Constructors
+  /**
+   * Compatible with JDK 1.2+.
+   */
+  private static final long serialVersionUID = 7159778400963954473L;
 
   /**
    * Create a new JarException without a descriptive error message.
    */
   public JarException()
   {
-    super();
   }
 
   /**
    * Create a new JarException with a descriptive error message indicating
    * what went wrong. This message can later be retrieved by calling the
    * <code>getMessage()</code> method.
-   * @see java.lang.Throwable@getMessage()
    *
    * @param message The descriptive error message
+   * @see #getMessage()
    */
   public JarException(String message)
   {
index 20220b1..9a15376 100644 (file)
@@ -174,10 +174,19 @@ public class Manifest implements Cloneable
   private static void read_main_section(Attributes attr,
                                        BufferedReader br) throws IOException
   {
-    read_version_info(attr, br);
+    // According to the spec we should actually call read_version_info() here.
     read_attributes(attr, br);
+    // Explicitly set Manifest-Version attribute if not set in Main
+    // attributes of Manifest.
+    if (attr.getValue(Attributes.Name.MANIFEST_VERSION) == null)
+           attr.putValue(Attributes.Name.MANIFEST_VERSION, "0.0");
   }
 
+  /**
+   * Pedantic method that requires the next attribute in the Manifest to be
+   * the "Manifest-Version". This follows the Manifest spec closely but
+   * reject some jar Manifest files out in the wild.
+   */
   private static void read_version_info(Attributes attr,
                                        BufferedReader br) throws IOException
   {
@@ -185,7 +194,7 @@ public class Manifest implements Cloneable
     try
       {
        String value = expect_header(version_header, br);
-       attr.putValue(version_header, value);
+       attr.putValue(Attributes.Name.MANIFEST_VERSION, value);
       }
     catch (IOException ioe)
       {