summaryrefslogtreecommitdiff
path: root/libjava/classpath/java/util
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/java/util')
-rw-r--r--libjava/classpath/java/util/.cvsignore1
-rw-r--r--libjava/classpath/java/util/AbstractCollection.java482
-rw-r--r--libjava/classpath/java/util/AbstractList.java1204
-rw-r--r--libjava/classpath/java/util/AbstractMap.java819
-rw-r--r--libjava/classpath/java/util/AbstractSequentialList.java235
-rw-r--r--libjava/classpath/java/util/AbstractSet.java146
-rw-r--r--libjava/classpath/java/util/ArrayList.java603
-rw-r--r--libjava/classpath/java/util/Arrays.java4034
-rw-r--r--libjava/classpath/java/util/BitSet.java758
-rw-r--r--libjava/classpath/java/util/Calendar.java1620
-rw-r--r--libjava/classpath/java/util/Collection.java290
-rw-r--r--libjava/classpath/java/util/Collections.java7623
-rw-r--r--libjava/classpath/java/util/Comparator.java119
-rw-r--r--libjava/classpath/java/util/ConcurrentModificationException.java92
-rw-r--r--libjava/classpath/java/util/Currency.java471
-rw-r--r--libjava/classpath/java/util/Date.java1256
-rw-r--r--libjava/classpath/java/util/Dictionary.java136
-rw-r--r--libjava/classpath/java/util/DuplicateFormatFlagsException.java88
-rw-r--r--libjava/classpath/java/util/EmptyStackException.java69
-rw-r--r--libjava/classpath/java/util/EnumMap.java405
-rw-r--r--libjava/classpath/java/util/EnumSet.java518
-rw-r--r--libjava/classpath/java/util/Enumeration.java82
-rw-r--r--libjava/classpath/java/util/EventListener.java54
-rw-r--r--libjava/classpath/java/util/EventListenerProxy.java75
-rw-r--r--libjava/classpath/java/util/EventObject.java101
-rw-r--r--libjava/classpath/java/util/FormatFlagsConversionMismatchException.java111
-rw-r--r--libjava/classpath/java/util/Formattable.java92
-rw-r--r--libjava/classpath/java/util/FormattableFlags.java123
-rw-r--r--libjava/classpath/java/util/Formatter.java1498
-rw-r--r--libjava/classpath/java/util/FormatterClosedException.java60
-rw-r--r--libjava/classpath/java/util/GregorianCalendar.java1365
-rw-r--r--libjava/classpath/java/util/HashMap.java907
-rw-r--r--libjava/classpath/java/util/HashSet.java293
-rw-r--r--libjava/classpath/java/util/Hashtable.java1397
-rw-r--r--libjava/classpath/java/util/IdentityHashMap.java990
-rw-r--r--libjava/classpath/java/util/IllegalFormatCodePointException.java85
-rw-r--r--libjava/classpath/java/util/IllegalFormatConversionException.java110
-rw-r--r--libjava/classpath/java/util/IllegalFormatException.java75
-rw-r--r--libjava/classpath/java/util/IllegalFormatFlagsException.java86
-rw-r--r--libjava/classpath/java/util/IllegalFormatPrecisionException.java85
-rw-r--r--libjava/classpath/java/util/IllegalFormatWidthException.java84
-rw-r--r--libjava/classpath/java/util/InputMismatchException.java73
-rw-r--r--libjava/classpath/java/util/InvalidPropertiesFormatException.java72
-rw-r--r--libjava/classpath/java/util/Iterator.java87
-rw-r--r--libjava/classpath/java/util/LinkedHashMap.java500
-rw-r--r--libjava/classpath/java/util/LinkedHashSet.java159
-rw-r--r--libjava/classpath/java/util/LinkedList.java1260
-rw-r--r--libjava/classpath/java/util/List.java451
-rw-r--r--libjava/classpath/java/util/ListIterator.java170
-rw-r--r--libjava/classpath/java/util/ListResourceBundle.java140
-rw-r--r--libjava/classpath/java/util/Locale.java1029
-rw-r--r--libjava/classpath/java/util/Map.java338
-rw-r--r--libjava/classpath/java/util/MissingFormatArgumentException.java90
-rw-r--r--libjava/classpath/java/util/MissingFormatWidthException.java88
-rw-r--r--libjava/classpath/java/util/MissingResourceException.java105
-rw-r--r--libjava/classpath/java/util/NoSuchElementException.java88
-rw-r--r--libjava/classpath/java/util/Observable.java182
-rw-r--r--libjava/classpath/java/util/Observer.java60
-rw-r--r--libjava/classpath/java/util/PriorityQueue.java336
-rw-r--r--libjava/classpath/java/util/Properties.java822
-rw-r--r--libjava/classpath/java/util/PropertyPermission.java271
-rw-r--r--libjava/classpath/java/util/PropertyPermissionCollection.java166
-rw-r--r--libjava/classpath/java/util/PropertyResourceBundle.java171
-rw-r--r--libjava/classpath/java/util/Random.java429
-rw-r--r--libjava/classpath/java/util/RandomAccess.java64
-rw-r--r--libjava/classpath/java/util/ResourceBundle.java625
-rw-r--r--libjava/classpath/java/util/Scanner.java2223
-rw-r--r--libjava/classpath/java/util/ServiceConfigurationError.java94
-rw-r--r--libjava/classpath/java/util/ServiceLoader.java274
-rw-r--r--libjava/classpath/java/util/Set.java265
-rw-r--r--libjava/classpath/java/util/SimpleTimeZone.java1052
-rw-r--r--libjava/classpath/java/util/SortedMap.java173
-rw-r--r--libjava/classpath/java/util/SortedSet.java176
-rw-r--r--libjava/classpath/java/util/Stack.java161
-rw-r--r--libjava/classpath/java/util/StringTokenizer.java269
-rw-r--r--libjava/classpath/java/util/TimeZone.java1781
-rw-r--r--libjava/classpath/java/util/Timer.java704
-rw-r--r--libjava/classpath/java/util/TimerTask.java145
-rw-r--r--libjava/classpath/java/util/TooManyListenersException.java78
-rw-r--r--libjava/classpath/java/util/TreeMap.java3322
-rw-r--r--libjava/classpath/java/util/TreeSet.java641
-rw-r--r--libjava/classpath/java/util/UUID.java372
-rw-r--r--libjava/classpath/java/util/UnknownFormatConversionException.java86
-rw-r--r--libjava/classpath/java/util/UnknownFormatFlagsException.java88
-rw-r--r--libjava/classpath/java/util/Vector.java958
-rw-r--r--libjava/classpath/java/util/WeakHashMap.java880
-rw-r--r--libjava/classpath/java/util/concurrent/CopyOnWriteArrayList.java1463
-rw-r--r--libjava/classpath/java/util/jar/Attributes.java629
-rw-r--r--libjava/classpath/java/util/jar/JarEntry.java172
-rw-r--r--libjava/classpath/java/util/jar/JarException.java77
-rw-r--r--libjava/classpath/java/util/jar/JarFile.java981
-rw-r--r--libjava/classpath/java/util/jar/JarInputStream.java200
-rw-r--r--libjava/classpath/java/util/jar/JarOutputStream.java113
-rw-r--r--libjava/classpath/java/util/jar/Manifest.java213
-rw-r--r--libjava/classpath/java/util/jar/package.html47
-rw-r--r--libjava/classpath/java/util/logging/ConsoleHandler.java125
-rw-r--r--libjava/classpath/java/util/logging/ErrorManager.java193
-rw-r--r--libjava/classpath/java/util/logging/FileHandler.java665
-rw-r--r--libjava/classpath/java/util/logging/Filter.java64
-rw-r--r--libjava/classpath/java/util/logging/Formatter.java171
-rw-r--r--libjava/classpath/java/util/logging/Handler.java386
-rw-r--r--libjava/classpath/java/util/logging/Level.java416
-rw-r--r--libjava/classpath/java/util/logging/LogManager.java986
-rw-r--r--libjava/classpath/java/util/logging/LogRecord.java672
-rw-r--r--libjava/classpath/java/util/logging/Logger.java1193
-rw-r--r--libjava/classpath/java/util/logging/LoggingMXBean.java85
-rw-r--r--libjava/classpath/java/util/logging/LoggingPermission.java75
-rw-r--r--libjava/classpath/java/util/logging/MemoryHandler.java345
-rw-r--r--libjava/classpath/java/util/logging/SimpleFormatter.java131
-rw-r--r--libjava/classpath/java/util/logging/SocketHandler.java220
-rw-r--r--libjava/classpath/java/util/logging/StreamHandler.java521
-rw-r--r--libjava/classpath/java/util/logging/XMLFormatter.java389
-rw-r--r--libjava/classpath/java/util/logging/package.html46
-rw-r--r--libjava/classpath/java/util/package.html48
-rw-r--r--libjava/classpath/java/util/prefs/AbstractPreferences.java1392
-rw-r--r--libjava/classpath/java/util/prefs/BackingStoreException.java104
-rw-r--r--libjava/classpath/java/util/prefs/InvalidPreferencesFormatException.java116
-rw-r--r--libjava/classpath/java/util/prefs/NodeChangeEvent.java111
-rw-r--r--libjava/classpath/java/util/prefs/NodeChangeListener.java64
-rw-r--r--libjava/classpath/java/util/prefs/PreferenceChangeEvent.java125
-rw-r--r--libjava/classpath/java/util/prefs/PreferenceChangeListener.java60
-rw-r--r--libjava/classpath/java/util/prefs/Preferences.java696
-rw-r--r--libjava/classpath/java/util/prefs/PreferencesFactory.java65
-rw-r--r--libjava/classpath/java/util/prefs/package.html46
-rw-r--r--libjava/classpath/java/util/regex/MatchResult.java81
-rw-r--r--libjava/classpath/java/util/regex/Matcher.java611
-rw-r--r--libjava/classpath/java/util/regex/Pattern.java271
-rw-r--r--libjava/classpath/java/util/regex/PatternSyntaxException.java135
-rw-r--r--libjava/classpath/java/util/regex/package.html46
-rw-r--r--libjava/classpath/java/util/spi/CurrencyNameProvider.java100
-rw-r--r--libjava/classpath/java/util/spi/LocaleNameProvider.java135
-rw-r--r--libjava/classpath/java/util/spi/LocaleServiceProvider.java125
-rw-r--r--libjava/classpath/java/util/spi/TimeZoneNameProvider.java97
-rw-r--r--libjava/classpath/java/util/spi/package.html50
-rw-r--r--libjava/classpath/java/util/zip/Adler32.java205
-rw-r--r--libjava/classpath/java/util/zip/CRC32.java132
-rw-r--r--libjava/classpath/java/util/zip/CheckedInputStream.java135
-rw-r--r--libjava/classpath/java/util/zip/CheckedOutputStream.java100
-rw-r--r--libjava/classpath/java/util/zip/Checksum.java86
-rw-r--r--libjava/classpath/java/util/zip/DataFormatException.java71
-rw-r--r--libjava/classpath/java/util/zip/Deflater.java537
-rw-r--r--libjava/classpath/java/util/zip/DeflaterConstants.java78
-rw-r--r--libjava/classpath/java/util/zip/DeflaterEngine.java698
-rw-r--r--libjava/classpath/java/util/zip/DeflaterHuffman.java776
-rw-r--r--libjava/classpath/java/util/zip/DeflaterOutputStream.java198
-rw-r--r--libjava/classpath/java/util/zip/DeflaterPending.java53
-rw-r--r--libjava/classpath/java/util/zip/GZIPInputStream.java355
-rw-r--r--libjava/classpath/java/util/zip/GZIPOutputStream.java151
-rw-r--r--libjava/classpath/java/util/zip/Inflater.java726
-rw-r--r--libjava/classpath/java/util/zip/InflaterDynHeader.java203
-rw-r--r--libjava/classpath/java/util/zip/InflaterHuffmanTree.java217
-rw-r--r--libjava/classpath/java/util/zip/InflaterInputStream.java265
-rw-r--r--libjava/classpath/java/util/zip/OutputWindow.java175
-rw-r--r--libjava/classpath/java/util/zip/PendingBuffer.java199
-rw-r--r--libjava/classpath/java/util/zip/StreamManipulator.java215
-rw-r--r--libjava/classpath/java/util/zip/ZipConstants.java93
-rw-r--r--libjava/classpath/java/util/zip/ZipEntry.java457
-rw-r--r--libjava/classpath/java/util/zip/ZipException.java72
-rw-r--r--libjava/classpath/java/util/zip/ZipFile.java782
-rw-r--r--libjava/classpath/java/util/zip/ZipInputStream.java381
-rw-r--r--libjava/classpath/java/util/zip/ZipOutputStream.java440
-rw-r--r--libjava/classpath/java/util/zip/package.html47
162 files changed, 73063 insertions, 0 deletions
diff --git a/libjava/classpath/java/util/.cvsignore b/libjava/classpath/java/util/.cvsignore
new file mode 100644
index 000000000..d41ae8d81
--- /dev/null
+++ b/libjava/classpath/java/util/.cvsignore
@@ -0,0 +1 @@
+LocaleData.java
diff --git a/libjava/classpath/java/util/AbstractCollection.java b/libjava/classpath/java/util/AbstractCollection.java
new file mode 100644
index 000000000..d3406c230
--- /dev/null
+++ b/libjava/classpath/java/util/AbstractCollection.java
@@ -0,0 +1,482 @@
+/* AbstractCollection.java -- Abstract implementation of most of Collection
+ Copyright (C) 1998, 2000, 2001, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.lang.reflect.Array;
+
+/**
+ * A basic implementation of most of the methods in the Collection interface to
+ * make it easier to create a collection. To create an unmodifiable Collection,
+ * just subclass AbstractCollection and provide implementations of the
+ * iterator() and size() methods. The Iterator returned by iterator() need only
+ * provide implementations of hasNext() and next() (that is, it may throw an
+ * UnsupportedOperationException if remove() is called). To create a modifiable
+ * Collection, you must in addition provide an implementation of the
+ * add(Object) method and the Iterator returned by iterator() must provide an
+ * implementation of remove(). Other methods should be overridden if the
+ * backing data structure allows for a more efficient implementation. The
+ * precise implementation used by AbstractCollection is documented, so that
+ * subclasses can tell which methods could be implemented more efficiently.
+ * <p>
+ *
+ * The programmer should provide a no-argument constructor, and one that
+ * accepts another Collection, as recommended by the Collection interface.
+ * Unfortunately, there is no way to enforce this in Java.
+ *
+ * @author Original author unknown
+ * @author Bryce McKinlay
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @see Collection
+ * @see AbstractSet
+ * @see AbstractList
+ * @since 1.2
+ * @status updated to 1.4
+ */
+public abstract class AbstractCollection<E>
+ implements Collection<E>, Iterable<E>
+{
+ /**
+ * The main constructor, for use by subclasses.
+ */
+ protected AbstractCollection()
+ {
+ }
+
+ /**
+ * Return an Iterator over this collection. The iterator must provide the
+ * hasNext and next methods and should in addition provide remove if the
+ * collection is modifiable.
+ *
+ * @return an iterator
+ */
+ public abstract Iterator<E> iterator();
+
+ /**
+ * Return the number of elements in this collection. If there are more than
+ * Integer.MAX_VALUE elements, return Integer.MAX_VALUE.
+ *
+ * @return the size
+ */
+ public abstract int size();
+
+ /**
+ * Add an object to the collection (optional operation). This implementation
+ * always throws an UnsupportedOperationException - it should be
+ * overridden if the collection is to be modifiable. If the collection
+ * does not accept duplicates, simply return false. Collections may specify
+ * limitations on what may be added.
+ *
+ * @param o the object to add
+ * @return true if the add operation caused the Collection to change
+ * @throws UnsupportedOperationException if the add operation is not
+ * supported on this collection
+ * @throws NullPointerException if the collection does not support null
+ * @throws ClassCastException if the object is of the wrong type
+ * @throws IllegalArgumentException if some aspect of the object prevents
+ * it from being added
+ */
+ public boolean add(E o)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Add all the elements of a given collection to this collection (optional
+ * operation). This implementation obtains an Iterator over the given
+ * collection and iterates over it, adding each element with the
+ * add(Object) method (thus this method will fail with an
+ * UnsupportedOperationException if the add method does). The behavior is
+ * unspecified if the specified collection is modified during the iteration,
+ * including the special case of trying addAll(this) on a non-empty
+ * collection.
+ *
+ * @param c the collection to add the elements of to this collection
+ * @return true if the add operation caused the Collection to change
+ * @throws UnsupportedOperationException if the add operation is not
+ * supported on this collection
+ * @throws NullPointerException if the specified collection is null
+ * @throws ClassCastException if the type of any element in c is
+ * not a valid type for addition.
+ * @throws IllegalArgumentException if some aspect of any element
+ * in c prevents it being added.
+ * @throws NullPointerException if any element in c is null and this
+ * collection doesn't allow null values.
+ * @see #add(Object)
+ */
+ public boolean addAll(Collection<? extends E> c)
+ {
+ Iterator<? extends E> itr = c.iterator();
+ boolean modified = false;
+ int pos = c.size();
+ while (--pos >= 0)
+ modified |= add(itr.next());
+ return modified;
+ }
+
+ /**
+ * Remove all elements from the collection (optional operation). This
+ * implementation obtains an iterator over the collection and calls next
+ * and remove on it repeatedly (thus this method will fail with an
+ * UnsupportedOperationException if the Iterator's remove method does)
+ * until there are no more elements to remove.
+ * Many implementations will have a faster way of doing this.
+ *
+ * @throws UnsupportedOperationException if the Iterator returned by
+ * iterator does not provide an implementation of remove
+ * @see Iterator#remove()
+ */
+ public void clear()
+ {
+ Iterator<E> itr = iterator();
+ int pos = size();
+ while (--pos >= 0)
+ {
+ itr.next();
+ itr.remove();
+ }
+ }
+
+ /**
+ * Test whether this collection contains a given object. That is, if the
+ * collection has an element e such that (o == null ? e == null :
+ * o.equals(e)). This implementation obtains an iterator over the collection
+ * and iterates over it, testing each element for equality with the given
+ * object. If it is equal, true is returned. Otherwise false is returned when
+ * the end of the collection is reached.
+ *
+ * @param o the object to remove from this collection
+ * @return true if this collection contains an object equal to o
+ */
+ public boolean contains(Object o)
+ {
+ Iterator<E> itr = iterator();
+ int pos = size();
+ while (--pos >= 0)
+ if (equals(o, itr.next()))
+ return true;
+ return false;
+ }
+
+ /**
+ * Tests whether this collection contains all the elements in a given
+ * collection. This implementation iterates over the given collection,
+ * testing whether each element is contained in this collection. If any one
+ * is not, false is returned. Otherwise true is returned.
+ *
+ * @param c the collection to test against
+ * @return true if this collection contains all the elements in the given
+ * collection
+ * @throws NullPointerException if the given collection is null
+ * @see #contains(Object)
+ */
+ public boolean containsAll(Collection<?> c)
+ {
+ Iterator<?> itr = c.iterator();
+ int pos = c.size();
+ while (--pos >= 0)
+ if (!contains(itr.next()))
+ return false;
+ return true;
+ }
+
+ /**
+ * Test whether this collection is empty. This implementation returns
+ * size() == 0.
+ *
+ * @return true if this collection is empty.
+ * @see #size()
+ */
+ public boolean isEmpty()
+ {
+ return size() == 0;
+ }
+
+ /**
+ * Remove a single instance of an object from this collection (optional
+ * operation). That is, remove one element e such that
+ * <code>(o == null ? e == null : o.equals(e))</code>, if such an element
+ * exists. This implementation obtains an iterator over the collection
+ * and iterates over it, testing each element for equality with the given
+ * object. If it is equal, it is removed by the iterator's remove method
+ * (thus this method will fail with an UnsupportedOperationException if
+ * the Iterator's remove method does). After the first element has been
+ * removed, true is returned; if the end of the collection is reached, false
+ * is returned.
+ *
+ * @param o the object to remove from this collection
+ * @return true if the remove operation caused the Collection to change, or
+ * equivalently if the collection did contain o.
+ * @throws UnsupportedOperationException if this collection's Iterator
+ * does not support the remove method
+ * @see Iterator#remove()
+ */
+ public boolean remove(Object o)
+ {
+ Iterator<E> itr = iterator();
+ int pos = size();
+ while (--pos >= 0)
+ if (equals(o, itr.next()))
+ {
+ itr.remove();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Remove from this collection all its elements that are contained in a given
+ * collection (optional operation). This implementation iterates over this
+ * collection, and for each element tests if it is contained in the given
+ * collection. If so, it is removed by the Iterator's remove method (thus
+ * this method will fail with an UnsupportedOperationException if the
+ * Iterator's remove method does).
+ *
+ * @param c the collection to remove the elements of
+ * @return true if the remove operation caused the Collection to change
+ * @throws UnsupportedOperationException if this collection's Iterator
+ * does not support the remove method
+ * @throws NullPointerException if the collection, c, is null.
+ * @see Iterator#remove()
+ */
+ public boolean removeAll(Collection<?> c)
+ {
+ return removeAllInternal(c);
+ }
+
+ /**
+ * Remove from this collection all its elements that are contained in a given
+ * collection (optional operation). This implementation iterates over this
+ * collection, and for each element tests if it is contained in the given
+ * collection. If so, it is removed by the Iterator's remove method (thus
+ * this method will fail with an UnsupportedOperationException if the
+ * Iterator's remove method does). This method is necessary for ArrayList,
+ * which cannot publicly override removeAll but can optimize this call.
+ *
+ * @param c the collection to remove the elements of
+ * @return true if the remove operation caused the Collection to change
+ * @throws UnsupportedOperationException if this collection's Iterator
+ * does not support the remove method
+ * @throws NullPointerException if the collection, c, is null.
+ * @see Iterator#remove()
+ */
+ // Package visible for use throughout java.util.
+ boolean removeAllInternal(Collection<?> c)
+ {
+ Iterator<E> itr = iterator();
+ boolean modified = false;
+ int pos = size();
+ while (--pos >= 0)
+ if (c.contains(itr.next()))
+ {
+ itr.remove();
+ modified = true;
+ }
+ return modified;
+ }
+
+ /**
+ * Remove from this collection all its elements that are not contained in a
+ * given collection (optional operation). This implementation iterates over
+ * this collection, and for each element tests if it is contained in the
+ * given collection. If not, it is removed by the Iterator's remove method
+ * (thus this method will fail with an UnsupportedOperationException if
+ * the Iterator's remove method does).
+ *
+ * @param c the collection to retain the elements of
+ * @return true if the remove operation caused the Collection to change
+ * @throws UnsupportedOperationException if this collection's Iterator
+ * does not support the remove method
+ * @throws NullPointerException if the collection, c, is null.
+ * @see Iterator#remove()
+ */
+ public boolean retainAll(Collection<?> c)
+ {
+ return retainAllInternal(c);
+ }
+
+ /**
+ * Remove from this collection all its elements that are not contained in a
+ * given collection (optional operation). This implementation iterates over
+ * this collection, and for each element tests if it is contained in the
+ * given collection. If not, it is removed by the Iterator's remove method
+ * (thus this method will fail with an UnsupportedOperationException if
+ * the Iterator's remove method does). This method is necessary for
+ * ArrayList, which cannot publicly override retainAll but can optimize
+ * this call.
+ *
+ * @param c the collection to retain the elements of
+ * @return true if the remove operation caused the Collection to change
+ * @throws UnsupportedOperationException if this collection's Iterator
+ * does not support the remove method
+ * @throws NullPointerException if the collection, c, is null.
+ * @see Iterator#remove()
+ */
+ // Package visible for use throughout java.util.
+ boolean retainAllInternal(Collection<?> c)
+ {
+ Iterator<E> itr = iterator();
+ boolean modified = false;
+ int pos = size();
+ while (--pos >= 0)
+ if (!c.contains(itr.next()))
+ {
+ itr.remove();
+ modified = true;
+ }
+ return modified;
+ }
+
+ /**
+ * Return an array containing the elements of this collection. This
+ * implementation creates an Object array of size size() and then iterates
+ * over the collection, setting each element of the array from the value
+ * returned by the iterator. The returned array is safe, and is not backed
+ * by the collection.
+ *
+ * @return an array containing the elements of this collection
+ */
+ public Object[] toArray()
+ {
+ Iterator<E> itr = iterator();
+ int size = size();
+ Object[] a = new Object[size];
+ for (int pos = 0; pos < size; pos++)
+ a[pos] = itr.next();
+ return a;
+ }
+
+ /**
+ * Copy the collection into a given array if it will fit, or into a
+ * dynamically created array of the same run-time type as the given array if
+ * not. If there is space remaining in the array, the first element after the
+ * end of the collection is set to null (this is only useful if the
+ * collection is known to contain no null elements, however). This
+ * implementation first tests whether the given array is large enough to hold
+ * all the elements of the collection. If not, the reflection API is used to
+ * allocate a new array of the same run-time type. Next an iterator is
+ * obtained over the collection and the elements are placed in the array as
+ * they are returned by the iterator. Finally the first spare element, if
+ * any, of the array is set to null, and the created array is returned.
+ * The returned array is safe; it is not backed by the collection. Note that
+ * null may not mark the last element, if the collection allows null
+ * elements.
+ *
+ * @param a the array to copy into, or of the correct run-time type
+ * @return the array that was produced
+ * @throws NullPointerException if the given array is null
+ * @throws ArrayStoreException if the type of the array precludes holding
+ * one of the elements of the Collection
+ */
+ public <T> T[] toArray(T[] a)
+ {
+ int size = size();
+ if (a.length < size)
+ a = (T[]) Array.newInstance(a.getClass().getComponentType(),
+ size);
+ else if (a.length > size)
+ a[size] = null;
+
+ Iterator<E> itr = iterator();
+ for (int pos = 0; pos < size; pos++)
+ a[pos] = (T) (itr.next());
+ return a;
+ }
+
+ /**
+ * Creates a String representation of the Collection. The string returned is
+ * of the form "[a, b, ...]" where a and b etc are the results of calling
+ * toString on the elements of the collection. This implementation obtains an
+ * Iterator over the Collection and adds each element to a StringBuffer as it
+ * is returned by the iterator. "<this>" is inserted when the collection
+ * contains itself (only works for direct containment, not for collections
+ * inside collections).
+ *
+ * @return a String representation of the Collection
+ */
+ public String toString()
+ {
+ Iterator itr = iterator();
+ CPStringBuilder r = new CPStringBuilder("[");
+ boolean hasNext = itr.hasNext();
+ while (hasNext)
+ {
+ Object o = itr.next();
+ if (o == this)
+ r.append("<this>");
+ else
+ r.append(o);
+ hasNext = itr.hasNext();
+ if (hasNext)
+ r.append(", ");
+ }
+ r.append("]");
+ return r.toString();
+ }
+
+ /**
+ * Compare two objects according to Collection semantics.
+ *
+ * @param o1 the first object
+ * @param o2 the second object
+ * @return o1 == null ? o2 == null : o1.equals(o2)
+ */
+ // Package visible for use throughout java.util.
+ // It may be inlined since it is final.
+ static final boolean equals(Object o1, Object o2)
+ {
+ return o1 == null ? o2 == null : o1.equals(o2);
+ }
+
+ /**
+ * Hash an object according to Collection semantics.
+ *
+ * @param o the object to hash
+ * @return o1 == null ? 0 : o1.hashCode()
+ */
+ // Package visible for use throughout java.util.
+ // It may be inlined since it is final.
+ static final int hashCode(Object o)
+ {
+ return o == null ? 0 : o.hashCode();
+ }
+}
diff --git a/libjava/classpath/java/util/AbstractList.java b/libjava/classpath/java/util/AbstractList.java
new file mode 100644
index 000000000..13ee28a04
--- /dev/null
+++ b/libjava/classpath/java/util/AbstractList.java
@@ -0,0 +1,1204 @@
+/* AbstractList.java -- Abstract implementation of most of List
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * A basic implementation of most of the methods in the List interface to make
+ * it easier to create a List based on a random-access data structure. If
+ * the list is sequential (such as a linked list), use AbstractSequentialList.
+ * To create an unmodifiable list, it is only necessary to override the
+ * size() and get(int) methods (this contrasts with all other abstract
+ * collection classes which require an iterator to be provided). To make the
+ * list modifiable, the set(int, Object) method should also be overridden, and
+ * to make the list resizable, the add(int, Object) and remove(int) methods
+ * should be overridden too. Other methods should be overridden if the
+ * backing data structure allows for a more efficient implementation.
+ * The precise implementation used by AbstractList is documented, so that
+ * subclasses can tell which methods could be implemented more efficiently.
+ * <p>
+ *
+ * As recommended by Collection and List, the subclass should provide at
+ * least a no-argument and a Collection constructor. This class is not
+ * synchronized.
+ *
+ * @author Original author unknown
+ * @author Bryce McKinlay
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Collection
+ * @see List
+ * @see AbstractSequentialList
+ * @see AbstractCollection
+ * @see ListIterator
+ * @since 1.2
+ * @status updated to 1.4
+ */
+public abstract class AbstractList<E>
+ extends AbstractCollection<E>
+ implements List<E>
+{
+ /**
+ * A count of the number of structural modifications that have been made to
+ * the list (that is, insertions and removals). Structural modifications
+ * are ones which change the list size or affect how iterations would
+ * behave. This field is available for use by Iterator and ListIterator,
+ * in order to throw a {@link ConcurrentModificationException} in response
+ * to the next operation on the iterator. This <i>fail-fast</i> behavior
+ * saves the user from many subtle bugs otherwise possible from concurrent
+ * modification during iteration.
+ * <p>
+ *
+ * To make lists fail-fast, increment this field by just 1 in the
+ * <code>add(int, Object)</code> and <code>remove(int)</code> methods.
+ * Otherwise, this field may be ignored.
+ */
+ protected transient int modCount;
+
+ /**
+ * The main constructor, for use by subclasses.
+ */
+ protected AbstractList()
+ {
+ }
+
+ /**
+ * Returns the elements at the specified position in the list.
+ *
+ * @param index the element to return
+ * @return the element at that position
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ */
+ public abstract E get(int index);
+
+ /**
+ * Insert an element into the list at a given position (optional operation).
+ * This shifts all existing elements from that position to the end one
+ * index to the right. This version of add has no return, since it is
+ * assumed to always succeed if there is no exception. This implementation
+ * always throws UnsupportedOperationException, and must be overridden to
+ * make a modifiable List. If you want fail-fast iterators, be sure to
+ * increment modCount when overriding this.
+ *
+ * @param index the location to insert the item
+ * @param o the object to insert
+ * @throws UnsupportedOperationException if this list does not support the
+ * add operation
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ * @throws ClassCastException if o cannot be added to this list due to its
+ * type
+ * @throws IllegalArgumentException if o cannot be added to this list for
+ * some other reason
+ * @see #modCount
+ */
+ public void add(int index, E o)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Add an element to the end of the list (optional operation). If the list
+ * imposes restraints on what can be inserted, such as no null elements,
+ * this should be documented. This implementation calls
+ * <code>add(size(), o);</code>, and will fail if that version does.
+ *
+ * @param o the object to add
+ * @return true, as defined by Collection for a modified list
+ * @throws UnsupportedOperationException if this list does not support the
+ * add operation
+ * @throws ClassCastException if o cannot be added to this list due to its
+ * type
+ * @throws IllegalArgumentException if o cannot be added to this list for
+ * some other reason
+ * @see #add(int, Object)
+ */
+ public boolean add(E o)
+ {
+ add(size(), o);
+ return true;
+ }
+
+ /**
+ * Insert the contents of a collection into the list at a given position
+ * (optional operation). Shift all elements at that position to the right
+ * by the number of elements inserted. This operation is undefined if
+ * this list is modified during the operation (for example, if you try
+ * to insert a list into itself). This implementation uses the iterator of
+ * the collection, repeatedly calling add(int, Object); this will fail
+ * if add does. This can often be made more efficient.
+ *
+ * @param index the location to insert the collection
+ * @param c the collection to insert
+ * @return true if the list was modified by this action, that is, if c is
+ * non-empty
+ * @throws UnsupportedOperationException if this list does not support the
+ * addAll operation
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ * @throws ClassCastException if some element of c cannot be added to this
+ * list due to its type
+ * @throws IllegalArgumentException if some element of c cannot be added
+ * to this list for some other reason
+ * @throws NullPointerException if the specified collection is null
+ * @see #add(int, Object)
+ */
+ public boolean addAll(int index, Collection<? extends E> c)
+ {
+ Iterator<? extends E> itr = c.iterator();
+ int size = c.size();
+ for (int pos = size; pos > 0; pos--)
+ add(index++, itr.next());
+ return size > 0;
+ }
+
+ /**
+ * Clear the list, such that a subsequent call to isEmpty() would return
+ * true (optional operation). This implementation calls
+ * <code>removeRange(0, size())</code>, so it will fail unless remove
+ * or removeRange is overridden.
+ *
+ * @throws UnsupportedOperationException if this list does not support the
+ * clear operation
+ * @see #remove(int)
+ * @see #removeRange(int, int)
+ */
+ public void clear()
+ {
+ removeRange(0, size());
+ }
+
+ /**
+ * Test whether this list is equal to another object. A List is defined to be
+ * equal to an object if and only if that object is also a List, and the two
+ * lists have the same sequence. Two lists l1 and l2 are equal if and only
+ * if <code>l1.size() == l2.size()</code>, and for every integer n between 0
+ * and <code>l1.size() - 1</code> inclusive, <code>l1.get(n) == null ?
+ * l2.get(n) == null : l1.get(n).equals(l2.get(n))</code>.
+ * <p>
+ *
+ * This implementation returns true if the object is this, or false if the
+ * object is not a List. Otherwise, it iterates over both lists (with
+ * iterator()), returning false if two elements compare false or one list
+ * is shorter, and true if the iteration completes successfully.
+ *
+ * @param o the object to test for equality with this list
+ * @return true if o is equal to this list
+ * @see Object#equals(Object)
+ * @see #hashCode()
+ */
+ public boolean equals(Object o)
+ {
+ if (o == this)
+ return true;
+ if (! (o instanceof List))
+ return false;
+ int size = size();
+ if (size != ((List) o).size())
+ return false;
+
+ Iterator<E> itr1 = iterator();
+ Iterator itr2 = ((List) o).iterator();
+
+ while (--size >= 0)
+ if (! equals(itr1.next(), itr2.next()))
+ return false;
+ return true;
+ }
+
+ /**
+ * 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)
+ */
+ public int hashCode()
+ {
+ int hashCode = 1;
+ Iterator<E> itr = iterator();
+ int pos = size();
+ while (--pos >= 0)
+ hashCode = 31 * hashCode + hashCode(itr.next());
+ return hashCode;
+ }
+
+ /**
+ * Obtain the first index at which a given object is to be found in this
+ * list. This implementation follows a listIterator() until a match is found,
+ * or returns -1 if the list end is reached.
+ *
+ * @param o the object to search for
+ * @return the least integer n such that <code>o == null ? get(n) == null :
+ * o.equals(get(n))</code>, or -1 if there is no such index
+ */
+ public int indexOf(Object o)
+ {
+ ListIterator<E> itr = listIterator();
+ int size = size();
+ for (int pos = 0; pos < size; pos++)
+ if (equals(o, itr.next()))
+ return pos;
+ return -1;
+ }
+
+ /**
+ * Obtain an Iterator over this list, whose sequence is the list order.
+ * This implementation uses size(), get(int), and remove(int) of the
+ * backing list, and does not support remove unless the list does. This
+ * implementation is fail-fast if you correctly maintain modCount.
+ * Also, this implementation is specified by Sun to be distinct from
+ * listIterator, although you could easily implement it as
+ * <code>return listIterator(0)</code>.
+ *
+ * @return an Iterator over the elements of this list, in order
+ * @see #modCount
+ */
+ public Iterator<E> iterator()
+ {
+ // Bah, Sun's implementation forbids using listIterator(0).
+ return new Iterator<E>()
+ {
+ private int pos = 0;
+ private int size = size();
+ private int last = -1;
+ private int knownMod = modCount;
+
+ // This will get inlined, since it is private.
+ /**
+ * Checks for modifications made to the list from
+ * elsewhere while iteration is in progress.
+ *
+ * @throws ConcurrentModificationException if the
+ * list has been modified elsewhere.
+ */
+ private void checkMod()
+ {
+ if (knownMod != modCount)
+ throw new ConcurrentModificationException();
+ }
+
+ /**
+ * Tests to see if there are any more objects to
+ * return.
+ *
+ * @return True if the end of the list has not yet been
+ * reached.
+ */
+ public boolean hasNext()
+ {
+ return pos < size;
+ }
+
+ /**
+ * Retrieves the next object from the list.
+ *
+ * @return The next object.
+ * @throws NoSuchElementException if there are
+ * no more objects to retrieve.
+ * @throws ConcurrentModificationException if the
+ * list has been modified elsewhere.
+ */
+ public E next()
+ {
+ checkMod();
+ if (pos == size)
+ throw new NoSuchElementException();
+ last = pos;
+ return get(pos++);
+ }
+
+ /**
+ * Removes the last object retrieved by <code>next()</code>
+ * from the list, if the list supports object removal.
+ *
+ * @throws ConcurrentModificationException if the list
+ * has been modified elsewhere.
+ * @throws IllegalStateException if the iterator is positioned
+ * before the start of the list or the last object has already
+ * been removed.
+ * @throws UnsupportedOperationException if the list does
+ * not support removing elements.
+ */
+ public void remove()
+ {
+ checkMod();
+ if (last < 0)
+ throw new IllegalStateException();
+ AbstractList.this.remove(last);
+ pos--;
+ size--;
+ last = -1;
+ knownMod = modCount;
+ }
+ };
+ }
+
+ /**
+ * Obtain the last index at which a given object is to be found in this
+ * list. This implementation grabs listIterator(size()), then searches
+ * backwards for a match or returns -1.
+ *
+ * @return the greatest integer n such that <code>o == null ? get(n) == null
+ * : o.equals(get(n))</code>, or -1 if there is no such index
+ */
+ public int lastIndexOf(Object o)
+ {
+ int pos = size();
+ ListIterator<E> itr = listIterator(pos);
+ while (--pos >= 0)
+ if (equals(o, itr.previous()))
+ return pos;
+ return -1;
+ }
+
+ /**
+ * Obtain a ListIterator over this list, starting at the beginning. This
+ * implementation returns listIterator(0).
+ *
+ * @return a ListIterator over the elements of this list, in order, starting
+ * at the beginning
+ */
+ public ListIterator<E> listIterator()
+ {
+ return listIterator(0);
+ }
+
+ /**
+ * Obtain a ListIterator over this list, starting at a given position.
+ * A first call to next() would return the same as get(index), and a
+ * first call to previous() would return the same as get(index - 1).
+ * <p>
+ *
+ * This implementation uses size(), get(int), set(int, Object),
+ * add(int, Object), and remove(int) of the backing list, and does not
+ * support remove, set, or add unless the list does. This implementation
+ * is fail-fast if you correctly maintain modCount.
+ *
+ * @param index the position, between 0 and size() inclusive, to begin the
+ * iteration from
+ * @return a ListIterator over the elements of this list, in order, starting
+ * at index
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ * @see #modCount
+ */
+ public ListIterator<E> listIterator(final int index)
+ {
+ if (index < 0 || index > size())
+ throw new IndexOutOfBoundsException("Index: " + index + ", Size:"
+ + size());
+
+ return new ListIterator<E>()
+ {
+ private int knownMod = modCount;
+ private int position = index;
+ private int lastReturned = -1;
+ private int size = size();
+
+ // This will get inlined, since it is private.
+ /**
+ * Checks for modifications made to the list from
+ * elsewhere while iteration is in progress.
+ *
+ * @throws ConcurrentModificationException if the
+ * list has been modified elsewhere.
+ */
+ private void checkMod()
+ {
+ if (knownMod != modCount)
+ throw new ConcurrentModificationException();
+ }
+
+ /**
+ * Tests to see if there are any more objects to
+ * return.
+ *
+ * @return True if the end of the list has not yet been
+ * reached.
+ */
+ public boolean hasNext()
+ {
+ return position < size;
+ }
+
+ /**
+ * Tests to see if there are objects prior to the
+ * current position in the list.
+ *
+ * @return True if objects exist prior to the current
+ * position of the iterator.
+ */
+ public boolean hasPrevious()
+ {
+ return position > 0;
+ }
+
+ /**
+ * Retrieves the next object from the list.
+ *
+ * @return The next object.
+ * @throws NoSuchElementException if there are no
+ * more objects to retrieve.
+ * @throws ConcurrentModificationException if the
+ * list has been modified elsewhere.
+ */
+ public E next()
+ {
+ checkMod();
+ if (position == size)
+ throw new NoSuchElementException();
+ lastReturned = position;
+ return get(position++);
+ }
+
+ /**
+ * Retrieves the previous object from the list.
+ *
+ * @return The next object.
+ * @throws NoSuchElementException if there are no
+ * previous objects to retrieve.
+ * @throws ConcurrentModificationException if the
+ * list has been modified elsewhere.
+ */
+ public E previous()
+ {
+ checkMod();
+ if (position == 0)
+ throw new NoSuchElementException();
+ lastReturned = --position;
+ return get(lastReturned);
+ }
+
+ /**
+ * Returns the index of the next element in the
+ * list, which will be retrieved by <code>next()</code>
+ *
+ * @return The index of the next element.
+ */
+ public int nextIndex()
+ {
+ return position;
+ }
+
+ /**
+ * Returns the index of the previous element in the
+ * list, which will be retrieved by <code>previous()</code>
+ *
+ * @return The index of the previous element.
+ */
+ public int previousIndex()
+ {
+ return position - 1;
+ }
+
+ /**
+ * Removes the last object retrieved by <code>next()</code>
+ * or <code>previous()</code> from the list, if the list
+ * supports object removal.
+ *
+ * @throws IllegalStateException if the iterator is positioned
+ * before the start of the list or the last object has already
+ * been removed.
+ * @throws UnsupportedOperationException if the list does
+ * not support removing elements.
+ * @throws ConcurrentModificationException if the list
+ * has been modified elsewhere.
+ */
+ public void remove()
+ {
+ checkMod();
+ if (lastReturned < 0)
+ throw new IllegalStateException();
+ AbstractList.this.remove(lastReturned);
+ size--;
+ position = lastReturned;
+ lastReturned = -1;
+ knownMod = modCount;
+ }
+
+ /**
+ * Replaces the last object retrieved by <code>next()</code>
+ * or <code>previous</code> with o, if the list supports object
+ * replacement and an add or remove operation has not already
+ * been performed.
+ *
+ * @throws IllegalStateException if the iterator is positioned
+ * before the start of the list or the last object has already
+ * been removed.
+ * @throws UnsupportedOperationException if the list doesn't support
+ * the addition or removal of elements.
+ * @throws ClassCastException if the type of o is not a valid type
+ * for this list.
+ * @throws IllegalArgumentException if something else related to o
+ * prevents its addition.
+ * @throws ConcurrentModificationException if the list
+ * has been modified elsewhere.
+ */
+ public void set(E o)
+ {
+ checkMod();
+ if (lastReturned < 0)
+ throw new IllegalStateException();
+ AbstractList.this.set(lastReturned, o);
+ }
+
+ /**
+ * Adds the supplied object before the element that would be returned
+ * by a call to <code>next()</code>, if the list supports addition.
+ *
+ * @param o The object to add to the list.
+ * @throws UnsupportedOperationException if the list doesn't support
+ * the addition of new elements.
+ * @throws ClassCastException if the type of o is not a valid type
+ * for this list.
+ * @throws IllegalArgumentException if something else related to o
+ * prevents its addition.
+ * @throws ConcurrentModificationException if the list
+ * has been modified elsewhere.
+ */
+ public void add(E o)
+ {
+ checkMod();
+ AbstractList.this.add(position++, o);
+ size++;
+ lastReturned = -1;
+ knownMod = modCount;
+ }
+ };
+ }
+
+ /**
+ * Remove the element at a given position in this list (optional operation).
+ * Shifts all remaining elements to the left to fill the gap. This
+ * implementation always throws an UnsupportedOperationException.
+ * If you want fail-fast iterators, be sure to increment modCount when
+ * overriding this.
+ *
+ * @param index the position within the list of the object to remove
+ * @return the object that was removed
+ * @throws UnsupportedOperationException if this list does not support the
+ * remove operation
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ * @see #modCount
+ */
+ public E remove(int index)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Remove a subsection of the list. This is called by the clear and
+ * removeRange methods of the class which implements subList, which are
+ * difficult for subclasses to override directly. Therefore, this method
+ * should be overridden instead by the more efficient implementation, if one
+ * exists. Overriding this can reduce quadratic efforts to constant time
+ * in some cases!
+ * <p>
+ *
+ * This implementation first checks for illegal or out of range arguments. It
+ * then obtains a ListIterator over the list using listIterator(fromIndex).
+ * It then calls next() and remove() on this iterator repeatedly, toIndex -
+ * fromIndex times.
+ *
+ * @param fromIndex the index, inclusive, to remove from.
+ * @param toIndex the index, exclusive, to remove to.
+ * @throws UnsupportedOperationException if the list does
+ * not support removing elements.
+ */
+ protected void removeRange(int fromIndex, int toIndex)
+ {
+ ListIterator<E> itr = listIterator(fromIndex);
+ for (int index = fromIndex; index < toIndex; index++)
+ {
+ itr.next();
+ itr.remove();
+ }
+ }
+
+ /**
+ * Replace an element of this list with another object (optional operation).
+ * This implementation always throws an UnsupportedOperationException.
+ *
+ * @param index the position within this list of the element to be replaced
+ * @param o the object to replace it with
+ * @return the object that was replaced
+ * @throws UnsupportedOperationException if this list does not support the
+ * set operation
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ * @throws ClassCastException if o cannot be added to this list due to its
+ * type
+ * @throws IllegalArgumentException if o cannot be added to this list for
+ * some other reason
+ */
+ public E set(int index, E o)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Obtain a List view of a subsection of this list, from fromIndex
+ * (inclusive) to toIndex (exclusive). If the two indices are equal, the
+ * sublist is empty. The returned list should be modifiable if and only
+ * if this list is modifiable. Changes to the returned list should be
+ * reflected in this list. If this list is structurally modified in
+ * any way other than through the returned list, the result of any subsequent
+ * operations on the returned list is undefined.
+ * <p>
+ *
+ * This implementation returns a subclass of AbstractList. It stores, in
+ * private fields, the offset and size of the sublist, and the expected
+ * modCount of the backing list. If the backing list implements RandomAccess,
+ * the sublist will also.
+ * <p>
+ *
+ * The subclass's <code>set(int, Object)</code>, <code>get(int)</code>,
+ * <code>add(int, Object)</code>, <code>remove(int)</code>,
+ * <code>addAll(int, Collection)</code> and
+ * <code>removeRange(int, int)</code> methods all delegate to the
+ * corresponding methods on the backing abstract list, after
+ * bounds-checking the index and adjusting for the offset. The
+ * <code>addAll(Collection c)</code> method merely returns addAll(size, c).
+ * The <code>listIterator(int)</code> method returns a "wrapper object"
+ * over a list iterator on the backing list, which is created with the
+ * corresponding method on the backing list. The <code>iterator()</code>
+ * method merely returns listIterator(), and the <code>size()</code> method
+ * merely returns the subclass's size field.
+ * <p>
+ *
+ * All methods first check to see if the actual modCount of the backing
+ * list is equal to its expected value, and throw a
+ * ConcurrentModificationException if it is not.
+ *
+ * @param fromIndex the index that the returned list should start from
+ * (inclusive)
+ * @param toIndex the index that the returned list should go to (exclusive)
+ * @return a List backed by a subsection of this list
+ * @throws IndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; size()
+ * @throws IllegalArgumentException if fromIndex &gt; toIndex
+ * @see ConcurrentModificationException
+ * @see RandomAccess
+ */
+ public List<E> subList(int fromIndex, int toIndex)
+ {
+ // This follows the specification of AbstractList, but is inconsistent
+ // with the one in List. Don't you love Sun's inconsistencies?
+ if (fromIndex > toIndex)
+ throw new IllegalArgumentException(fromIndex + " > " + toIndex);
+ if (fromIndex < 0 || toIndex > size())
+ throw new IndexOutOfBoundsException();
+
+ if (this instanceof RandomAccess)
+ return new RandomAccessSubList<E>(this, fromIndex, toIndex);
+ return new SubList<E>(this, fromIndex, toIndex);
+ }
+
+ /**
+ * This class follows the implementation requirements set forth in
+ * {@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)
+ */
+ private static class SubList<E> extends AbstractList<E>
+ {
+ // Package visible, for use by iterator.
+ /** The original list. */
+ final AbstractList<E> backingList;
+ /** The index of the first element of the sublist. */
+ final int offset;
+ /** The size of the sublist. */
+ int size;
+
+ /**
+ * Construct the sublist.
+ *
+ * @param backing the list this comes from
+ * @param fromIndex the lower bound, inclusive
+ * @param toIndex the upper bound, exclusive
+ */
+ SubList(AbstractList<E> backing, int fromIndex, int toIndex)
+ {
+ backingList = backing;
+ modCount = backing.modCount;
+ offset = fromIndex;
+ size = toIndex - fromIndex;
+ }
+
+ /**
+ * This method checks the two modCount fields to ensure that there has
+ * not been a concurrent modification, returning if all is okay.
+ *
+ * @throws ConcurrentModificationException if the backing list has been
+ * modified externally to this sublist
+ */
+ // This can be inlined. Package visible, for use by iterator.
+ void checkMod()
+ {
+ if (modCount != backingList.modCount)
+ throw new ConcurrentModificationException();
+ }
+
+ /**
+ * This method checks that a value is between 0 and size (inclusive). If
+ * it is not, an exception is thrown.
+ *
+ * @param index the value to check
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ */
+ // This will get inlined, since it is private.
+ private void checkBoundsInclusive(int index)
+ {
+ if (index < 0 || index > size)
+ throw new IndexOutOfBoundsException("Index: " + index + ", Size:"
+ + size);
+ }
+
+ /**
+ * This method checks that a value is between 0 (inclusive) and size
+ * (exclusive). If it is not, an exception is thrown.
+ *
+ * @param index the value to check
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ */
+ // This will get inlined, since it is private.
+ private void checkBoundsExclusive(int index)
+ {
+ if (index < 0 || index >= size)
+ throw new IndexOutOfBoundsException("Index: " + index + ", Size:"
+ + size);
+ }
+
+ /**
+ * Specified by AbstractList.subList to return the private field size.
+ *
+ * @return the sublist size
+ * @throws ConcurrentModificationException if the backing list has been
+ * modified externally to this sublist
+ */
+ public int size()
+ {
+ checkMod();
+ return size;
+ }
+
+ /**
+ * Specified by AbstractList.subList to delegate to the backing list.
+ *
+ * @param index the location to modify
+ * @param o the new value
+ * @return the old value
+ * @throws ConcurrentModificationException if the backing list has been
+ * modified externally to this sublist
+ * @throws UnsupportedOperationException if the backing list does not
+ * support the set operation
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ * @throws ClassCastException if o cannot be added to the backing list due
+ * to its type
+ * @throws IllegalArgumentException if o cannot be added to the backing list
+ * for some other reason
+ */
+ public E set(int index, E o)
+ {
+ checkMod();
+ checkBoundsExclusive(index);
+ return backingList.set(index + offset, o);
+ }
+
+ /**
+ * Specified by AbstractList.subList to delegate to the backing list.
+ *
+ * @param index the location to get from
+ * @return the object at that location
+ * @throws ConcurrentModificationException if the backing list has been
+ * modified externally to this sublist
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ */
+ public E get(int index)
+ {
+ checkMod();
+ checkBoundsExclusive(index);
+ return backingList.get(index + offset);
+ }
+
+ /**
+ * Specified by AbstractList.subList to delegate to the backing list.
+ *
+ * @param index the index to insert at
+ * @param o the object to add
+ * @throws ConcurrentModificationException if the backing list has been
+ * modified externally to this sublist
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ * @throws UnsupportedOperationException if the backing list does not
+ * support the add operation.
+ * @throws ClassCastException if o cannot be added to the backing list due
+ * to its type.
+ * @throws IllegalArgumentException if o cannot be added to the backing
+ * list for some other reason.
+ */
+ public void add(int index, E o)
+ {
+ checkMod();
+ checkBoundsInclusive(index);
+ backingList.add(index + offset, o);
+ size++;
+ modCount = backingList.modCount;
+ }
+
+ /**
+ * Specified by AbstractList.subList to delegate to the backing list.
+ *
+ * @param index the index to remove
+ * @return the removed object
+ * @throws ConcurrentModificationException if the backing list has been
+ * modified externally to this sublist
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ * @throws UnsupportedOperationException if the backing list does not
+ * support the remove operation
+ */
+ public E remove(int index)
+ {
+ checkMod();
+ checkBoundsExclusive(index);
+ E o = backingList.remove(index + offset);
+ size--;
+ modCount = backingList.modCount;
+ return o;
+ }
+
+ /**
+ * Specified by AbstractList.subList to delegate to the backing list.
+ * This does no bounds checking, as it assumes it will only be called
+ * by trusted code like clear() which has already checked the bounds.
+ *
+ * @param fromIndex the lower bound, inclusive
+ * @param toIndex the upper bound, exclusive
+ * @throws ConcurrentModificationException if the backing list has been
+ * modified externally to this sublist
+ * @throws UnsupportedOperationException if the backing list does
+ * not support removing elements.
+ */
+ protected void removeRange(int fromIndex, int toIndex)
+ {
+ checkMod();
+
+ backingList.removeRange(offset + fromIndex, offset + toIndex);
+ size -= toIndex - fromIndex;
+ modCount = backingList.modCount;
+ }
+
+ /**
+ * Specified by AbstractList.subList to delegate to the backing list.
+ *
+ * @param index the location to insert at
+ * @param c the collection to insert
+ * @return true if this list was modified, in other words, c is non-empty
+ * @throws ConcurrentModificationException if the backing list has been
+ * modified externally to this sublist
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ * @throws UnsupportedOperationException if this list does not support the
+ * addAll operation
+ * @throws ClassCastException if some element of c cannot be added to this
+ * list due to its type
+ * @throws IllegalArgumentException if some element of c cannot be added
+ * to this list for some other reason
+ * @throws NullPointerException if the specified collection is null
+ */
+ public boolean addAll(int index, Collection<? extends E> c)
+ {
+ checkMod();
+ checkBoundsInclusive(index);
+ int csize = c.size();
+ boolean result = backingList.addAll(offset + index, c);
+ size += csize;
+ modCount = backingList.modCount;
+ return result;
+ }
+
+ /**
+ * Specified by AbstractList.subList to return addAll(size, c).
+ *
+ * @param c the collection to insert
+ * @return true if this list was modified, in other words, c is non-empty
+ * @throws ConcurrentModificationException if the backing list has been
+ * modified externally to this sublist
+ * @throws UnsupportedOperationException if this list does not support the
+ * addAll operation
+ * @throws ClassCastException if some element of c cannot be added to this
+ * list due to its type
+ * @throws IllegalArgumentException if some element of c cannot be added
+ * to this list for some other reason
+ * @throws NullPointerException if the specified collection is null
+ */
+ public boolean addAll(Collection<? extends E> c)
+ {
+ return addAll(size, c);
+ }
+
+ /**
+ * Specified by AbstractList.subList to return listIterator().
+ *
+ * @return an iterator over the sublist
+ */
+ public Iterator<E> iterator()
+ {
+ return listIterator();
+ }
+
+ /**
+ * Specified by AbstractList.subList to return a wrapper around the
+ * backing list's iterator.
+ *
+ * @param index the start location of the iterator
+ * @return a list iterator over the sublist
+ * @throws ConcurrentModificationException if the backing list has been
+ * modified externally to this sublist
+ * @throws IndexOutOfBoundsException if the value is out of range
+ */
+ public ListIterator<E> listIterator(final int index)
+ {
+ checkMod();
+ checkBoundsInclusive(index);
+
+ return new ListIterator<E>()
+ {
+ private final ListIterator<E> i
+ = backingList.listIterator(index + offset);
+ private int position = index;
+
+ /**
+ * Tests to see if there are any more objects to
+ * return.
+ *
+ * @return True if the end of the list has not yet been
+ * reached.
+ */
+ public boolean hasNext()
+ {
+ return position < size;
+ }
+
+ /**
+ * Tests to see if there are objects prior to the
+ * current position in the list.
+ *
+ * @return True if objects exist prior to the current
+ * position of the iterator.
+ */
+ public boolean hasPrevious()
+ {
+ return position > 0;
+ }
+
+ /**
+ * Retrieves the next object from the list.
+ *
+ * @return The next object.
+ * @throws NoSuchElementException if there are no
+ * more objects to retrieve.
+ * @throws ConcurrentModificationException if the
+ * list has been modified elsewhere.
+ */
+ public E next()
+ {
+ if (position == size)
+ throw new NoSuchElementException();
+ position++;
+ return i.next();
+ }
+
+ /**
+ * Retrieves the previous object from the list.
+ *
+ * @return The next object.
+ * @throws NoSuchElementException if there are no
+ * previous objects to retrieve.
+ * @throws ConcurrentModificationException if the
+ * list has been modified elsewhere.
+ */
+ public E previous()
+ {
+ if (position == 0)
+ throw new NoSuchElementException();
+ position--;
+ return i.previous();
+ }
+
+ /**
+ * Returns the index of the next element in the
+ * list, which will be retrieved by <code>next()</code>
+ *
+ * @return The index of the next element.
+ */
+ public int nextIndex()
+ {
+ return i.nextIndex() - offset;
+ }
+
+ /**
+ * Returns the index of the previous element in the
+ * list, which will be retrieved by <code>previous()</code>
+ *
+ * @return The index of the previous element.
+ */
+ public int previousIndex()
+ {
+ return i.previousIndex() - offset;
+ }
+
+ /**
+ * Removes the last object retrieved by <code>next()</code>
+ * from the list, if the list supports object removal.
+ *
+ * @throws IllegalStateException if the iterator is positioned
+ * before the start of the list or the last object has already
+ * been removed.
+ * @throws UnsupportedOperationException if the list does
+ * not support removing elements.
+ */
+ public void remove()
+ {
+ i.remove();
+ size--;
+ position = nextIndex();
+ modCount = backingList.modCount;
+ }
+
+
+ /**
+ * Replaces the last object retrieved by <code>next()</code>
+ * or <code>previous</code> with o, if the list supports object
+ * replacement and an add or remove operation has not already
+ * been performed.
+ *
+ * @throws IllegalStateException if the iterator is positioned
+ * before the start of the list or the last object has already
+ * been removed.
+ * @throws UnsupportedOperationException if the list doesn't support
+ * the addition or removal of elements.
+ * @throws ClassCastException if the type of o is not a valid type
+ * for this list.
+ * @throws IllegalArgumentException if something else related to o
+ * prevents its addition.
+ * @throws ConcurrentModificationException if the list
+ * has been modified elsewhere.
+ */
+ public void set(E o)
+ {
+ i.set(o);
+ }
+
+ /**
+ * Adds the supplied object before the element that would be returned
+ * by a call to <code>next()</code>, if the list supports addition.
+ *
+ * @param o The object to add to the list.
+ * @throws UnsupportedOperationException if the list doesn't support
+ * the addition of new elements.
+ * @throws ClassCastException if the type of o is not a valid type
+ * for this list.
+ * @throws IllegalArgumentException if something else related to o
+ * prevents its addition.
+ * @throws ConcurrentModificationException if the list
+ * has been modified elsewhere.
+ */
+ public void add(E o)
+ {
+ i.add(o);
+ size++;
+ position++;
+ modCount = backingList.modCount;
+ }
+
+ // Here is the reason why the various modCount fields are mostly
+ // ignored in this wrapper listIterator.
+ // If the backing listIterator is failfast, then the following holds:
+ // Using any other method on this list will call a corresponding
+ // method on the backing list *after* the backing listIterator
+ // is created, which will in turn cause a ConcurrentModException
+ // when this listIterator comes to use the backing one. So it is
+ // implicitly failfast.
+ // If the backing listIterator is NOT failfast, then the whole of
+ // this list isn't failfast, because the modCount field of the
+ // backing list is not valid. It would still be *possible* to
+ // make the iterator failfast wrt modifications of the sublist
+ // only, but somewhat pointless when the list can be changed under
+ // us.
+ // Either way, no explicit handling of modCount is needed.
+ // However modCount = backingList.modCount must be executed in add
+ // and remove, and size must also be updated in these two methods,
+ // since they do not go through the corresponding methods of the subList.
+ };
+ }
+ } // class SubList
+
+ /**
+ * This class is a RandomAccess version of SubList, as required by
+ * {@link AbstractList#subList(int, int)}.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static final class RandomAccessSubList<E> extends SubList<E>
+ implements RandomAccess
+ {
+ /**
+ * Construct the sublist.
+ *
+ * @param backing the list this comes from
+ * @param fromIndex the lower bound, inclusive
+ * @param toIndex the upper bound, exclusive
+ */
+ RandomAccessSubList(AbstractList<E> backing, int fromIndex, int toIndex)
+ {
+ super(backing, fromIndex, toIndex);
+ }
+ } // class RandomAccessSubList
+
+} // class AbstractList
diff --git a/libjava/classpath/java/util/AbstractMap.java b/libjava/classpath/java/util/AbstractMap.java
new file mode 100644
index 000000000..e0e35712a
--- /dev/null
+++ b/libjava/classpath/java/util/AbstractMap.java
@@ -0,0 +1,819 @@
+/* AbstractMap.java -- Abstract implementation of most of Map
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.io.Serializable;
+
+/**
+ * An abstract implementation of Map to make it easier to create your own
+ * implementations. In order to create an unmodifiable Map, subclass
+ * AbstractMap and implement the <code>entrySet</code> (usually via an
+ * AbstractSet). To make it modifiable, also implement <code>put</code>,
+ * and have <code>entrySet().iterator()</code> support <code>remove</code>.
+ * <p>
+ *
+ * It is recommended that classes which extend this support at least the
+ * no-argument constructor, and a constructor which accepts another Map.
+ * Further methods in this class may be overridden if you have a more
+ * efficient implementation.
+ *
+ * @author Original author unknown
+ * @author Bryce McKinlay
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Map
+ * @see Collection
+ * @see HashMap
+ * @see LinkedHashMap
+ * @see TreeMap
+ * @see WeakHashMap
+ * @see IdentityHashMap
+ * @since 1.2
+ * @status updated to 1.4
+ */
+public abstract class AbstractMap<K, V> implements Map<K, V>
+{
+ /**
+ * A class containing an immutable key and value. The
+ * implementation of {@link Entry#setValue(V)} for this class
+ * simply throws an {@link UnsupportedOperationException},
+ * thus preventing changes being made. This is useful when
+ * a static thread-safe view of a map is required.
+ *
+ * @since 1.6
+ */
+ public static class SimpleImmutableEntry<K, V>
+ implements Entry<K, V>, Serializable
+ {
+ /**
+ * Compatible with JDK 1.6
+ */
+ private static final long serialVersionUID = 7138329143949025153L;
+
+ K key;
+ V value;
+
+ public SimpleImmutableEntry(K key, V value)
+ {
+ this.key = key;
+ this.value = value;
+ }
+
+ public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry)
+ {
+ this(entry.getKey(), entry.getValue());
+ }
+
+ public K getKey()
+ {
+ return key;
+ }
+
+ public V getValue()
+ {
+ return value;
+ }
+
+ public V setValue(V value)
+ {
+ throw new UnsupportedOperationException("setValue not supported on immutable entry");
+ }
+ }
+
+/** An "enum" of iterator types. */
+ // Package visible for use by subclasses.
+ static final int KEYS = 0,
+ VALUES = 1,
+ ENTRIES = 2;
+
+ /**
+ * The cache for {@link #keySet()}.
+ */
+ // Package visible for use by subclasses.
+ Set<K> keys;
+
+ /**
+ * The cache for {@link #values()}.
+ */
+ // Package visible for use by subclasses.
+ Collection<V> values;
+
+ /**
+ * The main constructor, for use by subclasses.
+ */
+ protected AbstractMap()
+ {
+ }
+
+ /**
+ * Returns a set view of the mappings in this Map. Each element in the
+ * set must be an implementation of Map.Entry. The set is backed by
+ * the map, so that changes in one show up in the other. Modifications
+ * made while an iterator is in progress cause undefined behavior. If
+ * the set supports removal, these methods must be valid:
+ * <code>Iterator.remove</code>, <code>Set.remove</code>,
+ * <code>removeAll</code>, <code>retainAll</code>, and <code>clear</code>.
+ * Element addition is not supported via this set.
+ *
+ * @return the entry set
+ * @see Map.Entry
+ */
+ public abstract Set<Map.Entry<K, V>> entrySet();
+
+ /**
+ * Remove all entries from this Map (optional operation). This default
+ * implementation calls entrySet().clear(). NOTE: If the entry set does
+ * not permit clearing, then this will fail, too. Subclasses often
+ * override this for efficiency. Your implementation of entrySet() should
+ * not call <code>AbstractMap.clear</code> unless you want an infinite loop.
+ *
+ * @throws UnsupportedOperationException if <code>entrySet().clear()</code>
+ * does not support clearing.
+ * @see Set#clear()
+ */
+ public void clear()
+ {
+ entrySet().clear();
+ }
+
+ /**
+ * Create a shallow copy of this Map, no keys or values are copied. The
+ * default implementation simply calls <code>super.clone()</code>.
+ *
+ * @return the shallow clone
+ * @throws CloneNotSupportedException if a subclass is not Cloneable
+ * @see Cloneable
+ * @see Object#clone()
+ */
+ protected Object clone() throws CloneNotSupportedException
+ {
+ AbstractMap<K, V> copy = (AbstractMap<K, V>) super.clone();
+ // Clear out the caches; they are stale.
+ copy.keys = null;
+ copy.values = null;
+ return copy;
+ }
+
+ /**
+ * Returns true if this contains a mapping for the given key. This
+ * implementation does a linear search, O(n), over the
+ * <code>entrySet()</code>, returning <code>true</code> if a match
+ * is found, <code>false</code> if the iteration ends. Many subclasses
+ * can implement this more efficiently.
+ *
+ * @param key the key to search for
+ * @return true if the map contains the key
+ * @throws NullPointerException if key is <code>null</code> but the map
+ * does not permit null keys
+ * @see #containsValue(Object)
+ */
+ public boolean containsKey(Object key)
+ {
+ Iterator<Map.Entry<K, V>> entries = entrySet().iterator();
+ int pos = size();
+ while (--pos >= 0)
+ if (equals(key, entries.next().getKey()))
+ return true;
+ return false;
+ }
+
+ /**
+ * Returns true if this contains at least one mapping with the given value.
+ * This implementation does a linear search, O(n), over the
+ * <code>entrySet()</code>, returning <code>true</code> if a match
+ * is found, <code>false</code> if the iteration ends. A match is
+ * defined as a value, v, where <code>(value == null ? v == null :
+ * value.equals(v))</code>. Subclasses are unlikely to implement
+ * this more efficiently.
+ *
+ * @param value the value to search for
+ * @return true if the map contains the value
+ * @see #containsKey(Object)
+ */
+ public boolean containsValue(Object value)
+ {
+ Iterator<Map.Entry<K, V>> entries = entrySet().iterator();
+ int pos = size();
+ while (--pos >= 0)
+ if (equals(value, entries.next().getValue()))
+ return true;
+ return false;
+ }
+
+ /**
+ * Compares the specified object with this map for equality. Returns
+ * <code>true</code> if the other object is a Map with the same mappings,
+ * that is,<br>
+ * <code>o instanceof Map && entrySet().equals(((Map) o).entrySet();</code>
+ *
+ * @param o the object to be compared
+ * @return true if the object equals this map
+ * @see Set#equals(Object)
+ */
+ public boolean equals(Object o)
+ {
+ return (o == this
+ || (o instanceof Map
+ && entrySet().equals(((Map<K, V>) o).entrySet())));
+ }
+
+ /**
+ * Returns the value mapped by the given key. Returns <code>null</code> if
+ * there is no mapping. However, in Maps that accept null values, you
+ * must rely on <code>containsKey</code> to determine if a mapping exists.
+ * This iteration takes linear time, searching entrySet().iterator() of
+ * the key. Many implementations override this method.
+ *
+ * @param key the key to look up
+ * @return the value associated with the key, or null if key not in map
+ * @throws NullPointerException if this map does not accept null keys
+ * @see #containsKey(Object)
+ */
+ public V get(Object key)
+ {
+ Iterator<Map.Entry<K, V>> entries = entrySet().iterator();
+ int pos = size();
+ while (--pos >= 0)
+ {
+ Map.Entry<K, V> entry = entries.next();
+ if (equals(key, entry.getKey()))
+ return entry.getValue();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the hash code for this map. As defined in Map, this is the sum
+ * of all hashcodes for each Map.Entry object in entrySet, or basically
+ * entrySet().hashCode().
+ *
+ * @return the hash code
+ * @see Map.Entry#hashCode()
+ * @see Set#hashCode()
+ */
+ public int hashCode()
+ {
+ return entrySet().hashCode();
+ }
+
+ /**
+ * Returns true if the map contains no mappings. This is implemented by
+ * <code>size() == 0</code>.
+ *
+ * @return true if the map is empty
+ * @see #size()
+ */
+ public boolean isEmpty()
+ {
+ return size() == 0;
+ }
+
+ /**
+ * Returns a set view of this map's keys. The set is backed by the map,
+ * so changes in one show up in the other. Modifications while an iteration
+ * is in progress produce undefined behavior. The set supports removal
+ * if entrySet() does, but does not support element addition.
+ * <p>
+ *
+ * This implementation creates an AbstractSet, where the iterator wraps
+ * the entrySet iterator, size defers to the Map's size, and contains
+ * defers to the Map's containsKey. The set is created on first use, and
+ * returned on subsequent uses, although since no synchronization occurs,
+ * there is a slight possibility of creating two sets.
+ *
+ * @return a Set view of the keys
+ * @see Set#iterator()
+ * @see #size()
+ * @see #containsKey(Object)
+ * @see #values()
+ */
+ public Set<K> keySet()
+ {
+ if (keys == null)
+ keys = new AbstractSet<K>()
+ {
+ /**
+ * Retrieves the number of keys in the backing map.
+ *
+ * @return The number of keys.
+ */
+ public int size()
+ {
+ return AbstractMap.this.size();
+ }
+
+ /**
+ * Returns true if the backing map contains the
+ * supplied key.
+ *
+ * @param key The key to search for.
+ * @return True if the key was found, false otherwise.
+ */
+ public boolean contains(Object key)
+ {
+ return containsKey(key);
+ }
+
+ /**
+ * Returns an iterator which iterates over the keys
+ * in the backing map, using a wrapper around the
+ * iterator returned by <code>entrySet()</code>.
+ *
+ * @return An iterator over the keys.
+ */
+ public Iterator<K> iterator()
+ {
+ return new Iterator<K>()
+ {
+ /**
+ * The iterator returned by <code>entrySet()</code>.
+ */
+ private final Iterator<Map.Entry<K, V>> map_iterator
+ = entrySet().iterator();
+
+ /**
+ * Returns true if a call to <code>next()</code> will
+ * return another key.
+ *
+ * @return True if the iterator has not yet reached
+ * the last key.
+ */
+ public boolean hasNext()
+ {
+ return map_iterator.hasNext();
+ }
+
+ /**
+ * Returns the key from the next entry retrieved
+ * by the underlying <code>entrySet()</code> iterator.
+ *
+ * @return The next key.
+ */
+ public K next()
+ {
+ return map_iterator.next().getKey();
+ }
+
+ /**
+ * Removes the map entry which has a key equal
+ * to that returned by the last call to
+ * <code>next()</code>.
+ *
+ * @throws UnsupportedOperationException if the
+ * map doesn't support removal.
+ */
+ public void remove()
+ {
+ map_iterator.remove();
+ }
+ };
+ }
+ };
+ return keys;
+ }
+
+ /**
+ * Associates the given key to the given value (optional operation). If the
+ * map already contains the key, its value is replaced. This implementation
+ * simply throws an UnsupportedOperationException. Be aware that in a map
+ * that permits <code>null</code> values, a null return does not always
+ * imply that the mapping was created.
+ *
+ * @param key the key to map
+ * @param value the value to be mapped
+ * @return the previous value of the key, or null if there was no mapping
+ * @throws UnsupportedOperationException if the operation is not supported
+ * @throws ClassCastException if the key or value is of the wrong type
+ * @throws IllegalArgumentException if something about this key or value
+ * prevents it from existing in this map
+ * @throws NullPointerException if the map forbids null keys or values
+ * @see #containsKey(Object)
+ */
+ public V put(K key, V value)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Copies all entries of the given map to this one (optional operation). If
+ * the map already contains a key, its value is replaced. This implementation
+ * simply iterates over the map's entrySet(), calling <code>put</code>,
+ * so it is not supported if puts are not.
+ *
+ * @param m the mapping to load into this map
+ * @throws UnsupportedOperationException if the operation is not supported
+ * by this map.
+ * @throws ClassCastException if a key or value is of the wrong type for
+ * adding to this map.
+ * @throws IllegalArgumentException if something about a key or value
+ * prevents it from existing in this map.
+ * @throws NullPointerException if the map forbids null keys or values.
+ * @throws NullPointerException if <code>m</code> is null.
+ * @see #put(Object, Object)
+ */
+ public void putAll(Map<? extends K, ? extends V> m)
+ {
+ // FIXME: bogus circumlocution.
+ Iterator entries2 = m.entrySet().iterator();
+ Iterator<Map.Entry<? extends K, ? extends V>> entries
+ = (Iterator<Map.Entry<? extends K, ? extends V>>) entries2;
+ int pos = m.size();
+ while (--pos >= 0)
+ {
+ Map.Entry<? extends K, ? extends V> entry = entries.next();
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ /**
+ * Removes the mapping for this key if present (optional operation). This
+ * implementation iterates over the entrySet searching for a matching
+ * key, at which point it calls the iterator's <code>remove</code> method.
+ * It returns the result of <code>getValue()</code> on the entry, if found,
+ * or null if no entry is found. Note that maps which permit null values
+ * may also return null if the key was removed. If the entrySet does not
+ * support removal, this will also fail. This is O(n), so many
+ * implementations override it for efficiency.
+ *
+ * @param key the key to remove
+ * @return the value the key mapped to, or null if not present.
+ * Null may also be returned if null values are allowed
+ * in the map and the value of this mapping is null.
+ * @throws UnsupportedOperationException if deletion is unsupported
+ * @see Iterator#remove()
+ */
+ public V remove(Object key)
+ {
+ Iterator<Map.Entry<K, V>> entries = entrySet().iterator();
+ int pos = size();
+ while (--pos >= 0)
+ {
+ Map.Entry<K, V> entry = entries.next();
+ if (equals(key, entry.getKey()))
+ {
+ // Must get the value before we remove it from iterator.
+ V r = entry.getValue();
+ entries.remove();
+ return r;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the number of key-value mappings in the map. If there are more
+ * than Integer.MAX_VALUE mappings, return Integer.MAX_VALUE. This is
+ * implemented as <code>entrySet().size()</code>.
+ *
+ * @return the number of mappings
+ * @see Set#size()
+ */
+ public int size()
+ {
+ return entrySet().size();
+ }
+
+ /**
+ * Returns a String representation of this map. This is a listing of the
+ * map entries (which are specified in Map.Entry as being
+ * <code>getKey() + "=" + getValue()</code>), separated by a comma and
+ * space (", "), and surrounded by braces ('{' and '}'). This implementation
+ * uses a StringBuffer and iterates over the entrySet to build the String.
+ * Note that this can fail with an exception if underlying keys or
+ * values complete abruptly in toString().
+ *
+ * @return a String representation
+ * @see Map.Entry#toString()
+ */
+ public String toString()
+ {
+ Iterator<Map.Entry<K, V>> entries = entrySet().iterator();
+ CPStringBuilder r = new CPStringBuilder("{");
+ for (int pos = size(); pos > 0; pos--)
+ {
+ Map.Entry<K, V> entry = entries.next();
+ r.append(entry.getKey());
+ r.append('=');
+ r.append(entry.getValue());
+ if (pos > 1)
+ r.append(", ");
+ }
+ r.append("}");
+ return r.toString();
+ }
+
+ /**
+ * Returns a collection or bag view of this map's values. The collection
+ * is backed by the map, so changes in one show up in the other.
+ * Modifications while an iteration is in progress produce undefined
+ * behavior. The collection supports removal if entrySet() does, but
+ * does not support element addition.
+ * <p>
+ *
+ * This implementation creates an AbstractCollection, where the iterator
+ * wraps the entrySet iterator, size defers to the Map's size, and contains
+ * defers to the Map's containsValue. The collection is created on first
+ * use, and returned on subsequent uses, although since no synchronization
+ * occurs, there is a slight possibility of creating two collections.
+ *
+ * @return a Collection view of the values
+ * @see Collection#iterator()
+ * @see #size()
+ * @see #containsValue(Object)
+ * @see #keySet()
+ */
+ public Collection<V> values()
+ {
+ if (values == null)
+ values = new AbstractCollection<V>()
+ {
+ /**
+ * Returns the number of values stored in
+ * the backing map.
+ *
+ * @return The number of values.
+ */
+ public int size()
+ {
+ return AbstractMap.this.size();
+ }
+
+ /**
+ * Returns true if the backing map contains
+ * the supplied value.
+ *
+ * @param value The value to search for.
+ * @return True if the value was found, false otherwise.
+ */
+ public boolean contains(Object value)
+ {
+ return containsValue(value);
+ }
+
+ /**
+ * Returns an iterator which iterates over the
+ * values in the backing map, by using a wrapper
+ * around the iterator returned by <code>entrySet()</code>.
+ *
+ * @return An iterator over the values.
+ */
+ public Iterator<V> iterator()
+ {
+ return new Iterator<V>()
+ {
+ /**
+ * The iterator returned by <code>entrySet()</code>.
+ */
+ private final Iterator<Map.Entry<K, V>> map_iterator
+ = entrySet().iterator();
+
+ /**
+ * Returns true if a call to <code>next()</call> will
+ * return another value.
+ *
+ * @return True if the iterator has not yet reached
+ * the last value.
+ */
+ public boolean hasNext()
+ {
+ return map_iterator.hasNext();
+ }
+
+ /**
+ * Returns the value from the next entry retrieved
+ * by the underlying <code>entrySet()</code> iterator.
+ *
+ * @return The next value.
+ */
+ public V next()
+ {
+ return map_iterator.next().getValue();
+ }
+
+ /**
+ * Removes the map entry which has a key equal
+ * to that returned by the last call to
+ * <code>next()</code>.
+ *
+ * @throws UnsupportedOperationException if the
+ * map doesn't support removal.
+ */
+ public void remove()
+ {
+ map_iterator.remove();
+ }
+ };
+ }
+ };
+ return values;
+ }
+
+ /**
+ * Compare two objects according to Collection semantics.
+ *
+ * @param o1 the first object
+ * @param o2 the second object
+ * @return o1 == o2 || (o1 != null && o1.equals(o2))
+ */
+ // Package visible for use throughout java.util.
+ // It may be inlined since it is final.
+ static final boolean equals(Object o1, Object o2)
+ {
+ return o1 == o2 || (o1 != null && o1.equals(o2));
+ }
+
+ /**
+ * Hash an object according to Collection semantics.
+ *
+ * @param o the object to hash
+ * @return o1 == null ? 0 : o1.hashCode()
+ */
+ // Package visible for use throughout java.util.
+ // It may be inlined since it is final.
+ static final int hashCode(Object o)
+ {
+ 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)
+ *
+ * @since 1.6
+ */
+ public static class SimpleEntry<K, V> implements Entry<K, V>, Serializable
+ {
+
+ /**
+ * Compatible with JDK 1.6
+ */
+ private static final long serialVersionUID = -8499721149061103585L;
+
+ /**
+ * The key. Package visible for direct manipulation.
+ */
+ K key;
+
+ /**
+ * The value. Package visible for direct manipulation.
+ */
+ V value;
+
+ /**
+ * Basic constructor initializes the fields.
+ * @param newKey the key
+ * @param newValue the value
+ */
+ public SimpleEntry(K newKey, V newValue)
+ {
+ key = newKey;
+ value = newValue;
+ }
+
+ public SimpleEntry(Entry<? extends K, ? extends V> entry)
+ {
+ this(entry.getKey(), entry.getValue());
+ }
+
+ /**
+ * 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:<br>
+ * <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 boolean equals(Object o)
+ {
+ if (! (o instanceof Map.Entry))
+ return false;
+ // Optimize for our own entries.
+ if (o instanceof SimpleEntry)
+ {
+ SimpleEntry e = (SimpleEntry) 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 K 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 V 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:<br>
+ * <pre>(getKey() == null ? 0 : getKey().hashCode())
+ * ^ (getValue() == null ? 0 : getValue().hashCode())</pre>
+ *
+ * @return the hash code
+ */
+ public 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.
+ * @throws UnsupportedOperationException if the map doesn't support
+ * <code>put()</code>.
+ * @throws ClassCastException if the value is of a type unsupported
+ * by the map.
+ * @throws IllegalArgumentException if something else about this
+ * value prevents it being stored in the map.
+ */
+ public V setValue(V newVal)
+ {
+ V 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 String toString()
+ {
+ return key + "=" + value;
+ }
+ } // class SimpleEntry
+
+
+}
diff --git a/libjava/classpath/java/util/AbstractSequentialList.java b/libjava/classpath/java/util/AbstractSequentialList.java
new file mode 100644
index 000000000..81b0714e1
--- /dev/null
+++ b/libjava/classpath/java/util/AbstractSequentialList.java
@@ -0,0 +1,235 @@
+/* AbstractSequentialList.java -- List implementation for sequential access
+ Copyright (C) 1998, 1999, 2000, 2001, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Abstract superclass to make it easier to implement the List interface when
+ * backed by a sequential-access store, such as a linked list. For random
+ * access data, use AbstractList. This class implements the random access
+ * methods (<code>get</code>, <code>set</code>, <code>add</code>, and
+ * <code>remove</code>) atop the list iterator, opposite of AbstractList's
+ * approach of implementing the iterator atop random access.
+ * <p>
+ *
+ * To implement a list, you need an implementation for <code>size()</code>
+ * and <code>listIterator</code>. With just <code>hasNext</code>,
+ * <code>next</code>, <code>hasPrevious</code>, <code>previous</code>,
+ * <code>nextIndex</code>, and <code>previousIndex</code>, you have an
+ * unmodifiable list. For a modifiable one, add <code>set</code>, and for
+ * a variable-size list, add <code>add</code> and <code>remove</code>.
+ * <p>
+ *
+ * The programmer should provide a no-argument constructor, and one that
+ * accepts another Collection, as recommended by the Collection interface.
+ * Unfortunately, there is no way to enforce this in Java.
+ *
+ * @author Original author unknown
+ * @author Bryce McKinlay
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Collection
+ * @see List
+ * @see AbstractList
+ * @see AbstractCollection
+ * @see ListIterator
+ * @see LinkedList
+ * @since 1.2
+ * @status updated to 1.4
+ */
+public abstract class AbstractSequentialList<E> extends AbstractList<E>
+{
+ /**
+ * The main constructor, for use by subclasses.
+ */
+ protected AbstractSequentialList()
+ {
+ }
+
+ /**
+ * Returns a ListIterator over the list, starting from position index.
+ * Subclasses must provide an implementation of this method.
+ *
+ * @param index the starting position of the list
+ * @return the list iterator
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ */
+ public abstract ListIterator<E> listIterator(int index);
+
+ /**
+ * Insert an element into the list at a given position (optional operation).
+ * This shifts all existing elements from that position to the end one
+ * index to the right. This version of add has no return, since it is
+ * assumed to always succeed if there is no exception. This iteration
+ * uses listIterator(index).add(o).
+ *
+ * @param index the location to insert the item
+ * @param o the object to insert
+ * @throws UnsupportedOperationException if this list does not support the
+ * add operation
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ * @throws ClassCastException if o cannot be added to this list due to its
+ * type
+ * @throws IllegalArgumentException if o cannot be added to this list for
+ * some other reason.
+ * @throws NullPointerException if o is null and the list does not permit
+ * the addition of null values.
+ */
+ public void add(int index, E o)
+ {
+ listIterator(index).add(o);
+ }
+
+ /**
+ * Insert the contents of a collection into the list at a given position
+ * (optional operation). Shift all elements at that position to the right
+ * by the number of elements inserted. This operation is undefined if
+ * this list is modified during the operation (for example, if you try
+ * to insert a list into itself).
+ * <p>
+ *
+ * This implementation grabs listIterator(index), then proceeds to use add
+ * for each element returned by c's iterator. Sun's online specs are wrong,
+ * claiming that this also calls next(): listIterator.add() correctly
+ * skips the added element.
+ *
+ * @param index the location to insert the collection
+ * @param c the collection to insert
+ * @return true if the list was modified by this action, that is, if c is
+ * non-empty
+ * @throws UnsupportedOperationException if this list does not support the
+ * addAll operation
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ * @throws ClassCastException if some element of c cannot be added to this
+ * list due to its type
+ * @throws IllegalArgumentException if some element of c cannot be added
+ * to this list for some other reason
+ * @throws NullPointerException if the specified collection is null
+ * @throws NullPointerException if an object, o, in c is null and the list
+ * does not permit the addition of null values.
+ * @see #add(int, Object)
+ */
+ public boolean addAll(int index, Collection<? extends E> c)
+ {
+ Iterator<? extends E> ci = c.iterator();
+ int size = c.size();
+ ListIterator<E> i = listIterator(index);
+ for (int pos = size; pos > 0; pos--)
+ i.add(ci.next());
+ return size > 0;
+ }
+
+ /**
+ * Get the element at a given index in this list. This implementation
+ * returns listIterator(index).next().
+ *
+ * @param index the index of the element to be returned
+ * @return the element at index index in this list
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ */
+ public E get(int index)
+ {
+ // This is a legal listIterator position, but an illegal get.
+ if (index == size())
+ throw new IndexOutOfBoundsException("Index: " + index + ", Size:"
+ + size());
+ return listIterator(index).next();
+ }
+
+ /**
+ * Obtain an Iterator over this list, whose sequence is the list order. This
+ * implementation returns listIterator().
+ *
+ * @return an Iterator over the elements of this list, in order
+ */
+ public Iterator<E> iterator()
+ {
+ return listIterator();
+ }
+
+ /**
+ * Remove the element at a given position in this list (optional operation).
+ * Shifts all remaining elements to the left to fill the gap. This
+ * implementation uses listIterator(index) and ListIterator.remove().
+ *
+ * @param index the position within the list of the object to remove
+ * @return the object that was removed
+ * @throws UnsupportedOperationException if this list does not support the
+ * remove operation
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ */
+ public E remove(int index)
+ {
+ // This is a legal listIterator position, but an illegal remove.
+ if (index == size())
+ throw new IndexOutOfBoundsException("Index: " + index + ", Size:"
+ + size());
+ ListIterator<E> i = listIterator(index);
+ E removed = i.next();
+ i.remove();
+ return removed;
+ }
+
+ /**
+ * Replace an element of this list with another object (optional operation).
+ * This implementation uses listIterator(index) and ListIterator.set(o).
+ *
+ * @param index the position within this list of the element to be replaced
+ * @param o the object to replace it with
+ * @return the object that was replaced
+ * @throws UnsupportedOperationException if this list does not support the
+ * set operation
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ * @throws ClassCastException if o cannot be added to this list due to its
+ * type
+ * @throws IllegalArgumentException if o cannot be added to this list for
+ * some other reason
+ * @throws NullPointerException if o is null and the list does not allow
+ * a value to be set to null.
+ */
+ public E set(int index, E o)
+ {
+ // This is a legal listIterator position, but an illegal set.
+ if (index == size())
+ throw new IndexOutOfBoundsException("Index: " + index + ", Size:"
+ + size());
+ ListIterator<E> i = listIterator(index);
+ E old = i.next();
+ i.set(o);
+ return old;
+ }
+}
diff --git a/libjava/classpath/java/util/AbstractSet.java b/libjava/classpath/java/util/AbstractSet.java
new file mode 100644
index 000000000..af1e4d125
--- /dev/null
+++ b/libjava/classpath/java/util/AbstractSet.java
@@ -0,0 +1,146 @@
+/* AbstractSet.java -- Abstract implementation of most of Set
+ Copyright (C) 1998, 2000, 2001, 2004, 2005
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * An abstract implementation of Set to make it easier to create your own
+ * implementations. In order to create a Set, subclass AbstractSet and
+ * implement the same methods that are required for AbstractCollection
+ * (although these methods must of course meet the requirements that Set puts
+ * on them - specifically, no element may be in the set more than once). This
+ * class simply provides implementations of equals() and hashCode() to fulfil
+ * the requirements placed on them by the Set interface.
+ *
+ * @author Original author unknown
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Collection
+ * @see AbstractCollection
+ * @see Set
+ * @see HashSet
+ * @see TreeSet
+ * @see LinkedHashSet
+ * @since 1.2
+ * @status updated to 1.4
+ */
+public abstract class AbstractSet<E>
+ extends AbstractCollection<E>
+ implements Set<E>
+{
+ /**
+ * The main constructor, for use by subclasses.
+ */
+ protected AbstractSet()
+ {
+ }
+
+ /**
+ * Tests whether the given object is equal to this Set. This implementation
+ * first checks whether this set <em>is</em> the given object, and returns
+ * true if so. Otherwise, if o is a Set and is the same size as this one, it
+ * returns the result of calling containsAll on the given Set. Otherwise, it
+ * returns false.
+ *
+ * @param o the Object to be tested for equality with this Set
+ * @return true if the given object is equal to this Set
+ */
+ public boolean equals(Object o)
+ {
+ return (o == this
+ || (o instanceof Set && ((Set) o).size() == size()
+ && containsAll((Collection) o)));
+ }
+
+ /**
+ * Returns a hash code for this Set. The hash code of a Set is the sum of the
+ * hash codes of all its elements, except that the hash code of null is
+ * defined to be zero. This implementation obtains an Iterator over the Set,
+ * and sums the results.
+ *
+ * @return a hash code for this Set
+ */
+ public int hashCode()
+ {
+ Iterator<E> itr = iterator();
+ int hash = 0;
+ int pos = size();
+ while (--pos >= 0)
+ hash += hashCode(itr.next());
+ return hash;
+ }
+
+ /**
+ * Removes from this set all elements in the given collection (optional
+ * operation). This implementation uses <code>size()</code> to determine
+ * the smaller collection. Then, if this set is smaller, it iterates
+ * over the set, calling Iterator.remove if the collection contains
+ * the element. If this set is larger, it iterates over the collection,
+ * calling Set.remove for all elements in the collection. Note that
+ * this operation will fail if a remove methods is not supported.
+ *
+ * @param c the collection of elements to remove
+ * @return true if the set was modified as a result
+ * @throws UnsupportedOperationException if remove is not supported
+ * @throws NullPointerException if the collection is null
+ * @see AbstractCollection#remove(Object)
+ * @see Collection#contains(Object)
+ * @see Iterator#remove()
+ */
+ public boolean removeAll(Collection<?> c)
+ {
+ int oldsize = size();
+ int count = c.size();
+ if (oldsize < count)
+ {
+ Iterator<E> i;
+ for (i = iterator(), count = oldsize; count > 0; count--)
+ {
+ if (c.contains(i.next()))
+ i.remove();
+ }
+ }
+ else
+ {
+ Iterator<?> i;
+ for (i = c.iterator(); count > 0; count--)
+ remove(i.next());
+ }
+ return oldsize != size();
+ }
+}
diff --git a/libjava/classpath/java/util/ArrayList.java b/libjava/classpath/java/util/ArrayList.java
new file mode 100644
index 000000000..0da2193af
--- /dev/null
+++ b/libjava/classpath/java/util/ArrayList.java
@@ -0,0 +1,603 @@
+/* ArrayList.java -- JDK1.2's answer to Vector; this is an array-backed
+ implementation of the List interface
+ Copyright (C) 1998, 1999, 2000, 2001, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Array;
+
+/**
+ * An array-backed implementation of the List interface. This implements
+ * all optional list operations, and permits null elements, so that it is
+ * better than Vector, which it replaces. Random access is roughly constant
+ * time, and iteration is roughly linear time, so it is nice and fast, with
+ * less overhead than a LinkedList.
+ * <p>
+ *
+ * Each list has a capacity, and as the array reaches that capacity it
+ * is automatically transferred to a larger array. You also have access to
+ * ensureCapacity and trimToSize to control the backing array's size, avoiding
+ * reallocation or wasted memory.
+ * <p>
+ *
+ * ArrayList is not synchronized, so if you need multi-threaded access,
+ * consider using:<br>
+ * <code>List l = Collections.synchronizedList(new ArrayList(...));</code>
+ * <p>
+ *
+ * The iterators are <i>fail-fast</i>, meaning that any structural
+ * modification, except for <code>remove()</code> called on the iterator
+ * itself, cause the iterator to throw a
+ * {@link ConcurrentModificationException} rather than exhibit
+ * non-deterministic behavior.
+ *
+ * @author Jon A. Zeppieri
+ * @author Bryce McKinlay
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Collection
+ * @see List
+ * @see LinkedList
+ * @see Vector
+ * @see Collections#synchronizedList(List)
+ * @see AbstractList
+ * @status updated to 1.4
+ */
+public class ArrayList<E> extends AbstractList<E>
+ implements List<E>, RandomAccess, Cloneable, Serializable
+{
+ /**
+ * Compatible with JDK 1.2
+ */
+ private static final long serialVersionUID = 8683452581122892189L;
+
+ /**
+ * The default capacity for new ArrayLists.
+ */
+ private static final int DEFAULT_CAPACITY = 10;
+
+ /**
+ * The number of elements in this list.
+ * @serial the list size
+ */
+ private int size;
+
+ /**
+ * Where the data is stored.
+ */
+ private transient E[] data;
+
+ /**
+ * Construct a new ArrayList with the supplied initial capacity.
+ *
+ * @param capacity initial capacity of this ArrayList
+ * @throws IllegalArgumentException if capacity is negative
+ */
+ public ArrayList(int capacity)
+ {
+ // Must explicitly check, to get correct exception.
+ if (capacity < 0)
+ throw new IllegalArgumentException();
+ data = (E[]) new Object[capacity];
+ }
+
+ /**
+ * Construct a new ArrayList with the default capacity (16).
+ */
+ public ArrayList()
+ {
+ this(DEFAULT_CAPACITY);
+ }
+
+ /**
+ * Construct a new ArrayList, and initialize it with the elements
+ * in the supplied Collection. The initial capacity is 110% of the
+ * Collection's size.
+ *
+ * @param c the collection whose elements will initialize this list
+ * @throws NullPointerException if c is null
+ */
+ public ArrayList(Collection<? extends E> c)
+ {
+ this((int) (c.size() * 1.1f));
+ addAll(c);
+ }
+
+ /**
+ * Trims the capacity of this List to be equal to its size;
+ * a memory saver.
+ */
+ public void trimToSize()
+ {
+ // Not a structural change from the perspective of iterators on this list,
+ // so don't update modCount.
+ if (size != data.length)
+ {
+ E[] newData = (E[]) new Object[size];
+ System.arraycopy(data, 0, newData, 0, size);
+ data = newData;
+ }
+ }
+
+ /**
+ * Guarantees that this list will have at least enough capacity to
+ * hold minCapacity elements. This implementation will grow the list to
+ * max(current * 2, minCapacity) if (minCapacity &gt; current). The JCL says
+ * explictly that "this method increases its capacity to minCap", while
+ * the JDK 1.3 online docs specify that the list will grow to at least the
+ * size specified.
+ *
+ * @param minCapacity the minimum guaranteed capacity
+ */
+ public void ensureCapacity(int minCapacity)
+ {
+ int current = data.length;
+
+ if (minCapacity > current)
+ {
+ E[] newData = (E[]) new Object[Math.max(current * 2, minCapacity)];
+ System.arraycopy(data, 0, newData, 0, size);
+ data = newData;
+ }
+ }
+
+ /**
+ * Returns the number of elements in this list.
+ *
+ * @return the list size
+ */
+ public int size()
+ {
+ return size;
+ }
+
+ /**
+ * Checks if the list is empty.
+ *
+ * @return true if there are no elements
+ */
+ public boolean isEmpty()
+ {
+ return size == 0;
+ }
+
+ /**
+ * Returns true iff element is in this ArrayList.
+ *
+ * @param e the element whose inclusion in the List is being tested
+ * @return true if the list contains e
+ */
+ public boolean contains(Object e)
+ {
+ return indexOf(e) != -1;
+ }
+
+ /**
+ * Returns the lowest index at which element appears in this List, or
+ * -1 if it does not appear.
+ *
+ * @param e the element whose inclusion in the List is being tested
+ * @return the index where e was found
+ */
+ public int indexOf(Object e)
+ {
+ for (int i = 0; i < size; i++)
+ if (equals(e, data[i]))
+ return i;
+ return -1;
+ }
+
+ /**
+ * Returns the highest index at which element appears in this List, or
+ * -1 if it does not appear.
+ *
+ * @param e the element whose inclusion in the List is being tested
+ * @return the index where e was found
+ */
+ public int lastIndexOf(Object e)
+ {
+ for (int i = size - 1; i >= 0; i--)
+ if (equals(e, data[i]))
+ return i;
+ return -1;
+ }
+
+ /**
+ * Creates a shallow copy of this ArrayList (elements are not cloned).
+ *
+ * @return the cloned object
+ */
+ public Object clone()
+ {
+ ArrayList<E> clone = null;
+ try
+ {
+ clone = (ArrayList<E>) super.clone();
+ clone.data = (E[]) data.clone();
+ }
+ catch (CloneNotSupportedException e)
+ {
+ // Impossible to get here.
+ }
+ return clone;
+ }
+
+ /**
+ * Returns an Object array containing all of the elements in this ArrayList.
+ * The array is independent of this list.
+ *
+ * @return an array representation of this list
+ */
+ public Object[] toArray()
+ {
+ E[] array = (E[]) new Object[size];
+ System.arraycopy(data, 0, array, 0, size);
+ return array;
+ }
+
+ /**
+ * Returns an Array whose component type is the runtime component type of
+ * the passed-in Array. The returned Array is populated with all of the
+ * elements in this ArrayList. If the passed-in Array is not large enough
+ * to store all of the elements in this List, a new Array will be created
+ * and returned; if the passed-in Array is <i>larger</i> than the size
+ * of this List, then size() index will be set to null.
+ *
+ * @param a the passed-in Array
+ * @return an array representation of this list
+ * @throws ArrayStoreException if the runtime type of a does not allow
+ * an element in this list
+ * @throws NullPointerException if a is null
+ */
+ public <T> T[] toArray(T[] a)
+ {
+ if (a.length < size)
+ a = (T[]) Array.newInstance(a.getClass().getComponentType(), size);
+ else if (a.length > size)
+ a[size] = null;
+ System.arraycopy(data, 0, a, 0, size);
+ return a;
+ }
+
+ /**
+ * Retrieves the element at the user-supplied index.
+ *
+ * @param index the index of the element we are fetching
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ */
+ public E get(int index)
+ {
+ checkBoundExclusive(index);
+ return data[index];
+ }
+
+ /**
+ * Sets the element at the specified index. The new element, e,
+ * can be an object of any type or null.
+ *
+ * @param index the index at which the element is being set
+ * @param e the element to be set
+ * @return the element previously at the specified index
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= 0
+ */
+ public E set(int index, E e)
+ {
+ checkBoundExclusive(index);
+ E result = data[index];
+ data[index] = e;
+ return result;
+ }
+
+ /**
+ * Appends the supplied element to the end of this list.
+ * The element, e, can be an object of any type or null.
+ *
+ * @param e the element to be appended to this list
+ * @return true, the add will always succeed
+ */
+ public boolean add(E e)
+ {
+ modCount++;
+ if (size == data.length)
+ ensureCapacity(size + 1);
+ data[size++] = e;
+ return true;
+ }
+
+ /**
+ * Adds the supplied element at the specified index, shifting all
+ * elements currently at that index or higher one to the right.
+ * The element, e, can be an object of any type or null.
+ *
+ * @param index the index at which the element is being added
+ * @param e the item being added
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ */
+ public void add(int index, E e)
+ {
+ checkBoundInclusive(index);
+ modCount++;
+ if (size == data.length)
+ ensureCapacity(size + 1);
+ if (index != size)
+ System.arraycopy(data, index, data, index + 1, size - index);
+ data[index] = e;
+ size++;
+ }
+
+ /**
+ * Removes the element at the user-supplied index.
+ *
+ * @param index the index of the element to be removed
+ * @return the removed Object
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ */
+ public E remove(int index)
+ {
+ checkBoundExclusive(index);
+ E r = data[index];
+ modCount++;
+ if (index != --size)
+ System.arraycopy(data, index + 1, data, index, size - index);
+ // Aid for garbage collection by releasing this pointer.
+ data[size] = null;
+ return r;
+ }
+
+ /**
+ * Removes all elements from this List
+ */
+ public void clear()
+ {
+ if (size > 0)
+ {
+ modCount++;
+ // Allow for garbage collection.
+ Arrays.fill(data, 0, size, null);
+ size = 0;
+ }
+ }
+
+ /**
+ * Add each element in the supplied Collection to this List. It is undefined
+ * what happens if you modify the list while this is taking place; for
+ * example, if the collection contains this list. c can contain objects
+ * of any type, as well as null values.
+ *
+ * @param c a Collection containing elements to be added to this List
+ * @return true if the list was modified, in other words c is not empty
+ * @throws NullPointerException if c is null
+ */
+ public boolean addAll(Collection<? extends E> c)
+ {
+ return addAll(size, c);
+ }
+
+ /**
+ * Add all elements in the supplied collection, inserting them beginning
+ * at the specified index. c can contain objects of any type, as well
+ * as null values.
+ *
+ * @param index the index at which the elements will be inserted
+ * @param c the Collection containing the elements to be inserted
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; 0
+ * @throws NullPointerException if c is null
+ */
+ public boolean addAll(int index, Collection<? extends E> c)
+ {
+ checkBoundInclusive(index);
+ Iterator<? extends E> itr = c.iterator();
+ int csize = c.size();
+
+ modCount++;
+ if (csize + size > data.length)
+ ensureCapacity(size + csize);
+ int end = index + csize;
+ if (size > 0 && index != size)
+ System.arraycopy(data, index, data, end, size - index);
+ size += csize;
+ for ( ; index < end; index++)
+ data[index] = itr.next();
+ return csize > 0;
+ }
+
+ /**
+ * Removes all elements in the half-open interval [fromIndex, toIndex).
+ * Does nothing when toIndex is equal to fromIndex.
+ *
+ * @param fromIndex the first index which will be removed
+ * @param toIndex one greater than the last index which will be removed
+ * @throws IndexOutOfBoundsException if fromIndex &gt; toIndex
+ */
+ protected void removeRange(int fromIndex, int toIndex)
+ {
+ int change = toIndex - fromIndex;
+ if (change > 0)
+ {
+ modCount++;
+ System.arraycopy(data, toIndex, data, fromIndex, size - toIndex);
+ size -= change;
+ }
+ else if (change < 0)
+ throw new IndexOutOfBoundsException();
+ }
+
+ /**
+ * Checks that the index is in the range of possible elements (inclusive).
+ *
+ * @param index the index to check
+ * @throws IndexOutOfBoundsException if index &gt; size
+ */
+ private void checkBoundInclusive(int index)
+ {
+ // Implementation note: we do not check for negative ranges here, since
+ // use of a negative index will cause an ArrayIndexOutOfBoundsException,
+ // a subclass of the required exception, with no effort on our part.
+ if (index > size)
+ raiseBoundsError(index);
+ }
+
+ /**
+ * Checks that the index is in the range of existing elements (exclusive).
+ *
+ * @param index the index to check
+ * @throws IndexOutOfBoundsException if index &gt;= size
+ */
+ private void checkBoundExclusive(int index)
+ {
+ // Implementation note: we do not check for negative ranges here, since
+ // use of a negative index will cause an ArrayIndexOutOfBoundsException,
+ // a subclass of the required exception, with no effort on our part.
+ if (index >= size)
+ raiseBoundsError(index);
+ }
+
+ /**
+ * Raise the ArrayIndexOfOutBoundsException.
+ *
+ * @param index the index of the access
+ * @throws IndexOutOfBoundsException unconditionally
+ */
+ private void raiseBoundsError(int index)
+ {
+ // Implementaion note: put in a separate method to make the JITs job easier
+ // (separate common from uncommon code at method boundaries when trivial to
+ // do so).
+ throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
+ }
+
+
+ /**
+ * Remove from this list all elements contained in the given collection.
+ * This is not public, due to Sun's API, but this performs in linear
+ * time while the default behavior of AbstractList would be quadratic.
+ *
+ * @param c the collection to filter out
+ * @return true if this list changed
+ * @throws NullPointerException if c is null
+ */
+ boolean removeAllInternal(Collection<?> c)
+ {
+ int i;
+ int j;
+ for (i = 0; i < size; i++)
+ if (c.contains(data[i]))
+ break;
+ if (i == size)
+ return false;
+
+ modCount++;
+ for (j = i++; i < size; i++)
+ if (! c.contains(data[i]))
+ data[j++] = data[i];
+ size -= i - j;
+ return true;
+ }
+
+ /**
+ * Retain in this vector only the elements contained in the given collection.
+ * This is not public, due to Sun's API, but this performs in linear
+ * time while the default behavior of AbstractList would be quadratic.
+ *
+ * @param c the collection to filter by
+ * @return true if this vector changed
+ * @throws NullPointerException if c is null
+ * @since 1.2
+ */
+ boolean retainAllInternal(Collection<?> c)
+ {
+ int i;
+ int j;
+ for (i = 0; i < size; i++)
+ if (! c.contains(data[i]))
+ break;
+ if (i == size)
+ return false;
+
+ modCount++;
+ for (j = i++; i < size; i++)
+ if (c.contains(data[i]))
+ data[j++] = data[i];
+ size -= i - j;
+ return true;
+ }
+
+ /**
+ * Serializes this object to the given stream.
+ *
+ * @param s the stream to write to
+ * @throws IOException if the underlying stream fails
+ * @serialData the size field (int), the length of the backing array
+ * (int), followed by its elements (Objects) in proper order.
+ */
+ private void writeObject(ObjectOutputStream s) throws IOException
+ {
+ // The 'size' field.
+ s.defaultWriteObject();
+ // We serialize unused list entries to preserve capacity.
+ int len = data.length;
+ s.writeInt(len);
+ // it would be more efficient to just write "size" items,
+ // this need readObject read "size" items too.
+ for (int i = 0; i < size; i++)
+ s.writeObject(data[i]);
+ }
+
+ /**
+ * Deserializes this object from the given stream.
+ *
+ * @param s the stream to read from
+ * @throws ClassNotFoundException if the underlying stream fails
+ * @throws IOException if the underlying stream fails
+ * @serialData the size field (int), the length of the backing array
+ * (int), followed by its elements (Objects) in proper order.
+ */
+ private void readObject(ObjectInputStream s)
+ throws IOException, ClassNotFoundException
+ {
+ // the `size' field.
+ s.defaultReadObject();
+ int capacity = s.readInt();
+ data = (E[]) new Object[capacity];
+ for (int i = 0; i < size; i++)
+ data[i] = (E) s.readObject();
+ }
+}
diff --git a/libjava/classpath/java/util/Arrays.java b/libjava/classpath/java/util/Arrays.java
new file mode 100644
index 000000000..dad55c459
--- /dev/null
+++ b/libjava/classpath/java/util/Arrays.java
@@ -0,0 +1,4034 @@
+/* Arrays.java -- Utility class with methods to operate on arrays
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.io.Serializable;
+import java.lang.reflect.Array;
+
+/**
+ * This class contains various static utility methods performing operations on
+ * arrays, and a method to provide a List "view" of an array to facilitate
+ * using arrays with Collection-based APIs. All methods throw a
+ * {@link NullPointerException} if the parameter array is null.
+ * <p>
+ *
+ * Implementations may use their own algorithms, but must obey the general
+ * properties; for example, the sort must be stable and n*log(n) complexity.
+ * Sun's implementation of sort, and therefore ours, is a tuned quicksort,
+ * adapted from Jon L. Bentley and M. Douglas McIlroy's "Engineering a Sort
+ * Function", Software-Practice and Experience, Vol. 23(11) P. 1249-1265
+ * (November 1993). This algorithm offers n*log(n) performance on many data
+ * sets that cause other quicksorts to degrade to quadratic performance.
+ *
+ * @author Original author unknown
+ * @author Bryce McKinlay
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Comparable
+ * @see Comparator
+ * @since 1.2
+ * @status updated to 1.4
+ */
+public class Arrays
+{
+ /**
+ * This class is non-instantiable.
+ */
+ private Arrays()
+ {
+ }
+
+
+// binarySearch
+ /**
+ * Perform a binary search of a byte array for a key. The array must be
+ * sorted (as by the sort() method) - if it is not, the behaviour of this
+ * method is undefined, and may be an infinite loop. If the array contains
+ * the key more than once, any one of them may be found. Note: although the
+ * specification allows for an infinite loop if the array is unsorted, it
+ * will not happen in this implementation.
+ *
+ * @param a the array to search (must be sorted)
+ * @param key the value to search for
+ * @return the index at which the key was found, or -n-1 if it was not
+ * found, where n is the index of the first value higher than key or
+ * a.length if there is no such value.
+ */
+ public static int binarySearch(byte[] a, byte key)
+ {
+ if (a.length == 0)
+ return -1;
+ return binarySearch(a, 0, a.length - 1, key);
+ }
+
+ /**
+ * Perform a binary search of a range of a byte array for a key. The range
+ * must be sorted (as by the <code>sort(byte[], int, int)</code> method) -
+ * if it is not, the behaviour of this method is undefined, and may be an
+ * infinite loop. If the array contains the key more than once, any one of
+ * them may be found. Note: although the specification allows for an infinite
+ * loop if the array is unsorted, it will not happen in this implementation.
+ *
+ * @param a the array to search (must be sorted)
+ * @param low the lowest index to search from.
+ * @param hi the highest index to search to.
+ * @param key the value to search for
+ * @return the index at which the key was found, or -n-1 if it was not
+ * found, where n is the index of the first value higher than key or
+ * a.length if there is no such value.
+ * @throws IllegalArgumentException if <code>low > hi</code>
+ * @throws ArrayIndexOutOfBoundsException if <code>low < 0</code> or
+ * <code>hi > a.length</code>.
+ */
+ public static int binarySearch(byte[] a, int low, int hi, byte key)
+ {
+ if (low > hi)
+ throw new IllegalArgumentException("The start index is higher than " +
+ "the finish index.");
+ if (low < 0 || hi > a.length)
+ throw new ArrayIndexOutOfBoundsException("One of the indices is out " +
+ "of bounds.");
+ int mid = 0;
+ while (low <= hi)
+ {
+ mid = (low + hi) >>> 1;
+ final byte d = a[mid];
+ if (d == key)
+ return mid;
+ else if (d > key)
+ hi = mid - 1;
+ else
+ // This gets the insertion point right on the last loop.
+ low = ++mid;
+ }
+ return -mid - 1;
+ }
+
+ /**
+ * Perform a binary search of a char array for a key. The array must be
+ * sorted (as by the sort() method) - if it is not, the behaviour of this
+ * method is undefined, and may be an infinite loop. If the array contains
+ * the key more than once, any one of them may be found. Note: although the
+ * specification allows for an infinite loop if the array is unsorted, it
+ * will not happen in this implementation.
+ *
+ * @param a the array to search (must be sorted)
+ * @param key the value to search for
+ * @return the index at which the key was found, or -n-1 if it was not
+ * found, where n is the index of the first value higher than key or
+ * a.length if there is no such value.
+ */
+ public static int binarySearch(char[] a, char key)
+ {
+ if (a.length == 0)
+ return -1;
+ return binarySearch(a, 0, a.length - 1, key);
+ }
+
+ /**
+ * Perform a binary search of a range of a char array for a key. The range
+ * must be sorted (as by the <code>sort(char[], int, int)</code> method) -
+ * if it is not, the behaviour of this method is undefined, and may be an
+ * infinite loop. If the array contains the key more than once, any one of
+ * them may be found. Note: although the specification allows for an infinite
+ * loop if the array is unsorted, it will not happen in this implementation.
+ *
+ * @param a the array to search (must be sorted)
+ * @param low the lowest index to search from.
+ * @param hi the highest index to search to.
+ * @param key the value to search for
+ * @return the index at which the key was found, or -n-1 if it was not
+ * found, where n is the index of the first value higher than key or
+ * a.length if there is no such value.
+ * @throws IllegalArgumentException if <code>low > hi</code>
+ * @throws ArrayIndexOutOfBoundsException if <code>low < 0</code> or
+ * <code>hi > a.length</code>.
+ */
+ public static int binarySearch(char[] a, int low, int hi, char key)
+ {
+ if (low > hi)
+ throw new IllegalArgumentException("The start index is higher than " +
+ "the finish index.");
+ if (low < 0 || hi > a.length)
+ throw new ArrayIndexOutOfBoundsException("One of the indices is out " +
+ "of bounds.");
+ int mid = 0;
+ while (low <= hi)
+ {
+ mid = (low + hi) >>> 1;
+ final char d = a[mid];
+ if (d == key)
+ return mid;
+ else if (d > key)
+ hi = mid - 1;
+ else
+ // This gets the insertion point right on the last loop.
+ low = ++mid;
+ }
+ return -mid - 1;
+ }
+
+ /**
+ * Perform a binary search of a short array for a key. The array must be
+ * sorted (as by the sort() method) - if it is not, the behaviour of this
+ * method is undefined, and may be an infinite loop. If the array contains
+ * the key more than once, any one of them may be found. Note: although the
+ * specification allows for an infinite loop if the array is unsorted, it
+ * will not happen in this implementation.
+ *
+ * @param a the array to search (must be sorted)
+ * @param key the value to search for
+ * @return the index at which the key was found, or -n-1 if it was not
+ * found, where n is the index of the first value higher than key or
+ * a.length if there is no such value.
+ */
+ public static int binarySearch(short[] a, short key)
+ {
+ if (a.length == 0)
+ return -1;
+ return binarySearch(a, 0, a.length - 1, key);
+ }
+
+ /**
+ * Perform a binary search of a range of a short array for a key. The range
+ * must be sorted (as by the <code>sort(short[], int, int)</code> method) -
+ * if it is not, the behaviour of this method is undefined, and may be an
+ * infinite loop. If the array contains the key more than once, any one of
+ * them may be found. Note: although the specification allows for an infinite
+ * loop if the array is unsorted, it will not happen in this implementation.
+ *
+ * @param a the array to search (must be sorted)
+ * @param low the lowest index to search from.
+ * @param hi the highest index to search to.
+ * @param key the value to search for
+ * @return the index at which the key was found, or -n-1 if it was not
+ * found, where n is the index of the first value higher than key or
+ * a.length if there is no such value.
+ * @throws IllegalArgumentException if <code>low > hi</code>
+ * @throws ArrayIndexOutOfBoundsException if <code>low < 0</code> or
+ * <code>hi > a.length</code>.
+ */
+ public static int binarySearch(short[] a, int low, int hi, short key)
+ {
+ if (low > hi)
+ throw new IllegalArgumentException("The start index is higher than " +
+ "the finish index.");
+ if (low < 0 || hi > a.length)
+ throw new ArrayIndexOutOfBoundsException("One of the indices is out " +
+ "of bounds.");
+ int mid = 0;
+ while (low <= hi)
+ {
+ mid = (low + hi) >>> 1;
+ final short d = a[mid];
+ if (d == key)
+ return mid;
+ else if (d > key)
+ hi = mid - 1;
+ else
+ // This gets the insertion point right on the last loop.
+ low = ++mid;
+ }
+ return -mid - 1;
+ }
+
+ /**
+ * Perform a binary search of an int array for a key. The array must be
+ * sorted (as by the sort() method) - if it is not, the behaviour of this
+ * method is undefined, and may be an infinite loop. If the array contains
+ * the key more than once, any one of them may be found. Note: although the
+ * specification allows for an infinite loop if the array is unsorted, it
+ * will not happen in this implementation.
+ *
+ * @param a the array to search (must be sorted)
+ * @param key the value to search for
+ * @return the index at which the key was found, or -n-1 if it was not
+ * found, where n is the index of the first value higher than key or
+ * a.length if there is no such value.
+ */
+ public static int binarySearch(int[] a, int key)
+ {
+ if (a.length == 0)
+ return -1;
+ return binarySearch(a, 0, a.length - 1, key);
+ }
+
+ /**
+ * Perform a binary search of a range of an integer array for a key. The range
+ * must be sorted (as by the <code>sort(int[], int, int)</code> method) -
+ * if it is not, the behaviour of this method is undefined, and may be an
+ * infinite loop. If the array contains the key more than once, any one of
+ * them may be found. Note: although the specification allows for an infinite
+ * loop if the array is unsorted, it will not happen in this implementation.
+ *
+ * @param a the array to search (must be sorted)
+ * @param low the lowest index to search from.
+ * @param hi the highest index to search to.
+ * @param key the value to search for
+ * @return the index at which the key was found, or -n-1 if it was not
+ * found, where n is the index of the first value higher than key or
+ * a.length if there is no such value.
+ * @throws IllegalArgumentException if <code>low > hi</code>
+ * @throws ArrayIndexOutOfBoundsException if <code>low < 0</code> or
+ * <code>hi > a.length</code>.
+ */
+ public static int binarySearch(int[] a, int low, int hi, int key)
+ {
+ if (low > hi)
+ throw new IllegalArgumentException("The start index is higher than " +
+ "the finish index.");
+ if (low < 0 || hi > a.length)
+ throw new ArrayIndexOutOfBoundsException("One of the indices is out " +
+ "of bounds.");
+ int mid = 0;
+ while (low <= hi)
+ {
+ mid = (low + hi) >>> 1;
+ final int d = a[mid];
+ if (d == key)
+ return mid;
+ else if (d > key)
+ hi = mid - 1;
+ else
+ // This gets the insertion point right on the last loop.
+ low = ++mid;
+ }
+ return -mid - 1;
+ }
+
+ /**
+ * Perform a binary search of a long array for a key. The array must be
+ * sorted (as by the sort() method) - if it is not, the behaviour of this
+ * method is undefined, and may be an infinite loop. If the array contains
+ * the key more than once, any one of them may be found. Note: although the
+ * specification allows for an infinite loop if the array is unsorted, it
+ * will not happen in this implementation.
+ *
+ * @param a the array to search (must be sorted)
+ * @param key the value to search for
+ * @return the index at which the key was found, or -n-1 if it was not
+ * found, where n is the index of the first value higher than key or
+ * a.length if there is no such value.
+ */
+ public static int binarySearch(long[] a, long key)
+ {
+ if (a.length == 0)
+ return -1;
+ return binarySearch(a, 0, a.length - 1, key);
+ }
+
+ /**
+ * Perform a binary search of a range of a long array for a key. The range
+ * must be sorted (as by the <code>sort(long[], int, int)</code> method) -
+ * if it is not, the behaviour of this method is undefined, and may be an
+ * infinite loop. If the array contains the key more than once, any one of
+ * them may be found. Note: although the specification allows for an infinite
+ * loop if the array is unsorted, it will not happen in this implementation.
+ *
+ * @param a the array to search (must be sorted)
+ * @param low the lowest index to search from.
+ * @param hi the highest index to search to.
+ * @param key the value to search for
+ * @return the index at which the key was found, or -n-1 if it was not
+ * found, where n is the index of the first value higher than key or
+ * a.length if there is no such value.
+ * @throws IllegalArgumentException if <code>low > hi</code>
+ * @throws ArrayIndexOutOfBoundsException if <code>low < 0</code> or
+ * <code>hi > a.length</code>.
+ */
+ public static int binarySearch(long[] a, int low, int hi, long key)
+ {
+ if (low > hi)
+ throw new IllegalArgumentException("The start index is higher than " +
+ "the finish index.");
+ if (low < 0 || hi > a.length)
+ throw new ArrayIndexOutOfBoundsException("One of the indices is out " +
+ "of bounds.");
+ int mid = 0;
+ while (low <= hi)
+ {
+ mid = (low + hi) >>> 1;
+ final long d = a[mid];
+ if (d == key)
+ return mid;
+ else if (d > key)
+ hi = mid - 1;
+ else
+ // This gets the insertion point right on the last loop.
+ low = ++mid;
+ }
+ return -mid - 1;
+ }
+
+ /**
+ * Perform a binary search of a float array for a key. The array must be
+ * sorted (as by the sort() method) - if it is not, the behaviour of this
+ * method is undefined, and may be an infinite loop. If the array contains
+ * the key more than once, any one of them may be found. Note: although the
+ * specification allows for an infinite loop if the array is unsorted, it
+ * will not happen in this implementation.
+ *
+ * @param a the array to search (must be sorted)
+ * @param key the value to search for
+ * @return the index at which the key was found, or -n-1 if it was not
+ * found, where n is the index of the first value higher than key or
+ * a.length if there is no such value.
+ */
+ public static int binarySearch(float[] a, float key)
+ {
+ if (a.length == 0)
+ return -1;
+ return binarySearch(a, 0, a.length - 1, key);
+ }
+
+ /**
+ * Perform a binary search of a range of a float array for a key. The range
+ * must be sorted (as by the <code>sort(float[], int, int)</code> method) -
+ * if it is not, the behaviour of this method is undefined, and may be an
+ * infinite loop. If the array contains the key more than once, any one of
+ * them may be found. Note: although the specification allows for an infinite
+ * loop if the array is unsorted, it will not happen in this implementation.
+ *
+ * @param a the array to search (must be sorted)
+ * @param low the lowest index to search from.
+ * @param hi the highest index to search to.
+ * @param key the value to search for
+ * @return the index at which the key was found, or -n-1 if it was not
+ * found, where n is the index of the first value higher than key or
+ * a.length if there is no such value.
+ * @throws IllegalArgumentException if <code>low > hi</code>
+ * @throws ArrayIndexOutOfBoundsException if <code>low < 0</code> or
+ * <code>hi > a.length</code>.
+ */
+ public static int binarySearch(float[] a, int low, int hi, float key)
+ {
+ if (low > hi)
+ throw new IllegalArgumentException("The start index is higher than " +
+ "the finish index.");
+ if (low < 0 || hi > a.length)
+ throw new ArrayIndexOutOfBoundsException("One of the indices is out " +
+ "of bounds.");
+ // Must use Float.compare to take into account NaN, +-0.
+ int mid = 0;
+ while (low <= hi)
+ {
+ mid = (low + hi) >>> 1;
+ final int r = Float.compare(a[mid], key);
+ if (r == 0)
+ return mid;
+ else if (r > 0)
+ hi = mid - 1;
+ else
+ // This gets the insertion point right on the last loop
+ low = ++mid;
+ }
+ return -mid - 1;
+ }
+
+ /**
+ * Perform a binary search of a double array for a key. The array must be
+ * sorted (as by the sort() method) - if it is not, the behaviour of this
+ * method is undefined, and may be an infinite loop. If the array contains
+ * the key more than once, any one of them may be found. Note: although the
+ * specification allows for an infinite loop if the array is unsorted, it
+ * will not happen in this implementation.
+ *
+ * @param a the array to search (must be sorted)
+ * @param key the value to search for
+ * @return the index at which the key was found, or -n-1 if it was not
+ * found, where n is the index of the first value higher than key or
+ * a.length if there is no such value.
+ */
+ public static int binarySearch(double[] a, double key)
+ {
+ if (a.length == 0)
+ return -1;
+ return binarySearch(a, 0, a.length - 1, key);
+ }
+
+ /**
+ * Perform a binary search of a range of a double array for a key. The range
+ * must be sorted (as by the <code>sort(double[], int, int)</code> method) -
+ * if it is not, the behaviour of this method is undefined, and may be an
+ * infinite loop. If the array contains the key more than once, any one of
+ * them may be found. Note: although the specification allows for an infinite
+ * loop if the array is unsorted, it will not happen in this implementation.
+ *
+ * @param a the array to search (must be sorted)
+ * @param low the lowest index to search from.
+ * @param hi the highest index to search to.
+ * @param key the value to search for
+ * @return the index at which the key was found, or -n-1 if it was not
+ * found, where n is the index of the first value higher than key or
+ * a.length if there is no such value.
+ * @throws IllegalArgumentException if <code>low > hi</code>
+ * @throws ArrayIndexOutOfBoundsException if <code>low < 0</code> or
+ * <code>hi > a.length</code>.
+ */
+ public static int binarySearch(double[] a, int low, int hi, double key)
+ {
+ if (low > hi)
+ throw new IllegalArgumentException("The start index is higher than " +
+ "the finish index.");
+ if (low < 0 || hi > a.length)
+ throw new ArrayIndexOutOfBoundsException("One of the indices is out " +
+ "of bounds.");
+ // Must use Double.compare to take into account NaN, +-0.
+ int mid = 0;
+ while (low <= hi)
+ {
+ mid = (low + hi) >>> 1;
+ final int r = Double.compare(a[mid], key);
+ if (r == 0)
+ return mid;
+ else if (r > 0)
+ hi = mid - 1;
+ else
+ // This gets the insertion point right on the last loop
+ low = ++mid;
+ }
+ return -mid - 1;
+ }
+
+ /**
+ * Perform a binary search of an Object array for a key, using the natural
+ * ordering of the elements. The array must be sorted (as by the sort()
+ * method) - if it is not, the behaviour of this method is undefined, and may
+ * be an infinite loop. Further, the key must be comparable with every item
+ * in the array. If the array contains the key more than once, any one of
+ * them may be found. Note: although the specification allows for an infinite
+ * loop if the array is unsorted, it will not happen in this (JCL)
+ * implementation.
+ *
+ * @param a the array to search (must be sorted)
+ * @param key the value to search for
+ * @return the index at which the key was found, or -n-1 if it was not
+ * found, where n is the index of the first value higher than key or
+ * a.length if there is no such value.
+ * @throws ClassCastException if key could not be compared with one of the
+ * elements of a
+ * @throws NullPointerException if a null element in a is compared
+ */
+ public static int binarySearch(Object[] a, Object key)
+ {
+ if (a.length == 0)
+ return -1;
+ return binarySearch(a, key, null);
+ }
+
+ /**
+ * Perform a binary search of a range of an Object array for a key. The range
+ * must be sorted (as by the <code>sort(Object[], int, int)</code> method) -
+ * if it is not, the behaviour of this method is undefined, and may be an
+ * infinite loop. If the array contains the key more than once, any one of
+ * them may be found. Note: although the specification allows for an infinite
+ * loop if the array is unsorted, it will not happen in this implementation.
+ *
+ * @param a the array to search (must be sorted)
+ * @param low the lowest index to search from.
+ * @param hi the highest index to search to.
+ * @param key the value to search for
+ * @return the index at which the key was found, or -n-1 if it was not
+ * found, where n is the index of the first value higher than key or
+ * a.length if there is no such value.
+ */
+ public static int binarySearch(Object[] a, int low, int hi, Object key)
+ {
+ return binarySearch(a, low, hi, key, null);
+ }
+
+ /**
+ * Perform a binary search of an Object array for a key, using a supplied
+ * Comparator. The array must be sorted (as by the sort() method with the
+ * same Comparator) - if it is not, the behaviour of this method is
+ * undefined, and may be an infinite loop. Further, the key must be
+ * comparable with every item in the array. If the array contains the key
+ * more than once, any one of them may be found. Note: although the
+ * specification allows for an infinite loop if the array is unsorted, it
+ * will not happen in this (JCL) implementation.
+ *
+ * @param a the array to search (must be sorted)
+ * @param key the value to search for
+ * @param c the comparator by which the array is sorted; or null to
+ * use the elements' natural order
+ * @return the index at which the key was found, or -n-1 if it was not
+ * found, where n is the index of the first value higher than key or
+ * a.length if there is no such value.
+ * @throws ClassCastException if key could not be compared with one of the
+ * elements of a
+ * @throws NullPointerException if a null element is compared with natural
+ * ordering (only possible when c is null)
+ */
+ public static <T> int binarySearch(T[] a, T key, Comparator<? super T> c)
+ {
+ if (a.length == 0)
+ return -1;
+ return binarySearch(a, 0, a.length - 1, key, c);
+ }
+
+ /**
+ * Perform a binary search of a range of an Object array for a key using
+ * a {@link Comparator}. The range must be sorted (as by the
+ * <code>sort(Object[], int, int)</code> method) - if it is not, the
+ * behaviour of this method is undefined, and may be an infinite loop. If
+ * the array contains the key more than once, any one of them may be found.
+ * Note: although the specification allows for an infinite loop if the array
+ * is unsorted, it will not happen in this implementation.
+ *
+ * @param a the array to search (must be sorted)
+ * @param low the lowest index to search from.
+ * @param hi the highest index to search to.
+ * @param key the value to search for
+ * @param c the comparator by which the array is sorted; or null to
+ * use the elements' natural order
+ * @return the index at which the key was found, or -n-1 if it was not
+ * found, where n is the index of the first value higher than key or
+ * a.length if there is no such value.
+ * @throws ClassCastException if key could not be compared with one of the
+ * elements of a
+ * @throws IllegalArgumentException if <code>low > hi</code>
+ * @throws ArrayIndexOutOfBoundsException if <code>low < 0</code> or
+ * <code>hi > a.length</code>.
+ */
+ public static <T> int binarySearch(T[] a, int low, int hi, T key,
+ Comparator<? super T> c)
+ {
+ if (low > hi)
+ throw new IllegalArgumentException("The start index is higher than " +
+ "the finish index.");
+ if (low < 0 || hi > a.length)
+ throw new ArrayIndexOutOfBoundsException("One of the indices is out " +
+ "of bounds.");
+ int mid = 0;
+ while (low <= hi)
+ {
+ mid = (low + hi) >>> 1;
+ // NOTE: Please keep the order of a[mid] and key. Although
+ // not required by the specs, the RI has it in this order as
+ // well, and real programs (erroneously) depend on it.
+ final int d = Collections.compare(a[mid], key, c);
+ if (d == 0)
+ return mid;
+ else if (d > 0)
+ hi = mid - 1;
+ else
+ // This gets the insertion point right on the last loop
+ low = ++mid;
+ }
+ return -mid - 1;
+ }
+
+
+// equals
+ /**
+ * Compare two boolean arrays for equality.
+ *
+ * @param a1 the first array to compare
+ * @param a2 the second array to compare
+ * @return true if a1 and a2 are both null, or if a2 is of the same length
+ * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
+ */
+ public static boolean equals(boolean[] a1, boolean[] a2)
+ {
+ // Quick test which saves comparing elements of the same array, and also
+ // catches the case that both are null.
+ if (a1 == a2)
+ return true;
+
+ if (null == a1 || null == a2)
+ return false;
+
+ // If they're the same length, test each element
+ if (a1.length == a2.length)
+ {
+ int i = a1.length;
+ while (--i >= 0)
+ if (a1[i] != a2[i])
+ return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Compare two byte arrays for equality.
+ *
+ * @param a1 the first array to compare
+ * @param a2 the second array to compare
+ * @return true if a1 and a2 are both null, or if a2 is of the same length
+ * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
+ */
+ public static boolean equals(byte[] a1, byte[] a2)
+ {
+ // Quick test which saves comparing elements of the same array, and also
+ // catches the case that both are null.
+ if (a1 == a2)
+ return true;
+
+ if (null == a1 || null == a2)
+ return false;
+
+ // If they're the same length, test each element
+ if (a1.length == a2.length)
+ {
+ int i = a1.length;
+ while (--i >= 0)
+ if (a1[i] != a2[i])
+ return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Compare two char arrays for equality.
+ *
+ * @param a1 the first array to compare
+ * @param a2 the second array to compare
+ * @return true if a1 and a2 are both null, or if a2 is of the same length
+ * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
+ */
+ public static boolean equals(char[] a1, char[] a2)
+ {
+ // Quick test which saves comparing elements of the same array, and also
+ // catches the case that both are null.
+ if (a1 == a2)
+ return true;
+
+ if (null == a1 || null == a2)
+ return false;
+
+ // If they're the same length, test each element
+ if (a1.length == a2.length)
+ {
+ int i = a1.length;
+ while (--i >= 0)
+ if (a1[i] != a2[i])
+ return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Compare two short arrays for equality.
+ *
+ * @param a1 the first array to compare
+ * @param a2 the second array to compare
+ * @return true if a1 and a2 are both null, or if a2 is of the same length
+ * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
+ */
+ public static boolean equals(short[] a1, short[] a2)
+ {
+ // Quick test which saves comparing elements of the same array, and also
+ // catches the case that both are null.
+ if (a1 == a2)
+ return true;
+
+ if (null == a1 || null == a2)
+ return false;
+
+ // If they're the same length, test each element
+ if (a1.length == a2.length)
+ {
+ int i = a1.length;
+ while (--i >= 0)
+ if (a1[i] != a2[i])
+ return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Compare two int arrays for equality.
+ *
+ * @param a1 the first array to compare
+ * @param a2 the second array to compare
+ * @return true if a1 and a2 are both null, or if a2 is of the same length
+ * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
+ */
+ public static boolean equals(int[] a1, int[] a2)
+ {
+ // Quick test which saves comparing elements of the same array, and also
+ // catches the case that both are null.
+ if (a1 == a2)
+ return true;
+
+ if (null == a1 || null == a2)
+ return false;
+
+ // If they're the same length, test each element
+ if (a1.length == a2.length)
+ {
+ int i = a1.length;
+ while (--i >= 0)
+ if (a1[i] != a2[i])
+ return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Compare two long arrays for equality.
+ *
+ * @param a1 the first array to compare
+ * @param a2 the second array to compare
+ * @return true if a1 and a2 are both null, or if a2 is of the same length
+ * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
+ */
+ public static boolean equals(long[] a1, long[] a2)
+ {
+ // Quick test which saves comparing elements of the same array, and also
+ // catches the case that both are null.
+ if (a1 == a2)
+ return true;
+
+ if (null == a1 || null == a2)
+ return false;
+
+ // If they're the same length, test each element
+ if (a1.length == a2.length)
+ {
+ int i = a1.length;
+ while (--i >= 0)
+ if (a1[i] != a2[i])
+ return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Compare two float arrays for equality.
+ *
+ * @param a1 the first array to compare
+ * @param a2 the second array to compare
+ * @return true if a1 and a2 are both null, or if a2 is of the same length
+ * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
+ */
+ public static boolean equals(float[] a1, float[] a2)
+ {
+ // Quick test which saves comparing elements of the same array, and also
+ // catches the case that both are null.
+ if (a1 == a2)
+ return true;
+
+ if (null == a1 || null == a2)
+ return false;
+
+ // Must use Float.compare to take into account NaN, +-0.
+ // If they're the same length, test each element
+ if (a1.length == a2.length)
+ {
+ int i = a1.length;
+ while (--i >= 0)
+ if (Float.compare(a1[i], a2[i]) != 0)
+ return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Compare two double arrays for equality.
+ *
+ * @param a1 the first array to compare
+ * @param a2 the second array to compare
+ * @return true if a1 and a2 are both null, or if a2 is of the same length
+ * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i]
+ */
+ public static boolean equals(double[] a1, double[] a2)
+ {
+ // Quick test which saves comparing elements of the same array, and also
+ // catches the case that both are null.
+ if (a1 == a2)
+ return true;
+
+ if (null == a1 || null == a2)
+ return false;
+
+ // Must use Double.compare to take into account NaN, +-0.
+ // If they're the same length, test each element
+ if (a1.length == a2.length)
+ {
+ int i = a1.length;
+ while (--i >= 0)
+ if (Double.compare(a1[i], a2[i]) != 0)
+ return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Compare two Object arrays for equality.
+ *
+ * @param a1 the first array to compare
+ * @param a2 the second array to compare
+ * @return true if a1 and a2 are both null, or if a1 is of the same length
+ * as a2, and for each 0 <= i < a.length, a1[i] == null ?
+ * a2[i] == null : a1[i].equals(a2[i]).
+ */
+ public static boolean equals(Object[] a1, Object[] a2)
+ {
+ // Quick test which saves comparing elements of the same array, and also
+ // catches the case that both are null.
+ if (a1 == a2)
+ return true;
+
+ if (null == a1 || null == a2)
+ return false;
+
+ // If they're the same length, test each element
+ if (a1.length == a2.length)
+ {
+ int i = a1.length;
+ while (--i >= 0)
+ if (! AbstractCollection.equals(a1[i], a2[i]))
+ return false;
+ return true;
+ }
+ return false;
+ }
+
+
+// fill
+ /**
+ * Fill an array with a boolean value.
+ *
+ * @param a the array to fill
+ * @param val the value to fill it with
+ */
+ public static void fill(boolean[] a, boolean val)
+ {
+ fill(a, 0, a.length, val);
+ }
+
+ /**
+ * Fill a range of an array with a boolean value.
+ *
+ * @param a the array to fill
+ * @param fromIndex the index to fill from, inclusive
+ * @param toIndex the index to fill to, exclusive
+ * @param val the value to fill with
+ * @throws IllegalArgumentException if fromIndex &gt; toIndex
+ * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; a.length
+ */
+ public static void fill(boolean[] a, int fromIndex, int toIndex, boolean val)
+ {
+ if (fromIndex > toIndex)
+ throw new IllegalArgumentException();
+ for (int i = fromIndex; i < toIndex; i++)
+ a[i] = val;
+ }
+
+ /**
+ * Fill an array with a byte value.
+ *
+ * @param a the array to fill
+ * @param val the value to fill it with
+ */
+ public static void fill(byte[] a, byte val)
+ {
+ fill(a, 0, a.length, val);
+ }
+
+ /**
+ * Fill a range of an array with a byte value.
+ *
+ * @param a the array to fill
+ * @param fromIndex the index to fill from, inclusive
+ * @param toIndex the index to fill to, exclusive
+ * @param val the value to fill with
+ * @throws IllegalArgumentException if fromIndex &gt; toIndex
+ * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; a.length
+ */
+ public static void fill(byte[] a, int fromIndex, int toIndex, byte val)
+ {
+ if (fromIndex > toIndex)
+ throw new IllegalArgumentException();
+ for (int i = fromIndex; i < toIndex; i++)
+ a[i] = val;
+ }
+
+ /**
+ * Fill an array with a char value.
+ *
+ * @param a the array to fill
+ * @param val the value to fill it with
+ */
+ public static void fill(char[] a, char val)
+ {
+ fill(a, 0, a.length, val);
+ }
+
+ /**
+ * Fill a range of an array with a char value.
+ *
+ * @param a the array to fill
+ * @param fromIndex the index to fill from, inclusive
+ * @param toIndex the index to fill to, exclusive
+ * @param val the value to fill with
+ * @throws IllegalArgumentException if fromIndex &gt; toIndex
+ * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; a.length
+ */
+ public static void fill(char[] a, int fromIndex, int toIndex, char val)
+ {
+ if (fromIndex > toIndex)
+ throw new IllegalArgumentException();
+ for (int i = fromIndex; i < toIndex; i++)
+ a[i] = val;
+ }
+
+ /**
+ * Fill an array with a short value.
+ *
+ * @param a the array to fill
+ * @param val the value to fill it with
+ */
+ public static void fill(short[] a, short val)
+ {
+ fill(a, 0, a.length, val);
+ }
+
+ /**
+ * Fill a range of an array with a short value.
+ *
+ * @param a the array to fill
+ * @param fromIndex the index to fill from, inclusive
+ * @param toIndex the index to fill to, exclusive
+ * @param val the value to fill with
+ * @throws IllegalArgumentException if fromIndex &gt; toIndex
+ * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; a.length
+ */
+ public static void fill(short[] a, int fromIndex, int toIndex, short val)
+ {
+ if (fromIndex > toIndex)
+ throw new IllegalArgumentException();
+ for (int i = fromIndex; i < toIndex; i++)
+ a[i] = val;
+ }
+
+ /**
+ * Fill an array with an int value.
+ *
+ * @param a the array to fill
+ * @param val the value to fill it with
+ */
+ public static void fill(int[] a, int val)
+ {
+ fill(a, 0, a.length, val);
+ }
+
+ /**
+ * Fill a range of an array with an int value.
+ *
+ * @param a the array to fill
+ * @param fromIndex the index to fill from, inclusive
+ * @param toIndex the index to fill to, exclusive
+ * @param val the value to fill with
+ * @throws IllegalArgumentException if fromIndex &gt; toIndex
+ * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; a.length
+ */
+ public static void fill(int[] a, int fromIndex, int toIndex, int val)
+ {
+ if (fromIndex > toIndex)
+ throw new IllegalArgumentException();
+ for (int i = fromIndex; i < toIndex; i++)
+ a[i] = val;
+ }
+
+ /**
+ * Fill an array with a long value.
+ *
+ * @param a the array to fill
+ * @param val the value to fill it with
+ */
+ public static void fill(long[] a, long val)
+ {
+ fill(a, 0, a.length, val);
+ }
+
+ /**
+ * Fill a range of an array with a long value.
+ *
+ * @param a the array to fill
+ * @param fromIndex the index to fill from, inclusive
+ * @param toIndex the index to fill to, exclusive
+ * @param val the value to fill with
+ * @throws IllegalArgumentException if fromIndex &gt; toIndex
+ * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; a.length
+ */
+ public static void fill(long[] a, int fromIndex, int toIndex, long val)
+ {
+ if (fromIndex > toIndex)
+ throw new IllegalArgumentException();
+ for (int i = fromIndex; i < toIndex; i++)
+ a[i] = val;
+ }
+
+ /**
+ * Fill an array with a float value.
+ *
+ * @param a the array to fill
+ * @param val the value to fill it with
+ */
+ public static void fill(float[] a, float val)
+ {
+ fill(a, 0, a.length, val);
+ }
+
+ /**
+ * Fill a range of an array with a float value.
+ *
+ * @param a the array to fill
+ * @param fromIndex the index to fill from, inclusive
+ * @param toIndex the index to fill to, exclusive
+ * @param val the value to fill with
+ * @throws IllegalArgumentException if fromIndex &gt; toIndex
+ * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; a.length
+ */
+ public static void fill(float[] a, int fromIndex, int toIndex, float val)
+ {
+ if (fromIndex > toIndex)
+ throw new IllegalArgumentException();
+ for (int i = fromIndex; i < toIndex; i++)
+ a[i] = val;
+ }
+
+ /**
+ * Fill an array with a double value.
+ *
+ * @param a the array to fill
+ * @param val the value to fill it with
+ */
+ public static void fill(double[] a, double val)
+ {
+ fill(a, 0, a.length, val);
+ }
+
+ /**
+ * Fill a range of an array with a double value.
+ *
+ * @param a the array to fill
+ * @param fromIndex the index to fill from, inclusive
+ * @param toIndex the index to fill to, exclusive
+ * @param val the value to fill with
+ * @throws IllegalArgumentException if fromIndex &gt; toIndex
+ * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; a.length
+ */
+ public static void fill(double[] a, int fromIndex, int toIndex, double val)
+ {
+ if (fromIndex > toIndex)
+ throw new IllegalArgumentException();
+ for (int i = fromIndex; i < toIndex; i++)
+ a[i] = val;
+ }
+
+ /**
+ * Fill an array with an Object value.
+ *
+ * @param a the array to fill
+ * @param val the value to fill it with
+ * @throws ClassCastException if val is not an instance of the element
+ * type of a.
+ */
+ public static void fill(Object[] a, Object val)
+ {
+ fill(a, 0, a.length, val);
+ }
+
+ /**
+ * Fill a range of an array with an Object value.
+ *
+ * @param a the array to fill
+ * @param fromIndex the index to fill from, inclusive
+ * @param toIndex the index to fill to, exclusive
+ * @param val the value to fill with
+ * @throws ClassCastException if val is not an instance of the element
+ * type of a.
+ * @throws IllegalArgumentException if fromIndex &gt; toIndex
+ * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; a.length
+ */
+ public static void fill(Object[] a, int fromIndex, int toIndex, Object val)
+ {
+ if (fromIndex > toIndex)
+ throw new IllegalArgumentException();
+ for (int i = fromIndex; i < toIndex; i++)
+ a[i] = val;
+ }
+
+
+// sort
+ // Thanks to Paul Fisher (rao@gnu.org) for finding this quicksort algorithm
+ // as specified by Sun and porting it to Java. The algorithm is an optimised
+ // quicksort, as described in Jon L. Bentley and M. Douglas McIlroy's
+ // "Engineering a Sort Function", Software-Practice and Experience, Vol.
+ // 23(11) P. 1249-1265 (November 1993). This algorithm gives n*log(n)
+ // performance on many arrays that would take quadratic time with a standard
+ // quicksort.
+
+ /**
+ * Performs a stable sort on the elements, arranging them according to their
+ * natural order.
+ *
+ * @param a the byte array to sort
+ */
+ public static void sort(byte[] a)
+ {
+ qsort(a, 0, a.length);
+ }
+
+ /**
+ * Performs a stable sort on the elements, arranging them according to their
+ * natural order.
+ *
+ * @param a the byte array to sort
+ * @param fromIndex the first index to sort (inclusive)
+ * @param toIndex the last index to sort (exclusive)
+ * @throws IllegalArgumentException if fromIndex &gt; toIndex
+ * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; a.length
+ */
+ public static void sort(byte[] a, int fromIndex, int toIndex)
+ {
+ if (fromIndex > toIndex)
+ throw new IllegalArgumentException();
+ if (fromIndex < 0)
+ throw new ArrayIndexOutOfBoundsException();
+ qsort(a, fromIndex, toIndex - fromIndex);
+ }
+
+ /**
+ * Finds the index of the median of three array elements.
+ *
+ * @param a the first index
+ * @param b the second index
+ * @param c the third index
+ * @param d the array
+ * @return the index (a, b, or c) which has the middle value of the three
+ */
+ private static int med3(int a, int b, int c, byte[] d)
+ {
+ return (d[a] < d[b]
+ ? (d[b] < d[c] ? b : d[a] < d[c] ? c : a)
+ : (d[b] > d[c] ? b : d[a] > d[c] ? c : a));
+ }
+
+ /**
+ * Swaps the elements at two locations of an array
+ *
+ * @param i the first index
+ * @param j the second index
+ * @param a the array
+ */
+ private static void swap(int i, int j, byte[] a)
+ {
+ byte c = a[i];
+ a[i] = a[j];
+ a[j] = c;
+ }
+
+ /**
+ * Swaps two ranges of an array.
+ *
+ * @param i the first range start
+ * @param j the second range start
+ * @param n the element count
+ * @param a the array
+ */
+ private static void vecswap(int i, int j, int n, byte[] a)
+ {
+ for ( ; n > 0; i++, j++, n--)
+ swap(i, j, a);
+ }
+
+ /**
+ * Performs a recursive modified quicksort.
+ *
+ * @param array the array to sort
+ * @param from the start index (inclusive)
+ * @param count the number of elements to sort
+ */
+ private static void qsort(byte[] array, int from, int count)
+ {
+ // Use an insertion sort on small arrays.
+ if (count <= 7)
+ {
+ for (int i = from + 1; i < from + count; i++)
+ for (int j = i; j > from && array[j - 1] > array[j]; j--)
+ swap(j, j - 1, array);
+ return;
+ }
+
+ // Determine a good median element.
+ int mid = from + count / 2;
+ int lo = from;
+ int hi = from + count - 1;
+
+ if (count > 40)
+ { // big arrays, pseudomedian of 9
+ int s = count / 8;
+ lo = med3(lo, lo + s, lo + 2 * s, array);
+ mid = med3(mid - s, mid, mid + s, array);
+ hi = med3(hi - 2 * s, hi - s, hi, array);
+ }
+ mid = med3(lo, mid, hi, array);
+
+ int a, b, c, d;
+ int comp;
+
+ // Pull the median element out of the fray, and use it as a pivot.
+ swap(from, mid, array);
+ a = b = from;
+ c = d = from + count - 1;
+
+ // Repeatedly move b and c to each other, swapping elements so
+ // that all elements before index b are less than the pivot, and all
+ // elements after index c are greater than the pivot. a and b track
+ // the elements equal to the pivot.
+ while (true)
+ {
+ while (b <= c && (comp = array[b] - array[from]) <= 0)
+ {
+ if (comp == 0)
+ {
+ swap(a, b, array);
+ a++;
+ }
+ b++;
+ }
+ while (c >= b && (comp = array[c] - array[from]) >= 0)
+ {
+ if (comp == 0)
+ {
+ swap(c, d, array);
+ d--;
+ }
+ c--;
+ }
+ if (b > c)
+ break;
+ swap(b, c, array);
+ b++;
+ c--;
+ }
+
+ // Swap pivot(s) back in place, the recurse on left and right sections.
+ hi = from + count;
+ int span;
+ span = Math.min(a - from, b - a);
+ vecswap(from, b - span, span, array);
+
+ span = Math.min(d - c, hi - d - 1);
+ vecswap(b, hi - span, span, array);
+
+ span = b - a;
+ if (span > 1)
+ qsort(array, from, span);
+
+ span = d - c;
+ if (span > 1)
+ qsort(array, hi - span, span);
+ }
+
+ /**
+ * Performs a stable sort on the elements, arranging them according to their
+ * natural order.
+ *
+ * @param a the char array to sort
+ */
+ public static void sort(char[] a)
+ {
+ qsort(a, 0, a.length);
+ }
+
+ /**
+ * Performs a stable sort on the elements, arranging them according to their
+ * natural order.
+ *
+ * @param a the char array to sort
+ * @param fromIndex the first index to sort (inclusive)
+ * @param toIndex the last index to sort (exclusive)
+ * @throws IllegalArgumentException if fromIndex &gt; toIndex
+ * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; a.length
+ */
+ public static void sort(char[] a, int fromIndex, int toIndex)
+ {
+ if (fromIndex > toIndex)
+ throw new IllegalArgumentException();
+ if (fromIndex < 0)
+ throw new ArrayIndexOutOfBoundsException();
+ qsort(a, fromIndex, toIndex - fromIndex);
+ }
+
+ /**
+ * Finds the index of the median of three array elements.
+ *
+ * @param a the first index
+ * @param b the second index
+ * @param c the third index
+ * @param d the array
+ * @return the index (a, b, or c) which has the middle value of the three
+ */
+ private static int med3(int a, int b, int c, char[] d)
+ {
+ return (d[a] < d[b]
+ ? (d[b] < d[c] ? b : d[a] < d[c] ? c : a)
+ : (d[b] > d[c] ? b : d[a] > d[c] ? c : a));
+ }
+
+ /**
+ * Swaps the elements at two locations of an array
+ *
+ * @param i the first index
+ * @param j the second index
+ * @param a the array
+ */
+ private static void swap(int i, int j, char[] a)
+ {
+ char c = a[i];
+ a[i] = a[j];
+ a[j] = c;
+ }
+
+ /**
+ * Swaps two ranges of an array.
+ *
+ * @param i the first range start
+ * @param j the second range start
+ * @param n the element count
+ * @param a the array
+ */
+ private static void vecswap(int i, int j, int n, char[] a)
+ {
+ for ( ; n > 0; i++, j++, n--)
+ swap(i, j, a);
+ }
+
+ /**
+ * Performs a recursive modified quicksort.
+ *
+ * @param array the array to sort
+ * @param from the start index (inclusive)
+ * @param count the number of elements to sort
+ */
+ private static void qsort(char[] array, int from, int count)
+ {
+ // Use an insertion sort on small arrays.
+ if (count <= 7)
+ {
+ for (int i = from + 1; i < from + count; i++)
+ for (int j = i; j > from && array[j - 1] > array[j]; j--)
+ swap(j, j - 1, array);
+ return;
+ }
+
+ // Determine a good median element.
+ int mid = from + count / 2;
+ int lo = from;
+ int hi = from + count - 1;
+
+ if (count > 40)
+ { // big arrays, pseudomedian of 9
+ int s = count / 8;
+ lo = med3(lo, lo + s, lo + 2 * s, array);
+ mid = med3(mid - s, mid, mid + s, array);
+ hi = med3(hi - 2 * s, hi - s, hi, array);
+ }
+ mid = med3(lo, mid, hi, array);
+
+ int a, b, c, d;
+ int comp;
+
+ // Pull the median element out of the fray, and use it as a pivot.
+ swap(from, mid, array);
+ a = b = from;
+ c = d = from + count - 1;
+
+ // Repeatedly move b and c to each other, swapping elements so
+ // that all elements before index b are less than the pivot, and all
+ // elements after index c are greater than the pivot. a and b track
+ // the elements equal to the pivot.
+ while (true)
+ {
+ while (b <= c && (comp = array[b] - array[from]) <= 0)
+ {
+ if (comp == 0)
+ {
+ swap(a, b, array);
+ a++;
+ }
+ b++;
+ }
+ while (c >= b && (comp = array[c] - array[from]) >= 0)
+ {
+ if (comp == 0)
+ {
+ swap(c, d, array);
+ d--;
+ }
+ c--;
+ }
+ if (b > c)
+ break;
+ swap(b, c, array);
+ b++;
+ c--;
+ }
+
+ // Swap pivot(s) back in place, the recurse on left and right sections.
+ hi = from + count;
+ int span;
+ span = Math.min(a - from, b - a);
+ vecswap(from, b - span, span, array);
+
+ span = Math.min(d - c, hi - d - 1);
+ vecswap(b, hi - span, span, array);
+
+ span = b - a;
+ if (span > 1)
+ qsort(array, from, span);
+
+ span = d - c;
+ if (span > 1)
+ qsort(array, hi - span, span);
+ }
+
+ /**
+ * Performs a stable sort on the elements, arranging them according to their
+ * natural order.
+ *
+ * @param a the short array to sort
+ */
+ public static void sort(short[] a)
+ {
+ qsort(a, 0, a.length);
+ }
+
+ /**
+ * Performs a stable sort on the elements, arranging them according to their
+ * natural order.
+ *
+ * @param a the short array to sort
+ * @param fromIndex the first index to sort (inclusive)
+ * @param toIndex the last index to sort (exclusive)
+ * @throws IllegalArgumentException if fromIndex &gt; toIndex
+ * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; a.length
+ */
+ public static void sort(short[] a, int fromIndex, int toIndex)
+ {
+ if (fromIndex > toIndex)
+ throw new IllegalArgumentException();
+ if (fromIndex < 0)
+ throw new ArrayIndexOutOfBoundsException();
+ qsort(a, fromIndex, toIndex - fromIndex);
+ }
+
+ /**
+ * Finds the index of the median of three array elements.
+ *
+ * @param a the first index
+ * @param b the second index
+ * @param c the third index
+ * @param d the array
+ * @return the index (a, b, or c) which has the middle value of the three
+ */
+ private static int med3(int a, int b, int c, short[] d)
+ {
+ return (d[a] < d[b]
+ ? (d[b] < d[c] ? b : d[a] < d[c] ? c : a)
+ : (d[b] > d[c] ? b : d[a] > d[c] ? c : a));
+ }
+
+ /**
+ * Swaps the elements at two locations of an array
+ *
+ * @param i the first index
+ * @param j the second index
+ * @param a the array
+ */
+ private static void swap(int i, int j, short[] a)
+ {
+ short c = a[i];
+ a[i] = a[j];
+ a[j] = c;
+ }
+
+ /**
+ * Swaps two ranges of an array.
+ *
+ * @param i the first range start
+ * @param j the second range start
+ * @param n the element count
+ * @param a the array
+ */
+ private static void vecswap(int i, int j, int n, short[] a)
+ {
+ for ( ; n > 0; i++, j++, n--)
+ swap(i, j, a);
+ }
+
+ /**
+ * Performs a recursive modified quicksort.
+ *
+ * @param array the array to sort
+ * @param from the start index (inclusive)
+ * @param count the number of elements to sort
+ */
+ private static void qsort(short[] array, int from, int count)
+ {
+ // Use an insertion sort on small arrays.
+ if (count <= 7)
+ {
+ for (int i = from + 1; i < from + count; i++)
+ for (int j = i; j > from && array[j - 1] > array[j]; j--)
+ swap(j, j - 1, array);
+ return;
+ }
+
+ // Determine a good median element.
+ int mid = from + count / 2;
+ int lo = from;
+ int hi = from + count - 1;
+
+ if (count > 40)
+ { // big arrays, pseudomedian of 9
+ int s = count / 8;
+ lo = med3(lo, lo + s, lo + 2 * s, array);
+ mid = med3(mid - s, mid, mid + s, array);
+ hi = med3(hi - 2 * s, hi - s, hi, array);
+ }
+ mid = med3(lo, mid, hi, array);
+
+ int a, b, c, d;
+ int comp;
+
+ // Pull the median element out of the fray, and use it as a pivot.
+ swap(from, mid, array);
+ a = b = from;
+ c = d = from + count - 1;
+
+ // Repeatedly move b and c to each other, swapping elements so
+ // that all elements before index b are less than the pivot, and all
+ // elements after index c are greater than the pivot. a and b track
+ // the elements equal to the pivot.
+ while (true)
+ {
+ while (b <= c && (comp = array[b] - array[from]) <= 0)
+ {
+ if (comp == 0)
+ {
+ swap(a, b, array);
+ a++;
+ }
+ b++;
+ }
+ while (c >= b && (comp = array[c] - array[from]) >= 0)
+ {
+ if (comp == 0)
+ {
+ swap(c, d, array);
+ d--;
+ }
+ c--;
+ }
+ if (b > c)
+ break;
+ swap(b, c, array);
+ b++;
+ c--;
+ }
+
+ // Swap pivot(s) back in place, the recurse on left and right sections.
+ hi = from + count;
+ int span;
+ span = Math.min(a - from, b - a);
+ vecswap(from, b - span, span, array);
+
+ span = Math.min(d - c, hi - d - 1);
+ vecswap(b, hi - span, span, array);
+
+ span = b - a;
+ if (span > 1)
+ qsort(array, from, span);
+
+ span = d - c;
+ if (span > 1)
+ qsort(array, hi - span, span);
+ }
+
+ /**
+ * Performs a stable sort on the elements, arranging them according to their
+ * natural order.
+ *
+ * @param a the int array to sort
+ */
+ public static void sort(int[] a)
+ {
+ qsort(a, 0, a.length);
+ }
+
+ /**
+ * Performs a stable sort on the elements, arranging them according to their
+ * natural order.
+ *
+ * @param a the int array to sort
+ * @param fromIndex the first index to sort (inclusive)
+ * @param toIndex the last index to sort (exclusive)
+ * @throws IllegalArgumentException if fromIndex &gt; toIndex
+ * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; a.length
+ */
+ public static void sort(int[] a, int fromIndex, int toIndex)
+ {
+ if (fromIndex > toIndex)
+ throw new IllegalArgumentException();
+ if (fromIndex < 0)
+ throw new ArrayIndexOutOfBoundsException();
+ qsort(a, fromIndex, toIndex - fromIndex);
+ }
+
+ /**
+ * Finds the index of the median of three array elements.
+ *
+ * @param a the first index
+ * @param b the second index
+ * @param c the third index
+ * @param d the array
+ * @return the index (a, b, or c) which has the middle value of the three
+ */
+ private static int med3(int a, int b, int c, int[] d)
+ {
+ return (d[a] < d[b]
+ ? (d[b] < d[c] ? b : d[a] < d[c] ? c : a)
+ : (d[b] > d[c] ? b : d[a] > d[c] ? c : a));
+ }
+
+ /**
+ * Swaps the elements at two locations of an array
+ *
+ * @param i the first index
+ * @param j the second index
+ * @param a the array
+ */
+ private static void swap(int i, int j, int[] a)
+ {
+ int c = a[i];
+ a[i] = a[j];
+ a[j] = c;
+ }
+
+ /**
+ * Swaps two ranges of an array.
+ *
+ * @param i the first range start
+ * @param j the second range start
+ * @param n the element count
+ * @param a the array
+ */
+ private static void vecswap(int i, int j, int n, int[] a)
+ {
+ for ( ; n > 0; i++, j++, n--)
+ swap(i, j, a);
+ }
+
+ /**
+ * Compares two integers in natural order, since a - b is inadequate.
+ *
+ * @param a the first int
+ * @param b the second int
+ * @return &lt; 0, 0, or &gt; 0 accorting to the comparison
+ */
+ private static int compare(int a, int b)
+ {
+ return a < b ? -1 : a == b ? 0 : 1;
+ }
+
+ /**
+ * Performs a recursive modified quicksort.
+ *
+ * @param array the array to sort
+ * @param from the start index (inclusive)
+ * @param count the number of elements to sort
+ */
+ private static void qsort(int[] array, int from, int count)
+ {
+ // Use an insertion sort on small arrays.
+ if (count <= 7)
+ {
+ for (int i = from + 1; i < from + count; i++)
+ for (int j = i; j > from && array[j - 1] > array[j]; j--)
+ swap(j, j - 1, array);
+ return;
+ }
+
+ // Determine a good median element.
+ int mid = from + count / 2;
+ int lo = from;
+ int hi = from + count - 1;
+
+ if (count > 40)
+ { // big arrays, pseudomedian of 9
+ int s = count / 8;
+ lo = med3(lo, lo + s, lo + 2 * s, array);
+ mid = med3(mid - s, mid, mid + s, array);
+ hi = med3(hi - 2 * s, hi - s, hi, array);
+ }
+ mid = med3(lo, mid, hi, array);
+
+ int a, b, c, d;
+ int comp;
+
+ // Pull the median element out of the fray, and use it as a pivot.
+ swap(from, mid, array);
+ a = b = from;
+ c = d = from + count - 1;
+
+ // Repeatedly move b and c to each other, swapping elements so
+ // that all elements before index b are less than the pivot, and all
+ // elements after index c are greater than the pivot. a and b track
+ // the elements equal to the pivot.
+ while (true)
+ {
+ while (b <= c && (comp = compare(array[b], array[from])) <= 0)
+ {
+ if (comp == 0)
+ {
+ swap(a, b, array);
+ a++;
+ }
+ b++;
+ }
+ while (c >= b && (comp = compare(array[c], array[from])) >= 0)
+ {
+ if (comp == 0)
+ {
+ swap(c, d, array);
+ d--;
+ }
+ c--;
+ }
+ if (b > c)
+ break;
+ swap(b, c, array);
+ b++;
+ c--;
+ }
+
+ // Swap pivot(s) back in place, the recurse on left and right sections.
+ hi = from + count;
+ int span;
+ span = Math.min(a - from, b - a);
+ vecswap(from, b - span, span, array);
+
+ span = Math.min(d - c, hi - d - 1);
+ vecswap(b, hi - span, span, array);
+
+ span = b - a;
+ if (span > 1)
+ qsort(array, from, span);
+
+ span = d - c;
+ if (span > 1)
+ qsort(array, hi - span, span);
+ }
+
+ /**
+ * Performs a stable sort on the elements, arranging them according to their
+ * natural order.
+ *
+ * @param a the long array to sort
+ */
+ public static void sort(long[] a)
+ {
+ qsort(a, 0, a.length);
+ }
+
+ /**
+ * Performs a stable sort on the elements, arranging them according to their
+ * natural order.
+ *
+ * @param a the long array to sort
+ * @param fromIndex the first index to sort (inclusive)
+ * @param toIndex the last index to sort (exclusive)
+ * @throws IllegalArgumentException if fromIndex &gt; toIndex
+ * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; a.length
+ */
+ public static void sort(long[] a, int fromIndex, int toIndex)
+ {
+ if (fromIndex > toIndex)
+ throw new IllegalArgumentException();
+ if (fromIndex < 0)
+ throw new ArrayIndexOutOfBoundsException();
+ qsort(a, fromIndex, toIndex - fromIndex);
+ }
+
+ /**
+ * Finds the index of the median of three array elements.
+ *
+ * @param a the first index
+ * @param b the second index
+ * @param c the third index
+ * @param d the array
+ * @return the index (a, b, or c) which has the middle value of the three
+ */
+ private static int med3(int a, int b, int c, long[] d)
+ {
+ return (d[a] < d[b]
+ ? (d[b] < d[c] ? b : d[a] < d[c] ? c : a)
+ : (d[b] > d[c] ? b : d[a] > d[c] ? c : a));
+ }
+
+ /**
+ * Swaps the elements at two locations of an array
+ *
+ * @param i the first index
+ * @param j the second index
+ * @param a the array
+ */
+ private static void swap(int i, int j, long[] a)
+ {
+ long c = a[i];
+ a[i] = a[j];
+ a[j] = c;
+ }
+
+ /**
+ * Swaps two ranges of an array.
+ *
+ * @param i the first range start
+ * @param j the second range start
+ * @param n the element count
+ * @param a the array
+ */
+ private static void vecswap(int i, int j, int n, long[] a)
+ {
+ for ( ; n > 0; i++, j++, n--)
+ swap(i, j, a);
+ }
+
+ /**
+ * Compares two longs in natural order, since a - b is inadequate.
+ *
+ * @param a the first long
+ * @param b the second long
+ * @return &lt; 0, 0, or &gt; 0 accorting to the comparison
+ */
+ private static int compare(long a, long b)
+ {
+ return a < b ? -1 : a == b ? 0 : 1;
+ }
+
+ /**
+ * Performs a recursive modified quicksort.
+ *
+ * @param array the array to sort
+ * @param from the start index (inclusive)
+ * @param count the number of elements to sort
+ */
+ private static void qsort(long[] array, int from, int count)
+ {
+ // Use an insertion sort on small arrays.
+ if (count <= 7)
+ {
+ for (int i = from + 1; i < from + count; i++)
+ for (int j = i; j > from && array[j - 1] > array[j]; j--)
+ swap(j, j - 1, array);
+ return;
+ }
+
+ // Determine a good median element.
+ int mid = from + count / 2;
+ int lo = from;
+ int hi = from + count - 1;
+
+ if (count > 40)
+ { // big arrays, pseudomedian of 9
+ int s = count / 8;
+ lo = med3(lo, lo + s, lo + 2 * s, array);
+ mid = med3(mid - s, mid, mid + s, array);
+ hi = med3(hi - 2 * s, hi - s, hi, array);
+ }
+ mid = med3(lo, mid, hi, array);
+
+ int a, b, c, d;
+ int comp;
+
+ // Pull the median element out of the fray, and use it as a pivot.
+ swap(from, mid, array);
+ a = b = from;
+ c = d = from + count - 1;
+
+ // Repeatedly move b and c to each other, swapping elements so
+ // that all elements before index b are less than the pivot, and all
+ // elements after index c are greater than the pivot. a and b track
+ // the elements equal to the pivot.
+ while (true)
+ {
+ while (b <= c && (comp = compare(array[b], array[from])) <= 0)
+ {
+ if (comp == 0)
+ {
+ swap(a, b, array);
+ a++;
+ }
+ b++;
+ }
+ while (c >= b && (comp = compare(array[c], array[from])) >= 0)
+ {
+ if (comp == 0)
+ {
+ swap(c, d, array);
+ d--;
+ }
+ c--;
+ }
+ if (b > c)
+ break;
+ swap(b, c, array);
+ b++;
+ c--;
+ }
+
+ // Swap pivot(s) back in place, the recurse on left and right sections.
+ hi = from + count;
+ int span;
+ span = Math.min(a - from, b - a);
+ vecswap(from, b - span, span, array);
+
+ span = Math.min(d - c, hi - d - 1);
+ vecswap(b, hi - span, span, array);
+
+ span = b - a;
+ if (span > 1)
+ qsort(array, from, span);
+
+ span = d - c;
+ if (span > 1)
+ qsort(array, hi - span, span);
+ }
+
+ /**
+ * Performs a stable sort on the elements, arranging them according to their
+ * natural order.
+ *
+ * @param a the float array to sort
+ */
+ public static void sort(float[] a)
+ {
+ qsort(a, 0, a.length);
+ }
+
+ /**
+ * Performs a stable sort on the elements, arranging them according to their
+ * natural order.
+ *
+ * @param a the float array to sort
+ * @param fromIndex the first index to sort (inclusive)
+ * @param toIndex the last index to sort (exclusive)
+ * @throws IllegalArgumentException if fromIndex &gt; toIndex
+ * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; a.length
+ */
+ public static void sort(float[] a, int fromIndex, int toIndex)
+ {
+ if (fromIndex > toIndex)
+ throw new IllegalArgumentException();
+ if (fromIndex < 0)
+ throw new ArrayIndexOutOfBoundsException();
+ qsort(a, fromIndex, toIndex - fromIndex);
+ }
+
+ /**
+ * Finds the index of the median of three array elements.
+ *
+ * @param a the first index
+ * @param b the second index
+ * @param c the third index
+ * @param d the array
+ * @return the index (a, b, or c) which has the middle value of the three
+ */
+ private static int med3(int a, int b, int c, float[] d)
+ {
+ return (Float.compare(d[a], d[b]) < 0
+ ? (Float.compare(d[b], d[c]) < 0 ? b
+ : Float.compare(d[a], d[c]) < 0 ? c : a)
+ : (Float.compare(d[b], d[c]) > 0 ? b
+ : Float.compare(d[a], d[c]) > 0 ? c : a));
+ }
+
+ /**
+ * Swaps the elements at two locations of an array
+ *
+ * @param i the first index
+ * @param j the second index
+ * @param a the array
+ */
+ private static void swap(int i, int j, float[] a)
+ {
+ float c = a[i];
+ a[i] = a[j];
+ a[j] = c;
+ }
+
+ /**
+ * Swaps two ranges of an array.
+ *
+ * @param i the first range start
+ * @param j the second range start
+ * @param n the element count
+ * @param a the array
+ */
+ private static void vecswap(int i, int j, int n, float[] a)
+ {
+ for ( ; n > 0; i++, j++, n--)
+ swap(i, j, a);
+ }
+
+ /**
+ * Performs a recursive modified quicksort.
+ *
+ * @param array the array to sort
+ * @param from the start index (inclusive)
+ * @param count the number of elements to sort
+ */
+ private static void qsort(float[] array, int from, int count)
+ {
+ // Use an insertion sort on small arrays.
+ if (count <= 7)
+ {
+ for (int i = from + 1; i < from + count; i++)
+ for (int j = i;
+ j > from && Float.compare(array[j - 1], array[j]) > 0;
+ j--)
+ {
+ swap(j, j - 1, array);
+ }
+ return;
+ }
+
+ // Determine a good median element.
+ int mid = from + count / 2;
+ int lo = from;
+ int hi = from + count - 1;
+
+ if (count > 40)
+ { // big arrays, pseudomedian of 9
+ int s = count / 8;
+ lo = med3(lo, lo + s, lo + 2 * s, array);
+ mid = med3(mid - s, mid, mid + s, array);
+ hi = med3(hi - 2 * s, hi - s, hi, array);
+ }
+ mid = med3(lo, mid, hi, array);
+
+ int a, b, c, d;
+ int comp;
+
+ // Pull the median element out of the fray, and use it as a pivot.
+ swap(from, mid, array);
+ a = b = from;
+ c = d = from + count - 1;
+
+ // Repeatedly move b and c to each other, swapping elements so
+ // that all elements before index b are less than the pivot, and all
+ // elements after index c are greater than the pivot. a and b track
+ // the elements equal to the pivot.
+ while (true)
+ {
+ while (b <= c && (comp = Float.compare(array[b], array[from])) <= 0)
+ {
+ if (comp == 0)
+ {
+ swap(a, b, array);
+ a++;
+ }
+ b++;
+ }
+ while (c >= b && (comp = Float.compare(array[c], array[from])) >= 0)
+ {
+ if (comp == 0)
+ {
+ swap(c, d, array);
+ d--;
+ }
+ c--;
+ }
+ if (b > c)
+ break;
+ swap(b, c, array);
+ b++;
+ c--;
+ }
+
+ // Swap pivot(s) back in place, the recurse on left and right sections.
+ hi = from + count;
+ int span;
+ span = Math.min(a - from, b - a);
+ vecswap(from, b - span, span, array);
+
+ span = Math.min(d - c, hi - d - 1);
+ vecswap(b, hi - span, span, array);
+
+ span = b - a;
+ if (span > 1)
+ qsort(array, from, span);
+
+ span = d - c;
+ if (span > 1)
+ qsort(array, hi - span, span);
+ }
+
+ /**
+ * Performs a stable sort on the elements, arranging them according to their
+ * natural order.
+ *
+ * @param a the double array to sort
+ */
+ public static void sort(double[] a)
+ {
+ qsort(a, 0, a.length);
+ }
+
+ /**
+ * Performs a stable sort on the elements, arranging them according to their
+ * natural order.
+ *
+ * @param a the double array to sort
+ * @param fromIndex the first index to sort (inclusive)
+ * @param toIndex the last index to sort (exclusive)
+ * @throws IllegalArgumentException if fromIndex &gt; toIndex
+ * @throws ArrayIndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; a.length
+ */
+ public static void sort(double[] a, int fromIndex, int toIndex)
+ {
+ if (fromIndex > toIndex)
+ throw new IllegalArgumentException();
+ if (fromIndex < 0)
+ throw new ArrayIndexOutOfBoundsException();
+ qsort(a, fromIndex, toIndex - fromIndex);
+ }
+
+ /**
+ * Finds the index of the median of three array elements.
+ *
+ * @param a the first index
+ * @param b the second index
+ * @param c the third index
+ * @param d the array
+ * @return the index (a, b, or c) which has the middle value of the three
+ */
+ private static int med3(int a, int b, int c, double[] d)
+ {
+ return (Double.compare(d[a], d[b]) < 0
+ ? (Double.compare(d[b], d[c]) < 0 ? b
+ : Double.compare(d[a], d[c]) < 0 ? c : a)
+ : (Double.compare(d[b], d[c]) > 0 ? b
+ : Double.compare(d[a], d[c]) > 0 ? c : a));
+ }
+
+ /**
+ * Swaps the elements at two locations of an array
+ *
+ * @param i the first index
+ * @param j the second index
+ * @param a the array
+ */
+ private static void swap(int i, int j, double[] a)
+ {
+ double c = a[i];
+ a[i] = a[j];
+ a[j] = c;
+ }
+
+ /**
+ * Swaps two ranges of an array.
+ *
+ * @param i the first range start
+ * @param j the second range start
+ * @param n the element count
+ * @param a the array
+ */
+ private static void vecswap(int i, int j, int n, double[] a)
+ {
+ for ( ; n > 0; i++, j++, n--)
+ swap(i, j, a);
+ }
+
+ /**
+ * Performs a recursive modified quicksort.
+ *
+ * @param array the array to sort
+ * @param from the start index (inclusive)
+ * @param count the number of elements to sort
+ */
+ private static void qsort(double[] array, int from, int count)
+ {
+ // Use an insertion sort on small arrays.
+ if (count <= 7)
+ {
+ for (int i = from + 1; i < from + count; i++)
+ for (int j = i;
+ j > from && Double.compare(array[j - 1], array[j]) > 0;
+ j--)
+ {
+ swap(j, j - 1, array);
+ }
+ return;
+ }
+
+ // Determine a good median element.
+ int mid = from + count / 2;
+ int lo = from;
+ int hi = from + count - 1;
+
+ if (count > 40)
+ { // big arrays, pseudomedian of 9
+ int s = count / 8;
+ lo = med3(lo, lo + s, lo + 2 * s, array);
+ mid = med3(mid - s, mid, mid + s, array);
+ hi = med3(hi - 2 * s, hi - s, hi, array);
+ }
+ mid = med3(lo, mid, hi, array);
+
+ int a, b, c, d;
+ int comp;
+
+ // Pull the median element out of the fray, and use it as a pivot.
+ swap(from, mid, array);
+ a = b = from;
+ c = d = from + count - 1;
+
+ // Repeatedly move b and c to each other, swapping elements so
+ // that all elements before index b are less than the pivot, and all
+ // elements after index c are greater than the pivot. a and b track
+ // the elements equal to the pivot.
+ while (true)
+ {
+ while (b <= c && (comp = Double.compare(array[b], array[from])) <= 0)
+ {
+ if (comp == 0)
+ {
+ swap(a, b, array);
+ a++;
+ }
+ b++;
+ }
+ while (c >= b && (comp = Double.compare(array[c], array[from])) >= 0)
+ {
+ if (comp == 0)
+ {
+ swap(c, d, array);
+ d--;
+ }
+ c--;
+ }
+ if (b > c)
+ break;
+ swap(b, c, array);
+ b++;
+ c--;
+ }
+
+ // Swap pivot(s) back in place, the recurse on left and right sections.
+ hi = from + count;
+ int span;
+ span = Math.min(a - from, b - a);
+ vecswap(from, b - span, span, array);
+
+ span = Math.min(d - c, hi - d - 1);
+ vecswap(b, hi - span, span, array);
+
+ span = b - a;
+ if (span > 1)
+ qsort(array, from, span);
+
+ span = d - c;
+ if (span > 1)
+ qsort(array, hi - span, span);
+ }
+
+ /**
+ * Sort an array of Objects according to their natural ordering. The sort is
+ * guaranteed to be stable, that is, equal elements will not be reordered.
+ * The sort algorithm is a mergesort with the merge omitted if the last
+ * element of one half comes before the first element of the other half. This
+ * algorithm gives guaranteed O(n*log(n)) time, at the expense of making a
+ * copy of the array.
+ *
+ * @param a the array to be sorted
+ * @throws ClassCastException if any two elements are not mutually
+ * comparable
+ * @throws NullPointerException if an element is null (since
+ * null.compareTo cannot work)
+ * @see Comparable
+ */
+ public static void sort(Object[] a)
+ {
+ sort(a, 0, a.length, null);
+ }
+
+ /**
+ * Sort an array of Objects according to a Comparator. The sort is
+ * guaranteed to be stable, that is, equal elements will not be reordered.
+ * The sort algorithm is a mergesort with the merge omitted if the last
+ * element of one half comes before the first element of the other half. This
+ * algorithm gives guaranteed O(n*log(n)) time, at the expense of making a
+ * copy of the array.
+ *
+ * @param a the array to be sorted
+ * @param c a Comparator to use in sorting the array; or null to indicate
+ * the elements' natural order
+ * @throws ClassCastException if any two elements are not mutually
+ * comparable by the Comparator provided
+ * @throws NullPointerException if a null element is compared with natural
+ * ordering (only possible when c is null)
+ */
+ public static <T> void sort(T[] a, Comparator<? super T> c)
+ {
+ sort(a, 0, a.length, c);
+ }
+
+ /**
+ * Sort an array of Objects according to their natural ordering. The sort is
+ * guaranteed to be stable, that is, equal elements will not be reordered.
+ * The sort algorithm is a mergesort with the merge omitted if the last
+ * element of one half comes before the first element of the other half. This
+ * algorithm gives guaranteed O(n*log(n)) time, at the expense of making a
+ * copy of the array.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element to be sorted
+ * @param toIndex the index of the last element to be sorted plus one
+ * @throws ClassCastException if any two elements are not mutually
+ * comparable
+ * @throws NullPointerException if an element is null (since
+ * null.compareTo cannot work)
+ * @throws ArrayIndexOutOfBoundsException if fromIndex and toIndex
+ * are not in range.
+ * @throws IllegalArgumentException if fromIndex &gt; toIndex
+ */
+ public static void sort(Object[] a, int fromIndex, int toIndex)
+ {
+ sort(a, fromIndex, toIndex, null);
+ }
+
+ /**
+ * Sort an array of Objects according to a Comparator. The sort is
+ * guaranteed to be stable, that is, equal elements will not be reordered.
+ * The sort algorithm is a mergesort with the merge omitted if the last
+ * element of one half comes before the first element of the other half. This
+ * algorithm gives guaranteed O(n*log(n)) time, at the expense of making a
+ * copy of the array.
+ *
+ * @param a the array to be sorted
+ * @param fromIndex the index of the first element to be sorted
+ * @param toIndex the index of the last element to be sorted plus one
+ * @param c a Comparator to use in sorting the array; or null to indicate
+ * the elements' natural order
+ * @throws ClassCastException if any two elements are not mutually
+ * comparable by the Comparator provided
+ * @throws ArrayIndexOutOfBoundsException if fromIndex and toIndex
+ * are not in range.
+ * @throws IllegalArgumentException if fromIndex &gt; toIndex
+ * @throws NullPointerException if a null element is compared with natural
+ * ordering (only possible when c is null)
+ */
+ public static <T> void sort(T[] a, int fromIndex, int toIndex,
+ Comparator<? super T> c)
+ {
+ if (fromIndex > toIndex)
+ throw new IllegalArgumentException("fromIndex " + fromIndex
+ + " > toIndex " + toIndex);
+ if (fromIndex < 0)
+ throw new ArrayIndexOutOfBoundsException();
+
+ // In general, the code attempts to be simple rather than fast, the
+ // idea being that a good optimising JIT will be able to optimise it
+ // better than I can, and if I try it will make it more confusing for
+ // the JIT. First presort the array in chunks of length 6 with insertion
+ // sort. A mergesort would give too much overhead for this length.
+ for (int chunk = fromIndex; chunk < toIndex; chunk += 6)
+ {
+ int end = Math.min(chunk + 6, toIndex);
+ for (int i = chunk + 1; i < end; i++)
+ {
+ if (Collections.compare(a[i - 1], a[i], c) > 0)
+ {
+ // not already sorted
+ int j = i;
+ T elem = a[j];
+ do
+ {
+ a[j] = a[j - 1];
+ j--;
+ }
+ while (j > chunk
+ && Collections.compare(a[j - 1], elem, c) > 0);
+ a[j] = elem;
+ }
+ }
+ }
+
+ int len = toIndex - fromIndex;
+ // If length is smaller or equal 6 we are done.
+ if (len <= 6)
+ return;
+
+ T[] src = a;
+ T[] dest = (T[]) new Object[len];
+ T[] t = null; // t is used for swapping src and dest
+
+ // The difference of the fromIndex of the src and dest array.
+ int srcDestDiff = -fromIndex;
+
+ // The merges are done in this loop
+ for (int size = 6; size < len; size <<= 1)
+ {
+ for (int start = fromIndex; start < toIndex; start += size << 1)
+ {
+ // mid is the start of the second sublist;
+ // end the start of the next sublist (or end of array).
+ int mid = start + size;
+ int end = Math.min(toIndex, mid + size);
+
+ // The second list is empty or the elements are already in
+ // order - no need to merge
+ if (mid >= end
+ || Collections.compare(src[mid - 1], src[mid], c) <= 0)
+ {
+ System.arraycopy(src, start,
+ dest, start + srcDestDiff, end - start);
+
+ // The two halves just need swapping - no need to merge
+ }
+ else if (Collections.compare(src[start], src[end - 1], c) > 0)
+ {
+ System.arraycopy(src, start,
+ dest, end - size + srcDestDiff, size);
+ System.arraycopy(src, mid,
+ dest, start + srcDestDiff, end - mid);
+
+ }
+ else
+ {
+ // Declare a lot of variables to save repeating
+ // calculations. Hopefully a decent JIT will put these
+ // in registers and make this fast
+ int p1 = start;
+ int p2 = mid;
+ int i = start + srcDestDiff;
+
+ // The main merge loop; terminates as soon as either
+ // half is ended
+ while (p1 < mid && p2 < end)
+ {
+ dest[i++] =
+ src[(Collections.compare(src[p1], src[p2], c) <= 0
+ ? p1++ : p2++)];
+ }
+
+ // Finish up by copying the remainder of whichever half
+ // wasn't finished.
+ if (p1 < mid)
+ System.arraycopy(src, p1, dest, i, mid - p1);
+ else
+ System.arraycopy(src, p2, dest, i, end - p2);
+ }
+ }
+ // swap src and dest ready for the next merge
+ t = src;
+ src = dest;
+ dest = t;
+ fromIndex += srcDestDiff;
+ toIndex += srcDestDiff;
+ srcDestDiff = -srcDestDiff;
+ }
+
+ // make sure the result ends up back in the right place. Note
+ // that src and dest may have been swapped above, so src
+ // contains the sorted array.
+ if (src != a)
+ {
+ // Note that fromIndex == 0.
+ System.arraycopy(src, 0, a, srcDestDiff, toIndex);
+ }
+ }
+
+ /**
+ * Returns a list "view" of the specified array. This method is intended to
+ * make it easy to use the Collections API with existing array-based APIs and
+ * programs. Changes in the list or the array show up in both places. The
+ * list does not support element addition or removal, but does permit
+ * value modification. The returned list implements both Serializable and
+ * RandomAccess.
+ *
+ * @param a the array to return a view of (<code>null</code> not permitted)
+ * @return a fixed-size list, changes to which "write through" to the array
+ *
+ * @throws NullPointerException if <code>a</code> is <code>null</code>.
+ * @see Serializable
+ * @see RandomAccess
+ * @see Arrays.ArrayList
+ */
+ public static <T> List<T> asList(final T... a)
+ {
+ return new Arrays.ArrayList(a);
+ }
+
+ /**
+ * Returns the hashcode of an array of long numbers. If two arrays
+ * are equal, according to <code>equals()</code>, they should have the
+ * same hashcode. The hashcode returned by the method is equal to that
+ * obtained by the corresponding <code>List</code> object. This has the same
+ * data, but represents longs in their wrapper class, <code>Long</code>.
+ * For <code>null</code>, 0 is returned.
+ *
+ * @param v an array of long numbers for which the hash code should be
+ * computed.
+ * @return the hash code of the array, or 0 if null was given.
+ * @since 1.5
+ */
+ public static int hashCode(long[] v)
+ {
+ if (v == null)
+ return 0;
+ int result = 1;
+ for (int i = 0; i < v.length; ++i)
+ {
+ int elt = (int) (v[i] ^ (v[i] >>> 32));
+ result = 31 * result + elt;
+ }
+ return result;
+ }
+
+ /**
+ * Returns the hashcode of an array of integer numbers. If two arrays
+ * are equal, according to <code>equals()</code>, they should have the
+ * same hashcode. The hashcode returned by the method is equal to that
+ * obtained by the corresponding <code>List</code> object. This has the same
+ * data, but represents ints in their wrapper class, <code>Integer</code>.
+ * For <code>null</code>, 0 is returned.
+ *
+ * @param v an array of integer numbers for which the hash code should be
+ * computed.
+ * @return the hash code of the array, or 0 if null was given.
+ * @since 1.5
+ */
+ public static int hashCode(int[] v)
+ {
+ if (v == null)
+ return 0;
+ int result = 1;
+ for (int i = 0; i < v.length; ++i)
+ result = 31 * result + v[i];
+ return result;
+ }
+
+ /**
+ * Returns the hashcode of an array of short numbers. If two arrays
+ * are equal, according to <code>equals()</code>, they should have the
+ * same hashcode. The hashcode returned by the method is equal to that
+ * obtained by the corresponding <code>List</code> object. This has the same
+ * data, but represents shorts in their wrapper class, <code>Short</code>.
+ * For <code>null</code>, 0 is returned.
+ *
+ * @param v an array of short numbers for which the hash code should be
+ * computed.
+ * @return the hash code of the array, or 0 if null was given.
+ * @since 1.5
+ */
+ public static int hashCode(short[] v)
+ {
+ if (v == null)
+ return 0;
+ int result = 1;
+ for (int i = 0; i < v.length; ++i)
+ result = 31 * result + v[i];
+ return result;
+ }
+
+ /**
+ * Returns the hashcode of an array of characters. If two arrays
+ * are equal, according to <code>equals()</code>, they should have the
+ * same hashcode. The hashcode returned by the method is equal to that
+ * obtained by the corresponding <code>List</code> object. This has the same
+ * data, but represents chars in their wrapper class, <code>Character</code>.
+ * For <code>null</code>, 0 is returned.
+ *
+ * @param v an array of characters for which the hash code should be
+ * computed.
+ * @return the hash code of the array, or 0 if null was given.
+ * @since 1.5
+ */
+ public static int hashCode(char[] v)
+ {
+ if (v == null)
+ return 0;
+ int result = 1;
+ for (int i = 0; i < v.length; ++i)
+ result = 31 * result + v[i];
+ return result;
+ }
+
+ /**
+ * Returns the hashcode of an array of bytes. If two arrays
+ * are equal, according to <code>equals()</code>, they should have the
+ * same hashcode. The hashcode returned by the method is equal to that
+ * obtained by the corresponding <code>List</code> object. This has the same
+ * data, but represents bytes in their wrapper class, <code>Byte</code>.
+ * For <code>null</code>, 0 is returned.
+ *
+ * @param v an array of bytes for which the hash code should be
+ * computed.
+ * @return the hash code of the array, or 0 if null was given.
+ * @since 1.5
+ */
+ public static int hashCode(byte[] v)
+ {
+ if (v == null)
+ return 0;
+ int result = 1;
+ for (int i = 0; i < v.length; ++i)
+ result = 31 * result + v[i];
+ return result;
+ }
+
+ /**
+ * Returns the hashcode of an array of booleans. If two arrays
+ * are equal, according to <code>equals()</code>, they should have the
+ * same hashcode. The hashcode returned by the method is equal to that
+ * obtained by the corresponding <code>List</code> object. This has the same
+ * data, but represents booleans in their wrapper class,
+ * <code>Boolean</code>. For <code>null</code>, 0 is returned.
+ *
+ * @param v an array of booleans for which the hash code should be
+ * computed.
+ * @return the hash code of the array, or 0 if null was given.
+ * @since 1.5
+ */
+ public static int hashCode(boolean[] v)
+ {
+ if (v == null)
+ return 0;
+ int result = 1;
+ for (int i = 0; i < v.length; ++i)
+ result = 31 * result + (v[i] ? 1231 : 1237);
+ return result;
+ }
+
+ /**
+ * Returns the hashcode of an array of floats. If two arrays
+ * are equal, according to <code>equals()</code>, they should have the
+ * same hashcode. The hashcode returned by the method is equal to that
+ * obtained by the corresponding <code>List</code> object. This has the same
+ * data, but represents floats in their wrapper class, <code>Float</code>.
+ * For <code>null</code>, 0 is returned.
+ *
+ * @param v an array of floats for which the hash code should be
+ * computed.
+ * @return the hash code of the array, or 0 if null was given.
+ * @since 1.5
+ */
+ public static int hashCode(float[] v)
+ {
+ if (v == null)
+ return 0;
+ int result = 1;
+ for (int i = 0; i < v.length; ++i)
+ result = 31 * result + Float.floatToIntBits(v[i]);
+ return result;
+ }
+
+ /**
+ * Returns the hashcode of an array of doubles. If two arrays
+ * are equal, according to <code>equals()</code>, they should have the
+ * same hashcode. The hashcode returned by the method is equal to that
+ * obtained by the corresponding <code>List</code> object. This has the same
+ * data, but represents doubles in their wrapper class, <code>Double</code>.
+ * For <code>null</code>, 0 is returned.
+ *
+ * @param v an array of doubles for which the hash code should be
+ * computed.
+ * @return the hash code of the array, or 0 if null was given.
+ * @since 1.5
+ */
+ public static int hashCode(double[] v)
+ {
+ if (v == null)
+ return 0;
+ int result = 1;
+ for (int i = 0; i < v.length; ++i)
+ {
+ long l = Double.doubleToLongBits(v[i]);
+ int elt = (int) (l ^ (l >>> 32));
+ result = 31 * result + elt;
+ }
+ return result;
+ }
+
+ /**
+ * Returns the hashcode of an array of objects. If two arrays
+ * are equal, according to <code>equals()</code>, they should have the
+ * same hashcode. The hashcode returned by the method is equal to that
+ * obtained by the corresponding <code>List</code> object.
+ * For <code>null</code>, 0 is returned.
+ *
+ * @param v an array of integer numbers for which the hash code should be
+ * computed.
+ * @return the hash code of the array, or 0 if null was given.
+ * @since 1.5
+ */
+ public static int hashCode(Object[] v)
+ {
+ if (v == null)
+ return 0;
+ int result = 1;
+ for (int i = 0; i < v.length; ++i)
+ {
+ int elt = v[i] == null ? 0 : v[i].hashCode();
+ result = 31 * result + elt;
+ }
+ return result;
+ }
+
+ public static int deepHashCode(Object[] v)
+ {
+ if (v == null)
+ return 0;
+ int result = 1;
+ for (int i = 0; i < v.length; ++i)
+ {
+ int elt;
+ if (v[i] == null)
+ elt = 0;
+ else if (v[i] instanceof boolean[])
+ elt = hashCode((boolean[]) v[i]);
+ else if (v[i] instanceof byte[])
+ elt = hashCode((byte[]) v[i]);
+ else if (v[i] instanceof char[])
+ elt = hashCode((char[]) v[i]);
+ else if (v[i] instanceof short[])
+ elt = hashCode((short[]) v[i]);
+ else if (v[i] instanceof int[])
+ elt = hashCode((int[]) v[i]);
+ else if (v[i] instanceof long[])
+ elt = hashCode((long[]) v[i]);
+ else if (v[i] instanceof float[])
+ elt = hashCode((float[]) v[i]);
+ else if (v[i] instanceof double[])
+ elt = hashCode((double[]) v[i]);
+ else if (v[i] instanceof Object[])
+ elt = hashCode((Object[]) v[i]);
+ else
+ elt = v[i].hashCode();
+ result = 31 * result + elt;
+ }
+ return result;
+ }
+
+ /** @since 1.5 */
+ public static boolean deepEquals(Object[] v1, Object[] v2)
+ {
+ if (v1 == null)
+ return v2 == null;
+ if (v2 == null || v1.length != v2.length)
+ return false;
+
+ for (int i = 0; i < v1.length; ++i)
+ {
+ Object e1 = v1[i];
+ Object e2 = v2[i];
+
+ if (e1 == e2)
+ continue;
+ if (e1 == null || e2 == null)
+ return false;
+
+ boolean check;
+ if (e1 instanceof boolean[] && e2 instanceof boolean[])
+ check = equals((boolean[]) e1, (boolean[]) e2);
+ else if (e1 instanceof byte[] && e2 instanceof byte[])
+ check = equals((byte[]) e1, (byte[]) e2);
+ else if (e1 instanceof char[] && e2 instanceof char[])
+ check = equals((char[]) e1, (char[]) e2);
+ else if (e1 instanceof short[] && e2 instanceof short[])
+ check = equals((short[]) e1, (short[]) e2);
+ else if (e1 instanceof int[] && e2 instanceof int[])
+ check = equals((int[]) e1, (int[]) e2);
+ else if (e1 instanceof long[] && e2 instanceof long[])
+ check = equals((long[]) e1, (long[]) e2);
+ else if (e1 instanceof float[] && e2 instanceof float[])
+ check = equals((float[]) e1, (float[]) e2);
+ else if (e1 instanceof double[] && e2 instanceof double[])
+ check = equals((double[]) e1, (double[]) e2);
+ else if (e1 instanceof Object[] && e2 instanceof Object[])
+ check = equals((Object[]) e1, (Object[]) e2);
+ else
+ check = e1.equals(e2);
+ if (! check)
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a String representation of the argument array. Returns "null"
+ * if <code>a</code> is null.
+ * @param v the array to represent
+ * @return a String representing this array
+ * @since 1.5
+ */
+ public static String toString(boolean[] v)
+ {
+ if (v == null)
+ return "null";
+ CPStringBuilder b = new CPStringBuilder("[");
+ for (int i = 0; i < v.length; ++i)
+ {
+ if (i > 0)
+ b.append(", ");
+ b.append(v[i]);
+ }
+ b.append("]");
+ return b.toString();
+ }
+
+ /**
+ * Returns a String representation of the argument array. Returns "null"
+ * if <code>a</code> is null.
+ * @param v the array to represent
+ * @return a String representing this array
+ * @since 1.5
+ */
+ public static String toString(byte[] v)
+ {
+ if (v == null)
+ return "null";
+ CPStringBuilder b = new CPStringBuilder("[");
+ for (int i = 0; i < v.length; ++i)
+ {
+ if (i > 0)
+ b.append(", ");
+ b.append(v[i]);
+ }
+ b.append("]");
+ return b.toString();
+ }
+
+ /**
+ * Returns a String representation of the argument array. Returns "null"
+ * if <code>a</code> is null.
+ * @param v the array to represent
+ * @return a String representing this array
+ * @since 1.5
+ */
+ public static String toString(char[] v)
+ {
+ if (v == null)
+ return "null";
+ CPStringBuilder b = new CPStringBuilder("[");
+ for (int i = 0; i < v.length; ++i)
+ {
+ if (i > 0)
+ b.append(", ");
+ b.append(v[i]);
+ }
+ b.append("]");
+ return b.toString();
+ }
+
+ /**
+ * Returns a String representation of the argument array. Returns "null"
+ * if <code>a</code> is null.
+ * @param v the array to represent
+ * @return a String representing this array
+ * @since 1.5
+ */
+ public static String toString(short[] v)
+ {
+ if (v == null)
+ return "null";
+ CPStringBuilder b = new CPStringBuilder("[");
+ for (int i = 0; i < v.length; ++i)
+ {
+ if (i > 0)
+ b.append(", ");
+ b.append(v[i]);
+ }
+ b.append("]");
+ return b.toString();
+ }
+
+ /**
+ * Returns a String representation of the argument array. Returns "null"
+ * if <code>a</code> is null.
+ * @param v the array to represent
+ * @return a String representing this array
+ * @since 1.5
+ */
+ public static String toString(int[] v)
+ {
+ if (v == null)
+ return "null";
+ CPStringBuilder b = new CPStringBuilder("[");
+ for (int i = 0; i < v.length; ++i)
+ {
+ if (i > 0)
+ b.append(", ");
+ b.append(v[i]);
+ }
+ b.append("]");
+ return b.toString();
+ }
+
+ /**
+ * Returns a String representation of the argument array. Returns "null"
+ * if <code>a</code> is null.
+ * @param v the array to represent
+ * @return a String representing this array
+ * @since 1.5
+ */
+ public static String toString(long[] v)
+ {
+ if (v == null)
+ return "null";
+ CPStringBuilder b = new CPStringBuilder("[");
+ for (int i = 0; i < v.length; ++i)
+ {
+ if (i > 0)
+ b.append(", ");
+ b.append(v[i]);
+ }
+ b.append("]");
+ return b.toString();
+ }
+
+ /**
+ * Returns a String representation of the argument array. Returns "null"
+ * if <code>a</code> is null.
+ * @param v the array to represent
+ * @return a String representing this array
+ * @since 1.5
+ */
+ public static String toString(float[] v)
+ {
+ if (v == null)
+ return "null";
+ CPStringBuilder b = new CPStringBuilder("[");
+ for (int i = 0; i < v.length; ++i)
+ {
+ if (i > 0)
+ b.append(", ");
+ b.append(v[i]);
+ }
+ b.append("]");
+ return b.toString();
+ }
+
+ /**
+ * Returns a String representation of the argument array. Returns "null"
+ * if <code>a</code> is null.
+ * @param v the array to represent
+ * @return a String representing this array
+ * @since 1.5
+ */
+ public static String toString(double[] v)
+ {
+ if (v == null)
+ return "null";
+ CPStringBuilder b = new CPStringBuilder("[");
+ for (int i = 0; i < v.length; ++i)
+ {
+ if (i > 0)
+ b.append(", ");
+ b.append(v[i]);
+ }
+ b.append("]");
+ return b.toString();
+ }
+
+ /**
+ * Returns a String representation of the argument array. Returns "null"
+ * if <code>a</code> is null.
+ * @param v the array to represent
+ * @return a String representing this array
+ * @since 1.5
+ */
+ public static String toString(Object[] v)
+ {
+ if (v == null)
+ return "null";
+ CPStringBuilder b = new CPStringBuilder("[");
+ for (int i = 0; i < v.length; ++i)
+ {
+ if (i > 0)
+ b.append(", ");
+ b.append(v[i]);
+ }
+ b.append("]");
+ return b.toString();
+ }
+
+ private static void deepToString(Object[] v, CPStringBuilder b, HashSet seen)
+ {
+ b.append("[");
+ for (int i = 0; i < v.length; ++i)
+ {
+ if (i > 0)
+ b.append(", ");
+ Object elt = v[i];
+ if (elt == null)
+ b.append("null");
+ else if (elt instanceof boolean[])
+ b.append(toString((boolean[]) elt));
+ else if (elt instanceof byte[])
+ b.append(toString((byte[]) elt));
+ else if (elt instanceof char[])
+ b.append(toString((char[]) elt));
+ else if (elt instanceof short[])
+ b.append(toString((short[]) elt));
+ else if (elt instanceof int[])
+ b.append(toString((int[]) elt));
+ else if (elt instanceof long[])
+ b.append(toString((long[]) elt));
+ else if (elt instanceof float[])
+ b.append(toString((float[]) elt));
+ else if (elt instanceof double[])
+ b.append(toString((double[]) elt));
+ else if (elt instanceof Object[])
+ {
+ Object[] os = (Object[]) elt;
+ if (seen.contains(os))
+ b.append("[...]");
+ else
+ {
+ seen.add(os);
+ deepToString(os, b, seen);
+ }
+ }
+ else
+ b.append(elt);
+ }
+ b.append("]");
+ }
+
+ /** @since 1.5 */
+ public static String deepToString(Object[] v)
+ {
+ if (v == null)
+ return "null";
+ HashSet seen = new HashSet();
+ CPStringBuilder b = new CPStringBuilder();
+ deepToString(v, b, seen);
+ return b.toString();
+ }
+
+ /**
+ * Inner class used by {@link #asList(Object[])} to provide a list interface
+ * to an array. The name, though it clashes with java.util.ArrayList, is
+ * Sun's choice for Serialization purposes. Element addition and removal
+ * is prohibited, but values can be modified.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @status updated to 1.4
+ */
+ private static final class ArrayList<E> extends AbstractList<E>
+ implements Serializable, RandomAccess
+ {
+ // We override the necessary methods, plus others which will be much
+ // more efficient with direct iteration rather than relying on iterator().
+
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = -2764017481108945198L;
+
+ /**
+ * The array we are viewing.
+ * @serial the array
+ */
+ private final E[] a;
+
+ /**
+ * Construct a list view of the array.
+ * @param a the array to view
+ * @throws NullPointerException if a is null
+ */
+ ArrayList(E[] a)
+ {
+ // We have to explicitly check.
+ if (a == null)
+ throw new NullPointerException();
+ this.a = a;
+ }
+
+ /**
+ * Returns the object at the specified index in
+ * the array.
+ *
+ * @param index The index to retrieve an object from.
+ * @return The object at the array index specified.
+ */
+ public E get(int index)
+ {
+ return a[index];
+ }
+
+ /**
+ * Returns the size of the array.
+ *
+ * @return The size.
+ */
+ public int size()
+ {
+ return a.length;
+ }
+
+ /**
+ * Replaces the object at the specified index
+ * with the supplied element.
+ *
+ * @param index The index at which to place the new object.
+ * @param element The new object.
+ * @return The object replaced by this operation.
+ */
+ public E set(int index, E element)
+ {
+ E old = a[index];
+ a[index] = element;
+ return old;
+ }
+
+ /**
+ * Returns true if the array contains the
+ * supplied object.
+ *
+ * @param o The object to look for.
+ * @return True if the object was found.
+ */
+ public boolean contains(Object o)
+ {
+ return lastIndexOf(o) >= 0;
+ }
+
+ /**
+ * Returns the first index at which the
+ * object, o, occurs in the array.
+ *
+ * @param o The object to search for.
+ * @return The first relevant index.
+ */
+ public int indexOf(Object o)
+ {
+ int size = a.length;
+ for (int i = 0; i < size; i++)
+ if (ArrayList.equals(o, a[i]))
+ return i;
+ return -1;
+ }
+
+ /**
+ * Returns the last index at which the
+ * object, o, occurs in the array.
+ *
+ * @param o The object to search for.
+ * @return The last relevant index.
+ */
+ public int lastIndexOf(Object o)
+ {
+ int i = a.length;
+ while (--i >= 0)
+ if (ArrayList.equals(o, a[i]))
+ return i;
+ return -1;
+ }
+
+ /**
+ * Transforms the list into an array of
+ * objects, by simplying cloning the array
+ * wrapped by this list.
+ *
+ * @return A clone of the internal array.
+ */
+ public Object[] toArray()
+ {
+ return (Object[]) a.clone();
+ }
+
+ /**
+ * Copies the objects from this list into
+ * the supplied array. The supplied array
+ * is shrunk or enlarged to the size of the
+ * internal array, and filled with its objects.
+ *
+ * @param array The array to fill with the objects in this list.
+ * @return The array containing the objects in this list,
+ * which may or may not be == to array.
+ */
+ public <T> T[] toArray(T[] array)
+ {
+ int size = a.length;
+ if (array.length < size)
+ array = (T[]) Array.newInstance(array.getClass().getComponentType(),
+ size);
+ else if (array.length > size)
+ array[size] = null;
+
+ System.arraycopy(a, 0, array, 0, size);
+ return array;
+ }
+ }
+
+ /**
+ * Returns a copy of the supplied array, truncating or padding as
+ * necessary with <code>false</code> to obtain the specified length.
+ * Indices that are valid for both arrays will return the same value.
+ * Indices that only exist in the returned array (due to the new length
+ * being greater than the original length) will return <code>false</code>.
+ * This is equivalent to calling
+ * <code>copyOfRange(original, 0, newLength)</code>.
+ *
+ * @param original the original array to be copied.
+ * @param newLength the length of the returned array.
+ * @return a copy of the original array, truncated or padded with
+ * <code>false</code> to obtain the required length.
+ * @throws NegativeArraySizeException if <code>newLength</code> is negative.
+ * @throws NullPointerException if <code>original</code> is <code>null</code>.
+ * @since 1.6
+ * @see #copyOfRange(boolean[],int,int)
+ */
+ public static boolean[] copyOf(boolean[] original, int newLength)
+ {
+ if (newLength < 0)
+ throw new NegativeArraySizeException("The array size is negative.");
+ return copyOfRange(original, 0, newLength);
+ }
+
+ /**
+ * Copies the specified range of the supplied array to a new
+ * array, padding as necessary with <code>false</code>
+ * if <code>to</code> is greater than the length of the original
+ * array. <code>from</code> must be in the range zero to
+ * <code>original.length</code> and can not be greater than
+ * <code>to</code>. The initial element of the
+ * returned array will be equal to <code>original[from]</code>,
+ * except where <code>from</code> is equal to <code>to</code>
+ * (where a zero-length array will be returned) or <code>
+ * <code>from</code> is equal to <code>original.length</code>
+ * (where an array padded with <code>false</code> will be
+ * returned). The returned array is always of length
+ * <code>to - from</code>.
+ *
+ * @param original the array from which to copy.
+ * @param from the initial index of the range, inclusive.
+ * @param to the final index of the range, exclusive.
+ * @return a copy of the specified range, with padding to
+ * obtain the required length.
+ * @throws ArrayIndexOutOfBoundsException if <code>from < 0</code>
+ * or <code>from > original.length</code>
+ * @throws IllegalArgumentException if <code>from > to</code>
+ * @throws NullPointerException if <code>original</code> is <code>null</code>.
+ * @since 1.6
+ * @see #copyOf(boolean[],int)
+ */
+ public static boolean[] copyOfRange(boolean[] original, int from, int to)
+ {
+ if (from > to)
+ throw new IllegalArgumentException("The initial index is after " +
+ "the final index.");
+ boolean[] newArray = new boolean[to - from];
+ if (to > original.length)
+ {
+ System.arraycopy(original, from, newArray, 0,
+ original.length - from);
+ fill(newArray, original.length, newArray.length, false);
+ }
+ else
+ System.arraycopy(original, from, newArray, 0, to - from);
+ return newArray;
+ }
+
+ /**
+ * Returns a copy of the supplied array, truncating or padding as
+ * necessary with <code>(byte)0</code> to obtain the specified length.
+ * Indices that are valid for both arrays will return the same value.
+ * Indices that only exist in the returned array (due to the new length
+ * being greater than the original length) will return <code>(byte)0</code>.
+ * This is equivalent to calling
+ * <code>copyOfRange(original, 0, newLength)</code>.
+ *
+ * @param original the original array to be copied.
+ * @param newLength the length of the returned array.
+ * @return a copy of the original array, truncated or padded with
+ * <code>(byte)0</code> to obtain the required length.
+ * @throws NegativeArraySizeException if <code>newLength</code> is negative.
+ * @throws NullPointerException if <code>original</code> is <code>null</code>.
+ * @since 1.6
+ * @see #copyOfRange(byte[],int,int)
+ */
+ public static byte[] copyOf(byte[] original, int newLength)
+ {
+ if (newLength < 0)
+ throw new NegativeArraySizeException("The array size is negative.");
+ return copyOfRange(original, 0, newLength);
+ }
+
+ /**
+ * Copies the specified range of the supplied array to a new
+ * array, padding as necessary with <code>(byte)0</code>
+ * if <code>to</code> is greater than the length of the original
+ * array. <code>from</code> must be in the range zero to
+ * <code>original.length</code> and can not be greater than
+ * <code>to</code>. The initial element of the
+ * returned array will be equal to <code>original[from]</code>,
+ * except where <code>from</code> is equal to <code>to</code>
+ * (where a zero-length array will be returned) or <code>
+ * <code>from</code> is equal to <code>original.length</code>
+ * (where an array padded with <code>(byte)0</code> will be
+ * returned). The returned array is always of length
+ * <code>to - from</code>.
+ *
+ * @param original the array from which to copy.
+ * @param from the initial index of the range, inclusive.
+ * @param to the final index of the range, exclusive.
+ * @return a copy of the specified range, with padding to
+ * obtain the required length.
+ * @throws ArrayIndexOutOfBoundsException if <code>from < 0</code>
+ * or <code>from > original.length</code>
+ * @throws IllegalArgumentException if <code>from > to</code>
+ * @throws NullPointerException if <code>original</code> is <code>null</code>.
+ * @since 1.6
+ * @see #copyOf(byte[],int)
+ */
+ public static byte[] copyOfRange(byte[] original, int from, int to)
+ {
+ if (from > to)
+ throw new IllegalArgumentException("The initial index is after " +
+ "the final index.");
+ byte[] newArray = new byte[to - from];
+ if (to > original.length)
+ {
+ System.arraycopy(original, from, newArray, 0,
+ original.length - from);
+ fill(newArray, original.length, newArray.length, (byte)0);
+ }
+ else
+ System.arraycopy(original, from, newArray, 0, to - from);
+ return newArray;
+ }
+
+ /**
+ * Returns a copy of the supplied array, truncating or padding as
+ * necessary with <code>'\0'</code> to obtain the specified length.
+ * Indices that are valid for both arrays will return the same value.
+ * Indices that only exist in the returned array (due to the new length
+ * being greater than the original length) will return <code>'\0'</code>.
+ * This is equivalent to calling
+ * <code>copyOfRange(original, 0, newLength)</code>.
+ *
+ * @param original the original array to be copied.
+ * @param newLength the length of the returned array.
+ * @return a copy of the original array, truncated or padded with
+ * <code>'\0'</code> to obtain the required length.
+ * @throws NegativeArraySizeException if <code>newLength</code> is negative.
+ * @throws NullPointerException if <code>original</code> is <code>null</code>.
+ * @since 1.6
+ * @see #copyOfRange(char[],int,int)
+ */
+ public static char[] copyOf(char[] original, int newLength)
+ {
+ if (newLength < 0)
+ throw new NegativeArraySizeException("The array size is negative.");
+ return copyOfRange(original, 0, newLength);
+ }
+
+ /**
+ * Copies the specified range of the supplied array to a new
+ * array, padding as necessary with <code>'\0'</code>
+ * if <code>to</code> is greater than the length of the original
+ * array. <code>from</code> must be in the range zero to
+ * <code>original.length</code> and can not be greater than
+ * <code>to</code>. The initial element of the
+ * returned array will be equal to <code>original[from]</code>,
+ * except where <code>from</code> is equal to <code>to</code>
+ * (where a zero-length array will be returned) or <code>
+ * <code>from</code> is equal to <code>original.length</code>
+ * (where an array padded with <code>'\0'</code> will be
+ * returned). The returned array is always of length
+ * <code>to - from</code>.
+ *
+ * @param original the array from which to copy.
+ * @param from the initial index of the range, inclusive.
+ * @param to the final index of the range, exclusive.
+ * @return a copy of the specified range, with padding to
+ * obtain the required length.
+ * @throws ArrayIndexOutOfBoundsException if <code>from < 0</code>
+ * or <code>from > original.length</code>
+ * @throws IllegalArgumentException if <code>from > to</code>
+ * @throws NullPointerException if <code>original</code> is <code>null</code>.
+ * @since 1.6
+ * @see #copyOf(char[],int)
+ */
+ public static char[] copyOfRange(char[] original, int from, int to)
+ {
+ if (from > to)
+ throw new IllegalArgumentException("The initial index is after " +
+ "the final index.");
+ char[] newArray = new char[to - from];
+ if (to > original.length)
+ {
+ System.arraycopy(original, from, newArray, 0,
+ original.length - from);
+ fill(newArray, original.length, newArray.length, '\0');
+ }
+ else
+ System.arraycopy(original, from, newArray, 0, to - from);
+ return newArray;
+ }
+
+ /**
+ * Returns a copy of the supplied array, truncating or padding as
+ * necessary with <code>0d</code> to obtain the specified length.
+ * Indices that are valid for both arrays will return the same value.
+ * Indices that only exist in the returned array (due to the new length
+ * being greater than the original length) will return <code>0d</code>.
+ * This is equivalent to calling
+ * <code>copyOfRange(original, 0, newLength)</code>.
+ *
+ * @param original the original array to be copied.
+ * @param newLength the length of the returned array.
+ * @return a copy of the original array, truncated or padded with
+ * <code>0d</code> to obtain the required length.
+ * @throws NegativeArraySizeException if <code>newLength</code> is negative.
+ * @throws NullPointerException if <code>original</code> is <code>null</code>.
+ * @since 1.6
+ * @see #copyOfRange(double[],int,int)
+ */
+ public static double[] copyOf(double[] original, int newLength)
+ {
+ if (newLength < 0)
+ throw new NegativeArraySizeException("The array size is negative.");
+ return copyOfRange(original, 0, newLength);
+ }
+
+ /**
+ * Copies the specified range of the supplied array to a new
+ * array, padding as necessary with <code>0d</code>
+ * if <code>to</code> is greater than the length of the original
+ * array. <code>from</code> must be in the range zero to
+ * <code>original.length</code> and can not be greater than
+ * <code>to</code>. The initial element of the
+ * returned array will be equal to <code>original[from]</code>,
+ * except where <code>from</code> is equal to <code>to</code>
+ * (where a zero-length array will be returned) or <code>
+ * <code>from</code> is equal to <code>original.length</code>
+ * (where an array padded with <code>0d</code> will be
+ * returned). The returned array is always of length
+ * <code>to - from</code>.
+ *
+ * @param original the array from which to copy.
+ * @param from the initial index of the range, inclusive.
+ * @param to the final index of the range, exclusive.
+ * @return a copy of the specified range, with padding to
+ * obtain the required length.
+ * @throws ArrayIndexOutOfBoundsException if <code>from < 0</code>
+ * or <code>from > original.length</code>
+ * @throws IllegalArgumentException if <code>from > to</code>
+ * @throws NullPointerException if <code>original</code> is <code>null</code>.
+ * @since 1.6
+ * @see #copyOf(double[],int)
+ */
+ public static double[] copyOfRange(double[] original, int from, int to)
+ {
+ if (from > to)
+ throw new IllegalArgumentException("The initial index is after " +
+ "the final index.");
+ double[] newArray = new double[to - from];
+ if (to > original.length)
+ {
+ System.arraycopy(original, from, newArray, 0,
+ original.length - from);
+ fill(newArray, original.length, newArray.length, 0d);
+ }
+ else
+ System.arraycopy(original, from, newArray, 0, to - from);
+ return newArray;
+ }
+
+ /**
+ * Returns a copy of the supplied array, truncating or padding as
+ * necessary with <code>0f</code> to obtain the specified length.
+ * Indices that are valid for both arrays will return the same value.
+ * Indices that only exist in the returned array (due to the new length
+ * being greater than the original length) will return <code>0f</code>.
+ * This is equivalent to calling
+ * <code>copyOfRange(original, 0, newLength)</code>.
+ *
+ * @param original the original array to be copied.
+ * @param newLength the length of the returned array.
+ * @return a copy of the original array, truncated or padded with
+ * <code>0f</code> to obtain the required length.
+ * @throws NegativeArraySizeException if <code>newLength</code> is negative.
+ * @throws NullPointerException if <code>original</code> is <code>null</code>.
+ * @since 1.6
+ * @see #copyOfRange(float[],int,int)
+ */
+ public static float[] copyOf(float[] original, int newLength)
+ {
+ if (newLength < 0)
+ throw new NegativeArraySizeException("The array size is negative.");
+ return copyOfRange(original, 0, newLength);
+ }
+
+ /**
+ * Copies the specified range of the supplied array to a new
+ * array, padding as necessary with <code>0f</code>
+ * if <code>to</code> is greater than the length of the original
+ * array. <code>from</code> must be in the range zero to
+ * <code>original.length</code> and can not be greater than
+ * <code>to</code>. The initial element of the
+ * returned array will be equal to <code>original[from]</code>,
+ * except where <code>from</code> is equal to <code>to</code>
+ * (where a zero-length array will be returned) or <code>
+ * <code>from</code> is equal to <code>original.length</code>
+ * (where an array padded with <code>0f</code> will be
+ * returned). The returned array is always of length
+ * <code>to - from</code>.
+ *
+ * @param original the array from which to copy.
+ * @param from the initial index of the range, inclusive.
+ * @param to the final index of the range, exclusive.
+ * @return a copy of the specified range, with padding to
+ * obtain the required length.
+ * @throws ArrayIndexOutOfBoundsException if <code>from < 0</code>
+ * or <code>from > original.length</code>
+ * @throws IllegalArgumentException if <code>from > to</code>
+ * @throws NullPointerException if <code>original</code> is <code>null</code>.
+ * @since 1.6
+ * @see #copyOf(float[],int)
+ */
+ public static float[] copyOfRange(float[] original, int from, int to)
+ {
+ if (from > to)
+ throw new IllegalArgumentException("The initial index is after " +
+ "the final index.");
+ float[] newArray = new float[to - from];
+ if (to > original.length)
+ {
+ System.arraycopy(original, from, newArray, 0,
+ original.length - from);
+ fill(newArray, original.length, newArray.length, 0f);
+ }
+ else
+ System.arraycopy(original, from, newArray, 0, to - from);
+ return newArray;
+ }
+
+ /**
+ * Returns a copy of the supplied array, truncating or padding as
+ * necessary with <code>0</code> to obtain the specified length.
+ * Indices that are valid for both arrays will return the same value.
+ * Indices that only exist in the returned array (due to the new length
+ * being greater than the original length) will return <code>0</code>.
+ * This is equivalent to calling
+ * <code>copyOfRange(original, 0, newLength)</code>.
+ *
+ * @param original the original array to be copied.
+ * @param newLength the length of the returned array.
+ * @return a copy of the original array, truncated or padded with
+ * <code>0</code> to obtain the required length.
+ * @throws NegativeArraySizeException if <code>newLength</code> is negative.
+ * @throws NullPointerException if <code>original</code> is <code>null</code>.
+ * @since 1.6
+ * @see #copyOfRange(int[],int,int)
+ */
+ public static int[] copyOf(int[] original, int newLength)
+ {
+ if (newLength < 0)
+ throw new NegativeArraySizeException("The array size is negative.");
+ return copyOfRange(original, 0, newLength);
+ }
+
+ /**
+ * Copies the specified range of the supplied array to a new
+ * array, padding as necessary with <code>0</code>
+ * if <code>to</code> is greater than the length of the original
+ * array. <code>from</code> must be in the range zero to
+ * <code>original.length</code> and can not be greater than
+ * <code>to</code>. The initial element of the
+ * returned array will be equal to <code>original[from]</code>,
+ * except where <code>from</code> is equal to <code>to</code>
+ * (where a zero-length array will be returned) or <code>
+ * <code>from</code> is equal to <code>original.length</code>
+ * (where an array padded with <code>0</code> will be
+ * returned). The returned array is always of length
+ * <code>to - from</code>.
+ *
+ * @param original the array from which to copy.
+ * @param from the initial index of the range, inclusive.
+ * @param to the final index of the range, exclusive.
+ * @return a copy of the specified range, with padding to
+ * obtain the required length.
+ * @throws ArrayIndexOutOfBoundsException if <code>from < 0</code>
+ * or <code>from > original.length</code>
+ * @throws IllegalArgumentException if <code>from > to</code>
+ * @throws NullPointerException if <code>original</code> is <code>null</code>.
+ * @since 1.6
+ * @see #copyOf(int[],int)
+ */
+ public static int[] copyOfRange(int[] original, int from, int to)
+ {
+ if (from > to)
+ throw new IllegalArgumentException("The initial index is after " +
+ "the final index.");
+ int[] newArray = new int[to - from];
+ if (to > original.length)
+ {
+ System.arraycopy(original, from, newArray, 0,
+ original.length - from);
+ fill(newArray, original.length, newArray.length, 0);
+ }
+ else
+ System.arraycopy(original, from, newArray, 0, to - from);
+ return newArray;
+ }
+
+ /**
+ * Returns a copy of the supplied array, truncating or padding as
+ * necessary with <code>0L</code> to obtain the specified length.
+ * Indices that are valid for both arrays will return the same value.
+ * Indices that only exist in the returned array (due to the new length
+ * being greater than the original length) will return <code>0L</code>.
+ * This is equivalent to calling
+ * <code>copyOfRange(original, 0, newLength)</code>.
+ *
+ * @param original the original array to be copied.
+ * @param newLength the length of the returned array.
+ * @return a copy of the original array, truncated or padded with
+ * <code>0L</code> to obtain the required length.
+ * @throws NegativeArraySizeException if <code>newLength</code> is negative.
+ * @throws NullPointerException if <code>original</code> is <code>null</code>.
+ * @since 1.6
+ * @see #copyOfRange(long[],int,int)
+ */
+ public static long[] copyOf(long[] original, int newLength)
+ {
+ if (newLength < 0)
+ throw new NegativeArraySizeException("The array size is negative.");
+ return copyOfRange(original, 0, newLength);
+ }
+
+ /**
+ * Copies the specified range of the supplied array to a new
+ * array, padding as necessary with <code>0L</code>
+ * if <code>to</code> is greater than the length of the original
+ * array. <code>from</code> must be in the range zero to
+ * <code>original.length</code> and can not be greater than
+ * <code>to</code>. The initial element of the
+ * returned array will be equal to <code>original[from]</code>,
+ * except where <code>from</code> is equal to <code>to</code>
+ * (where a zero-length array will be returned) or <code>
+ * <code>from</code> is equal to <code>original.length</code>
+ * (where an array padded with <code>0L</code> will be
+ * returned). The returned array is always of length
+ * <code>to - from</code>.
+ *
+ * @param original the array from which to copy.
+ * @param from the initial index of the range, inclusive.
+ * @param to the final index of the range, exclusive.
+ * @return a copy of the specified range, with padding to
+ * obtain the required length.
+ * @throws ArrayIndexOutOfBoundsException if <code>from < 0</code>
+ * or <code>from > original.length</code>
+ * @throws IllegalArgumentException if <code>from > to</code>
+ * @throws NullPointerException if <code>original</code> is <code>null</code>.
+ * @since 1.6
+ * @see #copyOf(long[],int)
+ */
+ public static long[] copyOfRange(long[] original, int from, int to)
+ {
+ if (from > to)
+ throw new IllegalArgumentException("The initial index is after " +
+ "the final index.");
+ long[] newArray = new long[to - from];
+ if (to > original.length)
+ {
+ System.arraycopy(original, from, newArray, 0,
+ original.length - from);
+ fill(newArray, original.length, newArray.length, 0L);
+ }
+ else
+ System.arraycopy(original, from, newArray, 0, to - from);
+ return newArray;
+ }
+
+ /**
+ * Returns a copy of the supplied array, truncating or padding as
+ * necessary with <code>(short)0</code> to obtain the specified length.
+ * Indices that are valid for both arrays will return the same value.
+ * Indices that only exist in the returned array (due to the new length
+ * being greater than the original length) will return <code>(short)0</code>.
+ * This is equivalent to calling
+ * <code>copyOfRange(original, 0, newLength)</code>.
+ *
+ * @param original the original array to be copied.
+ * @param newLength the length of the returned array.
+ * @return a copy of the original array, truncated or padded with
+ * <code>(short)0</code> to obtain the required length.
+ * @throws NegativeArraySizeException if <code>newLength</code> is negative.
+ * @throws NullPointerException if <code>original</code> is <code>null</code>.
+ * @since 1.6
+ * @see #copyOfRange(short[],int,int)
+ */
+ public static short[] copyOf(short[] original, int newLength)
+ {
+ if (newLength < 0)
+ throw new NegativeArraySizeException("The array size is negative.");
+ return copyOfRange(original, 0, newLength);
+ }
+
+ /**
+ * Copies the specified range of the supplied array to a new
+ * array, padding as necessary with <code>(short)0</code>
+ * if <code>to</code> is greater than the length of the original
+ * array. <code>from</code> must be in the range zero to
+ * <code>original.length</code> and can not be greater than
+ * <code>to</code>. The initial element of the
+ * returned array will be equal to <code>original[from]</code>,
+ * except where <code>from</code> is equal to <code>to</code>
+ * (where a zero-length array will be returned) or <code>
+ * <code>from</code> is equal to <code>original.length</code>
+ * (where an array padded with <code>(short)0</code> will be
+ * returned). The returned array is always of length
+ * <code>to - from</code>.
+ *
+ * @param original the array from which to copy.
+ * @param from the initial index of the range, inclusive.
+ * @param to the final index of the range, exclusive.
+ * @return a copy of the specified range, with padding to
+ * obtain the required length.
+ * @throws ArrayIndexOutOfBoundsException if <code>from < 0</code>
+ * or <code>from > original.length</code>
+ * @throws IllegalArgumentException if <code>from > to</code>
+ * @throws NullPointerException if <code>original</code> is <code>null</code>.
+ * @since 1.6
+ * @see #copyOf(short[],int)
+ */
+ public static short[] copyOfRange(short[] original, int from, int to)
+ {
+ if (from > to)
+ throw new IllegalArgumentException("The initial index is after " +
+ "the final index.");
+ short[] newArray = new short[to - from];
+ if (to > original.length)
+ {
+ System.arraycopy(original, from, newArray, 0,
+ original.length - from);
+ fill(newArray, original.length, newArray.length, (short)0);
+ }
+ else
+ System.arraycopy(original, from, newArray, 0, to - from);
+ return newArray;
+ }
+
+ /**
+ * Returns a copy of the supplied array, truncating or padding as
+ * necessary with <code>null</code> to obtain the specified length.
+ * Indices that are valid for both arrays will return the same value.
+ * Indices that only exist in the returned array (due to the new length
+ * being greater than the original length) will return <code>null</code>.
+ * This is equivalent to calling
+ * <code>copyOfRange(original, 0, newLength)</code>.
+ *
+ * @param original the original array to be copied.
+ * @param newLength the length of the returned array.
+ * @return a copy of the original array, truncated or padded with
+ * <code>null</code> to obtain the required length.
+ * @throws NegativeArraySizeException if <code>newLength</code> is negative.
+ * @throws NullPointerException if <code>original</code> is <code>null</code>.
+ * @since 1.6
+ * @see #copyOfRange(T[],int,int)
+ */
+ public static <T> T[] copyOf(T[] original, int newLength)
+ {
+ if (newLength < 0)
+ throw new NegativeArraySizeException("The array size is negative.");
+ return copyOfRange(original, 0, newLength);
+ }
+
+ /**
+ * Copies the specified range of the supplied array to a new
+ * array, padding as necessary with <code>null</code>
+ * if <code>to</code> is greater than the length of the original
+ * array. <code>from</code> must be in the range zero to
+ * <code>original.length</code> and can not be greater than
+ * <code>to</code>. The initial element of the
+ * returned array will be equal to <code>original[from]</code>,
+ * except where <code>from</code> is equal to <code>to</code>
+ * (where a zero-length array will be returned) or <code>
+ * <code>from</code> is equal to <code>original.length</code>
+ * (where an array padded with <code>null</code> will be
+ * returned). The returned array is always of length
+ * <code>to - from</code>.
+ *
+ * @param original the array from which to copy.
+ * @param from the initial index of the range, inclusive.
+ * @param to the final index of the range, exclusive.
+ * @return a copy of the specified range, with padding to
+ * obtain the required length.
+ * @throws ArrayIndexOutOfBoundsException if <code>from < 0</code>
+ * or <code>from > original.length</code>
+ * @throws IllegalArgumentException if <code>from > to</code>
+ * @throws NullPointerException if <code>original</code> is <code>null</code>.
+ * @since 1.6
+ * @see #copyOf(T[],int)
+ */
+ public static <T> T[] copyOfRange(T[] original, int from, int to)
+ {
+ if (from > to)
+ throw new IllegalArgumentException("The initial index is after " +
+ "the final index.");
+ Class elemType = original.getClass().getComponentType();
+ T[] newArray = (T[]) Array.newInstance(elemType, to - from);
+ if (to > original.length)
+ {
+ System.arraycopy(original, from, newArray, 0,
+ original.length - from);
+ fill(newArray, original.length, newArray.length, null);
+ }
+ else
+ System.arraycopy(original, from, newArray, 0, to - from);
+ return newArray;
+ }
+
+ /**
+ * Returns a copy of the supplied array, truncating or padding as
+ * necessary with <code>null</code> to obtain the specified length.
+ * Indices that are valid for both arrays will return the same value.
+ * Indices that only exist in the returned array (due to the new length
+ * being greater than the original length) will return <code>null</code>.
+ * This is equivalent to calling
+ * <code>copyOfRange(original, 0, newLength, newType)</code>. The returned
+ * array will be of the specified type, <code>newType</code>.
+ *
+ * @param original the original array to be copied.
+ * @param newLength the length of the returned array.
+ * @param newType the type of the returned array.
+ * @return a copy of the original array, truncated or padded with
+ * <code>null</code> to obtain the required length.
+ * @throws NegativeArraySizeException if <code>newLength</code> is negative.
+ * @throws NullPointerException if <code>original</code> is <code>null</code>.
+ * @since 1.6
+ * @see #copyOfRange(U[],int,int,Class)
+ */
+ public static <T,U> T[] copyOf(U[] original, int newLength,
+ Class<? extends T[]> newType)
+ {
+ if (newLength < 0)
+ throw new NegativeArraySizeException("The array size is negative.");
+ return copyOfRange(original, 0, newLength, newType);
+ }
+
+ /**
+ * Copies the specified range of the supplied array to a new
+ * array, padding as necessary with <code>null</code>
+ * if <code>to</code> is greater than the length of the original
+ * array. <code>from</code> must be in the range zero to
+ * <code>original.length</code> and can not be greater than
+ * <code>to</code>. The initial element of the
+ * returned array will be equal to <code>original[from]</code>,
+ * except where <code>from</code> is equal to <code>to</code>
+ * (where a zero-length array will be returned) or <code>
+ * <code>from</code> is equal to <code>original.length</code>
+ * (where an array padded with <code>null</code> will be
+ * returned). The returned array is always of length
+ * <code>to - from</code> and will be of the specified type,
+ * <code>newType</code>.
+ *
+ * @param original the array from which to copy.
+ * @param from the initial index of the range, inclusive.
+ * @param to the final index of the range, exclusive.
+ * @param newType the type of the returned array.
+ * @return a copy of the specified range, with padding to
+ * obtain the required length.
+ * @throws ArrayIndexOutOfBoundsException if <code>from < 0</code>
+ * or <code>from > original.length</code>
+ * @throws IllegalArgumentException if <code>from > to</code>
+ * @throws NullPointerException if <code>original</code> is <code>null</code>.
+ * @since 1.6
+ * @see #copyOf(T[],int)
+ */
+ public static <T,U> T[] copyOfRange(U[] original, int from, int to,
+ Class<? extends T[]> newType)
+ {
+ if (from > to)
+ throw new IllegalArgumentException("The initial index is after " +
+ "the final index.");
+ T[] newArray = (T[]) Array.newInstance(newType.getComponentType(),
+ to - from);
+ if (to > original.length)
+ {
+ System.arraycopy(original, from, newArray, 0,
+ original.length - from);
+ fill(newArray, original.length, newArray.length, null);
+ }
+ else
+ System.arraycopy(original, from, newArray, 0, to - from);
+ return newArray;
+ }
+}
diff --git a/libjava/classpath/java/util/BitSet.java b/libjava/classpath/java/util/BitSet.java
new file mode 100644
index 000000000..1072978a4
--- /dev/null
+++ b/libjava/classpath/java/util/BitSet.java
@@ -0,0 +1,758 @@
+/* BitSet.java -- A vector of bits.
+ Copyright (C) 1998, 1999, 2000, 2001, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.io.Serializable;
+
+/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
+ * hashCode algorithm taken from JDK 1.2 docs.
+ */
+
+/**
+ * This class can be thought of in two ways. You can see it as a
+ * vector of bits or as a set of non-negative integers. The name
+ * <code>BitSet</code> is a bit misleading.
+ *
+ * It is implemented by a bit vector, but its equally possible to see
+ * it as set of non-negative integer; each integer in the set is
+ * represented by a set bit at the corresponding index. The size of
+ * this structure is determined by the highest integer in the set.
+ *
+ * You can union, intersect and build (symmetric) remainders, by
+ * invoking the logical operations and, or, andNot, resp. xor.
+ *
+ * This implementation is NOT synchronized against concurrent access from
+ * multiple threads. Specifically, if one thread is reading from a bitset
+ * while another thread is simultaneously modifying it, the results are
+ * undefined.
+ *
+ * @author Jochen Hoenicke
+ * @author Tom Tromey (tromey@cygnus.com)
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @status updated to 1.4
+ */
+public class BitSet implements Cloneable, Serializable
+{
+ /**
+ * Compatible with JDK 1.0.
+ */
+ private static final long serialVersionUID = 7997698588986878753L;
+
+ /**
+ * A common mask.
+ */
+ private static final int LONG_MASK = 0x3f;
+
+ /**
+ * The actual bits.
+ * @serial the i'th bit is in bits[i/64] at position i%64 (where position
+ * 0 is the least significant).
+ */
+ private long[] bits;
+
+ /**
+ * Create a new empty bit set. All bits are initially false.
+ */
+ public BitSet()
+ {
+ this(64);
+ }
+
+ /**
+ * Create a new empty bit set, with a given size. This
+ * constructor reserves enough space to represent the integers
+ * from <code>0</code> to <code>nbits-1</code>.
+ *
+ * @param nbits the initial size of the bit set
+ * @throws NegativeArraySizeException if nbits &lt; 0
+ */
+ public BitSet(int nbits)
+ {
+ if (nbits < 0)
+ throw new NegativeArraySizeException();
+
+ int length = nbits >>> 6;
+ if ((nbits & LONG_MASK) != 0)
+ ++length;
+ bits = new long[length];
+ }
+
+ /**
+ * Performs the logical AND operation on this bit set and the
+ * given <code>set</code>. This means it builds the intersection
+ * of the two sets. The result is stored into this bit set.
+ *
+ * @param bs the second bit set
+ * @throws NullPointerException if bs is null
+ */
+ public void and(BitSet bs)
+ {
+ int max = Math.min(bits.length, bs.bits.length);
+ int i;
+ for (i = 0; i < max; ++i)
+ bits[i] &= bs.bits[i];
+ while (i < bits.length)
+ bits[i++] = 0;
+ }
+
+ /**
+ * Performs the logical AND operation on this bit set and the
+ * complement of the given <code>bs</code>. This means it
+ * selects every element in the first set, that isn't in the
+ * second set. The result is stored into this bit set and is
+ * effectively the set difference of the two.
+ *
+ * @param bs the second bit set
+ * @throws NullPointerException if bs is null
+ * @since 1.2
+ */
+ public void andNot(BitSet bs)
+ {
+ int i = Math.min(bits.length, bs.bits.length);
+ while (--i >= 0)
+ bits[i] &= ~bs.bits[i];
+ }
+
+ /**
+ * Returns the number of bits set to true.
+ *
+ * @return the number of true bits
+ * @since 1.4
+ */
+ public int cardinality()
+ {
+ int card = 0;
+ for (int i = bits.length - 1; i >= 0; i--)
+ {
+ long a = bits[i];
+ // Take care of common cases.
+ if (a == 0)
+ continue;
+ if (a == -1)
+ {
+ card += 64;
+ continue;
+ }
+
+ // Successively collapse alternating bit groups into a sum.
+ a = ((a >> 1) & 0x5555555555555555L) + (a & 0x5555555555555555L);
+ a = ((a >> 2) & 0x3333333333333333L) + (a & 0x3333333333333333L);
+ int b = (int) ((a >>> 32) + a);
+ b = ((b >> 4) & 0x0f0f0f0f) + (b & 0x0f0f0f0f);
+ b = ((b >> 8) & 0x00ff00ff) + (b & 0x00ff00ff);
+ card += ((b >> 16) & 0x0000ffff) + (b & 0x0000ffff);
+ }
+ return card;
+ }
+
+ /**
+ * Sets all bits in the set to false.
+ *
+ * @since 1.4
+ */
+ public void clear()
+ {
+ Arrays.fill(bits, 0);
+ }
+
+ /**
+ * Removes the integer <code>pos</code> from this set. That is
+ * the corresponding bit is cleared. If the index is not in the set,
+ * this method does nothing.
+ *
+ * @param pos a non-negative integer
+ * @throws IndexOutOfBoundsException if pos &lt; 0
+ */
+ public void clear(int pos)
+ {
+ int offset = pos >> 6;
+ ensure(offset);
+ // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException,
+ // so we'll just let that be our exception.
+ bits[offset] &= ~(1L << pos);
+ }
+
+ /**
+ * Sets the bits between from (inclusive) and to (exclusive) to false.
+ *
+ * @param from the start range (inclusive)
+ * @param to the end range (exclusive)
+ * @throws IndexOutOfBoundsException if from &lt; 0 || to &lt; 0 ||
+ * from &gt; to
+ * @since 1.4
+ */
+ public void clear(int from, int to)
+ {
+ if (from < 0 || from > to)
+ throw new IndexOutOfBoundsException();
+ if (from == to)
+ return;
+ int lo_offset = from >>> 6;
+ int hi_offset = to >>> 6;
+ ensure(hi_offset);
+ if (lo_offset == hi_offset)
+ {
+ bits[hi_offset] &= ((1L << from) - 1) | (-1L << to);
+ return;
+ }
+
+ bits[lo_offset] &= (1L << from) - 1;
+ bits[hi_offset] &= -1L << to;
+ for (int i = lo_offset + 1; i < hi_offset; i++)
+ bits[i] = 0;
+ }
+
+ /**
+ * Create a clone of this bit set, that is an instance of the same
+ * class and contains the same elements. But it doesn't change when
+ * this bit set changes.
+ *
+ * @return the clone of this object.
+ */
+ public Object clone()
+ {
+ try
+ {
+ BitSet bs = (BitSet) super.clone();
+ bs.bits = (long[]) bits.clone();
+ return bs;
+ }
+ catch (CloneNotSupportedException e)
+ {
+ // Impossible to get here.
+ return null;
+ }
+ }
+
+ /**
+ * Returns true if the <code>obj</code> is a bit set that contains
+ * exactly the same elements as this bit set, otherwise false.
+ *
+ * @param obj the object to compare to
+ * @return true if obj equals this bit set
+ */
+ public boolean equals(Object obj)
+ {
+ if (!(obj instanceof BitSet))
+ return false;
+ BitSet bs = (BitSet) obj;
+ int max = Math.min(bits.length, bs.bits.length);
+ int i;
+ for (i = 0; i < max; ++i)
+ if (bits[i] != bs.bits[i])
+ return false;
+ // If one is larger, check to make sure all extra bits are 0.
+ for (int j = i; j < bits.length; ++j)
+ if (bits[j] != 0)
+ return false;
+ for (int j = i; j < bs.bits.length; ++j)
+ if (bs.bits[j] != 0)
+ return false;
+ return true;
+ }
+
+ /**
+ * Sets the bit at the index to the opposite value.
+ *
+ * @param index the index of the bit
+ * @throws IndexOutOfBoundsException if index is negative
+ * @since 1.4
+ */
+ public void flip(int index)
+ {
+ int offset = index >> 6;
+ ensure(offset);
+ // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException,
+ // so we'll just let that be our exception.
+ bits[offset] ^= 1L << index;
+ }
+
+ /**
+ * Sets a range of bits to the opposite value.
+ *
+ * @param from the low index (inclusive)
+ * @param to the high index (exclusive)
+ * @throws IndexOutOfBoundsException if from &gt; to || from &lt; 0 ||
+ * to &lt; 0
+ * @since 1.4
+ */
+ public void flip(int from, int to)
+ {
+ if (from < 0 || from > to)
+ throw new IndexOutOfBoundsException();
+ if (from == to)
+ return;
+ int lo_offset = from >>> 6;
+ int hi_offset = to >>> 6;
+ ensure(hi_offset);
+ if (lo_offset == hi_offset)
+ {
+ bits[hi_offset] ^= (-1L << from) & ((1L << to) - 1);
+ return;
+ }
+
+ bits[lo_offset] ^= -1L << from;
+ bits[hi_offset] ^= (1L << to) - 1;
+ for (int i = lo_offset + 1; i < hi_offset; i++)
+ bits[i] ^= -1;
+ }
+
+ /**
+ * Returns true if the integer <code>bitIndex</code> is in this bit
+ * set, otherwise false.
+ *
+ * @param pos a non-negative integer
+ * @return the value of the bit at the specified position
+ * @throws IndexOutOfBoundsException if the pos is negative
+ */
+ public boolean get(int pos)
+ {
+ int offset = pos >> 6;
+ if (offset >= bits.length)
+ return false;
+ // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException,
+ // so we'll just let that be our exception.
+ return (bits[offset] & (1L << pos)) != 0;
+ }
+
+ /**
+ * Returns a new <code>BitSet</code> composed of a range of bits from
+ * this one.
+ *
+ * @param from the low index (inclusive)
+ * @param to the high index (exclusive)
+ * @throws IndexOutOfBoundsException if from &gt; to || from &lt; 0 ||
+ * to &lt; 0
+ * @since 1.4
+ */
+ public BitSet get(int from, int to)
+ {
+ if (from < 0 || from > to)
+ throw new IndexOutOfBoundsException();
+ BitSet bs = new BitSet(to - from);
+ int lo_offset = from >>> 6;
+ if (lo_offset >= bits.length || to == from)
+ return bs;
+
+ int lo_bit = from & LONG_MASK;
+ int hi_offset = to >>> 6;
+ if (lo_bit == 0)
+ {
+ int len = Math.min(hi_offset - lo_offset + 1, bits.length - lo_offset);
+ System.arraycopy(bits, lo_offset, bs.bits, 0, len);
+ if (hi_offset < bits.length)
+ bs.bits[hi_offset - lo_offset] &= (1L << to) - 1;
+ return bs;
+ }
+
+ int len = Math.min(hi_offset, bits.length - 1);
+ int reverse = 64 - lo_bit;
+ int i;
+ for (i = 0; lo_offset < len; lo_offset++, i++)
+ bs.bits[i] = ((bits[lo_offset] >>> lo_bit)
+ | (bits[lo_offset + 1] << reverse));
+ if ((to & LONG_MASK) > lo_bit)
+ bs.bits[i++] = bits[lo_offset] >>> lo_bit;
+ if (hi_offset < bits.length)
+ bs.bits[i - 1] &= (1L << (to - from)) - 1;
+ return bs;
+ }
+
+ /**
+ * Returns a hash code value for this bit set. The hash code of
+ * two bit sets containing the same integers is identical. The algorithm
+ * used to compute it is as follows:
+ *
+ * Suppose the bits in the BitSet were to be stored in an array of
+ * long integers called <code>bits</code>, in such a manner that
+ * bit <code>k</code> is set in the BitSet (for non-negative values
+ * of <code>k</code>) if and only if
+ *
+ * <code>((k/64) &lt; bits.length)
+ * && ((bits[k/64] & (1L &lt;&lt; (bit % 64))) != 0)
+ * </code>
+ *
+ * Then the following definition of the hashCode method
+ * would be a correct implementation of the actual algorithm:
+ *
+ *
+<pre>public int hashCode()
+{
+ long h = 1234;
+ for (int i = bits.length-1; i &gt;= 0; i--)
+ {
+ h ^= bits[i] * (i + 1);
+ }
+
+ return (int)((h >> 32) ^ h);
+}</pre>
+ *
+ * Note that the hash code values changes, if the set is changed.
+ *
+ * @return the hash code value for this bit set.
+ */
+ public int hashCode()
+ {
+ long h = 1234;
+ for (int i = bits.length; i > 0; )
+ h ^= i * bits[--i];
+ return (int) ((h >> 32) ^ h);
+ }
+
+ /**
+ * Returns true if the specified BitSet and this one share at least one
+ * common true bit.
+ *
+ * @param set the set to check for intersection
+ * @return true if the sets intersect
+ * @throws NullPointerException if set is null
+ * @since 1.4
+ */
+ public boolean intersects(BitSet set)
+ {
+ int i = Math.min(bits.length, set.bits.length);
+ while (--i >= 0)
+ if ((bits[i] & set.bits[i]) != 0)
+ return true;
+ return false;
+ }
+
+ /**
+ * Returns true if this set contains no true bits.
+ *
+ * @return true if all bits are false
+ * @since 1.4
+ */
+ public boolean isEmpty()
+ {
+ for (int i = bits.length - 1; i >= 0; i--)
+ if (bits[i] != 0)
+ return false;
+ return true;
+ }
+
+ /**
+ * Returns the logical number of bits actually used by this bit
+ * set. It returns the index of the highest set bit plus one.
+ * Note that this method doesn't return the number of set bits.
+ *
+ * @return the index of the highest set bit plus one.
+ */
+ public int length()
+ {
+ // Set i to highest index that contains a non-zero value.
+ int i;
+ for (i = bits.length - 1; i >= 0 && bits[i] == 0; --i)
+ ;
+
+ // if i < 0 all bits are cleared.
+ if (i < 0)
+ return 0;
+
+ // Now determine the exact length.
+ long b = bits[i];
+ int len = (i + 1) * 64;
+ // b >= 0 checks if the highest bit is zero.
+ while (b >= 0)
+ {
+ --len;
+ b <<= 1;
+ }
+
+ return len;
+ }
+
+ /**
+ * Returns the index of the next false bit, from the specified bit
+ * (inclusive).
+ *
+ * @param from the start location
+ * @return the first false bit
+ * @throws IndexOutOfBoundsException if from is negative
+ * @since 1.4
+ */
+ public int nextClearBit(int from)
+ {
+ int offset = from >> 6;
+ long mask = 1L << from;
+ while (offset < bits.length)
+ {
+ // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException,
+ // so we'll just let that be our exception.
+ long h = bits[offset];
+ do
+ {
+ if ((h & mask) == 0)
+ return from;
+ mask <<= 1;
+ from++;
+ }
+ while (mask != 0);
+ mask = 1;
+ offset++;
+ }
+ return from;
+ }
+
+ /**
+ * Returns the index of the next true bit, from the specified bit
+ * (inclusive). If there is none, -1 is returned. You can iterate over
+ * all true bits with this loop:<br>
+ *
+<pre>for (int i = bs.nextSetBit(0); i &gt;= 0; i = bs.nextSetBit(i + 1))
+{
+ // operate on i here
+}</pre>
+ *
+ * @param from the start location
+ * @return the first true bit, or -1
+ * @throws IndexOutOfBoundsException if from is negative
+ * @since 1.4
+ */
+ public int nextSetBit(int from)
+ {
+ int offset = from >> 6;
+ long mask = 1L << from;
+ while (offset < bits.length)
+ {
+ // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException,
+ // so we'll just let that be our exception.
+ long h = bits[offset];
+ do
+ {
+ if ((h & mask) != 0)
+ return from;
+ mask <<= 1;
+ from++;
+ }
+ while (mask != 0);
+ mask = 1;
+ offset++;
+ }
+ return -1;
+ }
+
+ /**
+ * Performs the logical OR operation on this bit set and the
+ * given <code>set</code>. This means it builds the union
+ * of the two sets. The result is stored into this bit set, which
+ * grows as necessary.
+ *
+ * @param bs the second bit set
+ * @throws NullPointerException if bs is null
+ */
+ public void or(BitSet bs)
+ {
+ ensure(bs.bits.length - 1);
+ for (int i = bs.bits.length - 1; i >= 0; i--)
+ bits[i] |= bs.bits[i];
+ }
+
+ /**
+ * Add the integer <code>bitIndex</code> to this set. That is
+ * the corresponding bit is set to true. If the index was already in
+ * the set, this method does nothing. The size of this structure
+ * is automatically increased as necessary.
+ *
+ * @param pos a non-negative integer.
+ * @throws IndexOutOfBoundsException if pos is negative
+ */
+ public void set(int pos)
+ {
+ int offset = pos >> 6;
+ ensure(offset);
+ // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException,
+ // so we'll just let that be our exception.
+ bits[offset] |= 1L << pos;
+ }
+
+ /**
+ * Sets the bit at the given index to the specified value. The size of
+ * this structure is automatically increased as necessary.
+ *
+ * @param index the position to set
+ * @param value the value to set it to
+ * @throws IndexOutOfBoundsException if index is negative
+ * @since 1.4
+ */
+ public void set(int index, boolean value)
+ {
+ if (value)
+ set(index);
+ else
+ clear(index);
+ }
+
+ /**
+ * Sets the bits between from (inclusive) and to (exclusive) to true.
+ *
+ * @param from the start range (inclusive)
+ * @param to the end range (exclusive)
+ * @throws IndexOutOfBoundsException if from &lt; 0 || from &gt; to ||
+ * to &lt; 0
+ * @since 1.4
+ */
+ public void set(int from, int to)
+ {
+ if (from < 0 || from > to)
+ throw new IndexOutOfBoundsException();
+ if (from == to)
+ return;
+ int lo_offset = from >>> 6;
+ int hi_offset = to >>> 6;
+ ensure(hi_offset);
+ if (lo_offset == hi_offset)
+ {
+ bits[hi_offset] |= (-1L << from) & ((1L << to) - 1);
+ return;
+ }
+
+ bits[lo_offset] |= -1L << from;
+ bits[hi_offset] |= (1L << to) - 1;
+ for (int i = lo_offset + 1; i < hi_offset; i++)
+ bits[i] = -1;
+ }
+
+ /**
+ * Sets the bits between from (inclusive) and to (exclusive) to the
+ * specified value.
+ *
+ * @param from the start range (inclusive)
+ * @param to the end range (exclusive)
+ * @param value the value to set it to
+ * @throws IndexOutOfBoundsException if from &lt; 0 || from &gt; to ||
+ * to &lt; 0
+ * @since 1.4
+ */
+ public void set(int from, int to, boolean value)
+ {
+ if (value)
+ set(from, to);
+ else
+ clear(from, to);
+ }
+
+ /**
+ * Returns the number of bits actually used by this bit set. Note
+ * that this method doesn't return the number of set bits, and that
+ * future requests for larger bits will make this automatically grow.
+ *
+ * @return the number of bits currently used.
+ */
+ public int size()
+ {
+ return bits.length * 64;
+ }
+
+ /**
+ * Returns the string representation of this bit set. This
+ * consists of a comma separated list of the integers in this set
+ * surrounded by curly braces. There is a space after each comma.
+ * A sample string is thus "{1, 3, 53}".
+ * @return the string representation.
+ */
+ public String toString()
+ {
+ CPStringBuilder r = new CPStringBuilder("{");
+ boolean first = true;
+ for (int i = 0; i < bits.length; ++i)
+ {
+ long bit = 1;
+ long word = bits[i];
+ if (word == 0)
+ continue;
+ for (int j = 0; j < 64; ++j)
+ {
+ if ((word & bit) != 0)
+ {
+ if (! first)
+ r.append(", ");
+ r.append(64 * i + j);
+ first = false;
+ }
+ bit <<= 1;
+ }
+ }
+ return r.append("}").toString();
+ }
+
+ /**
+ * Performs the logical XOR operation on this bit set and the
+ * given <code>set</code>. This means it builds the symmetric
+ * remainder of the two sets (the elements that are in one set,
+ * but not in the other). The result is stored into this bit set,
+ * which grows as necessary.
+ *
+ * @param bs the second bit set
+ * @throws NullPointerException if bs is null
+ */
+ public void xor(BitSet bs)
+ {
+ ensure(bs.bits.length - 1);
+ for (int i = bs.bits.length - 1; i >= 0; i--)
+ bits[i] ^= bs.bits[i];
+ }
+
+ /**
+ * Make sure the vector is big enough.
+ *
+ * @param lastElt the size needed for the bits array
+ */
+ private void ensure(int lastElt)
+ {
+ if (lastElt >= bits.length)
+ {
+ long[] nd = new long[lastElt + 1];
+ System.arraycopy(bits, 0, nd, 0, bits.length);
+ bits = nd;
+ }
+ }
+
+ // This is used by EnumSet for efficiency.
+ final boolean containsAll(BitSet other)
+ {
+ for (int i = other.bits.length - 1; i >= 0; i--)
+ {
+ if ((bits[i] & other.bits[i]) != other.bits[i])
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/libjava/classpath/java/util/Calendar.java b/libjava/classpath/java/util/Calendar.java
new file mode 100644
index 000000000..8123b1706
--- /dev/null
+++ b/libjava/classpath/java/util/Calendar.java
@@ -0,0 +1,1620 @@
+/* Calendar.java --
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006,
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+import java.text.DateFormatSymbols;
+
+/**
+ * This class is an abstract base class for Calendars, which can be
+ * used to convert between <code>Date</code> objects and a set of
+ * integer fields which represent <code>YEAR</code>,
+ * <code>MONTH</code>, <code>DAY</code>, etc. The <code>Date</code>
+ * object represents a time in milliseconds since the Epoch. <br>
+ *
+ * This class is locale sensitive. To get the Object matching the
+ * current locale you can use <code>getInstance</code>. You can even provide
+ * a locale or a timezone. <code>getInstance</code> returns currently
+ * a <code>GregorianCalendar</code> for the current date. <br>
+ *
+ * If you want to convert a date from the Year, Month, Day, DayOfWeek,
+ * etc. Representation to a <code>Date</code>-Object, you can create
+ * a new Calendar with <code>getInstance()</code>,
+ * <code>clear()</code> all fields, <code>set(int,int)</code> the
+ * fields you need and convert it with <code>getTime()</code>. <br>
+ *
+ * If you want to convert a <code>Date</code>-object to the Calendar
+ * representation, create a new Calendar, assign the
+ * <code>Date</code>-Object with <code>setTime()</code>, and read the
+ * fields with <code>get(int)</code>. <br>
+ *
+ * When computing the date from time fields, it may happen, that there
+ * are either two few fields set, or some fields are inconsistent. This
+ * cases will handled in a calendar specific way. Missing fields are
+ * replaced by the fields of the epoch: 1970 January 1 00:00. <br>
+ *
+ * To understand, how the day of year is computed out of the fields
+ * look at the following table. It is traversed from top to bottom,
+ * 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>
+ *
+ * The hour_of_day-field takes precedence over the ampm and
+ * hour_of_ampm fields. <br>
+ *
+ * <STRONG>Note:</STRONG> This can differ for non-Gregorian calendar. <br>
+ *
+ * To convert a calendar to a human readable form and vice versa, use
+ * the <code>java.text.DateFormat</code> class. <br>
+ *
+ * Other useful things you can do with an calendar, is
+ * <code>roll</code>ing fields (that means increase/decrease a
+ * specific field by one, propagating overflows), or
+ * <code>add</code>ing/substracting a fixed amount to a field.
+ *
+ * @author Aaron M. Renn (arenn@urbanophile.com)
+ * @author Jochen Hoenicke (Jochen.Hoenicke@Informatik.Uni-Oldenburg.de)
+ * @author Warren Levy (warrenl@cygnus.com)
+ * @author Jeff Sturm (jsturm@one-point.com)
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Bryce McKinlay (mckinlay@redhat.com)
+ * @author Ingo Proetel (proetel@aicas.com)
+ * @author Jerry Quinn (jlquinn@optonline.net)
+ * @author Jeroen Frijters (jeroen@frijters.net)
+ * @author Noa Resare (noa@resare.com)
+ * @author Sven de Marothy (sven@physto.se)
+ * @author David Gilbert (david.gilbert@object-refinery.com)
+ * @author Olivier Jolly (olivier.jolly@pcedev.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @see Date
+ * @see GregorianCalendar
+ * @see TimeZone
+ * @see java.text.DateFormat
+ */
+public abstract class Calendar
+ implements Serializable, Cloneable, Comparable<Calendar>
+{
+ /**
+ * Constant representing the era time field.
+ */
+ public static final int ERA = 0;
+
+ /**
+ * Constant representing the year time field.
+ */
+ public static final int YEAR = 1;
+
+ /**
+ * Constant representing the month time field. This field
+ * should contain one of the JANUARY,...,DECEMBER constants below.
+ */
+ public static final int MONTH = 2;
+
+ /**
+ * Constant representing the week of the year field.
+ * @see #setFirstDayOfWeek(int)
+ */
+ public static final int WEEK_OF_YEAR = 3;
+
+ /**
+ * Constant representing the week of the month time field.
+ * @see #setFirstDayOfWeek(int)
+ */
+ public static final int WEEK_OF_MONTH = 4;
+
+ /**
+ * Constant representing the day time field, synonym for DAY_OF_MONTH.
+ */
+ public static final int DATE = 5;
+
+ /**
+ * Constant representing the day time field.
+ */
+ public static final int DAY_OF_MONTH = 5;
+
+ /**
+ * Constant representing the day of year time field. This is
+ * 1 for the first day in month.
+ */
+ public static final int DAY_OF_YEAR = 6;
+
+ /**
+ * Constant representing the day of week time field. This field
+ * should contain one of the SUNDAY,...,SATURDAY constants below.
+ */
+ public static final int DAY_OF_WEEK = 7;
+
+ /**
+ * Constant representing the day-of-week-in-month field. For
+ * instance this field contains 2 for the second thursday in a
+ * month. If you give a negative number here, the day will count
+ * from the end of the month.
+ */
+ public static final int DAY_OF_WEEK_IN_MONTH = 8;
+
+ /**
+ * Constant representing the part of the day for 12-hour clock. This
+ * should be one of AM or PM.
+ */
+ public static final int AM_PM = 9;
+
+ /**
+ * Constant representing the hour time field for 12-hour clock.
+ */
+ public static final int HOUR = 10;
+
+ /**
+ * Constant representing the hour of day time field for 24-hour clock.
+ */
+ public static final int HOUR_OF_DAY = 11;
+
+ /**
+ * Constant representing the minute of hour time field.
+ */
+ public static final int MINUTE = 12;
+
+ /**
+ * Constant representing the second time field.
+ */
+ public static final int SECOND = 13;
+
+ /**
+ * Constant representing the millisecond time field.
+ */
+ public static final int MILLISECOND = 14;
+
+ /**
+ * Constant representing the time zone offset time field for the
+ * time given in the other fields. It is measured in
+ * milliseconds. The default is the offset of the time zone.
+ */
+ public static final int ZONE_OFFSET = 15;
+
+ /**
+ * Constant representing the daylight saving time offset in
+ * milliseconds. The default is the value given by the time zone.
+ */
+ public static final int DST_OFFSET = 16;
+
+ /**
+ * Number of time fields.
+ */
+ public static final int FIELD_COUNT = 17;
+
+ /**
+ * Constant representing Sunday.
+ */
+ public static final int SUNDAY = 1;
+
+ /**
+ * Constant representing Monday.
+ */
+ public static final int MONDAY = 2;
+
+ /**
+ * Constant representing Tuesday.
+ */
+ public static final int TUESDAY = 3;
+
+ /**
+ * Constant representing Wednesday.
+ */
+ public static final int WEDNESDAY = 4;
+
+ /**
+ * Constant representing Thursday.
+ */
+ public static final int THURSDAY = 5;
+
+ /**
+ * Constant representing Friday.
+ */
+ public static final int FRIDAY = 6;
+
+ /**
+ * Constant representing Saturday.
+ */
+ public static final int SATURDAY = 7;
+
+ /**
+ * Constant representing January.
+ */
+ public static final int JANUARY = 0;
+
+ /**
+ * Constant representing February.
+ */
+ public static final int FEBRUARY = 1;
+
+ /**
+ * Constant representing March.
+ */
+ public static final int MARCH = 2;
+
+ /**
+ * Constant representing April.
+ */
+ public static final int APRIL = 3;
+
+ /**
+ * Constant representing May.
+ */
+ public static final int MAY = 4;
+
+ /**
+ * Constant representing June.
+ */
+ public static final int JUNE = 5;
+
+ /**
+ * Constant representing July.
+ */
+ public static final int JULY = 6;
+
+ /**
+ * Constant representing August.
+ */
+ public static final int AUGUST = 7;
+
+ /**
+ * Constant representing September.
+ */
+ public static final int SEPTEMBER = 8;
+
+ /**
+ * Constant representing October.
+ */
+ public static final int OCTOBER = 9;
+
+ /**
+ * Constant representing November.
+ */
+ public static final int NOVEMBER = 10;
+
+ /**
+ * Constant representing December.
+ */
+ public static final int DECEMBER = 11;
+
+ /**
+ * Constant representing Undecimber. This is an artificial name useful
+ * for lunar calendars.
+ */
+ public static final int UNDECIMBER = 12;
+
+ /**
+ * Useful constant for 12-hour clock.
+ */
+ public static final int AM = 0;
+
+ /**
+ * Useful constant for 12-hour clock.
+ */
+ public static final int PM = 1;
+
+ /**
+ * A style specifier for {@link #getDisplayNames(int,int,Locale)}
+ * stating that names should be returned in both long and short variants.
+ *
+ * @since 1.6
+ * @see #SHORT
+ * @see #LONG
+ */
+ public static final int ALL_STYLES = 0;
+
+ /**
+ * A style specifier for {@link #getDisplayName(int,int,Locale)}
+ * and {@link #getDisplayNames(int,int,Locale)} stating that names
+ * should be returned in their short variant if applicable.
+ *
+ * @since 1.6
+ */
+ public static final int SHORT = 1;
+
+ /**
+ * A style specifier for {@link #getDisplayName(int,int,Locale)}
+ * and {@link #getDisplayNames(int,int,Locale)} stating that names
+ * should be returned in their long variant if applicable.
+ *
+ * @since 1.6
+ */
+ public static final int LONG = 2;
+
+ /**
+ * The time fields. The array is indexed by the constants YEAR to
+ * DST_OFFSET.
+ * @serial
+ */
+ protected int[] fields = new int[FIELD_COUNT];
+
+ /**
+ * The flags which tell if the fields above have a value.
+ * @serial
+ */
+ protected boolean[] isSet = new boolean[FIELD_COUNT];
+
+ /**
+ * The time in milliseconds since the epoch.
+ * @serial
+ */
+ protected long time;
+
+ /**
+ * Tells if the above field has a valid value.
+ * @serial
+ */
+ protected boolean isTimeSet;
+
+ /**
+ * Tells if the fields have a valid value. This superseeds the isSet
+ * array.
+ * @serial
+ */
+ protected boolean areFieldsSet;
+
+ /**
+ * The time zone of this calendar. Used by sub classes to do UTC / local
+ * time conversion. Sub classes can access this field with getTimeZone().
+ * @serial
+ */
+ private TimeZone zone;
+
+ /**
+ * This is the default calendar class, that is returned on
+ * java.util.Calendar.getInstance().
+ * XXX - this isn't localized anywhere, is it?
+ * @see java.util.Calendar#getInstance()
+ */
+ private static final String calendarClassName = "java.util.GregorianCalendar";
+
+ /**
+ * Specifies if the date/time interpretation should be lenient.
+ * If the flag is set, a date such as "February 30, 1996" will be
+ * treated as the 29th day after the February 1. If this flag
+ * is false, such dates will cause an exception.
+ * @serial
+ */
+ private boolean lenient;
+
+ /**
+ * Sets what the first day of week is. This is used for
+ * WEEK_OF_MONTH and WEEK_OF_YEAR fields.
+ * @serial
+ */
+ private int firstDayOfWeek;
+
+ /**
+ * Sets how many days are required in the first week of the year.
+ * If the first day of the year should be the first week you should
+ * set this value to 1. If the first week must be a full week, set
+ * it to 7.
+ * @serial
+ */
+ private int minimalDaysInFirstWeek;
+
+ /**
+ * Is set to true if DST_OFFSET is explicitly set. In that case
+ * it's value overrides the value computed from the current
+ * time and the timezone.
+ */
+ private boolean explicitDSTOffset = false;
+
+ /**
+ * The version of the serialized data on the stream.
+ * <dl><dt>0 or not present</dt>
+ * <dd> JDK 1.1.5 or later.</dd>
+ * <dt>1</dt>
+ * <dd>JDK 1.1.6 or later. This always writes a correct `time' value
+ * on the stream, as well as the other fields, to be compatible with
+ * earlier versions</dd></dl>
+ * @since JDK1.1.6
+ * @serial
+ */
+ private int serialVersionOnStream = 1;
+
+ /**
+ * XXX - I have not checked the compatibility. The documentation of
+ * the serialized-form is quite hairy...
+ */
+ static final long serialVersionUID = -1807547505821590642L;
+
+ /**
+ * The name of the resource bundle. Used only by getBundle()
+ */
+ private static final String bundleName = "gnu.java.locale.LocaleInformation";
+
+ /**
+ * get resource bundle:
+ * The resources should be loaded via this method only. Iff an application
+ * uses this method, the resourcebundle is required.
+ */
+ private static ResourceBundle getBundle(Locale locale)
+ {
+ return ResourceBundle.getBundle(bundleName, locale,
+ ClassLoader.getSystemClassLoader());
+ }
+
+ /**
+ * The set of properties for obtaining the minimum number of days in
+ * the first week.
+ */
+ private static transient final Properties properties;
+
+ /**
+ * Reads in the properties.
+ */
+ static
+ {
+ properties = new Properties();
+ try
+ {
+ properties.load(Calendar.class.getResourceAsStream("weeks.properties"));
+ }
+ catch (IOException exception)
+ {
+ System.out.println("Failed to load weeks resource: " + exception);
+ }
+ }
+
+ /**
+ * Constructs a new Calendar with the default time zone and the default
+ * locale.
+ */
+ protected Calendar()
+ {
+ this(TimeZone.getDefault(), Locale.getDefault());
+ }
+
+ /**
+ * Constructs a new Calendar with the given time zone and the given
+ * locale.
+ * @param zone a time zone.
+ * @param locale a locale.
+ */
+ protected Calendar(TimeZone zone, Locale locale)
+ {
+ this.zone = zone;
+ lenient = true;
+ String[] days = { "", "sun", "mon", "tue", "wed", "thu", "fri", "sat" };
+
+ String country = locale.getCountry();
+ String min = properties.getProperty("minDays." + country);
+ if (min == null)
+ min = properties.getProperty("minDays.DEFAULT");
+ String first = properties.getProperty("firstDay." + country);
+ if (first == null)
+ first = properties.getProperty("firstDay.DEFAULT");
+ try
+ {
+ if (min != null)
+ minimalDaysInFirstWeek = Integer.parseInt(min);
+ }
+ catch (NumberFormatException ex)
+ {
+ minimalDaysInFirstWeek = 1;
+ }
+
+ firstDayOfWeek = 1;
+ if (first != null)
+ for (int i = 0; i < 8; i++)
+ if (days[i].equals(first))
+ firstDayOfWeek = i;
+
+ clear();
+ }
+
+ /**
+ * Creates a calendar representing the actual time, using the default
+ * time zone and locale.
+ *
+ * @return The new calendar.
+ */
+ public static synchronized Calendar getInstance()
+ {
+ return getInstance(TimeZone.getDefault(), Locale.getDefault());
+ }
+
+ /**
+ * Creates a calendar representing the actual time, using the given
+ * time zone and the default locale.
+ *
+ * @param zone a time zone (<code>null</code> not permitted).
+ *
+ * @return The new calendar.
+ *
+ * @throws NullPointerException if <code>zone</code> is <code>null</code>.
+ */
+ public static synchronized Calendar getInstance(TimeZone zone)
+ {
+ return getInstance(zone, Locale.getDefault());
+ }
+
+ /**
+ * Creates a calendar representing the actual time, using the default
+ * time zone and the given locale.
+ *
+ * @param locale a locale (<code>null</code> not permitted).
+ *
+ * @return The new calendar.
+ *
+ * @throws NullPointerException if <code>locale</code> is <code>null</code>.
+ */
+ public static synchronized Calendar getInstance(Locale locale)
+ {
+ return getInstance(TimeZone.getDefault(), locale);
+ }
+
+ /**
+ * Cache of locale->calendar-class mappings. This avoids having to do a ResourceBundle
+ * lookup for every getInstance call.
+ */
+ private static final HashMap<Locale,Class> cache = new HashMap<Locale,Class>();
+
+ /** Preset argument types for calendar-class constructor lookup. */
+ private static Class[] ctorArgTypes = new Class[]
+ {
+ TimeZone.class, Locale.class
+ };
+
+ /**
+ * Creates a calendar representing the actual time, using the given
+ * time zone and locale.
+ *
+ * @param zone a time zone (<code>null</code> not permitted).
+ * @param locale a locale (<code>null</code> not permitted).
+ *
+ * @return The new calendar.
+ *
+ * @throws NullPointerException if <code>zone</code> or <code>locale</code>
+ * is <code>null</code>.
+ */
+ public static synchronized Calendar getInstance(TimeZone zone, Locale locale)
+ {
+ Class calendarClass = cache.get(locale);
+ Throwable exception = null;
+
+ try
+ {
+ if (calendarClass == null)
+ {
+ calendarClass = Class.forName(calendarClassName);
+ if (Calendar.class.isAssignableFrom(calendarClass))
+ cache.put(locale, calendarClass);
+ }
+
+ // GregorianCalendar is by far the most common case. Optimize by
+ // avoiding reflection.
+ if (calendarClass == GregorianCalendar.class)
+ return new GregorianCalendar(zone, locale);
+
+ if (Calendar.class.isAssignableFrom(calendarClass))
+ {
+ Constructor ctor = calendarClass.getConstructor(ctorArgTypes);
+ return (Calendar) ctor.newInstance(new Object[] { zone, locale });
+ }
+ }
+ catch (ClassNotFoundException ex)
+ {
+ exception = ex;
+ }
+ catch (IllegalAccessException ex)
+ {
+ exception = ex;
+ }
+ catch (NoSuchMethodException ex)
+ {
+ exception = ex;
+ }
+ catch (InstantiationException ex)
+ {
+ exception = ex;
+ }
+ catch (InvocationTargetException ex)
+ {
+ exception = ex;
+ }
+
+ throw new RuntimeException("Error instantiating calendar for locale "
+ + locale, exception);
+ }
+
+ /**
+ * Gets the set of locales for which a Calendar is available.
+ * @exception MissingResourceException if locale data couldn't be found.
+ * @return the set of locales.
+ */
+ public static synchronized Locale[] getAvailableLocales()
+ {
+ ResourceBundle rb = getBundle(new Locale("", ""));
+ return (Locale[]) rb.getObject("availableLocales");
+ }
+
+ /**
+ * Converts the time field values (<code>fields</code>) to
+ * milliseconds since the epoch UTC (<code>time</code>). Override
+ * this method if you write your own Calendar. */
+ protected abstract void computeTime();
+
+ /**
+ * Converts the milliseconds since the epoch UTC
+ * (<code>time</code>) to time fields
+ * (<code>fields</code>). Override this method if you write your
+ * own Calendar.
+ */
+ protected abstract void computeFields();
+
+ /**
+ * Converts the time represented by this object to a
+ * <code>Date</code>-Object.
+ * @return the Date.
+ */
+ public final Date getTime()
+ {
+ if (! isTimeSet)
+ computeTime();
+ return new Date(time);
+ }
+
+ /**
+ * Sets this Calendar's time to the given Date. All time fields
+ * are invalidated by this method.
+ *
+ * @param date the date (<code>null</code> not permitted).
+ *
+ * @throws NullPointerException if <code>date</code> is <code>null</code>.
+ */
+ public final void setTime(Date date)
+ {
+ setTimeInMillis(date.getTime());
+ }
+
+ /**
+ * Returns the time represented by this Calendar.
+ * @return the time in milliseconds since the epoch.
+ * @specnote This was made public in 1.4.
+ */
+ public long getTimeInMillis()
+ {
+ if (! isTimeSet)
+ computeTime();
+ return time;
+ }
+
+ /**
+ * Sets this Calendar's time to the given Time. All time fields
+ * are invalidated by this method.
+ * @param time the time in milliseconds since the epoch
+ * @specnote This was made public in 1.4.
+ */
+ public void setTimeInMillis(long time)
+ {
+ clear();
+ this.time = time;
+ isTimeSet = true;
+ computeFields();
+ }
+
+ /**
+ * Gets the value of the specified field. They are recomputed
+ * if they are invalid.
+ * @param field the time field. One of the time field constants.
+ * @return the value of the specified field
+ * @throws ArrayIndexOutOfBoundsException if the field is outside
+ * the valid range. The value of field must be >= 0 and
+ * <= <code>FIELD_COUNT</code>.
+ * @specnote Not final since JDK 1.4
+ */
+ public int get(int field)
+ {
+ // If the requested field is invalid, force all fields to be recomputed.
+ if (! isSet[field])
+ areFieldsSet = false;
+ complete();
+ return fields[field];
+ }
+
+ /**
+ * Gets the value of the specified field. This method doesn't
+ * recompute the fields, if they are invalid.
+ * @param field the time field. One of the time field constants.
+ * @return the value of the specified field, undefined if
+ * <code>areFieldsSet</code> or <code>isSet[field]</code> is false.
+ * @throws ArrayIndexOutOfBoundsException if the field is outside
+ * the valid range. The value of field must be >= 0 and
+ * <= <code>FIELD_COUNT</code>.
+ */
+ protected final int internalGet(int field)
+ {
+ return fields[field];
+ }
+
+ /**
+ * Sets the time field with the given value. This does invalidate
+ * the time in milliseconds.
+ * @param field the time field. One of the time field constants
+ * @param value the value to be set.
+ * @throws ArrayIndexOutOfBoundsException if field is outside
+ * the valid range. The value of field must be >= 0 and
+ * <= <code>FIELD_COUNT</code>.
+ * @specnote Not final since JDK 1.4
+ */
+ public void set(int field, int value)
+ {
+ if (isTimeSet)
+ for (int i = 0; i < FIELD_COUNT; i++)
+ isSet[i] = false;
+ isTimeSet = false;
+ fields[field] = value;
+ isSet[field] = true;
+
+ // The five valid date patterns, in order of priority
+ // 1 YEAR + MONTH + DAY_OF_MONTH
+ // 2 YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
+ // 3 YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
+ // 4 YEAR + DAY_OF_YEAR
+ // 5 YEAR + DAY_OF_WEEK + WEEK_OF_YEAR
+ switch (field)
+ {
+ case MONTH: // pattern 1,2 or 3
+ isSet[DAY_OF_YEAR] = false;
+ isSet[WEEK_OF_YEAR] = false;
+ break;
+ case DAY_OF_MONTH: // pattern 1
+ isSet[YEAR] = true;
+ isSet[MONTH] = true;
+ isSet[WEEK_OF_MONTH] = true;
+ isSet[DAY_OF_WEEK] = false;
+ isSet[DAY_OF_WEEK_IN_MONTH] = false;
+ isSet[DAY_OF_YEAR] = false;
+ isSet[WEEK_OF_YEAR] = false;
+ break;
+ case WEEK_OF_MONTH: // pattern 2
+ if (! isSet[DAY_OF_WEEK])
+ fields[DAY_OF_WEEK] = getFirstDayOfWeek();
+ isSet[YEAR] = true;
+ isSet[MONTH] = true;
+ isSet[DAY_OF_WEEK] = true;
+ isSet[DAY_OF_MONTH] = false;
+ isSet[DAY_OF_WEEK_IN_MONTH] = false;
+ isSet[DAY_OF_YEAR] = false;
+ isSet[WEEK_OF_YEAR] = false;
+ break;
+ case DAY_OF_WEEK_IN_MONTH: // pattern 3
+ if (! isSet[DAY_OF_WEEK])
+ fields[DAY_OF_WEEK] = getFirstDayOfWeek();
+ isSet[YEAR] = true;
+ isSet[MONTH] = true;
+ isSet[DAY_OF_WEEK] = true;
+ isSet[DAY_OF_YEAR] = false;
+ isSet[DAY_OF_MONTH] = false;
+ isSet[WEEK_OF_MONTH] = false;
+ isSet[WEEK_OF_YEAR] = false;
+ break;
+ case DAY_OF_YEAR: // pattern 4
+ isSet[YEAR] = true;
+ isSet[MONTH] = false;
+ isSet[WEEK_OF_MONTH] = false;
+ isSet[DAY_OF_MONTH] = false;
+ isSet[DAY_OF_WEEK] = false;
+ isSet[WEEK_OF_YEAR] = false;
+ isSet[DAY_OF_WEEK_IN_MONTH] = false;
+ break;
+ case WEEK_OF_YEAR: // pattern 5
+ if (! isSet[DAY_OF_WEEK])
+ fields[DAY_OF_WEEK] = getFirstDayOfWeek();
+ isSet[YEAR] = true;
+ isSet[DAY_OF_WEEK] = true;
+ isSet[MONTH] = false;
+ isSet[DAY_OF_MONTH] = false;
+ isSet[WEEK_OF_MONTH] = false;
+ isSet[DAY_OF_YEAR] = false;
+ isSet[DAY_OF_WEEK_IN_MONTH] = false;
+ break;
+ case AM_PM:
+ isSet[HOUR] = true;
+ isSet[HOUR_OF_DAY] = false;
+ break;
+ case HOUR_OF_DAY:
+ isSet[AM_PM] = false;
+ isSet[HOUR] = false;
+ break;
+ case HOUR:
+ isSet[AM_PM] = true;
+ isSet[HOUR_OF_DAY] = false;
+ break;
+ case DST_OFFSET:
+ explicitDSTOffset = true;
+ }
+
+ // May have crossed over a DST boundary.
+ if (! explicitDSTOffset && (field != DST_OFFSET && field != ZONE_OFFSET))
+ isSet[DST_OFFSET] = false;
+ }
+
+ /**
+ * Sets the fields for year, month, and date
+ * @param year the year.
+ * @param month the month, one of the constants JANUARY..UNDICEMBER.
+ * @param date the day of the month
+ */
+ public final void set(int year, int month, int date)
+ {
+ isTimeSet = false;
+ fields[YEAR] = year;
+ fields[MONTH] = month;
+ fields[DATE] = date;
+ isSet[YEAR] = isSet[MONTH] = isSet[DATE] = true;
+ isSet[WEEK_OF_YEAR] = false;
+ isSet[DAY_OF_YEAR] = false;
+ isSet[WEEK_OF_MONTH] = false;
+ isSet[DAY_OF_WEEK] = false;
+ isSet[DAY_OF_WEEK_IN_MONTH] = false;
+ isSet[ERA] = false;
+
+ if (! explicitDSTOffset)
+ isSet[DST_OFFSET] = false; // May have crossed a DST boundary.
+ }
+
+ /**
+ * Sets the fields for year, month, date, hour, and minute
+ * @param year the year.
+ * @param month the month, one of the constants JANUARY..UNDICEMBER.
+ * @param date the day of the month
+ * @param hour the hour of day.
+ * @param minute the minute.
+ */
+ public final void set(int year, int month, int date, int hour, int minute)
+ {
+ set(year, month, date);
+ fields[HOUR_OF_DAY] = hour;
+ fields[MINUTE] = minute;
+ isSet[HOUR_OF_DAY] = isSet[MINUTE] = true;
+ isSet[AM_PM] = false;
+ isSet[HOUR] = false;
+ }
+
+ /**
+ * Sets the fields for year, month, date, hour, and minute
+ * @param year the year.
+ * @param month the month, one of the constants JANUARY..UNDICEMBER.
+ * @param date the day of the month
+ * @param hour the hour of day.
+ * @param minute the minute.
+ * @param second the second.
+ */
+ public final void set(int year, int month, int date, int hour, int minute,
+ int second)
+ {
+ set(year, month, date, hour, minute);
+ fields[SECOND] = second;
+ isSet[SECOND] = true;
+ }
+
+ /**
+ * Clears the values of all the time fields.
+ */
+ public final void clear()
+ {
+ isTimeSet = false;
+ areFieldsSet = false;
+ int zoneOffs = zone.getRawOffset();
+ int[] tempFields =
+ {
+ 1, 1970, JANUARY, 1, 1, 1, 1, THURSDAY, 1, AM, 0, 0, 0,
+ 0, 0, zoneOffs, 0
+ };
+ fields = tempFields;
+ for (int i = 0; i < FIELD_COUNT; i++)
+ isSet[i] = false;
+ }
+
+ /**
+ * Clears the values of the specified time field.
+ * @param field the time field. One of the time field constants.
+ * @throws ArrayIndexOutOfBoundsException if field is outside
+ * the valid range. The value of field must be >= 0 and
+ * <= <code>FIELD_COUNT</code>.
+ */
+ public final void clear(int field)
+ {
+ int[] tempFields =
+ {
+ 1, 1970, JANUARY, 1, 1, 1, 1, THURSDAY, 1, AM, 0, 0, 0,
+ 0, 0, zone.getRawOffset(), 0
+ };
+ complete();
+ isTimeSet = false;
+ areFieldsSet = false;
+ isSet[field] = false;
+ fields[field] = tempFields[field];
+ }
+
+ /**
+ * Determines if the specified field has a valid value.
+ * @return true if the specified field has a value.
+ * @throws ArrayIndexOutOfBoundsException if the field is outside
+ * the valid range. The value of field must be >= 0 and
+ * <= <code>FIELD_COUNT</code>.
+ */
+ public final boolean isSet(int field)
+ {
+ return isSet[field];
+ }
+
+ /**
+ * Fills any unset fields in the time field list
+ */
+ protected void complete()
+ {
+ if (! isTimeSet)
+ computeTime();
+ if (! areFieldsSet)
+ computeFields();
+ }
+
+ /**
+ * Compares the given calendar with this.
+ * @param o the object to that we should compare.
+ * @return true, if the given object is a calendar, that represents
+ * the same time (but doesn't necessary have the same fields).
+ */
+ public boolean equals(Object o)
+ {
+ if (! (o instanceof Calendar))
+ return false;
+ Calendar cal = (Calendar) o;
+ if (getTimeInMillis() == ((Calendar) o).getTimeInMillis()
+ && cal.getFirstDayOfWeek() == getFirstDayOfWeek()
+ && cal.isLenient() == isLenient()
+ && cal.getMinimalDaysInFirstWeek() == getMinimalDaysInFirstWeek())
+ {
+ TimeZone self = getTimeZone();
+ TimeZone oth = cal.getTimeZone();
+ return self == null ? oth == null : self.equals(oth);
+ }
+ return false;
+ }
+
+ /**
+ * Returns a hash code for this calendar.
+ * @return a hash code, which fullfits the general contract of
+ * <code>hashCode()</code>
+ */
+ public int hashCode()
+ {
+ long time = getTimeInMillis();
+ int val = (int) ((time & 0xffffffffL) ^ (time >> 32));
+ val += (getFirstDayOfWeek() + (isLenient() ? 1230 : 1237)
+ + getMinimalDaysInFirstWeek());
+ TimeZone self = getTimeZone();
+ if (self != null)
+ val ^= self.hashCode();
+ return val;
+ }
+
+ /**
+ * Compares the given calendar with this.
+ * @param o the object to that we should compare.
+ * @return true, if the given object is a calendar, and this calendar
+ * represents a smaller time than the calendar o.
+ * @exception ClassCastException if o is not an calendar.
+ * @since JDK1.2 you don't need to override this method
+ */
+ public boolean before(Object o)
+ {
+ return getTimeInMillis() < ((Calendar) o).getTimeInMillis();
+ }
+
+ /**
+ * Compares the given calendar with this.
+ * @param o the object to that we should compare.
+ * @return true, if the given object is a calendar, and this calendar
+ * represents a bigger time than the calendar o.
+ * @exception ClassCastException if o is not an calendar.
+ * @since JDK1.2 you don't need to override this method
+ */
+ public boolean after(Object o)
+ {
+ return getTimeInMillis() > ((Calendar) o).getTimeInMillis();
+ }
+
+ /**
+ * Adds the specified amount of time to the given time field. The
+ * amount may be negative to subtract the time. If the field overflows
+ * it does what you expect: Jan, 25 + 10 Days is Feb, 4.
+ * @param field the time field. One of the time field constants.
+ * @param amount the amount of time.
+ * @throws ArrayIndexOutOfBoundsException if the field is outside
+ * the valid range. The value of field must be >= 0 and
+ * <= <code>FIELD_COUNT</code>.
+ */
+ public abstract void add(int field, int amount);
+
+ /**
+ * Rolls the specified time field up or down. This means add one
+ * to the specified field, but don't change the other fields. If
+ * the maximum for this field is reached, start over with the
+ * minimum value. <br>
+ *
+ * <strong>Note:</strong> There may be situation, where the other
+ * fields must be changed, e.g rolling the month on May, 31.
+ * The date June, 31 is automatically converted to July, 1.
+ * @param field the time field. One of the time field constants.
+ * @param up the direction, true for up, false for down.
+ * @throws ArrayIndexOutOfBoundsException if the field is outside
+ * the valid range. The value of field must be >= 0 and
+ * <= <code>FIELD_COUNT</code>.
+ */
+ public abstract void roll(int field, boolean up);
+
+ /**
+ * Rolls up or down the specified time field by the given amount.
+ * A negative amount rolls down. The default implementation is
+ * call <code>roll(int, boolean)</code> for the specified amount.
+ *
+ * Subclasses should override this method to do more intuitiv things.
+ *
+ * @param field the time field. One of the time field constants.
+ * @param amount the amount to roll by, positive for rolling up,
+ * negative for rolling down.
+ * @throws ArrayIndexOutOfBoundsException if the field is outside
+ * the valid range. The value of field must be >= 0 and
+ * <= <code>FIELD_COUNT</code>.
+ * @since JDK1.2
+ */
+ public void roll(int field, int amount)
+ {
+ while (amount > 0)
+ {
+ roll(field, true);
+ amount--;
+ }
+ while (amount < 0)
+ {
+ roll(field, false);
+ amount++;
+ }
+ }
+
+ /**
+ * Sets the time zone to the specified value.
+ * @param zone the new time zone
+ */
+ public void setTimeZone(TimeZone zone)
+ {
+ this.zone = zone;
+ computeTime();
+ computeFields();
+ }
+
+ /**
+ * Gets the time zone of this calendar
+ * @return the current time zone.
+ */
+ public TimeZone getTimeZone()
+ {
+ return zone;
+ }
+
+ /**
+ * Specifies if the date/time interpretation should be lenient.
+ * If the flag is set, a date such as "February 30, 1996" will be
+ * treated as the 29th day after the February 1. If this flag
+ * is false, such dates will cause an exception.
+ * @param lenient true, if the date should be interpreted linient,
+ * false if it should be interpreted strict.
+ */
+ public void setLenient(boolean lenient)
+ {
+ this.lenient = lenient;
+ }
+
+ /**
+ * Tells if the date/time interpretation is lenient.
+ * @return true, if the date should be interpreted linient,
+ * false if it should be interpreted strict.
+ */
+ public boolean isLenient()
+ {
+ return lenient;
+ }
+
+ /**
+ * Sets what the first day of week is. This is used for
+ * WEEK_OF_MONTH and WEEK_OF_YEAR fields.
+ * @param value the first day of week. One of SUNDAY to SATURDAY.
+ */
+ public void setFirstDayOfWeek(int value)
+ {
+ firstDayOfWeek = value;
+ }
+
+ /**
+ * Gets what the first day of week is. This is used for
+ * WEEK_OF_MONTH and WEEK_OF_YEAR fields.
+ * @return the first day of week. One of SUNDAY to SATURDAY.
+ */
+ public int getFirstDayOfWeek()
+ {
+ return firstDayOfWeek;
+ }
+
+ /**
+ * Sets how many days are required in the first week of the year.
+ * If the first day of the year should be the first week you should
+ * set this value to 1. If the first week must be a full week, set
+ * it to 7.
+ * @param value the minimal days required in the first week.
+ */
+ public void setMinimalDaysInFirstWeek(int value)
+ {
+ minimalDaysInFirstWeek = value;
+ }
+
+ /**
+ * Gets how many days are required in the first week of the year.
+ * @return the minimal days required in the first week.
+ * @see #setMinimalDaysInFirstWeek
+ */
+ public int getMinimalDaysInFirstWeek()
+ {
+ return minimalDaysInFirstWeek;
+ }
+
+ /**
+ * Gets the smallest value that is allowed for the specified field.
+ * @param field the time field. One of the time field constants.
+ * @return the smallest value.
+ */
+ public abstract int getMinimum(int field);
+
+ /**
+ * Gets the biggest value that is allowed for the specified field.
+ * @param field the time field. One of the time field constants.
+ * @return the biggest value.
+ */
+ public abstract int getMaximum(int field);
+
+ /**
+ * Gets the greatest minimum value that is allowed for the specified field.
+ * @param field the time field. One of the time field constants.
+ * @return the greatest minimum value.
+ */
+ public abstract int getGreatestMinimum(int field);
+
+ /**
+ * Gets the smallest maximum value that is allowed for the
+ * specified field. For example this is 28 for DAY_OF_MONTH.
+ * @param field the time field. One of the time field constants.
+ * @return the least maximum value.
+ */
+ public abstract int getLeastMaximum(int field);
+
+ /**
+ * Gets the actual minimum value that is allowed for the specified field.
+ * This value is dependent on the values of the other fields.
+ * @param field the time field. One of the time field constants.
+ * @return the actual minimum value.
+ * @throws ArrayIndexOutOfBoundsException if the field is outside
+ * the valid range. The value of field must be >= 0 and
+ * <= <code>FIELD_COUNT</code>.
+ * @since jdk1.2
+ */
+ public int getActualMinimum(int field)
+ {
+ Calendar tmp = (Calendar) clone(); // To avoid restoring state
+ int min = tmp.getGreatestMinimum(field);
+ int end = tmp.getMinimum(field);
+ tmp.set(field, min);
+ for (; min > end; min--)
+ {
+ tmp.add(field, -1); // Try to get smaller
+ if (tmp.get(field) != min - 1)
+ break; // Done if not successful
+ }
+ return min;
+ }
+
+ /**
+ * Gets the actual maximum value that is allowed for the specified field.
+ * This value is dependent on the values of the other fields.
+ * @param field the time field. One of the time field constants.
+ * @return the actual maximum value.
+ * @throws ArrayIndexOutOfBoundsException if the field is outside
+ * the valid range. The value of field must be >= 0 and
+ * <= <code>FIELD_COUNT</code>.
+ * @since jdk1.2
+ */
+ public int getActualMaximum(int field)
+ {
+ Calendar tmp = (Calendar) clone(); // To avoid restoring state
+ int max = tmp.getLeastMaximum(field);
+ int end = tmp.getMaximum(field);
+ tmp.set(field, max);
+ for (; max < end; max++)
+ {
+ tmp.add(field, 1);
+ if (tmp.get(field) != max + 1)
+ break;
+ }
+ return max;
+ }
+
+ /**
+ * Compares the time of two calendar instances.
+ * @param cal the calendar to which the time should be compared.
+ * @return 0 if the two calendars are set to the same time,
+ * less than 0 if the time of this calendar is before that of
+ * <code>cal</code>, or more than 0 if the time of this calendar is after
+ * that of <code>cal</code>.
+ *
+ * @param cal the calendar to compare this instance with.
+ * @throws NullPointerException if <code>cal</code> is null.
+ * @throws IllegalArgumentException if either calendar has fields set to
+ * invalid values.
+ * @since 1.5
+ */
+ public int compareTo(Calendar cal)
+ {
+ long t1 = getTimeInMillis();
+ long t2 = cal.getTimeInMillis();
+ if(t1 == t2)
+ return 0;
+ if(t1 > t2)
+ return 1;
+ return -1;
+ }
+
+ /**
+ * Return a clone of this object.
+ */
+ public Object clone()
+ {
+ try
+ {
+ Calendar cal = (Calendar) super.clone();
+ cal.fields = (int[]) fields.clone();
+ cal.isSet = (boolean[]) isSet.clone();
+ return cal;
+ }
+ catch (CloneNotSupportedException ex)
+ {
+ return null;
+ }
+ }
+
+ private static final String[] fieldNames =
+ {
+ ",ERA=", ",YEAR=", ",MONTH=",
+ ",WEEK_OF_YEAR=",
+ ",WEEK_OF_MONTH=",
+ ",DAY_OF_MONTH=",
+ ",DAY_OF_YEAR=", ",DAY_OF_WEEK=",
+ ",DAY_OF_WEEK_IN_MONTH=",
+ ",AM_PM=", ",HOUR=",
+ ",HOUR_OF_DAY=", ",MINUTE=",
+ ",SECOND=", ",MILLISECOND=",
+ ",ZONE_OFFSET=", ",DST_OFFSET="
+ };
+
+ /**
+ * Returns a string representation of this object. It is mainly
+ * for debugging purposes and its content is implementation
+ * specific.
+ */
+ public String toString()
+ {
+ CPStringBuilder sb = new CPStringBuilder(getClass().getName());
+ sb.append('[');
+ sb.append("time=");
+ if (isTimeSet)
+ sb.append(time);
+ else
+ sb.append("?");
+ sb.append(",zone=" + zone);
+ sb.append(",areFieldsSet=" + areFieldsSet);
+ for (int i = 0; i < FIELD_COUNT; i++)
+ {
+ sb.append(fieldNames[i]);
+ if (isSet[i])
+ sb.append(fields[i]);
+ else
+ sb.append("?");
+ }
+ sb.append(",lenient=").append(lenient);
+ sb.append(",firstDayOfWeek=").append(firstDayOfWeek);
+ sb.append(",minimalDaysInFirstWeek=").append(minimalDaysInFirstWeek);
+ sb.append("]");
+ return sb.toString();
+ }
+
+ /**
+ * Saves the state of the object to the stream. Ideally we would
+ * only write the time field, but we need to be compatible with
+ * earlier versions. <br>
+ *
+ * This doesn't write the JDK1.1 field nextStamp to the stream, as
+ * I don't know what it is good for, and because the documentation
+ * says, that it could be omitted. */
+ private void writeObject(ObjectOutputStream stream) throws IOException
+ {
+ if (! isTimeSet)
+ computeTime();
+ stream.defaultWriteObject();
+ }
+
+ /**
+ * Reads the object back from stream (deserialization).
+ */
+ private void readObject(ObjectInputStream stream)
+ throws IOException, ClassNotFoundException
+ {
+ stream.defaultReadObject();
+ if (! isTimeSet)
+ computeTime();
+
+ if (serialVersionOnStream > 1)
+ {
+ // This is my interpretation of the serial number:
+ // Sun wants to remove all fields from the stream someday
+ // and will then increase the serialVersion number again.
+ // We prepare to be compatible.
+ fields = new int[FIELD_COUNT];
+ isSet = new boolean[FIELD_COUNT];
+ areFieldsSet = false;
+ }
+ }
+
+ /**
+ * Returns a localised textual representation of the current value
+ * of the given field using the specified style. If there is no
+ * applicable textual representation (e.g. the field has a numeric
+ * value), then <code>null</code> is returned. If one does exist,
+ * then the value is obtained from {@link #get(int)} and converted
+ * appropriately. For example, if the <code>MONTH</code> field is
+ * requested, then <code>get(MONTH)</code> is called. This is then
+ * converted to a textual representation based on its value and
+ * the style requested; if the <code>LONG</code> style is requested
+ * and the returned value is <code>11</code> from a
+ * {@link GregorianCalendar} implementation, then <code>"December"</code>
+ * is returned. By default, a textual representation is available
+ * for all fields which have an applicable value obtainable from
+ * {@link java.text.DateFormatSymbols}.
+ *
+ * @param field the calendar field whose textual representation should
+ * be obtained.
+ * @param style the style to use; either {@link #LONG} or {@link #SHORT}.
+ * @param locale the locale to use for translation.
+ * @return the textual representation of the given field in the specified
+ * style, or <code>null</code> if none is applicable.
+ * @throws IllegalArgumentException if <code>field</code> or <code>style</code>
+ * or invalid, or the calendar is non-lenient
+ * and has invalid values.
+ * @throws NullPointerException if <code>locale</code> is <code>null</code>.
+ * @since 1.6
+ */
+ public String getDisplayName(int field, int style, Locale locale)
+ {
+ if (field < 0 || field >= FIELD_COUNT)
+ throw new IllegalArgumentException("The field value, " + field +
+ ", is invalid.");
+ if (style != SHORT && style != LONG)
+ throw new IllegalArgumentException("The style must be either " +
+ "short or long.");
+ if (field == YEAR || field == WEEK_OF_YEAR ||
+ field == WEEK_OF_MONTH || field == DAY_OF_MONTH ||
+ field == DAY_OF_YEAR || field == DAY_OF_WEEK_IN_MONTH ||
+ field == HOUR || field == HOUR_OF_DAY || field == MINUTE ||
+ field == SECOND || field == MILLISECOND)
+ return null;
+
+ int value = get(field);
+ DateFormatSymbols syms = DateFormatSymbols.getInstance(locale);
+ if (field == ERA)
+ return syms.getEras()[value];
+ if (field == MONTH)
+ if (style == LONG)
+ return syms.getMonths()[value];
+ else
+ return syms.getShortMonths()[value];
+ if (field == DAY_OF_WEEK)
+ if (style == LONG)
+ return syms.getWeekdays()[value];
+ else
+ return syms.getShortWeekdays()[value];
+ if (field == AM_PM)
+ return syms.getAmPmStrings()[value];
+ if (field == ZONE_OFFSET)
+ if (style == LONG)
+ return syms.getZoneStrings()[value][1];
+ else
+ return syms.getZoneStrings()[value][2];
+ if (field == DST_OFFSET)
+ if (style == LONG)
+ return syms.getZoneStrings()[value][3];
+ else
+ return syms.getZoneStrings()[value][4];
+
+ throw new InternalError("Failed to resolve field " + field +
+ " with style " + style + " for locale " +
+ locale);
+ }
+
+ /**
+ * Returns a map linking all specified textual representations
+ * of the given field to their numerical values. The textual
+ * representations included are determined by the specified
+ * style and locale. For example, if the style <code>LONG</code>
+ * is specified and the German locale, then the map will
+ * contain "Montag" to {@link #MONDAY}, "Dienstag" to
+ * {@link #TUESDAY}, "Mittwoch" to {@link #WEDNESDAY} and
+ * so on. The default implementation uses the values returned
+ * by {@link DateFormatSymbols} so, for example, the style
+ * {@link #ALL_STYLES} and the field {@link #MONTH} will return
+ * a map filled with the values returned from
+ * {@link DateFormatSymbols#getMonths()} and
+ * {@link DateFormatSymbols#getShortMonths()}. If there are
+ * no textual representations for a given field (usually because
+ * it is purely numeric, such as the year in the
+ * {@link GregorianCalendar}), <code>null</code> is returned.
+ *
+ * @param field the calendar field whose textual representation should
+ * be obtained.
+ * @param style the style to use; either {@link #LONG}, {@link #SHORT}
+ * or {@link ALL_STYLES}.
+ * @param locale the locale to use for translation.
+ * @return a map of the textual representations of the given field in the
+ * specified style to their numeric values, or <code>null</code>
+ * if none is applicable.
+ * @throws IllegalArgumentException if <code>field</code> or <code>style</code>
+ * or invalid, or the calendar is non-lenient
+ * and has invalid values.
+ * @throws NullPointerException if <code>locale</code> is <code>null</code>.
+ * @since 1.6
+ */
+ public Map<String,Integer> getDisplayNames(int field, int style, Locale locale)
+ {
+ if (field < 0 || field >= FIELD_COUNT)
+ throw new IllegalArgumentException("The field value, " + field +
+ ", is invalid.");
+ if (style != SHORT && style != LONG && style != ALL_STYLES)
+ throw new IllegalArgumentException("The style must be either " +
+ "short, long or all styles.");
+ if (field == YEAR || field == WEEK_OF_YEAR ||
+ field == WEEK_OF_MONTH || field == DAY_OF_MONTH ||
+ field == DAY_OF_YEAR || field == DAY_OF_WEEK_IN_MONTH ||
+ field == HOUR || field == HOUR_OF_DAY || field == MINUTE ||
+ field == SECOND || field == MILLISECOND)
+ return null;
+
+ DateFormatSymbols syms = DateFormatSymbols.getInstance(locale);
+ Map<String,Integer> map = new HashMap<String,Integer>();
+ if (field == ERA)
+ {
+ String[] eras = syms.getEras();
+ for (int a = 0; a < eras.length; ++a)
+ map.put(eras[a], a);
+ return map;
+ }
+ if (field == MONTH)
+ {
+ if (style == LONG || style == ALL_STYLES)
+ {
+ String[] months = syms.getMonths();
+ for (int a = 0; a < months.length; ++a)
+ map.put(months[a], a);
+ }
+ if (style == SHORT || style == ALL_STYLES)
+ {
+ String[] months = syms.getShortMonths();
+ for (int a = 0; a < months.length; ++a)
+ map.put(months[a], a);
+ }
+ return map;
+ }
+ if (field == DAY_OF_WEEK)
+ {
+ if (style == LONG || style == ALL_STYLES)
+ {
+ String[] weekdays = syms.getWeekdays();
+ for (int a = SUNDAY; a < weekdays.length; ++a)
+ map.put(weekdays[a], a);
+ }
+ if (style == SHORT || style == ALL_STYLES)
+ {
+ String[] weekdays = syms.getShortWeekdays();
+ for (int a = SUNDAY; a < weekdays.length; ++a)
+ map.put(weekdays[a], a);
+ }
+ return map;
+ }
+ if (field == AM_PM)
+ {
+ String[] ampms = syms.getAmPmStrings();
+ for (int a = 0; a < ampms.length; ++a)
+ map.put(ampms[a], a);
+ return map;
+ }
+ if (field == ZONE_OFFSET)
+ {
+ String[][] zones = syms.getZoneStrings();
+ for (int a = 0; a < zones.length; ++a)
+ {
+ if (style == LONG || style == ALL_STYLES)
+ map.put(zones[a][1], a);
+ if (style == SHORT || style == ALL_STYLES)
+ map.put(zones[a][2], a);
+ }
+ return map;
+ }
+ if (field == DST_OFFSET)
+ {
+ String[][] zones = syms.getZoneStrings();
+ for (int a = 0; a < zones.length; ++a)
+ {
+ if (style == LONG || style == ALL_STYLES)
+ map.put(zones[a][3], a);
+ if (style == SHORT || style == ALL_STYLES)
+ map.put(zones[a][4], a);
+ }
+ return map;
+ }
+
+ throw new InternalError("Failed to resolve field " + field +
+ " with style " + style + " for locale " +
+ locale);
+ }
+
+}
diff --git a/libjava/classpath/java/util/Collection.java b/libjava/classpath/java/util/Collection.java
new file mode 100644
index 000000000..b57566f70
--- /dev/null
+++ b/libjava/classpath/java/util/Collection.java
@@ -0,0 +1,290 @@
+/* Collection.java -- Interface that represents a collection of objects
+ Copyright (C) 1998, 2001, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Interface that represents a collection of objects. This interface is the
+ * root of the collection hierarchy, and does not provide any guarantees about
+ * the order of its elements or whether or not duplicate elements are
+ * permitted.
+ * <p>
+ * All methods of this interface that are defined to modify the collection are
+ * defined as <dfn>optional</dfn>. An optional operation may throw an
+ * UnsupportedOperationException if the data backing this collection does not
+ * support such a modification. This may mean that the data structure is
+ * immutable, or that it is read-only but may change ("unmodifiable"), or
+ * that it is modifiable but of fixed size (such as an array), or any number
+ * of other combinations.
+ * <p>
+ * A class that wishes to implement this interface should consider subclassing
+ * AbstractCollection, which provides basic implementations of most of the
+ * methods of this interface. Classes that are prepared to make guarantees
+ * about ordering or about absence of duplicate elements should consider
+ * implementing List or Set respectively, both of which are subinterfaces of
+ * Collection.
+ * <p>
+ * A general-purpose implementation of the Collection interface should in most
+ * cases provide at least two constructors: One which takes no arguments and
+ * creates an empty collection, and one which takes a Collection as an argument
+ * and returns a collection containing the same elements (that is, creates a
+ * copy of the argument using its own implementation).
+ *
+ * @author Original author unknown
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @see List
+ * @see Set
+ * @see Map
+ * @see SortedSet
+ * @see SortedMap
+ * @see HashSet
+ * @see TreeSet
+ * @see ArrayList
+ * @see LinkedList
+ * @see Vector
+ * @see Collections
+ * @see Arrays
+ * @see AbstractCollection
+ * @since 1.2
+ * @status updated to 1.4
+ */
+public interface Collection<E> extends Iterable<E>
+{
+ /**
+ * Add an element to this collection.
+ *
+ * @param o the object to add.
+ * @return true if the collection was modified as a result of this action.
+ * @throws UnsupportedOperationException if this collection does not
+ * support the add operation.
+ * @throws ClassCastException if o cannot be added to this collection due
+ * to its type.
+ * @throws NullPointerException if o is null and this collection doesn't
+ * support the addition of null values.
+ * @throws IllegalArgumentException if o cannot be added to this
+ * collection for some other reason.
+ */
+ boolean add(E o);
+
+ /**
+ * Add the contents of a given collection to this collection.
+ *
+ * @param c the collection to add.
+ * @return true if the collection was modified as a result of this action.
+ * @throws UnsupportedOperationException if this collection does not
+ * support the addAll operation.
+ * @throws ClassCastException if some element of c cannot be added to this
+ * collection due to its type.
+ * @throws NullPointerException if some element of c is null and this
+ * collection does not support the addition of null values.
+ * @throws NullPointerException if c itself is null.
+ * @throws IllegalArgumentException if some element of c cannot be added
+ * to this collection for some other reason.
+ */
+ boolean addAll(Collection<? extends E> c);
+
+ /**
+ * Clear the collection, such that a subsequent call to isEmpty() would
+ * return true.
+ *
+ * @throws UnsupportedOperationException if this collection does not
+ * support the clear operation.
+ */
+ void clear();
+
+ /**
+ * Test whether this collection contains a given object as one of its
+ * elements.
+ *
+ * @param o the element to look for.
+ * @return true if this collection contains at least one element e such that
+ * <code>o == null ? e == null : o.equals(e)</code>.
+ * @throws ClassCastException if the type of o is not a valid type for this
+ * collection.
+ * @throws NullPointerException if o is null and this collection doesn't
+ * support null values.
+ */
+ boolean contains(Object o);
+
+ /**
+ * Test whether this collection contains every element in a given collection.
+ *
+ * @param c the collection to test for.
+ * @return true if for every element o in c, contains(o) would return true.
+ * @throws ClassCastException if the type of any element in c is not a valid
+ * type for this collection.
+ * @throws NullPointerException if some element of c is null and this
+ * collection does not support null values.
+ * @throws NullPointerException if c itself is null.
+ */
+ boolean containsAll(Collection<?> c);
+
+ /**
+ * Test whether this collection is equal to some object. The Collection
+ * interface does not explicitly require any behaviour from this method, and
+ * it may be left to the default implementation provided by Object. The Set
+ * and List interfaces do, however, require specific behaviour from this
+ * method.
+ * <p>
+ * If an implementation of Collection, which is not also an implementation of
+ * Set or List, should choose to implement this method, it should take care
+ * to obey the contract of the equals method of Object. In particular, care
+ * should be taken to return false when o is a Set or a List, in order to
+ * preserve the symmetry of the relation.
+ *
+ * @param o the object to compare to this collection.
+ * @return true if the o is equal to this collection.
+ */
+ boolean equals(Object o);
+
+ /**
+ * Obtain a hash code for this collection. The Collection interface does not
+ * explicitly require any behaviour from this method, and it may be left to
+ * the default implementation provided by Object. The Set and List interfaces
+ * do, however, require specific behaviour from this method.
+ * <p>
+ * If an implementation of Collection, which is not also an implementation of
+ * Set or List, should choose to implement this method, it should take care
+ * to obey the contract of the hashCode method of Object. Note that this
+ * method renders it impossible to correctly implement both Set and List, as
+ * the required implementations are mutually exclusive.
+ *
+ * @return a hash code for this collection.
+ */
+ int hashCode();
+
+ /**
+ * Test whether this collection is empty, that is, if size() == 0.
+ *
+ * @return true if this collection contains no elements.
+ */
+ boolean isEmpty();
+
+ /**
+ * Obtain an Iterator over this collection.
+ *
+ * @return an Iterator over the elements of this collection, in any order.
+ */
+ Iterator<E> iterator();
+
+ /**
+ * Remove a single occurrence of an object from this collection. That is,
+ * remove an element e, if one exists, such that <code>o == null ? e == null
+ * : o.equals(e)</code>.
+ *
+ * @param o the object to remove.
+ * @return true if the collection changed as a result of this call, that is,
+ * if the collection contained at least one occurrence of o.
+ * @throws UnsupportedOperationException if this collection does not
+ * support the remove operation.
+ * @throws ClassCastException if the type of o is not a valid type
+ * for this collection.
+ * @throws NullPointerException if o is null and the collection doesn't
+ * support null values.
+ */
+ boolean remove(Object o);
+
+ /**
+ * Remove all elements of a given collection from this collection. That is,
+ * remove every element e such that c.contains(e).
+ *
+ * @param c The collection of objects to be removed.
+ * @return true if this collection was modified as a result of this call.
+ * @throws UnsupportedOperationException if this collection does not
+ * support the removeAll operation.
+ * @throws ClassCastException if the type of any element in c is not a valid
+ * type for this collection.
+ * @throws NullPointerException if some element of c is null and this
+ * collection does not support removing null values.
+ * @throws NullPointerException if c itself is null.
+ */
+ boolean removeAll(Collection<?> c);
+
+ /**
+ * Remove all elements of this collection that are not contained in a given
+ * collection. That is, remove every element e such that !c.contains(e).
+ *
+ * @param c The collection of objects to be retained.
+ * @return true if this collection was modified as a result of this call.
+ * @throws UnsupportedOperationException if this collection does not
+ * support the retainAll operation.
+ * @throws ClassCastException if the type of any element in c is not a valid
+ * type for this collection.
+ * @throws NullPointerException if some element of c is null and this
+ * collection does not support retaining null values.
+ * @throws NullPointerException if c itself is null.
+ */
+ boolean retainAll(Collection<?> c);
+
+ /**
+ * Get the number of elements in this collection.
+ *
+ * @return the number of elements in the collection.
+ */
+ int size();
+
+ /**
+ * Copy the current contents of this collection into an array.
+ *
+ * @return an array of type Object[] and length equal to the size of this
+ * collection, containing the elements currently in this collection, in
+ * any order.
+ */
+ Object[] toArray();
+
+ /**
+ * Copy the current contents of this collection into an array. If the array
+ * passed as an argument has length less than the size of this collection, an
+ * array of the same run-time type as a, and length equal to the size of this
+ * collection, is allocated using Reflection. Otherwise, a itself is used.
+ * The elements of this collection are copied into it, and if there is space
+ * in the array, the following element is set to null. The resultant array is
+ * returned.
+ * Note: The fact that the following element is set to null is only useful
+ * if it is known that this collection does not contain any null elements.
+ *
+ * @param a the array to copy this collection into.
+ * @return an array containing the elements currently in this collection, in
+ * any order.
+ * @throws ArrayStoreException if the type of any element of the
+ * collection is not a subtype of the element type of a.
+ */
+ <T> T[] toArray(T[] a);
+}
diff --git a/libjava/classpath/java/util/Collections.java b/libjava/classpath/java/util/Collections.java
new file mode 100644
index 000000000..828c6ecea
--- /dev/null
+++ b/libjava/classpath/java/util/Collections.java
@@ -0,0 +1,7623 @@
+/* Collections.java -- Utility class with methods to operate on collections
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.io.Serializable;
+
+/**
+ * Utility class consisting of static methods that operate on, or return
+ * Collections. Contains methods to sort, search, reverse, fill and shuffle
+ * Collections, methods to facilitate interoperability with legacy APIs that
+ * are unaware of collections, a method to return a list which consists of
+ * multiple copies of one element, and methods which "wrap" collections to give
+ * them extra properties, such as thread-safety and unmodifiability.
+ * <p>
+ *
+ * All methods which take a collection throw a {@link NullPointerException} if
+ * that collection is null. Algorithms which can change a collection may, but
+ * are not required, to throw the {@link UnsupportedOperationException} that
+ * the underlying collection would throw during an attempt at modification.
+ * For example,
+ * <code>Collections.singleton("").addAll(Collections.EMPTY_SET)</code>
+ * does not throw a exception, even though addAll is an unsupported operation
+ * on a singleton; the reason for this is that addAll did not attempt to
+ * modify the set.
+ *
+ * @author Original author unknown
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @see Collection
+ * @see Set
+ * @see List
+ * @see Map
+ * @see Arrays
+ * @since 1.2
+ * @status updated to 1.5
+ */
+public class Collections
+{
+ /**
+ * Constant used to decide cutoff for when a non-RandomAccess list should
+ * be treated as sequential-access. Basically, quadratic behavior is
+ * acceptable for small lists when the overhead is so small in the first
+ * place. I arbitrarily set it to 16, so it may need some tuning.
+ */
+ private static final int LARGE_LIST_SIZE = 16;
+
+ /**
+ * Determines if a list should be treated as a sequential-access one.
+ * Rather than the old method of JDK 1.3 of assuming only instanceof
+ * AbstractSequentialList should be sequential, this uses the new method
+ * of JDK 1.4 of assuming anything that does NOT implement RandomAccess
+ * and exceeds a large (unspecified) size should be sequential.
+ *
+ * @param l the list to check
+ * @return <code>true</code> if it should be treated as sequential-access
+ */
+ private static boolean isSequential(List<?> l)
+ {
+ return ! (l instanceof RandomAccess) && l.size() > LARGE_LIST_SIZE;
+ }
+
+ /**
+ * This class is non-instantiable.
+ */
+ private Collections()
+ {
+ }
+
+ /**
+ * An immutable, serializable, empty Set.
+ * @see Serializable
+ */
+ public static final Set EMPTY_SET = new EmptySet();
+
+ /**
+ * Returns an immutable, serializable parameterized empty set.
+ * Unlike the constant <code>EMPTY_SET</code>, the set returned by
+ * this method is type-safe.
+ *
+ * @return an empty parameterized set.
+ * @since 1.5
+ */
+ public static final <T> Set<T> emptySet()
+ {
+ /* FIXME: Could this be optimized? */
+ return new EmptySet<T>();
+ }
+
+ /**
+ * The implementation of {@link #EMPTY_SET}. This class name is required
+ * for compatibility with Sun's JDK serializability.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static final class EmptySet<T> extends AbstractSet<T>
+ implements Serializable
+ {
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = 1582296315990362920L;
+
+ /**
+ * A private constructor adds overhead.
+ */
+ EmptySet()
+ {
+ }
+
+ /**
+ * The size: always 0!
+ * @return 0.
+ */
+ public int size()
+ {
+ return 0;
+ }
+
+ /**
+ * Returns an iterator that does not iterate.
+ * @return A non-iterating iterator.
+ */
+ // This is really cheating! I think it's perfectly valid, though.
+ public Iterator<T> iterator()
+ {
+ return (Iterator<T>) 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.
+ * @param o The object to search for.
+ * @return <code>false</code>.
+ */
+ public boolean contains(Object o)
+ {
+ return false;
+ }
+
+ /**
+ * This is true only if the given collection is also empty.
+ * @param c The collection of objects which are to be compared
+ * against the members of this set.
+ * @return <code>true</code> if c is empty.
+ */
+ public boolean containsAll(Collection<?> c)
+ {
+ return c.isEmpty();
+ }
+
+ /**
+ * Equal only if the other set is empty.
+ * @param o The object to compare with this set.
+ * @return <code>true</code> if o is an empty instance of <code>Set</code>.
+ */
+ public boolean equals(Object o)
+ {
+ return o instanceof Set && ((Set) o).isEmpty();
+ }
+
+ /**
+ * The hashcode is always 0.
+ * @return 0.
+ */
+ public int hashCode()
+ {
+ return 0;
+ }
+
+ /**
+ * Always succeeds with a <code>false</code> result.
+ * @param o The object to remove.
+ * @return <code>false</code>.
+ */
+ public boolean remove(Object o)
+ {
+ return false;
+ }
+
+ /**
+ * Always succeeds with a <code>false</code> result.
+ * @param c The collection of objects which should
+ * all be removed from this set.
+ * @return <code>false</code>.
+ */
+ public boolean removeAll(Collection<?> c)
+ {
+ return false;
+ }
+
+ /**
+ * Always succeeds with a <code>false</code> result.
+ * @param c The collection of objects which should
+ * all be retained within this set.
+ * @return <code>false</code>.
+ */
+ public boolean retainAll(Collection<?> c)
+ {
+ return false;
+ }
+
+ /**
+ * The array is always empty.
+ * @return A new array with a size of 0.
+ */
+ public Object[] toArray()
+ {
+ return new Object[0];
+ }
+
+ /**
+ * We don't even need to use reflection!
+ * @param a An existing array, which can be empty.
+ * @return The original array with any existing
+ * initial element set to null.
+ */
+ public <E> E[] toArray(E[] a)
+ {
+ if (a.length > 0)
+ a[0] = null;
+ return a;
+ }
+
+ /**
+ * The string never changes.
+ *
+ * @return the string "[]".
+ */
+ public String toString()
+ {
+ return "[]";
+ }
+ } // class EmptySet
+
+ /**
+ * An immutable, serializable, empty List, which implements RandomAccess.
+ * @see Serializable
+ * @see RandomAccess
+ */
+ public static final List EMPTY_LIST = new EmptyList();
+
+ /**
+ * Returns an immutable, serializable parameterized empty list.
+ * Unlike the constant <code>EMPTY_LIST</code>, the list returned by
+ * this method is type-safe.
+ *
+ * @return an empty parameterized list.
+ * @since 1.5
+ */
+ public static final <T> List<T> emptyList()
+ {
+ /* FIXME: Could this be optimized? */
+ return new EmptyList<T>();
+ }
+
+ /**
+ * The implementation of {@link #EMPTY_LIST}. This class name is required
+ * for compatibility with Sun's JDK serializability.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static final class EmptyList<T> extends AbstractList<T>
+ implements Serializable, RandomAccess
+ {
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = 8842843931221139166L;
+
+ /**
+ * A private constructor adds overhead.
+ */
+ EmptyList()
+ {
+ }
+
+ /**
+ * The size is always 0.
+ * @return 0.
+ */
+ public int size()
+ {
+ return 0;
+ }
+
+ /**
+ * No matter the index, it is out of bounds. This
+ * method never returns, throwing an exception instead.
+ *
+ * @param index The index of the element to retrieve.
+ * @return the object at the specified index.
+ * @throws IndexOutOfBoundsException as any given index
+ * is outside the bounds of an empty array.
+ */
+ public T get(int index)
+ {
+ throw new IndexOutOfBoundsException();
+ }
+
+ // The remaining methods are optional, but provide a performance
+ // advantage by not allocating unnecessary iterators in AbstractList.
+ /**
+ * Never contains anything.
+ * @param o The object to search for.
+ * @return <code>false</code>.
+ */
+ public boolean contains(Object o)
+ {
+ return false;
+ }
+
+ /**
+ * This is true only if the given collection is also empty.
+ * @param c The collection of objects, which should be compared
+ * against the members of this list.
+ * @return <code>true</code> if c is also empty.
+ */
+ public boolean containsAll(Collection<?> c)
+ {
+ return c.isEmpty();
+ }
+
+ /**
+ * Equal only if the other list is empty.
+ * @param o The object to compare against this list.
+ * @return <code>true</code> if o is also an empty instance of
+ * <code>List</code>.
+ */
+ public boolean equals(Object o)
+ {
+ return o instanceof List && ((List) o).isEmpty();
+ }
+
+ /**
+ * The hashcode is always 1.
+ * @return 1.
+ */
+ public int hashCode()
+ {
+ return 1;
+ }
+
+ /**
+ * Returns -1.
+ * @param o The object to search for.
+ * @return -1.
+ */
+ public int indexOf(Object o)
+ {
+ return -1;
+ }
+
+ /**
+ * Returns -1.
+ * @param o The object to search for.
+ * @return -1.
+ */
+ public int lastIndexOf(Object o)
+ {
+ return -1;
+ }
+
+ /**
+ * Always succeeds with <code>false</code> result.
+ * @param o The object to remove.
+ * @return -1.
+ */
+ public boolean remove(Object o)
+ {
+ return false;
+ }
+
+ /**
+ * Always succeeds with <code>false</code> result.
+ * @param c The collection of objects which should
+ * all be removed from this list.
+ * @return <code>false</code>.
+ */
+ public boolean removeAll(Collection<?> c)
+ {
+ return false;
+ }
+
+ /**
+ * Always succeeds with <code>false</code> result.
+ * @param c The collection of objects which should
+ * all be retained within this list.
+ * @return <code>false</code>.
+ */
+ public boolean retainAll(Collection<?> c)
+ {
+ return false;
+ }
+
+ /**
+ * The array is always empty.
+ * @return A new array with a size of 0.
+ */
+ public Object[] toArray()
+ {
+ return new Object[0];
+ }
+
+ /**
+ * We don't even need to use reflection!
+ * @param a An existing array, which can be empty.
+ * @return The original array with any existing
+ * initial element set to null.
+ */
+ public <E> E[] toArray(E[] a)
+ {
+ if (a.length > 0)
+ a[0] = null;
+ return a;
+ }
+
+ /**
+ * The string never changes.
+ *
+ * @return the string "[]".
+ */
+ public String toString()
+ {
+ return "[]";
+ }
+ } // class EmptyList
+
+ /**
+ * An immutable, serializable, empty Map.
+ * @see Serializable
+ */
+ public static final Map EMPTY_MAP = new EmptyMap();
+
+ /**
+ * Returns an immutable, serializable parameterized empty map.
+ * Unlike the constant <code>EMPTY_MAP</code>, the map returned by
+ * this method is type-safe.
+ *
+ * @return an empty parameterized map.
+ * @since 1.5
+ */
+ public static final <K,V> Map<K,V> emptyMap()
+ {
+ /* FIXME: Could this be optimized? */
+ return new EmptyMap<K,V>();
+ }
+
+ /**
+ * The implementation of {@link #EMPTY_MAP}. This class name is required
+ * for compatibility with Sun's JDK serializability.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static final class EmptyMap<K, V> extends AbstractMap<K, V>
+ implements Serializable
+ {
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = 6428348081105594320L;
+
+ /**
+ * A private constructor adds overhead.
+ */
+ EmptyMap()
+ {
+ }
+
+ /**
+ * There are no entries.
+ * @return The empty set.
+ */
+ public Set<Map.Entry<K, V>> entrySet()
+ {
+ return EMPTY_SET;
+ }
+
+ // The remaining methods are optional, but provide a performance
+ // advantage by not allocating unnecessary iterators in AbstractMap.
+ /**
+ * No entries!
+ * @param key The key to search for.
+ * @return <code>false</code>.
+ */
+ public boolean containsKey(Object key)
+ {
+ return false;
+ }
+
+ /**
+ * No entries!
+ * @param value The value to search for.
+ * @return <code>false</code>.
+ */
+ public boolean containsValue(Object value)
+ {
+ return false;
+ }
+
+ /**
+ * Equal to all empty maps.
+ * @param o The object o to compare against this map.
+ * @return <code>true</code> if o is also an empty instance of
+ * <code>Map</code>.
+ */
+ public boolean equals(Object o)
+ {
+ return o instanceof Map && ((Map) o).isEmpty();
+ }
+
+ /**
+ * No mappings, so this returns null.
+ * @param o The key of the object to retrieve.
+ * @return null.
+ */
+ public V get(Object o)
+ {
+ return null;
+ }
+
+ /**
+ * The hashcode is always 0.
+ * @return 0.
+ */
+ public int hashCode()
+ {
+ return 0;
+ }
+
+ /**
+ * No entries.
+ * @return The empty set.
+ */
+ public Set<K> keySet()
+ {
+ return EMPTY_SET;
+ }
+
+ /**
+ * Remove always succeeds, with null result.
+ * @param o The key of the mapping to remove.
+ * @return null, as there is never a mapping for o.
+ */
+ public V remove(Object o)
+ {
+ return null;
+ }
+
+ /**
+ * Size is always 0.
+ * @return 0.
+ */
+ public int size()
+ {
+ return 0;
+ }
+
+ /**
+ * No entries. Technically, EMPTY_SET, while more specific than a general
+ * Collection, will work. Besides, that's what the JDK uses!
+ * @return The empty set.
+ */
+ public Collection<V> values()
+ {
+ return EMPTY_SET;
+ }
+
+ /**
+ * The string never changes.
+ *
+ * @return the string "[]".
+ */
+ public String toString()
+ {
+ return "[]";
+ }
+ } // class EmptyMap
+
+
+ /**
+ * 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
+ * clever, but worth it for removing a duplicate of the search code.
+ * Note: This code is also used in Arrays (for sort as well as search).
+ */
+ static final <T> int compare(T o1, T o2, Comparator<? super T> c)
+ {
+ return c == null ? ((Comparable) o1).compareTo(o2) : c.compare(o1, o2);
+ }
+
+ /**
+ * Perform a binary search of a List for a key, using the natural ordering of
+ * the elements. The list must be sorted (as by the sort() method) - if it is
+ * not, the behavior of this method is undefined, and may be an infinite
+ * loop. Further, the key must be comparable with every item in the list. If
+ * the list contains the key more than once, any one of them may be found.
+ * <p>
+ *
+ * This algorithm behaves in log(n) time for {@link RandomAccess} lists,
+ * and uses a linear search with O(n) link traversals and log(n) comparisons
+ * with {@link AbstractSequentialList} lists. Note: although the
+ * specification allows for an infinite loop if the list is unsorted, it will
+ * not happen in this (Classpath) implementation.
+ *
+ * @param l the list to search (must be sorted)
+ * @param key the value to search for
+ * @return the index at which the key was found, or -n-1 if it was not
+ * found, where n is the index of the first value higher than key or
+ * a.length if there is no such value
+ * @throws ClassCastException if key could not be compared with one of the
+ * elements of l
+ * @throws NullPointerException if a null element has compareTo called
+ * @see #sort(List)
+ */
+ public static <T> int binarySearch(List<? extends Comparable<? super T>> l,
+ T key)
+ {
+ return binarySearch(l, key, null);
+ }
+
+ /**
+ * Perform a binary search of a List for a key, using a supplied Comparator.
+ * The list must be sorted (as by the sort() method with the same Comparator)
+ * - if it is not, the behavior of this method is undefined, and may be an
+ * infinite loop. Further, the key must be comparable with every item in the
+ * list. If the list contains the key more than once, any one of them may be
+ * found. If the comparator is null, the elements' natural ordering is used.
+ * <p>
+ *
+ * This algorithm behaves in log(n) time for {@link RandomAccess} lists,
+ * and uses a linear search with O(n) link traversals and log(n) comparisons
+ * with {@link AbstractSequentialList} lists. Note: although the
+ * specification allows for an infinite loop if the list is unsorted, it will
+ * not happen in this (Classpath) implementation.
+ *
+ * @param l the list to search (must be sorted)
+ * @param key the value to search for
+ * @param c the comparator by which the list is sorted
+ * @return the index at which the key was found, or -n-1 if it was not
+ * found, where n is the index of the first value higher than key or
+ * a.length if there is no such value
+ * @throws ClassCastException if key could not be compared with one of the
+ * elements of l
+ * @throws NullPointerException if a null element is compared with natural
+ * ordering (only possible when c is null)
+ * @see #sort(List, Comparator)
+ */
+ public static <T> int binarySearch(List<? extends T> l, T key,
+ Comparator<? super T> c)
+ {
+ int pos = 0;
+ int low = 0;
+ int hi = l.size() - 1;
+
+ // We use a linear search with log(n) comparisons using an iterator
+ // if the list is sequential-access.
+ if (isSequential(l))
+ {
+ ListIterator<T> itr = ((List<T>) l).listIterator();
+ int i = 0;
+ T o = itr.next(); // Assumes list is not empty (see isSequential)
+ boolean forward = true;
+ while (low <= hi)
+ {
+ pos = (low + hi) >>> 1;
+ if (i < pos)
+ {
+ if (!forward)
+ itr.next(); // Changing direction first.
+ for ( ; i != pos; i++, o = itr.next())
+ ;
+ forward = true;
+ }
+ else
+ {
+ if (forward)
+ itr.previous(); // Changing direction first.
+ for ( ; i != pos; i--, o = itr.previous())
+ ;
+ forward = false;
+ }
+ final int d = compare(o, key, c);
+ if (d == 0)
+ return pos;
+ else if (d > 0)
+ hi = pos - 1;
+ else
+ // This gets the insertion point right on the last loop
+ low = ++pos;
+ }
+ }
+ else
+ {
+ while (low <= hi)
+ {
+ pos = (low + hi) >>> 1;
+ final int d = compare(((List<T>) l).get(pos), key, c);
+ if (d == 0)
+ return pos;
+ else if (d > 0)
+ hi = pos - 1;
+ else
+ // This gets the insertion point right on the last loop
+ low = ++pos;
+ }
+ }
+
+ // If we failed to find it, we do the same whichever search we did.
+ return -pos - 1;
+ }
+
+ /**
+ * Copy one list to another. If the destination list is longer than the
+ * source list, the remaining elements are unaffected. This method runs in
+ * linear time.
+ *
+ * @param dest the destination list
+ * @param source the source list
+ * @throws IndexOutOfBoundsException if the destination list is shorter
+ * than the source list (the destination will be unmodified)
+ * @throws UnsupportedOperationException if dest.listIterator() does not
+ * support the set operation
+ */
+ public static <T> void copy(List<? super T> dest, List<? extends T> source)
+ {
+ int pos = source.size();
+ if (dest.size() < pos)
+ throw new IndexOutOfBoundsException("Source does not fit in dest");
+
+ Iterator<? extends T> i1 = source.iterator();
+ ListIterator<? super T> i2 = dest.listIterator();
+
+ while (--pos >= 0)
+ {
+ i2.next();
+ i2.set(i1.next());
+ }
+ }
+
+ /**
+ * Returns an Enumeration over a collection. This allows interoperability
+ * with legacy APIs that require an Enumeration as input.
+ *
+ * @param c the Collection to iterate over
+ * @return an Enumeration backed by an Iterator over c
+ */
+ public static <T> Enumeration<T> enumeration(Collection<T> c)
+ {
+ final Iterator<T> i = c.iterator();
+ return new Enumeration<T>()
+ {
+ /**
+ * Returns <code>true</code> if there are more elements to
+ * be enumerated.
+ *
+ * @return The result of <code>hasNext()</code>
+ * called on the underlying iterator.
+ */
+ public final boolean hasMoreElements()
+ {
+ return i.hasNext();
+ }
+
+ /**
+ * Returns the next element to be enumerated.
+ *
+ * @return The result of <code>next()</code>
+ * called on the underlying iterator.
+ */
+ public final T nextElement()
+ {
+ return i.next();
+ }
+ };
+ }
+
+ /**
+ * Replace every element of a list with a given value. This method runs in
+ * linear time.
+ *
+ * @param l the list to fill.
+ * @param val the object to vill the list with.
+ * @throws UnsupportedOperationException if l.listIterator() does not
+ * support the set operation.
+ */
+ public static <T> void fill(List<? super T> l, T val)
+ {
+ ListIterator<? super T> itr = l.listIterator();
+ for (int i = l.size() - 1; i >= 0; --i)
+ {
+ itr.next();
+ itr.set(val);
+ }
+ }
+
+ /**
+ * Returns the starting index where the specified sublist first occurs
+ * in a larger list, or -1 if there is no matching position. If
+ * <code>target.size() &gt; source.size()</code>, this returns -1,
+ * otherwise this implementation uses brute force, checking for
+ * <code>source.sublist(i, i + target.size()).equals(target)</code>
+ * for all possible i.
+ *
+ * @param source the list to search
+ * @param target the sublist to search for
+ * @return the index where found, or -1
+ * @since 1.4
+ */
+ public static int indexOfSubList(List<?> source, List<?> target)
+ {
+ int ssize = source.size();
+ for (int i = 0, j = target.size(); j <= ssize; i++, j++)
+ if (source.subList(i, j).equals(target))
+ return i;
+ return -1;
+ }
+
+ /**
+ * Returns the starting index where the specified sublist last occurs
+ * in a larger list, or -1 if there is no matching position. If
+ * <code>target.size() &gt; source.size()</code>, this returns -1,
+ * otherwise this implementation uses brute force, checking for
+ * <code>source.sublist(i, i + target.size()).equals(target)</code>
+ * for all possible i.
+ *
+ * @param source the list to search
+ * @param target the sublist to search for
+ * @return the index where found, or -1
+ * @since 1.4
+ */
+ public static int lastIndexOfSubList(List<?> source, List<?> target)
+ {
+ int ssize = source.size();
+ for (int i = ssize - target.size(), j = ssize; i >= 0; i--, j--)
+ if (source.subList(i, j).equals(target))
+ return i;
+ return -1;
+ }
+
+ /**
+ * Returns an ArrayList holding the elements visited by a given
+ * Enumeration. This method exists for interoperability between legacy
+ * APIs and the new Collection API.
+ *
+ * @param e the enumeration to put in a list
+ * @return a list containing the enumeration elements
+ * @see ArrayList
+ * @since 1.4
+ */
+ public static <T> ArrayList<T> list(Enumeration<T> e)
+ {
+ ArrayList<T> l = new ArrayList<T>();
+ while (e.hasMoreElements())
+ l.add(e.nextElement());
+ return l;
+ }
+
+ /**
+ * Find the maximum element in a Collection, according to the natural
+ * ordering of the elements. This implementation iterates over the
+ * Collection, so it works in linear time.
+ *
+ * @param c the Collection to find the maximum element of
+ * @return the maximum element of c
+ * @exception NoSuchElementException if c is empty
+ * @exception ClassCastException if elements in c are not mutually comparable
+ * @exception NullPointerException if null.compareTo is called
+ */
+ public static <T extends Object & Comparable<? super T>>
+ T max(Collection<? extends T> c)
+ {
+ return max(c, null);
+ }
+
+ /**
+ * Find the maximum element in a Collection, according to a specified
+ * Comparator. This implementation iterates over the Collection, so it
+ * works in linear time.
+ *
+ * @param c the Collection to find the maximum element of
+ * @param order the Comparator to order the elements by, or null for natural
+ * ordering
+ * @return the maximum element of c
+ * @throws NoSuchElementException if c is empty
+ * @throws ClassCastException if elements in c are not mutually comparable
+ * @throws NullPointerException if null is compared by natural ordering
+ * (only possible when order is null)
+ */
+ public static <T> T max(Collection<? extends T> c,
+ Comparator<? super T> order)
+ {
+ Iterator<? extends T> itr = c.iterator();
+ T max = itr.next(); // throws NoSuchElementException
+ int csize = c.size();
+ for (int i = 1; i < csize; i++)
+ {
+ T o = itr.next();
+ if (compare(max, o, order) < 0)
+ max = o;
+ }
+ return max;
+ }
+
+ /**
+ * Find the minimum element in a Collection, according to the natural
+ * ordering of the elements. This implementation iterates over the
+ * Collection, so it works in linear time.
+ *
+ * @param c the Collection to find the minimum element of
+ * @return the minimum element of c
+ * @throws NoSuchElementException if c is empty
+ * @throws ClassCastException if elements in c are not mutually comparable
+ * @throws NullPointerException if null.compareTo is called
+ */
+ public static <T extends Object & Comparable<? super T>>
+ T min(Collection<? extends T> c)
+ {
+ return min(c, null);
+ }
+
+ /**
+ * Find the minimum element in a Collection, according to a specified
+ * Comparator. This implementation iterates over the Collection, so it
+ * works in linear time.
+ *
+ * @param c the Collection to find the minimum element of
+ * @param order the Comparator to order the elements by, or null for natural
+ * ordering
+ * @return the minimum element of c
+ * @throws NoSuchElementException if c is empty
+ * @throws ClassCastException if elements in c are not mutually comparable
+ * @throws NullPointerException if null is compared by natural ordering
+ * (only possible when order is null)
+ */
+ public static <T> T min(Collection<? extends T> c,
+ Comparator<? super T> order)
+ {
+ Iterator<? extends T> itr = c.iterator();
+ T min = itr.next(); // throws NoSuchElementExcception
+ int csize = c.size();
+ for (int i = 1; i < csize; i++)
+ {
+ T o = itr.next();
+ if (compare(min, o, order) > 0)
+ min = o;
+ }
+ return min;
+ }
+
+ /**
+ * Creates an immutable list consisting of the same object repeated n times.
+ * The returned object is tiny, consisting of only a single reference to the
+ * object and a count of the number of elements. It is Serializable, and
+ * implements RandomAccess. You can use it in tandem with List.addAll for
+ * fast list construction.
+ *
+ * @param n the number of times to repeat the object
+ * @param o the object to repeat
+ * @return a List consisting of n copies of o
+ * @throws IllegalArgumentException if n &lt; 0
+ * @see List#addAll(Collection)
+ * @see Serializable
+ * @see RandomAccess
+ */
+ public static <T> List<T> nCopies(final int n, final T o)
+ {
+ return new CopiesList<T>(n, o);
+ }
+
+ /**
+ * The implementation of {@link #nCopies(int, Object)}. This class name
+ * is required for compatibility with Sun's JDK serializability.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static final class CopiesList<T> extends AbstractList<T>
+ implements Serializable, RandomAccess
+ {
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = 2739099268398711800L;
+
+ /**
+ * The count of elements in this list.
+ * @serial the list size
+ */
+ private final int n;
+
+ /**
+ * The repeated list element.
+ * @serial the list contents
+ */
+ private final T element;
+
+ /**
+ * Constructs the list.
+ *
+ * @param n the count
+ * @param o the object
+ * @throws IllegalArgumentException if n &lt; 0
+ */
+ CopiesList(int n, T o)
+ {
+ if (n < 0)
+ throw new IllegalArgumentException();
+ this.n = n;
+ element = o;
+ }
+
+ /**
+ * The size is fixed.
+ * @return The size of the list.
+ */
+ public int size()
+ {
+ return n;
+ }
+
+ /**
+ * The same element is returned.
+ * @param index The index of the element to be returned (irrelevant
+ * as the list contains only copies of <code>element</code>).
+ * @return The element used by this list.
+ */
+ public T get(int index)
+ {
+ if (index < 0 || index >= n)
+ throw new IndexOutOfBoundsException();
+ return element;
+ }
+
+ // The remaining methods are optional, but provide a performance
+ // advantage by not allocating unnecessary iterators in AbstractList.
+ /**
+ * This list only contains one element.
+ * @param o The object to search for.
+ * @return <code>true</code> if o is the element used by this list.
+ */
+ public boolean contains(Object o)
+ {
+ return n > 0 && equals(o, element);
+ }
+
+ /**
+ * The index is either 0 or -1.
+ * @param o The object to find the index of.
+ * @return 0 if <code>o == element</code>, -1 if not.
+ */
+ public int indexOf(Object o)
+ {
+ return (n > 0 && equals(o, element)) ? 0 : -1;
+ }
+
+ /**
+ * The index is either n-1 or -1.
+ * @param o The object to find the last index of.
+ * @return The last index in the list if <code>o == element</code>,
+ * -1 if not.
+ */
+ public int lastIndexOf(Object o)
+ {
+ return equals(o, element) ? n - 1 : -1;
+ }
+
+ /**
+ * A subList is just another CopiesList.
+ * @param from The starting bound of the sublist.
+ * @param to The ending bound of the sublist.
+ * @return A list of copies containing <code>from - to</code>
+ * elements, all of which are equal to the element
+ * used by this list.
+ */
+ public List<T> subList(int from, int to)
+ {
+ if (from < 0 || to > n)
+ throw new IndexOutOfBoundsException();
+ return new CopiesList<T>(to - from, element);
+ }
+
+ /**
+ * The array is easy.
+ * @return An array of size n filled with copies of
+ * the element used by this list.
+ */
+ public Object[] toArray()
+ {
+ Object[] a = new Object[n];
+ Arrays.fill(a, element);
+ return a;
+ }
+
+ /**
+ * The string is easy to generate.
+ * @return A string representation of the list.
+ */
+ public String toString()
+ {
+ CPStringBuilder r = new CPStringBuilder("{");
+ for (int i = n - 1; --i > 0; )
+ r.append(element).append(", ");
+ r.append(element).append("}");
+ return r.toString();
+ }
+ } // class CopiesList
+
+ /**
+ * Replace all instances of one object with another in the specified list.
+ * The list does not change size. An element e is replaced if
+ * <code>oldval == null ? e == null : oldval.equals(e)</code>.
+ *
+ * @param list the list to iterate over
+ * @param oldval the element to replace
+ * @param newval the new value for the element
+ * @return <code>true</code> if a replacement occurred.
+ * @throws UnsupportedOperationException if the list iterator does not allow
+ * for the set operation
+ * @throws ClassCastException if newval is of a type which cannot be added
+ * to the list
+ * @throws IllegalArgumentException if some other aspect of newval stops
+ * it being added to the list
+ * @since 1.4
+ */
+ public static <T> boolean replaceAll(List<T> list, T oldval, T newval)
+ {
+ ListIterator<T> itr = list.listIterator();
+ boolean replace_occured = false;
+ for (int i = list.size(); --i >= 0; )
+ if (AbstractCollection.equals(oldval, itr.next()))
+ {
+ itr.set(newval);
+ replace_occured = true;
+ }
+ return replace_occured;
+ }
+
+ /**
+ * Reverse a given list. This method works in linear time.
+ *
+ * @param l the list to reverse
+ * @throws UnsupportedOperationException if l.listIterator() does not
+ * support the set operation
+ */
+ public static void reverse(List<?> l)
+ {
+ ListIterator i1 = l.listIterator();
+ int pos1 = 1;
+ int pos2 = l.size();
+ ListIterator i2 = l.listIterator(pos2);
+ while (pos1 < pos2)
+ {
+ Object o1 = i1.next();
+ Object o2 = i2.previous();
+ i1.set(o2);
+ i2.set(o1);
+ ++pos1;
+ --pos2;
+ }
+ }
+
+ /**
+ * Get a comparator that implements the reverse of the ordering
+ * specified by the given Comparator. If the Comparator is null,
+ * this is equivalent to {@link #reverseOrder()}. The return value
+ * of this method is Serializable, if the specified Comparator is
+ * either Serializable or null.
+ *
+ * @param c the comparator to invert
+ * @return a comparator that imposes reverse ordering
+ * @see Comparable
+ * @see Serializable
+ *
+ * @since 1.5
+ */
+ public static <T> Comparator<T> reverseOrder(final Comparator<T> c)
+ {
+ if (c == null)
+ return (Comparator<T>) rcInstance;
+ return new ReverseComparator<T> ()
+ {
+ public int compare(T a, T b)
+ {
+ return - c.compare(a, b);
+ }
+ };
+ }
+
+ /**
+ * Get a comparator that implements the reverse of natural ordering. In
+ * other words, this sorts Comparable objects opposite of how their
+ * compareTo method would sort. This makes it easy to sort into reverse
+ * order, by simply passing Collections.reverseOrder() to the sort method.
+ * The return value of this method is Serializable.
+ *
+ * @return a comparator that imposes reverse natural ordering
+ * @see Comparable
+ * @see Serializable
+ */
+ public static <T> Comparator<T> reverseOrder()
+ {
+ return (Comparator<T>) rcInstance;
+ }
+
+ /**
+ * The object for {@link #reverseOrder()}.
+ */
+ private static final ReverseComparator rcInstance = new ReverseComparator();
+
+ /**
+ * The implementation of {@link #reverseOrder()}. This class name
+ * is required for compatibility with Sun's JDK serializability.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static class ReverseComparator<T>
+ implements Comparator<T>, Serializable
+ {
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = 7207038068494060240L;
+
+ /**
+ * A private constructor adds overhead.
+ */
+ ReverseComparator()
+ {
+ }
+
+ /**
+ * Compare two objects in reverse natural order.
+ *
+ * @param a the first object
+ * @param b the second object
+ * @return &lt;, ==, or &gt; 0 according to b.compareTo(a)
+ */
+ public int compare(T a, T b)
+ {
+ return ((Comparable) b).compareTo(a);
+ }
+ }
+
+ /**
+ * Rotate the elements in a list by a specified distance. After calling this
+ * method, the element now at index <code>i</code> was formerly at index
+ * <code>(i - distance) mod list.size()</code>. The list size is unchanged.
+ * <p>
+ *
+ * For example, suppose a list contains <code>[t, a, n, k, s]</code>. After
+ * either <code>Collections.rotate(l, 4)</code> or
+ * <code>Collections.rotate(l, -1)</code>, the new contents are
+ * <code>[s, t, a, n, k]</code>. This can be applied to sublists to rotate
+ * just a portion of the list. For example, to move element <code>a</code>
+ * forward two positions in the original example, use
+ * <code>Collections.rotate(l.subList(1, 3+1), -1)</code>, which will
+ * result in <code>[t, n, k, a, s]</code>.
+ * <p>
+ *
+ * If the list is small or implements {@link RandomAccess}, the
+ * implementation exchanges the first element to its destination, then the
+ * displaced element, and so on until a circuit has been completed. The
+ * process is repeated if needed on the second element, and so forth, until
+ * all elements have been swapped. For large non-random lists, the
+ * implementation breaks the list into two sublists at index
+ * <code>-distance mod size</code>, calls {@link #reverse(List)} on the
+ * pieces, then reverses the overall list.
+ *
+ * @param list the list to rotate
+ * @param distance the distance to rotate by; unrestricted in value
+ * @throws UnsupportedOperationException if the list does not support set
+ * @since 1.4
+ */
+ public static void rotate(List<?> list, int distance)
+ {
+ int size = list.size();
+ if (size == 0)
+ return;
+ distance %= size;
+ if (distance == 0)
+ return;
+ if (distance < 0)
+ distance += size;
+
+ if (isSequential(list))
+ {
+ reverse(list);
+ reverse(list.subList(0, distance));
+ reverse(list.subList(distance, size));
+ }
+ else
+ {
+ // Determine the least common multiple of distance and size, as there
+ // are (distance / LCM) loops to cycle through.
+ int a = size;
+ int lcm = distance;
+ int b = a % lcm;
+ while (b != 0)
+ {
+ a = lcm;
+ lcm = b;
+ b = a % lcm;
+ }
+
+ // Now, make the swaps. We must take the remainder every time through
+ // the inner loop so that we don't overflow i to negative values.
+ List<Object> objList = (List<Object>) list;
+ while (--lcm >= 0)
+ {
+ Object o = objList.get(lcm);
+ for (int i = lcm + distance; i != lcm; i = (i + distance) % size)
+ o = objList.set(i, o);
+ objList.set(lcm, o);
+ }
+ }
+ }
+
+ /**
+ * Shuffle a list according to a default source of randomness. The algorithm
+ * used iterates backwards over the list, swapping each element with an
+ * element randomly selected from the elements in positions less than or
+ * equal to it (using r.nextInt(int)).
+ * <p>
+ *
+ * This algorithm would result in a perfectly fair shuffle (that is, each
+ * element would have an equal chance of ending up in any position) if r were
+ * a perfect source of randomness. In practice the results are merely very
+ * close to perfect.
+ * <p>
+ *
+ * This method operates in linear time. To do this on large lists which do
+ * not implement {@link RandomAccess}, a temporary array is used to acheive
+ * this speed, since it would be quadratic access otherwise.
+ *
+ * @param l the list to shuffle
+ * @throws UnsupportedOperationException if l.listIterator() does not
+ * support the set operation
+ */
+ public static void shuffle(List<?> l)
+ {
+ if (defaultRandom == null)
+ {
+ synchronized (Collections.class)
+ {
+ if (defaultRandom == null)
+ defaultRandom = new Random();
+ }
+ }
+ shuffle(l, defaultRandom);
+ }
+
+ /**
+ * Cache a single Random object for use by shuffle(List). This improves
+ * performance as well as ensuring that sequential calls to shuffle() will
+ * not result in the same shuffle order occurring: the resolution of
+ * System.currentTimeMillis() is not sufficient to guarantee a unique seed.
+ */
+ private static Random defaultRandom = null;
+
+ /**
+ * Shuffle a list according to a given source of randomness. The algorithm
+ * used iterates backwards over the list, swapping each element with an
+ * element randomly selected from the elements in positions less than or
+ * equal to it (using r.nextInt(int)).
+ * <p>
+ *
+ * This algorithm would result in a perfectly fair shuffle (that is, each
+ * element would have an equal chance of ending up in any position) if r were
+ * a perfect source of randomness. In practise (eg if r = new Random()) the
+ * results are merely very close to perfect.
+ * <p>
+ *
+ * This method operates in linear time. To do this on large lists which do
+ * not implement {@link RandomAccess}, a temporary array is used to acheive
+ * this speed, since it would be quadratic access otherwise.
+ *
+ * @param l the list to shuffle
+ * @param r the source of randomness to use for the shuffle
+ * @throws UnsupportedOperationException if l.listIterator() does not
+ * support the set operation
+ */
+ public static void shuffle(List<?> l, Random r)
+ {
+ int lsize = l.size();
+ List<Object> list = (List<Object>) l;
+ ListIterator<Object> i = list.listIterator(lsize);
+ boolean sequential = isSequential(l);
+ Object[] a = null; // stores a copy of the list for the sequential case
+
+ if (sequential)
+ a = list.toArray();
+
+ for (int pos = lsize - 1; pos > 0; --pos)
+ {
+ // Obtain a random position to swap with. pos + 1 is used so that the
+ // range of the random number includes the current position.
+ int swap = r.nextInt(pos + 1);
+
+ // Swap the desired element.
+ Object o;
+ if (sequential)
+ {
+ o = a[swap];
+ a[swap] = i.previous();
+ }
+ else
+ o = list.set(swap, i.previous());
+
+ i.set(o);
+ }
+ }
+
+ /**
+ * Returns the frequency of the specified object within the supplied
+ * collection. The frequency represents the number of occurrences of
+ * elements within the collection which return <code>true</code> when
+ * compared with the object using the <code>equals</code> method.
+ *
+ * @param c the collection to scan for occurrences of the object.
+ * @param o the object to locate occurrances of within the collection.
+ * @throws NullPointerException if the collection is <code>null</code>.
+ * @since 1.5
+ */
+ public static int frequency (Collection<?> c, Object o)
+ {
+ int result = 0;
+ final Iterator<?> it = c.iterator();
+ while (it.hasNext())
+ {
+ Object v = it.next();
+ if (AbstractCollection.equals(o, v))
+ ++result;
+ }
+ return result;
+ }
+
+ /**
+ * Adds all the specified elements to the given collection, in a similar
+ * way to the <code>addAll</code> method of the <code>Collection</code>.
+ * However, this is a variable argument method which allows the new elements
+ * to be specified individually or in array form, as opposed to the list
+ * required by the collection's <code>addAll</code> method. This has
+ * benefits in both simplicity (multiple elements can be added without
+ * having to be wrapped inside a grouping structure) and efficiency
+ * (as a redundant list doesn't have to be created to add an individual
+ * set of elements or an array).
+ *
+ * @param c the collection to which the elements should be added.
+ * @param a the elements to be added to the collection.
+ * @return true if the collection changed its contents as a result.
+ * @throws UnsupportedOperationException if the collection does not support
+ * addition.
+ * @throws NullPointerException if one or more elements in a are null,
+ * and the collection does not allow null
+ * elements. This exception is also thrown
+ * if either <code>c</code> or <code>a</code>
+ * are null.
+ * @throws IllegalArgumentException if the collection won't allow an element
+ * to be added for some other reason.
+ * @since 1.5
+ */
+ public static <T> boolean addAll(Collection<? super T> c, T... a)
+ {
+ boolean overall = false;
+
+ for (T element : a)
+ {
+ boolean result = c.add(element);
+ if (result)
+ overall = true;
+ }
+ return overall;
+ }
+
+ /**
+ * Returns true if the two specified collections have no elements in
+ * common. This method may give unusual results if one or both collections
+ * use a non-standard equality test. In the trivial case of comparing
+ * a collection with itself, this method returns true if, and only if,
+ * the collection is empty.
+ *
+ * @param c1 the first collection to compare.
+ * @param c2 the second collection to compare.
+ * @return true if the collections are disjoint.
+ * @throws NullPointerException if either collection is null.
+ * @since 1.5
+ */
+ public static boolean disjoint(Collection<?> c1, Collection<?> c2)
+ {
+ Collection<Object> oc1 = (Collection<Object>) c1;
+ final Iterator<Object> it = oc1.iterator();
+ while (it.hasNext())
+ if (c2.contains(it.next()))
+ return false;
+ return true;
+ }
+
+
+ /**
+ * Obtain an immutable Set consisting of a single element. The return value
+ * of this method is Serializable.
+ *
+ * @param o the single element
+ * @return an immutable Set containing only o
+ * @see Serializable
+ */
+ public static <T> Set<T> singleton(T o)
+ {
+ return new SingletonSet<T>(o);
+ }
+
+ /**
+ * The implementation of {@link #singleton(Object)}. This class name
+ * is required for compatibility with Sun's JDK serializability.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static final class SingletonSet<T> extends AbstractSet<T>
+ implements Serializable
+ {
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = 3193687207550431679L;
+
+
+ /**
+ * The single element; package visible for use in nested class.
+ * @serial the singleton
+ */
+ final T element;
+
+ /**
+ * Construct a singleton.
+ * @param o the element
+ */
+ SingletonSet(T o)
+ {
+ element = o;
+ }
+
+ /**
+ * The size: always 1!
+ * @return 1.
+ */
+ public int size()
+ {
+ return 1;
+ }
+
+ /**
+ * Returns an iterator over the lone element.
+ */
+ public Iterator<T> iterator()
+ {
+ return new Iterator<T>()
+ {
+ /**
+ * Flag to indicate whether or not the element has
+ * been retrieved.
+ */
+ private boolean hasNext = true;
+
+ /**
+ * Returns <code>true</code> if elements still remain to be
+ * iterated through.
+ *
+ * @return <code>true</code> if the element has not yet been returned.
+ */
+ public boolean hasNext()
+ {
+ return hasNext;
+ }
+
+ /**
+ * Returns the element.
+ *
+ * @return The element used by this singleton.
+ * @throws NoSuchElementException if the object
+ * has already been retrieved.
+ */
+ public T next()
+ {
+ if (hasNext)
+ {
+ hasNext = false;
+ return element;
+ }
+ else
+ throw new NoSuchElementException();
+ }
+
+ /**
+ * Removes the element from the singleton.
+ * As this set is immutable, this will always
+ * throw an exception.
+ *
+ * @throws UnsupportedOperationException as the
+ * singleton set doesn't support
+ * <code>remove()</code>.
+ */
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ // The remaining methods are optional, but provide a performance
+ // advantage by not allocating unnecessary iterators in AbstractSet.
+ /**
+ * The set only contains one element.
+ *
+ * @param o The object to search for.
+ * @return <code>true</code> if o == the element of the singleton.
+ */
+ public boolean contains(Object o)
+ {
+ return equals(o, element);
+ }
+
+ /**
+ * This is true if the other collection only contains the element.
+ *
+ * @param c A collection to compare against this singleton.
+ * @return <code>true</code> if c only contains either no elements or
+ * elements equal to the element in this singleton.
+ */
+ public boolean containsAll(Collection<?> c)
+ {
+ Iterator<?> i = c.iterator();
+ int pos = c.size();
+ while (--pos >= 0)
+ if (! equals(i.next(), element))
+ return false;
+ return true;
+ }
+
+ /**
+ * The hash is just that of the element.
+ *
+ * @return The hashcode of the element.
+ */
+ public int hashCode()
+ {
+ return hashCode(element);
+ }
+
+ /**
+ * Returning an array is simple.
+ *
+ * @return An array containing the element.
+ */
+ public Object[] toArray()
+ {
+ return new Object[] {element};
+ }
+
+ /**
+ * Obvious string.
+ *
+ * @return The string surrounded by enclosing
+ * square brackets.
+ */
+ public String toString()
+ {
+ return "[" + element + "]";
+ }
+ } // class SingletonSet
+
+ /**
+ * Obtain an immutable List consisting of a single element. The return value
+ * of this method is Serializable, and implements RandomAccess.
+ *
+ * @param o the single element
+ * @return an immutable List containing only o
+ * @see Serializable
+ * @see RandomAccess
+ * @since 1.3
+ */
+ public static <T> List<T> singletonList(T o)
+ {
+ return new SingletonList<T>(o);
+ }
+
+ /**
+ * The implementation of {@link #singletonList(Object)}. This class name
+ * is required for compatibility with Sun's JDK serializability.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static final class SingletonList<T> extends AbstractList<T>
+ implements Serializable, RandomAccess
+ {
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = 3093736618740652951L;
+
+ /**
+ * The single element.
+ * @serial the singleton
+ */
+ private final T element;
+
+ /**
+ * Construct a singleton.
+ * @param o the element
+ */
+ SingletonList(T o)
+ {
+ element = o;
+ }
+
+ /**
+ * The size: always 1!
+ * @return 1.
+ */
+ public int size()
+ {
+ return 1;
+ }
+
+ /**
+ * Only index 0 is valid.
+ * @param index The index of the element
+ * to retrieve.
+ * @return The singleton's element if the
+ * index is 0.
+ * @throws IndexOutOfBoundsException if
+ * index is not 0.
+ */
+ public T get(int index)
+ {
+ if (index == 0)
+ return element;
+ throw new IndexOutOfBoundsException();
+ }
+
+ // The remaining methods are optional, but provide a performance
+ // advantage by not allocating unnecessary iterators in AbstractList.
+ /**
+ * The set only contains one element.
+ *
+ * @param o The object to search for.
+ * @return <code>true</code> if o == the singleton element.
+ */
+ public boolean contains(Object o)
+ {
+ return equals(o, element);
+ }
+
+ /**
+ * This is true if the other collection only contains the element.
+ *
+ * @param c A collection to compare against this singleton.
+ * @return <code>true</code> if c only contains either no elements or
+ * elements equal to the element in this singleton.
+ */
+ public boolean containsAll(Collection<?> c)
+ {
+ Iterator<?> i = c.iterator();
+ int pos = c.size();
+ while (--pos >= 0)
+ if (! equals(i.next(), element))
+ return false;
+ return true;
+ }
+
+ /**
+ * Speed up the hashcode computation.
+ *
+ * @return The hashcode of the list, based
+ * on the hashcode of the singleton element.
+ */
+ public int hashCode()
+ {
+ return 31 + hashCode(element);
+ }
+
+ /**
+ * Either the list has it or not.
+ *
+ * @param o The object to find the first index of.
+ * @return 0 if o is the singleton element, -1 if not.
+ */
+ public int indexOf(Object o)
+ {
+ return equals(o, element) ? 0 : -1;
+ }
+
+ /**
+ * Either the list has it or not.
+ *
+ * @param o The object to find the last index of.
+ * @return 0 if o is the singleton element, -1 if not.
+ */
+ public int lastIndexOf(Object o)
+ {
+ return equals(o, element) ? 0 : -1;
+ }
+
+ /**
+ * Sublists are limited in scope.
+ *
+ * @param from The starting bound for the sublist.
+ * @param to The ending bound for the sublist.
+ * @return Either an empty list if both bounds are
+ * 0 or 1, or this list if the bounds are 0 and 1.
+ * @throws IllegalArgumentException if <code>from > to</code>
+ * @throws IndexOutOfBoundsException if either bound is greater
+ * than 1.
+ */
+ public List<T> subList(int from, int to)
+ {
+ if (from == to && (to == 0 || to == 1))
+ return EMPTY_LIST;
+ if (from == 0 && to == 1)
+ return this;
+ if (from > to)
+ throw new IllegalArgumentException();
+ throw new IndexOutOfBoundsException();
+ }
+
+ /**
+ * Returning an array is simple.
+ *
+ * @return An array containing the element.
+ */
+ public Object[] toArray()
+ {
+ return new Object[] {element};
+ }
+
+ /**
+ * Obvious string.
+ *
+ * @return The string surrounded by enclosing
+ * square brackets.
+ */
+ public String toString()
+ {
+ return "[" + element + "]";
+ }
+ } // class SingletonList
+
+ /**
+ * Obtain an immutable Map consisting of a single key-value pair.
+ * The return value of this method is Serializable.
+ *
+ * @param key the single key
+ * @param value the single value
+ * @return an immutable Map containing only the single key-value pair
+ * @see Serializable
+ * @since 1.3
+ */
+ public static <K, V> Map<K, V> singletonMap(K key, V value)
+ {
+ return new SingletonMap<K, V>(key, value);
+ }
+
+ /**
+ * The implementation of {@link #singletonMap(Object, Object)}. This class
+ * name is required for compatibility with Sun's JDK serializability.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static final class SingletonMap<K, V> extends AbstractMap<K, V>
+ implements Serializable
+ {
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = -6979724477215052911L;
+
+ /**
+ * The single key.
+ * @serial the singleton key
+ */
+ private final K k;
+
+ /**
+ * The corresponding value.
+ * @serial the singleton value
+ */
+ private final V v;
+
+ /**
+ * Cache the entry set.
+ */
+ private transient Set<Map.Entry<K, V>> entries;
+
+ /**
+ * Construct a singleton.
+ * @param key the key
+ * @param value the value
+ */
+ SingletonMap(K key, V value)
+ {
+ k = key;
+ v = value;
+ }
+
+ /**
+ * There is a single immutable entry.
+ *
+ * @return A singleton containing the map entry.
+ */
+ public Set<Map.Entry<K, V>> entrySet()
+ {
+ if (entries == null)
+ {
+ Map.Entry<K,V> entry = new AbstractMap.SimpleEntry<K, V>(k, v)
+ {
+ /**
+ * Sets the value of the map entry to the supplied value.
+ * An exception is always thrown, as the map is immutable.
+ *
+ * @param o The new value.
+ * @return The old value.
+ * @throws UnsupportedOperationException as setting the value
+ * is not supported.
+ */
+ public V setValue(V o)
+ {
+ throw new UnsupportedOperationException();
+ }
+ };
+ entries = singleton(entry);
+ }
+ return entries;
+ }
+
+ // The remaining methods are optional, but provide a performance
+ // advantage by not allocating unnecessary iterators in AbstractMap.
+ /**
+ * Single entry.
+ *
+ * @param key The key to look for.
+ * @return <code>true</code> if the key is the same as the one used by
+ * this map.
+ */
+ public boolean containsKey(Object key)
+ {
+ return equals(key, k);
+ }
+
+ /**
+ * Single entry.
+ *
+ * @param value The value to look for.
+ * @return <code>true</code> if the value is the same as the one used by
+ * this map.
+ */
+ public boolean containsValue(Object value)
+ {
+ return equals(value, v);
+ }
+
+ /**
+ * Single entry.
+ *
+ * @param key The key of the value to be retrieved.
+ * @return The singleton value if the key is the same as the
+ * singleton key, null otherwise.
+ */
+ public V get(Object key)
+ {
+ return equals(key, k) ? v : null;
+ }
+
+ /**
+ * Calculate the hashcode directly.
+ *
+ * @return The hashcode computed from the singleton key
+ * and the singleton value.
+ */
+ public int hashCode()
+ {
+ return hashCode(k) ^ hashCode(v);
+ }
+
+ /**
+ * Return the keyset.
+ *
+ * @return A singleton containing the key.
+ */
+ public Set<K> keySet()
+ {
+ if (keys == null)
+ keys = singleton(k);
+ return keys;
+ }
+
+ /**
+ * The size: always 1!
+ *
+ * @return 1.
+ */
+ public int size()
+ {
+ return 1;
+ }
+
+ /**
+ * Return the values. Technically, a singleton, while more specific than
+ * a general Collection, will work. Besides, that's what the JDK uses!
+ *
+ * @return A singleton containing the value.
+ */
+ public Collection<V> values()
+ {
+ if (values == null)
+ values = singleton(v);
+ return values;
+ }
+
+ /**
+ * Obvious string.
+ *
+ * @return A string containing the string representations of the key
+ * and its associated value.
+ */
+ public String toString()
+ {
+ return "{" + k + "=" + v + "}";
+ }
+ } // class SingletonMap
+
+ /**
+ * Sort a list according to the natural ordering of its elements. The list
+ * must be modifiable, but can be of fixed size. The sort algorithm is
+ * precisely that used by Arrays.sort(Object[]), which offers guaranteed
+ * nlog(n) performance. This implementation dumps the list into an array,
+ * sorts the array, and then iterates over the list setting each element from
+ * the array.
+ *
+ * @param l the List to sort (<code>null</code> not permitted)
+ * @throws ClassCastException if some items are not mutually comparable
+ * @throws UnsupportedOperationException if the List is not modifiable
+ * @throws NullPointerException if the list is <code>null</code>, or contains
+ * some element that is <code>null</code>.
+ * @see Arrays#sort(Object[])
+ */
+ public static <T extends Comparable<? super T>> void sort(List<T> l)
+ {
+ sort(l, null);
+ }
+
+ /**
+ * Sort a list according to a specified Comparator. The list must be
+ * modifiable, but can be of fixed size. The sort algorithm is precisely that
+ * used by Arrays.sort(Object[], Comparator), which offers guaranteed
+ * nlog(n) performance. This implementation dumps the list into an array,
+ * sorts the array, and then iterates over the list setting each element from
+ * the array.
+ *
+ * @param l the List to sort (<code>null</code> not permitted)
+ * @param c the Comparator specifying the ordering for the elements, or
+ * <code>null</code> for natural ordering
+ * @throws ClassCastException if c will not compare some pair of items
+ * @throws UnsupportedOperationException if the List is not modifiable
+ * @throws NullPointerException if the List is <code>null</code> or
+ * <code>null</code> is compared by natural ordering (only possible
+ * when c is <code>null</code>)
+ *
+ * @see Arrays#sort(Object[], Comparator)
+ */
+ public static <T> void sort(List<T> l, Comparator<? super T> c)
+ {
+ T[] a = (T[]) l.toArray();
+ Arrays.sort(a, c);
+ ListIterator<T> i = l.listIterator();
+ for (int pos = 0, alen = a.length; pos < alen; pos++)
+ {
+ i.next();
+ i.set(a[pos]);
+ }
+ }
+
+ /**
+ * Swaps the elements at the specified positions within the list. Equal
+ * positions have no effect.
+ *
+ * @param l the list to work on
+ * @param i the first index to swap
+ * @param j the second index
+ * @throws UnsupportedOperationException if list.set is not supported
+ * @throws IndexOutOfBoundsException if either i or j is &lt; 0 or &gt;=
+ * list.size()
+ * @since 1.4
+ */
+ public static void swap(List<?> l, int i, int j)
+ {
+ List<Object> list = (List<Object>) l;
+ list.set(i, list.set(j, list.get(i)));
+ }
+
+
+ /**
+ * Returns a synchronized (thread-safe) collection wrapper backed by the
+ * given collection. Notice that element access through the iterators
+ * is thread-safe, but if the collection can be structurally modified
+ * (adding or removing elements) then you should synchronize around the
+ * iteration to avoid non-deterministic behavior:<br>
+ * <pre>
+ * Collection c = Collections.synchronizedCollection(new Collection(...));
+ * ...
+ * synchronized (c)
+ * {
+ * Iterator i = c.iterator();
+ * while (i.hasNext())
+ * foo(i.next());
+ * }
+ * </pre><p>
+ *
+ * Since the collection might be a List or a Set, and those have incompatible
+ * equals and hashCode requirements, this relies on Object's implementation
+ * rather than passing those calls on to the wrapped collection. The returned
+ * Collection implements Serializable, but can only be serialized if
+ * the collection it wraps is likewise Serializable.
+ *
+ * @param c the collection to wrap
+ * @return a synchronized view of the collection
+ * @see Serializable
+ */
+ public static <T> Collection<T> synchronizedCollection(Collection<T> c)
+ {
+ return new SynchronizedCollection<T>(c);
+ }
+
+ /**
+ * The implementation of {@link #synchronizedCollection(Collection)}. This
+ * class name is required for compatibility with Sun's JDK serializability.
+ * Package visible, so that collections such as the one for
+ * Hashtable.values() can specify which object to synchronize on.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ static class SynchronizedCollection<T>
+ implements Collection<T>, Serializable
+ {
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = 3053995032091335093L;
+
+ /**
+ * The wrapped collection. Package visible for use by subclasses.
+ * @serial the real collection
+ */
+ final Collection<T> c;
+
+ /**
+ * The object to synchronize on. When an instance is created via public
+ * methods, it will be this; but other uses like SynchronizedMap.values()
+ * must specify another mutex. Package visible for use by subclasses.
+ * @serial the lock
+ */
+ final Object mutex;
+
+ /**
+ * Wrap a given collection.
+ * @param c the collection to wrap
+ * @throws NullPointerException if c is null
+ */
+ SynchronizedCollection(Collection<T> c)
+ {
+ this.c = c;
+ mutex = this;
+ if (c == null)
+ throw new NullPointerException();
+ }
+
+ /**
+ * Called only by trusted code to specify the mutex as well as the
+ * collection.
+ * @param sync the mutex
+ * @param c the collection
+ */
+ SynchronizedCollection(Object sync, Collection<T> c)
+ {
+ this.c = c;
+ mutex = sync;
+ }
+
+ /**
+ * Adds the object to the underlying collection, first
+ * obtaining a lock on the mutex.
+ *
+ * @param o The object to add.
+ * @return <code>true</code> if the collection was modified as a result
+ * of this action.
+ * @throws UnsupportedOperationException if this collection does not
+ * support the add operation.
+ * @throws ClassCastException if o cannot be added to this collection due
+ * to its type.
+ * @throws NullPointerException if o is null and this collection doesn't
+ * support the addition of null values.
+ * @throws IllegalArgumentException if o cannot be added to this
+ * collection for some other reason.
+ */
+ public boolean add(T o)
+ {
+ synchronized (mutex)
+ {
+ return c.add(o);
+ }
+ }
+
+ /**
+ * Adds the objects in col to the underlying collection, first
+ * obtaining a lock on the mutex.
+ *
+ * @param col The collection to take the new objects from.
+ * @return <code>true</code> if the collection was modified as a result
+ * of this action.
+ * @throws UnsupportedOperationException if this collection does not
+ * support the addAll operation.
+ * @throws ClassCastException if some element of col cannot be added to this
+ * collection due to its type.
+ * @throws NullPointerException if some element of col is null and this
+ * collection does not support the addition of null values.
+ * @throws NullPointerException if col itself is null.
+ * @throws IllegalArgumentException if some element of col cannot be added
+ * to this collection for some other reason.
+ */
+ public boolean addAll(Collection<? extends T> col)
+ {
+ synchronized (mutex)
+ {
+ return c.addAll(col);
+ }
+ }
+
+ /**
+ * Removes all objects from the underlying collection,
+ * first obtaining a lock on the mutex.
+ *
+ * @throws UnsupportedOperationException if this collection does not
+ * support the clear operation.
+ */
+ public void clear()
+ {
+ synchronized (mutex)
+ {
+ c.clear();
+ }
+ }
+
+ /**
+ * Checks for the existence of o within the underlying
+ * collection, first obtaining a lock on the mutex.
+ *
+ * @param o the element to look for.
+ * @return <code>true</code> if this collection contains at least one
+ * element e such that <code>o == null ? e == null : o.equals(e)</code>.
+ * @throws ClassCastException if the type of o is not a valid type for this
+ * collection.
+ * @throws NullPointerException if o is null and this collection doesn't
+ * support null values.
+ */
+ public boolean contains(Object o)
+ {
+ synchronized (mutex)
+ {
+ return c.contains(o);
+ }
+ }
+
+ /**
+ * Checks for the existence of each object in cl
+ * within the underlying collection, first obtaining
+ * a lock on the mutex.
+ *
+ * @param c1 the collection to test for.
+ * @return <code>true</code> if for every element o in c, contains(o)
+ * would return <code>true</code>.
+ * @throws ClassCastException if the type of any element in cl is not a valid
+ * type for this collection.
+ * @throws NullPointerException if some element of cl is null and this
+ * collection does not support null values.
+ * @throws NullPointerException if cl itself is null.
+ */
+ public boolean containsAll(Collection<?> c1)
+ {
+ synchronized (mutex)
+ {
+ return c.containsAll(c1);
+ }
+ }
+
+ /**
+ * Returns <code>true</code> if there are no objects in the underlying
+ * collection. A lock on the mutex is obtained before the
+ * check is performed.
+ *
+ * @return <code>true</code> if this collection contains no elements.
+ */
+ public boolean isEmpty()
+ {
+ synchronized (mutex)
+ {
+ return c.isEmpty();
+ }
+ }
+
+ /**
+ * Returns a synchronized iterator wrapper around the underlying
+ * collection's iterator. A lock on the mutex is obtained before
+ * retrieving the collection's iterator.
+ *
+ * @return An iterator over the elements in the underlying collection,
+ * which returns each element in any order.
+ */
+ public Iterator<T> iterator()
+ {
+ synchronized (mutex)
+ {
+ return new SynchronizedIterator<T>(mutex, c.iterator());
+ }
+ }
+
+ /**
+ * Removes the specified object from the underlying collection,
+ * first obtaining a lock on the mutex.
+ *
+ * @param o The object to remove.
+ * @return <code>true</code> if the collection changed as a result of this call, that is,
+ * if the collection contained at least one occurrence of o.
+ * @throws UnsupportedOperationException if this collection does not
+ * support the remove operation.
+ * @throws ClassCastException if the type of o is not a valid type
+ * for this collection.
+ * @throws NullPointerException if o is null and the collection doesn't
+ * support null values.
+ */
+ public boolean remove(Object o)
+ {
+ synchronized (mutex)
+ {
+ return c.remove(o);
+ }
+ }
+
+ /**
+ * Removes all elements, e, of the underlying
+ * collection for which <code>col.contains(e)</code>
+ * returns <code>true</code>. A lock on the mutex is obtained
+ * before the operation proceeds.
+ *
+ * @param col The collection of objects to be removed.
+ * @return <code>true</code> if this collection was modified as a result of this call.
+ * @throws UnsupportedOperationException if this collection does not
+ * support the removeAll operation.
+ * @throws ClassCastException if the type of any element in c is not a valid
+ * type for this collection.
+ * @throws NullPointerException if some element of c is null and this
+ * collection does not support removing null values.
+ * @throws NullPointerException if c itself is null.
+ */
+ public boolean removeAll(Collection<?> col)
+ {
+ synchronized (mutex)
+ {
+ return c.removeAll(col);
+ }
+ }
+
+ /**
+ * Retains all elements, e, of the underlying
+ * collection for which <code>col.contains(e)</code>
+ * returns <code>true</code>. That is, every element that doesn't
+ * exist in col is removed. A lock on the mutex is obtained
+ * before the operation proceeds.
+ *
+ * @param col The collection of objects to be removed.
+ * @return <code>true</code> if this collection was modified as a result of this call.
+ * @throws UnsupportedOperationException if this collection does not
+ * support the removeAll operation.
+ * @throws ClassCastException if the type of any element in c is not a valid
+ * type for this collection.
+ * @throws NullPointerException if some element of c is null and this
+ * collection does not support removing null values.
+ * @throws NullPointerException if c itself is null.
+ */
+ public boolean retainAll(Collection<?> col)
+ {
+ synchronized (mutex)
+ {
+ return c.retainAll(col);
+ }
+ }
+
+ /**
+ * Retrieves the size of the underlying collection.
+ * A lock on the mutex is obtained before the collection
+ * is accessed.
+ *
+ * @return The size of the collection.
+ */
+ public int size()
+ {
+ synchronized (mutex)
+ {
+ return c.size();
+ }
+ }
+
+ /**
+ * Returns an array containing each object within the underlying
+ * collection. A lock is obtained on the mutex before the collection
+ * is accessed.
+ *
+ * @return An array of objects, matching the collection in size. The
+ * elements occur in any order.
+ */
+ public Object[] toArray()
+ {
+ synchronized (mutex)
+ {
+ return c.toArray();
+ }
+ }
+
+ /**
+ * Copies the elements in the underlying collection to the supplied
+ * array. If <code>a.length < size()</code>, a new array of the
+ * same run-time type is created, with a size equal to that of
+ * the collection. If <code>a.length > size()</code>, then the
+ * elements from 0 to <code>size() - 1</code> contain the elements
+ * from this collection. The following element is set to null
+ * to indicate the end of the collection objects. However, this
+ * only makes a difference if null is not a permitted value within
+ * the collection.
+ * Before the copying takes place, a lock is obtained on the mutex.
+ *
+ * @param a An array to copy elements to.
+ * @return An array containing the elements of the underlying collection.
+ * @throws ArrayStoreException if the type of any element of the
+ * collection is not a subtype of the element type of a.
+ */
+ public <T> T[] toArray(T[] a)
+ {
+ synchronized (mutex)
+ {
+ return c.toArray(a);
+ }
+ }
+
+ /**
+ * Returns a string representation of the underlying collection.
+ * A lock is obtained on the mutex before the string is created.
+ *
+ * @return A string representation of the collection.
+ */
+ public String toString()
+ {
+ synchronized (mutex)
+ {
+ return c.toString();
+ }
+ }
+ } // class SynchronizedCollection
+
+ /**
+ * The implementation of the various iterator methods in the
+ * synchronized classes. These iterators must "sync" on the same object
+ * as the collection they iterate over.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static class SynchronizedIterator<T> implements Iterator<T>
+ {
+ /**
+ * The object to synchronize on. Package visible for use by subclass.
+ */
+ final Object mutex;
+
+ /**
+ * The wrapped iterator.
+ */
+ private final Iterator<T> i;
+
+ /**
+ * Only trusted code creates a wrapper, with the specified sync.
+ * @param sync the mutex
+ * @param i the wrapped iterator
+ */
+ SynchronizedIterator(Object sync, Iterator<T> i)
+ {
+ this.i = i;
+ mutex = sync;
+ }
+
+ /**
+ * Retrieves the next object in the underlying collection.
+ * A lock is obtained on the mutex before the collection is accessed.
+ *
+ * @return The next object in the collection.
+ * @throws NoSuchElementException if there are no more elements
+ */
+ public T next()
+ {
+ synchronized (mutex)
+ {
+ return i.next();
+ }
+ }
+
+ /**
+ * Returns <code>true</code> if objects can still be retrieved from the iterator
+ * using <code>next()</code>. A lock is obtained on the mutex before
+ * the collection is accessed.
+ *
+ * @return <code>true</code> if at least one element is still to be returned by
+ * <code>next()</code>.
+ */
+ public boolean hasNext()
+ {
+ synchronized (mutex)
+ {
+ return i.hasNext();
+ }
+ }
+
+ /**
+ * Removes the object that was last returned by <code>next()</code>
+ * from the underlying collection. Only one call to this method is
+ * allowed per call to the <code>next()</code> method, and it does
+ * not affect the value that will be returned by <code>next()</code>.
+ * Thus, if element n was retrieved from the collection by
+ * <code>next()</code>, it is this element that gets removed.
+ * Regardless of whether this takes place or not, element n+1 is
+ * still returned on the subsequent <code>next()</code> call.
+ *
+ * @throws IllegalStateException if next has not yet been called or remove
+ * has already been called since the last call to next.
+ * @throws UnsupportedOperationException if this Iterator does not support
+ * the remove operation.
+ */
+ public void remove()
+ {
+ synchronized (mutex)
+ {
+ i.remove();
+ }
+ }
+ } // class SynchronizedIterator
+
+ /**
+ * Returns a synchronized (thread-safe) list wrapper backed by the
+ * given list. Notice that element access through the iterators
+ * is thread-safe, but if the list can be structurally modified
+ * (adding or removing elements) then you should synchronize around the
+ * iteration to avoid non-deterministic behavior:<br>
+ * <pre>
+ * List l = Collections.synchronizedList(new List(...));
+ * ...
+ * synchronized (l)
+ * {
+ * Iterator i = l.iterator();
+ * while (i.hasNext())
+ * foo(i.next());
+ * }
+ * </pre><p>
+ *
+ * The returned List implements Serializable, but can only be serialized if
+ * the list it wraps is likewise Serializable. In addition, if the wrapped
+ * list implements RandomAccess, this does too.
+ *
+ * @param l the list to wrap
+ * @return a synchronized view of the list
+ * @see Serializable
+ * @see RandomAccess
+ */
+ public static <T> List<T> synchronizedList(List<T> l)
+ {
+ if (l instanceof RandomAccess)
+ return new SynchronizedRandomAccessList<T>(l);
+ return new SynchronizedList<T>(l);
+ }
+
+ /**
+ * The implementation of {@link #synchronizedList(List)} for sequential
+ * lists. This class name is required for compatibility with Sun's JDK
+ * serializability. Package visible, so that lists such as Vector.subList()
+ * can specify which object to synchronize on.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ static class SynchronizedList<T> extends SynchronizedCollection<T>
+ implements List<T>
+ {
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = -7754090372962971524L;
+
+ /**
+ * The wrapped list; stored both here and in the superclass to avoid
+ * excessive casting. Package visible for use by subclass.
+ * @serial the wrapped list
+ */
+ final List<T> list;
+
+ /**
+ * Wrap a given list.
+ * @param l the list to wrap
+ * @throws NullPointerException if l is null
+ */
+ SynchronizedList(List<T> l)
+ {
+ super(l);
+ list = l;
+ }
+
+ /**
+ * Called only by trusted code to specify the mutex as well as the list.
+ * @param sync the mutex
+ * @param l the list
+ */
+ SynchronizedList(Object sync, List<T> l)
+ {
+ super(sync, l);
+ list = l;
+ }
+
+ /**
+ * Insert an element into the underlying list at a given position (optional
+ * operation). This shifts all existing elements from that position to the
+ * end one index to the right. This version of add has no return, since it is
+ * assumed to always succeed if there is no exception. Before the
+ * addition takes place, a lock is obtained on the mutex.
+ *
+ * @param index the location to insert the item
+ * @param o the object to insert
+ * @throws UnsupportedOperationException if this list does not support the
+ * add operation
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ * @throws ClassCastException if o cannot be added to this list due to its
+ * type
+ * @throws IllegalArgumentException if o cannot be added to this list for
+ * some other reason
+ * @throws NullPointerException if o is null and this list doesn't support
+ * the addition of null values.
+ */
+ public void add(int index, T o)
+ {
+ synchronized (mutex)
+ {
+ list.add(index, o);
+ }
+ }
+
+ /**
+ * Add the contents of a collection to the underlying list at the given
+ * index (optional operation). If the list imposes restraints on what
+ * can be inserted, such as no null elements, this should be documented.
+ * A lock is obtained on the mutex before any of the elements are added.
+ *
+ * @param index the index at which to insert
+ * @param c the collection to add
+ * @return <code>true</code>, as defined by Collection for a modified list
+ * @throws UnsupportedOperationException if this list does not support the
+ * add operation
+ * @throws ClassCastException if o cannot be added to this list due to its
+ * type
+ * @throws IllegalArgumentException if o cannot be added to this list for
+ * some other reason
+ * @throws NullPointerException if o is null and this list doesn't support
+ * the addition of null values.
+ */
+ public boolean addAll(int index, Collection<? extends T> c)
+ {
+ synchronized (mutex)
+ {
+ return list.addAll(index, c);
+ }
+ }
+
+ /**
+ * Tests whether the underlying list is equal to the supplied object.
+ * The object is deemed to be equal if it is also a <code>List</code>
+ * of equal size and with the same elements (i.e. each element, e1,
+ * in list, l1, and each element, e2, in l2, must return <code>true</code> for
+ * <code>e1 == null ? e2 == null : e1.equals(e2)</code>. Before the
+ * comparison is made, a lock is obtained on the mutex.
+ *
+ * @param o The object to test for equality with the underlying list.
+ * @return <code>true</code> if o is equal to the underlying list under the above
+ * definition.
+ */
+ public boolean equals(Object o)
+ {
+ synchronized (mutex)
+ {
+ return list.equals(o);
+ }
+ }
+
+ /**
+ * Retrieves the object at the specified index. A lock
+ * is obtained on the mutex before the list is accessed.
+ *
+ * @param index the index of the element to be returned
+ * @return the element at index index in this list
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ */
+ public T get(int index)
+ {
+ synchronized (mutex)
+ {
+ return list.get(index);
+ }
+ }
+
+ /**
+ * Obtains a hashcode for the underlying list, first obtaining
+ * a lock on the mutex. The calculation of the hashcode is
+ * detailed in the documentation for the <code>List</code>
+ * interface.
+ *
+ * @return The hashcode of the underlying list.
+ * @see List#hashCode()
+ */
+ public int hashCode()
+ {
+ synchronized (mutex)
+ {
+ return list.hashCode();
+ }
+ }
+
+ /**
+ * Obtain the first index at which a given object is to be found in the
+ * underlying list. A lock is obtained on the mutex before the list is
+ * accessed.
+ *
+ * @param o the object to search for
+ * @return the least integer n such that <code>o == null ? get(n) == null :
+ * o.equals(get(n))</code>, or -1 if there is no such index.
+ * @throws ClassCastException if the type of o is not a valid
+ * type for this list.
+ * @throws NullPointerException if o is null and this
+ * list does not support null values.
+ */
+
+ public int indexOf(Object o)
+ {
+ synchronized (mutex)
+ {
+ return list.indexOf(o);
+ }
+ }
+
+ /**
+ * Obtain the last index at which a given object is to be found in this
+ * underlying list. A lock is obtained on the mutex before the list
+ * is accessed.
+ *
+ * @return the greatest integer n such that <code>o == null ? get(n) == null
+ * : o.equals(get(n))</code>, or -1 if there is no such index.
+ * @throws ClassCastException if the type of o is not a valid
+ * type for this list.
+ * @throws NullPointerException if o is null and this
+ * list does not support null values.
+ */
+ public int lastIndexOf(Object o)
+ {
+ synchronized (mutex)
+ {
+ return list.lastIndexOf(o);
+ }
+ }
+
+ /**
+ * Retrieves a synchronized wrapper around the underlying list's
+ * list iterator. A lock is obtained on the mutex before the
+ * list iterator is retrieved.
+ *
+ * @return A list iterator over the elements in the underlying list.
+ * The list iterator allows additional list-specific operations
+ * to be performed, in addition to those supplied by the
+ * standard iterator.
+ */
+ public ListIterator<T> listIterator()
+ {
+ synchronized (mutex)
+ {
+ return new SynchronizedListIterator<T>(mutex, list.listIterator());
+ }
+ }
+
+ /**
+ * Retrieves a synchronized wrapper around the underlying list's
+ * list iterator. A lock is obtained on the mutex before the
+ * list iterator is retrieved. The iterator starts at the
+ * index supplied, leading to the element at that index being
+ * the first one returned by <code>next()</code>. Calling
+ * <code>previous()</code> from this initial position returns
+ * index - 1.
+ *
+ * @param index the position, between 0 and size() inclusive, to begin the
+ * iteration from
+ * @return A list iterator over the elements in the underlying list.
+ * The list iterator allows additional list-specific operations
+ * to be performed, in addition to those supplied by the
+ * standard iterator.
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ */
+ public ListIterator<T> listIterator(int index)
+ {
+ synchronized (mutex)
+ {
+ return new SynchronizedListIterator<T>(mutex,
+ list.listIterator(index));
+ }
+ }
+
+ /**
+ * Remove the element at a given position in the underlying list (optional
+ * operation). All remaining elements are shifted to the left to fill the gap.
+ * A lock on the mutex is obtained before the element is removed.
+ *
+ * @param index the position within the list of the object to remove
+ * @return the object that was removed
+ * @throws UnsupportedOperationException if this list does not support the
+ * remove operation
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ */
+ public T remove(int index)
+ {
+ synchronized (mutex)
+ {
+ return list.remove(index);
+ }
+ }
+
+ /**
+ * Replace an element of the underlying list with another object (optional
+ * operation). A lock is obtained on the mutex before the element is
+ * replaced.
+ *
+ * @param index the position within this list of the element to be replaced
+ * @param o the object to replace it with
+ * @return the object that was replaced
+ * @throws UnsupportedOperationException if this list does not support the
+ * set operation.
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ * @throws ClassCastException if o cannot be added to this list due to its
+ * type
+ * @throws IllegalArgumentException if o cannot be added to this list for
+ * some other reason
+ * @throws NullPointerException if o is null and this
+ * list does not support null values.
+ */
+ public T set(int index, T o)
+ {
+ synchronized (mutex)
+ {
+ return list.set(index, o);
+ }
+ }
+
+ /**
+ * Obtain a List view of a subsection of the underlying list, from fromIndex
+ * (inclusive) to toIndex (exclusive). If the two indices are equal, the
+ * sublist is empty. The returned list should be modifiable if and only
+ * if this list is modifiable. Changes to the returned list should be
+ * reflected in this list. If this list is structurally modified in
+ * any way other than through the returned list, the result of any subsequent
+ * operations on the returned list is undefined. A lock is obtained
+ * on the mutex before the creation of the sublist. The returned list
+ * is also synchronized, using the same mutex.
+ *
+ * @param fromIndex the index that the returned list should start from
+ * (inclusive)
+ * @param toIndex the index that the returned list should go to (exclusive)
+ * @return a List backed by a subsection of this list
+ * @throws IndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; size() || fromIndex &gt; toIndex
+ */
+ public List<T> subList(int fromIndex, int toIndex)
+ {
+ synchronized (mutex)
+ {
+ return new SynchronizedList<T>(mutex,
+ list.subList(fromIndex, toIndex));
+ }
+ }
+ } // class SynchronizedList
+
+ /**
+ * The implementation of {@link #synchronizedList(List)} for random-access
+ * lists. This class name is required for compatibility with Sun's JDK
+ * serializability.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static final class SynchronizedRandomAccessList<T>
+ extends SynchronizedList<T> implements RandomAccess
+ {
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = 1530674583602358482L;
+
+ /**
+ * Wrap a given list.
+ * @param l the list to wrap
+ * @throws NullPointerException if l is null
+ */
+ SynchronizedRandomAccessList(List<T> l)
+ {
+ super(l);
+ }
+
+ /**
+ * Called only by trusted code to specify the mutex as well as the
+ * collection.
+ * @param sync the mutex
+ * @param l the list
+ */
+ SynchronizedRandomAccessList(Object sync, List<T> l)
+ {
+ super(sync, l);
+ }
+
+ /**
+ * Obtain a List view of a subsection of the underlying list, from fromIndex
+ * (inclusive) to toIndex (exclusive). If the two indices are equal, the
+ * sublist is empty. The returned list should be modifiable if and only
+ * if this list is modifiable. Changes to the returned list should be
+ * reflected in this list. If this list is structurally modified in
+ * any way other than through the returned list, the result of any subsequent
+ * operations on the returned list is undefined. A lock is obtained
+ * on the mutex before the creation of the sublist. The returned list
+ * is also synchronized, using the same mutex. Random accessibility
+ * is also extended to the new list.
+ *
+ * @param fromIndex the index that the returned list should start from
+ * (inclusive)
+ * @param toIndex the index that the returned list should go to (exclusive)
+ * @return a List backed by a subsection of this list
+ * @throws IndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; size() || fromIndex &gt; toIndex
+ */
+ public List<T> subList(int fromIndex, int toIndex)
+ {
+ synchronized (mutex)
+ {
+ return new SynchronizedRandomAccessList<T>(mutex,
+ list.subList(fromIndex,
+ toIndex));
+ }
+ }
+ } // class SynchronizedRandomAccessList
+
+ /**
+ * The implementation of {@link SynchronizedList#listIterator()}. This
+ * iterator must "sync" on the same object as the list it iterates over.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static final class SynchronizedListIterator<T>
+ extends SynchronizedIterator<T> implements ListIterator<T>
+ {
+ /**
+ * The wrapped iterator, stored both here and in the superclass to
+ * avoid excessive casting.
+ */
+ private final ListIterator<T> li;
+
+ /**
+ * Only trusted code creates a wrapper, with the specified sync.
+ * @param sync the mutex
+ * @param li the wrapped iterator
+ */
+ SynchronizedListIterator(Object sync, ListIterator<T> li)
+ {
+ super(sync, li);
+ this.li = li;
+ }
+
+ /**
+ * Insert an element into the underlying list at the current position of
+ * the iterator (optional operation). The element is inserted in between
+ * the element that would be returned by <code>previous()</code> and the
+ * element that would be returned by <code>next()</code>. After the
+ * insertion, a subsequent call to next is unaffected, but
+ * a call to previous returns the item that was added. The values returned
+ * by nextIndex() and previousIndex() are incremented. A lock is obtained
+ * on the mutex before the addition takes place.
+ *
+ * @param o the object to insert into the list
+ * @throws ClassCastException if the object is of a type which cannot be added
+ * to this list.
+ * @throws IllegalArgumentException if some other aspect of the object stops
+ * it being added to this list.
+ * @throws UnsupportedOperationException if this ListIterator does not
+ * support the add operation.
+ */
+ public void add(T o)
+ {
+ synchronized (mutex)
+ {
+ li.add(o);
+ }
+ }
+
+ /**
+ * Tests whether there are elements remaining in the underlying list
+ * in the reverse direction. In other words, <code>previous()</code>
+ * will not fail with a NoSuchElementException. A lock is obtained
+ * on the mutex before the check takes place.
+ *
+ * @return <code>true</code> if the list continues in the reverse direction
+ */
+ public boolean hasPrevious()
+ {
+ synchronized (mutex)
+ {
+ return li.hasPrevious();
+ }
+ }
+
+ /**
+ * Find the index of the element that would be returned by a call to
+ * <code>next()</code>. If hasNext() returns <code>false</code>, this
+ * returns the list size. A lock is obtained on the mutex before the
+ * query takes place.
+ *
+ * @return the index of the element that would be returned by next()
+ */
+ public int nextIndex()
+ {
+ synchronized (mutex)
+ {
+ return li.nextIndex();
+ }
+ }
+
+ /**
+ * Obtain the previous element from the underlying list. Repeated
+ * calls to previous may be used to iterate backwards over the entire list,
+ * or calls to next and previous may be used together to go forwards and
+ * backwards. Alternating calls to next and previous will return the same
+ * element. A lock is obtained on the mutex before the object is retrieved.
+ *
+ * @return the next element in the list in the reverse direction
+ * @throws NoSuchElementException if there are no more elements
+ */
+ public T previous()
+ {
+ synchronized (mutex)
+ {
+ return li.previous();
+ }
+ }
+
+ /**
+ * Find the index of the element that would be returned by a call to
+ * previous. If hasPrevious() returns <code>false</code>, this returns -1.
+ * A lock is obtained on the mutex before the query takes place.
+ *
+ * @return the index of the element that would be returned by previous()
+ */
+ public int previousIndex()
+ {
+ synchronized (mutex)
+ {
+ return li.previousIndex();
+ }
+ }
+
+ /**
+ * Replace the element last returned by a call to <code>next()</code> or
+ * <code>previous()</code> with a given object (optional operation). This
+ * method may only be called if neither <code>add()</code> nor
+ * <code>remove()</code> have been called since the last call to
+ * <code>next()</code> or <code>previous</code>. A lock is obtained
+ * on the mutex before the list is modified.
+ *
+ * @param o the object to replace the element with
+ * @throws ClassCastException the object is of a type which cannot be added
+ * to this list
+ * @throws IllegalArgumentException some other aspect of the object stops
+ * it being added to this list
+ * @throws IllegalStateException if neither next or previous have been
+ * called, or if add or remove has been called since the last call
+ * to next or previous
+ * @throws UnsupportedOperationException if this ListIterator does not
+ * support the set operation
+ */
+ public void set(T o)
+ {
+ synchronized (mutex)
+ {
+ li.set(o);
+ }
+ }
+ } // class SynchronizedListIterator
+
+ /**
+ * Returns a synchronized (thread-safe) map wrapper backed by the given
+ * map. Notice that element access through the collection views and their
+ * iterators are thread-safe, but if the map can be structurally modified
+ * (adding or removing elements) then you should synchronize around the
+ * iteration to avoid non-deterministic behavior:<br>
+ * <pre>
+ * Map m = Collections.synchronizedMap(new Map(...));
+ * ...
+ * Set s = m.keySet(); // safe outside a synchronized block
+ * synchronized (m) // synch on m, not s
+ * {
+ * Iterator i = s.iterator();
+ * while (i.hasNext())
+ * foo(i.next());
+ * }
+ * </pre><p>
+ *
+ * The returned Map implements Serializable, but can only be serialized if
+ * the map it wraps is likewise Serializable.
+ *
+ * @param m the map to wrap
+ * @return a synchronized view of the map
+ * @see Serializable
+ */
+ public static <K, V> Map<K, V> synchronizedMap(Map<K, V> m)
+ {
+ return new SynchronizedMap<K, V>(m);
+ }
+
+ /**
+ * The implementation of {@link #synchronizedMap(Map)}. This
+ * class name is required for compatibility with Sun's JDK serializability.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static class SynchronizedMap<K, V> implements Map<K, V>, Serializable
+ {
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = 1978198479659022715L;
+
+ /**
+ * The wrapped map.
+ * @serial the real map
+ */
+ private final Map<K, V> m;
+
+ /**
+ * The object to synchronize on. When an instance is created via public
+ * methods, it will be this; but other uses like
+ * SynchronizedSortedMap.subMap() must specify another mutex. Package
+ * visible for use by subclass.
+ * @serial the lock
+ */
+ final Object mutex;
+
+ /**
+ * Cache the entry set.
+ */
+ private transient Set<Map.Entry<K, V>> entries;
+
+ /**
+ * Cache the key set.
+ */
+ private transient Set<K> keys;
+
+ /**
+ * Cache the value collection.
+ */
+ private transient Collection<V> values;
+
+ /**
+ * Wrap a given map.
+ * @param m the map to wrap
+ * @throws NullPointerException if m is null
+ */
+ SynchronizedMap(Map<K, V> m)
+ {
+ this.m = m;
+ mutex = this;
+ if (m == null)
+ throw new NullPointerException();
+ }
+
+ /**
+ * Called only by trusted code to specify the mutex as well as the map.
+ * @param sync the mutex
+ * @param m the map
+ */
+ SynchronizedMap(Object sync, Map<K, V> m)
+ {
+ this.m = m;
+ mutex = sync;
+ }
+
+ /**
+ * Clears all the entries from the underlying map. A lock is obtained
+ * on the mutex before the map is cleared.
+ *
+ * @throws UnsupportedOperationException if clear is not supported
+ */
+ public void clear()
+ {
+ synchronized (mutex)
+ {
+ m.clear();
+ }
+ }
+
+ /**
+ * Returns <code>true</code> if the underlying map contains a entry for the given key.
+ * A lock is obtained on the mutex before the map is queried.
+ *
+ * @param key the key to search for.
+ * @return <code>true</code> if the underlying map contains the key.
+ * @throws ClassCastException if the key is of an inappropriate type.
+ * @throws NullPointerException if key is <code>null</code> but the map
+ * does not permit null keys.
+ */
+ public boolean containsKey(Object key)
+ {
+ synchronized (mutex)
+ {
+ return m.containsKey(key);
+ }
+ }
+
+ /**
+ * Returns <code>true</code> if the underlying map contains at least one entry with the
+ * given value. In other words, returns <code>true</code> if a value v exists where
+ * <code>(value == null ? v == null : value.equals(v))</code>. This usually
+ * requires linear time. A lock is obtained on the mutex before the map
+ * is queried.
+ *
+ * @param value the value to search for
+ * @return <code>true</code> if the map contains the value
+ * @throws ClassCastException if the type of the value is not a valid type
+ * for this map.
+ * @throws NullPointerException if the value is null and the map doesn't
+ * support null values.
+ */
+ public boolean containsValue(Object value)
+ {
+ synchronized (mutex)
+ {
+ return m.containsValue(value);
+ }
+ }
+
+ // This is one of the ickiest cases of nesting I've ever seen. It just
+ // means "return a SynchronizedSet, except that the iterator() method
+ // returns an SynchronizedIterator whose next() method returns a
+ // synchronized wrapper around its normal return value".
+ public Set<Map.Entry<K, V>> entrySet()
+ {
+ // Define this here to spare some nesting.
+ class SynchronizedMapEntry<K, V> implements Map.Entry<K, V>
+ {
+ final Map.Entry<K, V> e;
+ SynchronizedMapEntry(Map.Entry<K, V> o)
+ {
+ e = o;
+ }
+
+ /**
+ * Returns <code>true</code> if the object, o, implements <code>Map.Entry</code>
+ * with the same key and value as the underlying entry. A lock is
+ * obtained on the mutex before the comparison takes place.
+ *
+ * @param o The object to compare with this entry.
+ * @return <code>true</code> if o is equivalent to the underlying map entry.
+ */
+ public boolean equals(Object o)
+ {
+ synchronized (mutex)
+ {
+ return e.equals(o);
+ }
+ }
+
+ /**
+ * Returns the key used in the underlying map entry. A lock is obtained
+ * on the mutex before the key is retrieved.
+ *
+ * @return The key of the underlying map entry.
+ */
+ public K getKey()
+ {
+ synchronized (mutex)
+ {
+ return e.getKey();
+ }
+ }
+
+ /**
+ * Returns the value used in the underlying map entry. A lock is obtained
+ * on the mutex before the value is retrieved.
+ *
+ * @return The value of the underlying map entry.
+ */
+ public V getValue()
+ {
+ synchronized (mutex)
+ {
+ return e.getValue();
+ }
+ }
+
+ /**
+ * Computes the hash code for the underlying map entry.
+ * This computation is described in the documentation for the
+ * <code>Map</code> interface. A lock is obtained on the mutex
+ * before the underlying map is accessed.
+ *
+ * @return The hash code of the underlying map entry.
+ * @see Map#hashCode()
+ */
+ public int hashCode()
+ {
+ synchronized (mutex)
+ {
+ return e.hashCode();
+ }
+ }
+
+ /**
+ * Replaces the value in the underlying map entry with the specified
+ * object (optional operation). A lock is obtained on the mutex
+ * before the map is altered. The map entry, in turn, will alter
+ * the underlying map object. The operation is undefined if the
+ * <code>remove()</code> method of the iterator has been called
+ * beforehand.
+ *
+ * @param value the new value to store
+ * @return the old value
+ * @throws UnsupportedOperationException if the operation is not supported.
+ * @throws ClassCastException if the value is of the wrong type.
+ * @throws IllegalArgumentException if something about the value
+ * prevents it from existing in this map.
+ * @throws NullPointerException if the map forbids null values.
+ */
+ public V setValue(V value)
+ {
+ synchronized (mutex)
+ {
+ return e.setValue(value);
+ }
+ }
+
+ /**
+ * Returns a textual representation of the underlying map entry.
+ * A lock is obtained on the mutex before the entry is accessed.
+ *
+ * @return The contents of the map entry in <code>String</code> form.
+ */
+ public String toString()
+ {
+ synchronized (mutex)
+ {
+ return e.toString();
+ }
+ }
+ } // class SynchronizedMapEntry
+
+ // Now the actual code.
+ if (entries == null)
+ synchronized (mutex)
+ {
+ entries = new SynchronizedSet<Map.Entry<K, V>>(mutex, m.entrySet())
+ {
+ /**
+ * Returns an iterator over the set. The iterator has no specific order,
+ * unless further specified. A lock is obtained on the set's mutex
+ * before the iterator is created. The created iterator is also
+ * thread-safe.
+ *
+ * @return A synchronized set iterator.
+ */
+ public Iterator<Map.Entry<K, V>> iterator()
+ {
+ synchronized (super.mutex)
+ {
+ return new SynchronizedIterator<Map.Entry<K, V>>(super.mutex,
+ c.iterator())
+ {
+ /**
+ * Retrieves the next map entry from the iterator.
+ * A lock is obtained on the iterator's mutex before
+ * the entry is created. The new map entry is enclosed in
+ * a thread-safe wrapper.
+ *
+ * @return A synchronized map entry.
+ */
+ public Map.Entry<K, V> next()
+ {
+ synchronized (super.mutex)
+ {
+ return new SynchronizedMapEntry<K, V>(super.next());
+ }
+ }
+ };
+ }
+ }
+ };
+ }
+ return entries;
+ }
+
+ /**
+ * Returns <code>true</code> if the object, o, is also an instance
+ * of <code>Map</code> and contains an equivalent
+ * entry set to that of the underlying map. A lock
+ * is obtained on the mutex before the objects are
+ * compared.
+ *
+ * @param o The object to compare.
+ * @return <code>true</code> if o and the underlying map are equivalent.
+ */
+ public boolean equals(Object o)
+ {
+ synchronized (mutex)
+ {
+ return m.equals(o);
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or null
+ * if no such mapping exists. An ambiguity exists with maps
+ * that accept null values as a return value of null could
+ * be due to a non-existent mapping or simply a null value
+ * for that key. To resolve this, <code>containsKey</code>
+ * should be used. A lock is obtained on the mutex before
+ * the value is retrieved from the underlying map.
+ *
+ * @param key The key of the required mapping.
+ * @return The value associated with the given key, or
+ * null if no such mapping exists.
+ * @throws ClassCastException if the key is an inappropriate type.
+ * @throws NullPointerException if this map does not accept null keys.
+ */
+ public V get(Object key)
+ {
+ synchronized (mutex)
+ {
+ return m.get(key);
+ }
+ }
+
+ /**
+ * Calculates the hash code of the underlying map as the
+ * sum of the hash codes of all entries. A lock is obtained
+ * on the mutex before the hash code is computed.
+ *
+ * @return The hash code of the underlying map.
+ */
+ public int hashCode()
+ {
+ synchronized (mutex)
+ {
+ return m.hashCode();
+ }
+ }
+
+ /**
+ * Returns <code>true</code> if the underlying map contains no entries.
+ * A lock is obtained on the mutex before the map is examined.
+ *
+ * @return <code>true</code> if the map is empty.
+ */
+ public boolean isEmpty()
+ {
+ synchronized (mutex)
+ {
+ return m.isEmpty();
+ }
+ }
+
+ /**
+ * Returns a thread-safe set view of the keys in the underlying map. The
+ * set is backed by the map, so that changes in one show up in the other.
+ * Modifications made while an iterator is in progress cause undefined
+ * behavior. If the set supports removal, these methods remove the
+ * underlying mapping from the map: <code>Iterator.remove</code>,
+ * <code>Set.remove</code>, <code>removeAll</code>, <code>retainAll</code>,
+ * and <code>clear</code>. Element addition, via <code>add</code> or
+ * <code>addAll</code>, is not supported via this set. A lock is obtained
+ * on the mutex before the set is created.
+ *
+ * @return A synchronized set containing the keys of the underlying map.
+ */
+ public Set<K> keySet()
+ {
+ if (keys == null)
+ synchronized (mutex)
+ {
+ keys = new SynchronizedSet<K>(mutex, m.keySet());
+ }
+ return keys;
+ }
+
+ /**
+ * Associates the given key to the given value (optional operation). If the
+ * underlying map already contains the key, its value is replaced. Be aware
+ * that in a map that permits <code>null</code> values, a null return does not
+ * always imply that the mapping was created. A lock is obtained on the mutex
+ * before the modification is made.
+ *
+ * @param key the key to map.
+ * @param value the value to be mapped.
+ * @return the previous value of the key, or null if there was no mapping
+ * @throws UnsupportedOperationException if the operation is not supported
+ * @throws ClassCastException if the key or value is of the wrong type
+ * @throws IllegalArgumentException if something about this key or value
+ * prevents it from existing in this map
+ * @throws NullPointerException if either the key or the value is null,
+ * and the map forbids null keys or values
+ * @see #containsKey(Object)
+ */
+ public V put(K key, V value)
+ {
+ synchronized (mutex)
+ {
+ return m.put(key, value);
+ }
+ }
+
+ /**
+ * Copies all entries of the given map to the underlying one (optional
+ * operation). If the map already contains a key, its value is replaced.
+ * A lock is obtained on the mutex before the operation proceeds.
+ *
+ * @param map the mapping to load into this map
+ * @throws UnsupportedOperationException if the operation is not supported
+ * @throws ClassCastException if a key or value is of the wrong type
+ * @throws IllegalArgumentException if something about a key or value
+ * prevents it from existing in this map
+ * @throws NullPointerException if the map forbids null keys or values, or
+ * if <code>m</code> is null.
+ * @see #put(Object, Object)
+ */
+ public void putAll(Map<? extends K, ? extends V> map)
+ {
+ synchronized (mutex)
+ {
+ m.putAll(map);
+ }
+ }
+
+ /**
+ * Removes the mapping for the key, o, if present (optional operation). If
+ * the key is not present, this returns null. Note that maps which permit
+ * null values may also return null if the key was removed. A prior
+ * <code>containsKey()</code> check is required to avoid this ambiguity.
+ * Before the mapping is removed, a lock is obtained on the mutex.
+ *
+ * @param o the key to remove
+ * @return the value the key mapped to, or null if not present
+ * @throws UnsupportedOperationException if deletion is unsupported
+ * @throws NullPointerException if the key is null and this map doesn't
+ * support null keys.
+ * @throws ClassCastException if the type of the key is not a valid type
+ * for this map.
+ */
+ public V remove(Object o)
+ {
+ synchronized (mutex)
+ {
+ return m.remove(o);
+ }
+ }
+
+ /**
+ * Retrieves the size of the underlying map. A lock
+ * is obtained on the mutex before access takes place.
+ * Maps with a size greater than <code>Integer.MAX_VALUE</code>
+ * return <code>Integer.MAX_VALUE</code> instead.
+ *
+ * @return The size of the underlying map.
+ */
+ public int size()
+ {
+ synchronized (mutex)
+ {
+ return m.size();
+ }
+ }
+
+ /**
+ * Returns a textual representation of the underlying
+ * map. A lock is obtained on the mutex before the map
+ * is accessed.
+ *
+ * @return The map in <code>String</code> form.
+ */
+ public String toString()
+ {
+ synchronized (mutex)
+ {
+ return m.toString();
+ }
+ }
+
+ /**
+ * Returns a synchronized collection view of the values in the underlying
+ * map. The collection is backed by the map, so that changes in one show up in
+ * the other. Modifications made while an iterator is in progress cause
+ * undefined behavior. If the collection supports removal, these methods
+ * remove the underlying mapping from the map: <code>Iterator.remove</code>,
+ * <code>Collection.remove</code>, <code>removeAll</code>,
+ * <code>retainAll</code>, and <code>clear</code>. Element addition, via
+ * <code>add</code> or <code>addAll</code>, is not supported via this
+ * collection. A lock is obtained on the mutex before the collection
+ * is created.
+ *
+ * @return the collection of all values in the underlying map.
+ */
+ public Collection<V> values()
+ {
+ if (values == null)
+ synchronized (mutex)
+ {
+ values = new SynchronizedCollection<V>(mutex, m.values());
+ }
+ return values;
+ }
+ } // class SynchronizedMap
+
+ /**
+ * Returns a synchronized (thread-safe) set wrapper backed by the given
+ * set. Notice that element access through the iterator is thread-safe, but
+ * if the set can be structurally modified (adding or removing elements)
+ * then you should synchronize around the iteration to avoid
+ * non-deterministic behavior:<br>
+ * <pre>
+ * Set s = Collections.synchronizedSet(new Set(...));
+ * ...
+ * synchronized (s)
+ * {
+ * Iterator i = s.iterator();
+ * while (i.hasNext())
+ * foo(i.next());
+ * }
+ * </pre><p>
+ *
+ * The returned Set implements Serializable, but can only be serialized if
+ * the set it wraps is likewise Serializable.
+ *
+ * @param s the set to wrap
+ * @return a synchronized view of the set
+ * @see Serializable
+ */
+ public static <T> Set<T> synchronizedSet(Set<T> s)
+ {
+ return new SynchronizedSet<T>(s);
+ }
+
+ /**
+ * The implementation of {@link #synchronizedSet(Set)}. This class
+ * name is required for compatibility with Sun's JDK serializability.
+ * Package visible, so that sets such as Hashtable.keySet()
+ * can specify which object to synchronize on.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ static class SynchronizedSet<T> extends SynchronizedCollection<T>
+ implements Set<T>
+ {
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = 487447009682186044L;
+
+ /**
+ * Wrap a given set.
+ * @param s the set to wrap
+ * @throws NullPointerException if s is null
+ */
+ SynchronizedSet(Set<T> s)
+ {
+ super(s);
+ }
+
+ /**
+ * Called only by trusted code to specify the mutex as well as the set.
+ * @param sync the mutex
+ * @param s the set
+ */
+ SynchronizedSet(Object sync, Set<T> s)
+ {
+ super(sync, s);
+ }
+
+ /**
+ * Returns <code>true</code> if the object, o, is a <code>Set</code>
+ * of the same size as the underlying set, and contains
+ * each element, e, which occurs in the underlying set.
+ * A lock is obtained on the mutex before the comparison
+ * takes place.
+ *
+ * @param o The object to compare against.
+ * @return <code>true</code> if o is an equivalent set.
+ */
+ public boolean equals(Object o)
+ {
+ synchronized (mutex)
+ {
+ return c.equals(o);
+ }
+ }
+
+ /**
+ * Computes the hash code for the underlying set as the
+ * sum of the hash code of all elements within the set.
+ * A lock is obtained on the mutex before the computation
+ * occurs.
+ *
+ * @return The hash code for the underlying set.
+ */
+ public int hashCode()
+ {
+ synchronized (mutex)
+ {
+ return c.hashCode();
+ }
+ }
+ } // class SynchronizedSet
+
+ /**
+ * Returns a synchronized (thread-safe) sorted map wrapper backed by the
+ * given map. Notice that element access through the collection views,
+ * subviews, and their iterators are thread-safe, but if the map can be
+ * structurally modified (adding or removing elements) then you should
+ * synchronize around the iteration to avoid non-deterministic behavior:<br>
+ * <pre>
+ * SortedMap m = Collections.synchronizedSortedMap(new SortedMap(...));
+ * ...
+ * Set s = m.keySet(); // safe outside a synchronized block
+ * SortedMap m2 = m.headMap(foo); // safe outside a synchronized block
+ * Set s2 = m2.keySet(); // safe outside a synchronized block
+ * synchronized (m) // synch on m, not m2, s or s2
+ * {
+ * Iterator i = s.iterator();
+ * while (i.hasNext())
+ * foo(i.next());
+ * i = s2.iterator();
+ * while (i.hasNext())
+ * bar(i.next());
+ * }
+ * </pre><p>
+ *
+ * The returned SortedMap implements Serializable, but can only be
+ * serialized if the map it wraps is likewise Serializable.
+ *
+ * @param m the sorted map to wrap
+ * @return a synchronized view of the sorted map
+ * @see Serializable
+ */
+ public static <K, V> SortedMap<K, V> synchronizedSortedMap(SortedMap<K, V> m)
+ {
+ return new SynchronizedSortedMap<K, V>(m);
+ }
+
+ /**
+ * The implementation of {@link #synchronizedSortedMap(SortedMap)}. This
+ * class name is required for compatibility with Sun's JDK serializability.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static final class SynchronizedSortedMap<K, V>
+ extends SynchronizedMap<K, V>
+ implements SortedMap<K, V>
+ {
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = -8798146769416483793L;
+
+ /**
+ * The wrapped map; stored both here and in the superclass to avoid
+ * excessive casting.
+ * @serial the wrapped map
+ */
+ private final SortedMap<K, V> sm;
+
+ /**
+ * Wrap a given map.
+ * @param sm the map to wrap
+ * @throws NullPointerException if sm is null
+ */
+ SynchronizedSortedMap(SortedMap<K, V> sm)
+ {
+ super(sm);
+ this.sm = sm;
+ }
+
+ /**
+ * Called only by trusted code to specify the mutex as well as the map.
+ * @param sync the mutex
+ * @param sm the map
+ */
+ SynchronizedSortedMap(Object sync, SortedMap<K, V> sm)
+ {
+ super(sync, sm);
+ this.sm = sm;
+ }
+
+ /**
+ * Returns the comparator used in sorting the underlying map, or null if
+ * it is the keys' natural ordering. A lock is obtained on the mutex
+ * before the comparator is retrieved.
+ *
+ * @return the sorting comparator.
+ */
+ public Comparator<? super K> comparator()
+ {
+ synchronized (mutex)
+ {
+ return sm.comparator();
+ }
+ }
+
+ /**
+ * Returns the first, lowest sorted, key from the underlying map.
+ * A lock is obtained on the mutex before the map is accessed.
+ *
+ * @return the first key.
+ * @throws NoSuchElementException if this map is empty.
+ */
+ public K firstKey()
+ {
+ synchronized (mutex)
+ {
+ return sm.firstKey();
+ }
+ }
+
+ /**
+ * Returns a submap containing the keys from the first
+ * key (as returned by <code>firstKey()</code>) to
+ * the key before that specified. The submap supports all
+ * operations supported by the underlying map and all actions
+ * taking place on the submap are also reflected in the underlying
+ * map. A lock is obtained on the mutex prior to submap creation.
+ * This operation is equivalent to <code>subMap(firstKey(), toKey)</code>.
+ * The submap retains the thread-safe status of this map.
+ *
+ * @param toKey the exclusive upper range of the submap.
+ * @return a submap from <code>firstKey()</code> to the
+ * the key preceding toKey.
+ * @throws ClassCastException if toKey is not comparable to the underlying
+ * map's contents.
+ * @throws IllegalArgumentException if toKey is outside the map's range.
+ * @throws NullPointerException if toKey is null. but the map does not allow
+ * null keys.
+ */
+ public SortedMap<K, V> headMap(K toKey)
+ {
+ synchronized (mutex)
+ {
+ return new SynchronizedSortedMap<K, V>(mutex, sm.headMap(toKey));
+ }
+ }
+
+ /**
+ * Returns the last, highest sorted, key from the underlying map.
+ * A lock is obtained on the mutex before the map is accessed.
+ *
+ * @return the last key.
+ * @throws NoSuchElementException if this map is empty.
+ */
+ public K lastKey()
+ {
+ synchronized (mutex)
+ {
+ return sm.lastKey();
+ }
+ }
+
+ /**
+ * Returns a submap containing the keys from fromKey to
+ * the key before toKey. The submap supports all
+ * operations supported by the underlying map and all actions
+ * taking place on the submap are also reflected in the underlying
+ * map. A lock is obtained on the mutex prior to submap creation.
+ * The submap retains the thread-safe status of this map.
+ *
+ * @param fromKey the inclusive lower range of the submap.
+ * @param toKey the exclusive upper range of the submap.
+ * @return a submap from fromKey to the key preceding toKey.
+ * @throws ClassCastException if fromKey or toKey is not comparable
+ * to the underlying map's contents.
+ * @throws IllegalArgumentException if fromKey or toKey is outside the map's
+ * range.
+ * @throws NullPointerException if fromKey or toKey is null. but the map does
+ * not allow null keys.
+ */
+ public SortedMap<K, V> subMap(K fromKey, K toKey)
+ {
+ synchronized (mutex)
+ {
+ return new SynchronizedSortedMap<K, V>(mutex,
+ sm.subMap(fromKey, toKey));
+ }
+ }
+
+ /**
+ * Returns a submap containing all the keys from fromKey onwards.
+ * The submap supports all operations supported by the underlying
+ * map and all actions taking place on the submap are also reflected
+ * in the underlying map. A lock is obtained on the mutex prior to
+ * submap creation. The submap retains the thread-safe status of
+ * this map.
+ *
+ * @param fromKey the inclusive lower range of the submap.
+ * @return a submap from fromKey to <code>lastKey()</code>.
+ * @throws ClassCastException if fromKey is not comparable to the underlying
+ * map's contents.
+ * @throws IllegalArgumentException if fromKey is outside the map's range.
+ * @throws NullPointerException if fromKey is null. but the map does not allow
+ * null keys.
+ */
+ public SortedMap<K, V> tailMap(K fromKey)
+ {
+ synchronized (mutex)
+ {
+ return new SynchronizedSortedMap<K, V>(mutex, sm.tailMap(fromKey));
+ }
+ }
+ } // class SynchronizedSortedMap
+
+ /**
+ * Returns a synchronized (thread-safe) sorted set wrapper backed by the
+ * given set. Notice that element access through the iterator and through
+ * subviews are thread-safe, but if the set can be structurally modified
+ * (adding or removing elements) then you should synchronize around the
+ * iteration to avoid non-deterministic behavior:<br>
+ * <pre>
+ * SortedSet s = Collections.synchronizedSortedSet(new SortedSet(...));
+ * ...
+ * SortedSet s2 = s.headSet(foo); // safe outside a synchronized block
+ * synchronized (s) // synch on s, not s2
+ * {
+ * Iterator i = s2.iterator();
+ * while (i.hasNext())
+ * foo(i.next());
+ * }
+ * </pre><p>
+ *
+ * The returned SortedSet implements Serializable, but can only be
+ * serialized if the set it wraps is likewise Serializable.
+ *
+ * @param s the sorted set to wrap
+ * @return a synchronized view of the sorted set
+ * @see Serializable
+ */
+ public static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s)
+ {
+ return new SynchronizedSortedSet<T>(s);
+ }
+
+ /**
+ * The implementation of {@link #synchronizedSortedSet(SortedSet)}. This
+ * class name is required for compatibility with Sun's JDK serializability.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static final class SynchronizedSortedSet<T>
+ extends SynchronizedSet<T>
+ implements SortedSet<T>
+ {
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = 8695801310862127406L;
+
+ /**
+ * The wrapped set; stored both here and in the superclass to avoid
+ * excessive casting.
+ * @serial the wrapped set
+ */
+ private final SortedSet<T> ss;
+
+ /**
+ * Wrap a given set.
+ * @param ss the set to wrap
+ * @throws NullPointerException if ss is null
+ */
+ SynchronizedSortedSet(SortedSet<T> ss)
+ {
+ super(ss);
+ this.ss = ss;
+ }
+
+ /**
+ * Called only by trusted code to specify the mutex as well as the set.
+ * @param sync the mutex
+ * @param ss the set
+ */
+ SynchronizedSortedSet(Object sync, SortedSet<T> ss)
+ {
+ super(sync, ss);
+ this.ss = ss;
+ }
+
+ /**
+ * Returns the comparator used in sorting the underlying set, or null if
+ * it is the elements' natural ordering. A lock is obtained on the mutex
+ * before the comparator is retrieved.
+ *
+ * @return the sorting comparator.
+ */
+ public Comparator<? super T> comparator()
+ {
+ synchronized (mutex)
+ {
+ return ss.comparator();
+ }
+ }
+
+ /**
+ * Returns the first, lowest sorted, element from the underlying set.
+ * A lock is obtained on the mutex before the set is accessed.
+ *
+ * @return the first element.
+ * @throws NoSuchElementException if this set is empty.
+ */
+ public T first()
+ {
+ synchronized (mutex)
+ {
+ return ss.first();
+ }
+ }
+
+ /**
+ * Returns a subset containing the element from the first
+ * element (as returned by <code>first()</code>) to
+ * the element before that specified. The subset supports all
+ * operations supported by the underlying set and all actions
+ * taking place on the subset are also reflected in the underlying
+ * set. A lock is obtained on the mutex prior to subset creation.
+ * This operation is equivalent to <code>subSet(first(), toElement)</code>.
+ * The subset retains the thread-safe status of this set.
+ *
+ * @param toElement the exclusive upper range of the subset.
+ * @return a subset from <code>first()</code> to the
+ * the element preceding toElement.
+ * @throws ClassCastException if toElement is not comparable to the underlying
+ * set's contents.
+ * @throws IllegalArgumentException if toElement is outside the set's range.
+ * @throws NullPointerException if toElement is null. but the set does not allow
+ * null elements.
+ */
+ public SortedSet<T> headSet(T toElement)
+ {
+ synchronized (mutex)
+ {
+ return new SynchronizedSortedSet<T>(mutex, ss.headSet(toElement));
+ }
+ }
+
+ /**
+ * Returns the last, highest sorted, element from the underlying set.
+ * A lock is obtained on the mutex before the set is accessed.
+ *
+ * @return the last element.
+ * @throws NoSuchElementException if this set is empty.
+ */
+ public T last()
+ {
+ synchronized (mutex)
+ {
+ return ss.last();
+ }
+ }
+
+ /**
+ * Returns a subset containing the elements from fromElement to
+ * the element before toElement. The subset supports all
+ * operations supported by the underlying set and all actions
+ * taking place on the subset are also reflected in the underlying
+ * set. A lock is obtained on the mutex prior to subset creation.
+ * The subset retains the thread-safe status of this set.
+ *
+ * @param fromElement the inclusive lower range of the subset.
+ * @param toElement the exclusive upper range of the subset.
+ * @return a subset from fromElement to the element preceding toElement.
+ * @throws ClassCastException if fromElement or toElement is not comparable
+ * to the underlying set's contents.
+ * @throws IllegalArgumentException if fromElement or toElement is outside the set's
+ * range.
+ * @throws NullPointerException if fromElement or toElement is null. but the set does
+ * not allow null elements.
+ */
+ public SortedSet<T> subSet(T fromElement, T toElement)
+ {
+ synchronized (mutex)
+ {
+ return new SynchronizedSortedSet<T>(mutex,
+ ss.subSet(fromElement,
+ toElement));
+ }
+ }
+
+ /**
+ * Returns a subset containing all the elements from fromElement onwards.
+ * The subset supports all operations supported by the underlying
+ * set and all actions taking place on the subset are also reflected
+ * in the underlying set. A lock is obtained on the mutex prior to
+ * subset creation. The subset retains the thread-safe status of
+ * this set.
+ *
+ * @param fromElement the inclusive lower range of the subset.
+ * @return a subset from fromElement to <code>last()</code>.
+ * @throws ClassCastException if fromElement is not comparable to the underlying
+ * set's contents.
+ * @throws IllegalArgumentException if fromElement is outside the set's range.
+ * @throws NullPointerException if fromElement is null. but the set does not allow
+ * null elements.
+ */
+ public SortedSet<T> tailSet(T fromElement)
+ {
+ synchronized (mutex)
+ {
+ return new SynchronizedSortedSet<T>(mutex, ss.tailSet(fromElement));
+ }
+ }
+ } // class SynchronizedSortedSet
+
+
+ /**
+ * Returns an unmodifiable view of the given collection. This allows
+ * "read-only" access, although changes in the backing collection show up
+ * in this view. Attempts to modify the collection directly or via iterators
+ * will fail with {@link UnsupportedOperationException}. Although this view
+ * prevents changes to the structure of the collection and its elements, the values
+ * referenced by the objects in the collection can still be modified.
+ * <p>
+ *
+ * Since the collection might be a List or a Set, and those have incompatible
+ * equals and hashCode requirements, this relies on Object's implementation
+ * rather than passing those calls on to the wrapped collection. The returned
+ * Collection implements Serializable, but can only be serialized if
+ * the collection it wraps is likewise Serializable.
+ *
+ * @param c the collection to wrap
+ * @return a read-only view of the collection
+ * @see Serializable
+ */
+ public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c)
+ {
+ return new UnmodifiableCollection<T>(c);
+ }
+
+ /**
+ * The implementation of {@link #unmodifiableCollection(Collection)}. This
+ * class name is required for compatibility with Sun's JDK serializability.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static class UnmodifiableCollection<T>
+ implements Collection<T>, Serializable
+ {
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = 1820017752578914078L;
+
+ /**
+ * The wrapped collection. Package visible for use by subclasses.
+ * @serial the real collection
+ */
+ final Collection<? extends T> c;
+
+ /**
+ * Wrap a given collection.
+ * @param c the collection to wrap
+ * @throws NullPointerException if c is null
+ */
+ UnmodifiableCollection(Collection<? extends T> c)
+ {
+ this.c = c;
+ if (c == null)
+ throw new NullPointerException();
+ }
+
+ /**
+ * Blocks the addition of elements to the underlying collection.
+ * This method never returns, throwing an exception instead.
+ *
+ * @param o the object to add.
+ * @return <code>true</code> if the collection was modified as a result of this action.
+ * @throws UnsupportedOperationException as an unmodifiable collection does not
+ * support the add operation.
+ */
+ public boolean add(T o)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Blocks the addition of a collection of elements to the underlying
+ * collection. This method never returns, throwing an exception instead.
+ *
+ * @param c the collection to add.
+ * @return <code>true</code> if the collection was modified as a result of this action.
+ * @throws UnsupportedOperationException as an unmodifiable collection does not
+ * support the <code>addAll</code> operation.
+ */
+ public boolean addAll(Collection<? extends T> c)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Blocks the clearing of the underlying collection. This method never
+ * returns, throwing an exception instead.
+ *
+ * @throws UnsupportedOperationException as an unmodifiable collection does
+ * not support the <code>clear()</code> operation.
+ */
+ public void clear()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Test whether the underlying collection contains a given object as one of its
+ * elements.
+ *
+ * @param o the element to look for.
+ * @return <code>true</code> if the underlying collection contains at least
+ * one element e such that
+ * <code>o == null ? e == null : o.equals(e)</code>.
+ * @throws ClassCastException if the type of o is not a valid type for the
+ * underlying collection.
+ * @throws NullPointerException if o is null and the underlying collection
+ * doesn't support null values.
+ */
+ public boolean contains(Object o)
+ {
+ return c.contains(o);
+ }
+
+ /**
+ * Test whether the underlying collection contains every element in a given
+ * collection.
+ *
+ * @param c1 the collection to test for.
+ * @return <code>true</code> if for every element o in c, contains(o) would
+ * return <code>true</code>.
+ * @throws ClassCastException if the type of any element in c is not a valid
+ * type for the underlying collection.
+ * @throws NullPointerException if some element of c is null and the underlying
+ * collection does not support null values.
+ * @throws NullPointerException if c itself is null.
+ */
+ public boolean containsAll(Collection<?> c1)
+ {
+ return c.containsAll(c1);
+ }
+
+ /**
+ * Tests whether the underlying collection is empty, that is,
+ * if size() == 0.
+ *
+ * @return <code>true</code> if this collection contains no elements.
+ */
+ public boolean isEmpty()
+ {
+ return c.isEmpty();
+ }
+
+ /**
+ * Obtain an Iterator over the underlying collection, which maintains
+ * its unmodifiable nature.
+ *
+ * @return an UnmodifiableIterator over the elements of the underlying
+ * collection, in any order.
+ */
+ public Iterator<T> iterator()
+ {
+ return new UnmodifiableIterator<T>(c.iterator());
+ }
+
+ /**
+ * Blocks the removal of an object from the underlying collection.
+ * This method never returns, throwing an exception instead.
+ *
+ * @param o The object to remove.
+ * @return <code>true</code> if the object was removed (i.e. the underlying
+ * collection returned 1 or more instances of o).
+ * @throws UnsupportedOperationException as an unmodifiable collection
+ * does not support the <code>remove()</code> operation.
+ */
+ public boolean remove(Object o)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Blocks the removal of a collection of objects from the underlying
+ * collection. This method never returns, throwing an exception
+ * instead.
+ *
+ * @param c The collection of objects to remove.
+ * @return <code>true</code> if the collection was modified.
+ * @throws UnsupportedOperationException as an unmodifiable collection
+ * does not support the <code>removeAll()</code> operation.
+ */
+ public boolean removeAll(Collection<?> c)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Blocks the removal of all elements from the underlying collection,
+ * except those in the supplied collection. This method never returns,
+ * throwing an exception instead.
+ *
+ * @param c The collection of objects to retain.
+ * @return <code>true</code> if the collection was modified.
+ * @throws UnsupportedOperationException as an unmodifiable collection
+ * does not support the <code>retainAll()</code> operation.
+ */
+ public boolean retainAll(Collection<?> c)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Retrieves the number of elements in the underlying collection.
+ *
+ * @return the number of elements in the collection.
+ */
+ public int size()
+ {
+ return c.size();
+ }
+
+ /**
+ * Copy the current contents of the underlying collection into an array.
+ *
+ * @return an array of type Object[] with a length equal to the size of the
+ * underlying collection and containing the elements currently in
+ * the underlying collection, in any order.
+ */
+ public Object[] toArray()
+ {
+ return c.toArray();
+ }
+
+ /**
+ * Copy the current contents of the underlying collection into an array. If
+ * the array passed as an argument has length less than the size of the
+ * underlying collection, an array of the same run-time type as a, with a length
+ * equal to the size of the underlying collection, is allocated using reflection.
+ * Otherwise, a itself is used. The elements of the underlying collection are
+ * copied into it, and if there is space in the array, the following element is
+ * set to null. The resultant array is returned.
+ * Note: The fact that the following element is set to null is only useful
+ * if it is known that this collection does not contain any null elements.
+ *
+ * @param a the array to copy this collection into.
+ * @return an array containing the elements currently in the underlying
+ * collection, in any order.
+ * @throws ArrayStoreException if the type of any element of the
+ * collection is not a subtype of the element type of a.
+ */
+ public <S> S[] toArray(S[] a)
+ {
+ return c.toArray(a);
+ }
+
+ /**
+ * A textual representation of the unmodifiable collection.
+ *
+ * @return The unmodifiable collection in the form of a <code>String</code>.
+ */
+ public String toString()
+ {
+ return c.toString();
+ }
+ } // class UnmodifiableCollection
+
+ /**
+ * The implementation of the various iterator methods in the
+ * unmodifiable classes.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static class UnmodifiableIterator<T> implements Iterator<T>
+ {
+ /**
+ * The wrapped iterator.
+ */
+ private final Iterator<? extends T> i;
+
+ /**
+ * Only trusted code creates a wrapper.
+ * @param i the wrapped iterator
+ */
+ UnmodifiableIterator(Iterator<? extends T> i)
+ {
+ this.i = i;
+ }
+
+ /**
+ * Obtains the next element in the underlying collection.
+ *
+ * @return the next element in the collection.
+ * @throws NoSuchElementException if there are no more elements.
+ */
+ public T next()
+ {
+ return i.next();
+ }
+
+ /**
+ * Tests whether there are still elements to be retrieved from the
+ * underlying collection by <code>next()</code>. When this method
+ * returns <code>true</code>, an exception will not be thrown on calling
+ * <code>next()</code>.
+ *
+ * @return <code>true</code> if there is at least one more element in the underlying
+ * collection.
+ */
+ public boolean hasNext()
+ {
+ return i.hasNext();
+ }
+
+ /**
+ * Blocks the removal of elements from the underlying collection by the
+ * iterator.
+ *
+ * @throws UnsupportedOperationException as an unmodifiable collection
+ * does not support the removal of elements by its iterator.
+ */
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+ } // class UnmodifiableIterator
+
+ /**
+ * Returns an unmodifiable view of the given list. This allows
+ * "read-only" access, although changes in the backing list show up
+ * in this view. Attempts to modify the list directly, via iterators, or
+ * via sublists, will fail with {@link UnsupportedOperationException}.
+ * Although this view prevents changes to the structure of the list and
+ * its elements, the values referenced by the objects in the list can
+ * still be modified.
+ * <p>
+ *
+ * The returned List implements Serializable, but can only be serialized if
+ * the list it wraps is likewise Serializable. In addition, if the wrapped
+ * list implements RandomAccess, this does too.
+ *
+ * @param l the list to wrap
+ * @return a read-only view of the list
+ * @see Serializable
+ * @see RandomAccess
+ */
+ public static <T> List<T> unmodifiableList(List<? extends T> l)
+ {
+ if (l instanceof RandomAccess)
+ return new UnmodifiableRandomAccessList<T>(l);
+ return new UnmodifiableList<T>(l);
+ }
+
+ /**
+ * The implementation of {@link #unmodifiableList(List)} for sequential
+ * lists. This class name is required for compatibility with Sun's JDK
+ * serializability.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static class UnmodifiableList<T> extends UnmodifiableCollection<T>
+ implements List<T>
+ {
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = -283967356065247728L;
+
+
+ /**
+ * The wrapped list; stored both here and in the superclass to avoid
+ * excessive casting. Package visible for use by subclass.
+ * @serial the wrapped list
+ */
+ final List<T> list;
+
+ /**
+ * Wrap a given list.
+ * @param l the list to wrap
+ * @throws NullPointerException if l is null
+ */
+ UnmodifiableList(List<? extends T> l)
+ {
+ super(l);
+ list = (List<T>) l;
+ }
+
+ /**
+ * Blocks the addition of an element to the underlying
+ * list at a specific index. This method never returns,
+ * throwing an exception instead.
+ *
+ * @param index The index at which to place the new element.
+ * @param o the object to add.
+ * @throws UnsupportedOperationException as an unmodifiable
+ * list doesn't support the <code>add()</code> operation.
+ */
+ public void add(int index, T o)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Blocks the addition of a collection of elements to the
+ * underlying list at a specific index. This method never
+ * returns, throwing an exception instead.
+ *
+ * @param index The index at which to place the new element.
+ * @param c the collections of objects to add.
+ * @throws UnsupportedOperationException as an unmodifiable
+ * list doesn't support the <code>addAll()</code> operation.
+ */
+ public boolean addAll(int index, Collection<? extends T> c)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns <code>true</code> if the object, o, is an instance of
+ * <code>List</code> with the same size and elements
+ * as the underlying list.
+ *
+ * @param o The object to compare.
+ * @return <code>true</code> if o is equivalent to the underlying list.
+ */
+ public boolean equals(Object o)
+ {
+ return list.equals(o);
+ }
+
+ /**
+ * Retrieves the element at a given index in the underlying list.
+ *
+ * @param index the index of the element to be returned
+ * @return the element at index index in this list
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ */
+ public T get(int index)
+ {
+ return list.get(index);
+ }
+
+ /**
+ * Computes the hash code for the underlying list.
+ * The exact computation is described in the documentation
+ * of the <code>List</code> interface.
+ *
+ * @return The hash code of the underlying list.
+ * @see List#hashCode()
+ */
+ public int hashCode()
+ {
+ return list.hashCode();
+ }
+
+ /**
+ * Obtain the first index at which a given object is to be found in the
+ * underlying list.
+ *
+ * @param o the object to search for
+ * @return the least integer n such that <code>o == null ? get(n) == null :
+ * o.equals(get(n))</code>, or -1 if there is no such index.
+ * @throws ClassCastException if the type of o is not a valid
+ * type for the underlying list.
+ * @throws NullPointerException if o is null and the underlying
+ * list does not support null values.
+ */
+ public int indexOf(Object o)
+ {
+ return list.indexOf(o);
+ }
+
+ /**
+ * Obtain the last index at which a given object is to be found in the
+ * underlying list.
+ *
+ * @return the greatest integer n such that <code>o == null ? get(n) == null
+ * : o.equals(get(n))</code>, or -1 if there is no such index.
+ * @throws ClassCastException if the type of o is not a valid
+ * type for the underlying list.
+ * @throws NullPointerException if o is null and the underlying
+ * list does not support null values.
+ */
+ public int lastIndexOf(Object o)
+ {
+ return list.lastIndexOf(o);
+ }
+
+ /**
+ * Obtains a list iterator over the underlying list, starting at the beginning
+ * and maintaining the unmodifiable nature of this list.
+ *
+ * @return a <code>UnmodifiableListIterator</code> over the elements of the
+ * underlying list, in order, starting at the beginning.
+ */
+ public ListIterator<T> listIterator()
+ {
+ return new UnmodifiableListIterator<T>(list.listIterator());
+ }
+
+ /**
+ * Obtains a list iterator over the underlying list, starting at the specified
+ * index and maintaining the unmodifiable nature of this list. An initial call
+ * to <code>next()</code> will retrieve the element at the specified index,
+ * and an initial call to <code>previous()</code> will retrieve the element
+ * at index - 1.
+ *
+ *
+ * @param index the position, between 0 and size() inclusive, to begin the
+ * iteration from.
+ * @return a <code>UnmodifiableListIterator</code> over the elements of the
+ * underlying list, in order, starting at the specified index.
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ */
+ public ListIterator<T> listIterator(int index)
+ {
+ return new UnmodifiableListIterator<T>(list.listIterator(index));
+ }
+
+ /**
+ * Blocks the removal of the element at the specified index.
+ * This method never returns, throwing an exception instead.
+ *
+ * @param index The index of the element to remove.
+ * @return the removed element.
+ * @throws UnsupportedOperationException as an unmodifiable
+ * list does not support the <code>remove()</code>
+ * operation.
+ */
+ public T remove(int index)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Blocks the replacement of the element at the specified index.
+ * This method never returns, throwing an exception instead.
+ *
+ * @param index The index of the element to replace.
+ * @param o The new object to place at the specified index.
+ * @return the replaced element.
+ * @throws UnsupportedOperationException as an unmodifiable
+ * list does not support the <code>set()</code>
+ * operation.
+ */
+ public T set(int index, T o)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Obtain a List view of a subsection of the underlying list, from
+ * fromIndex (inclusive) to toIndex (exclusive). If the two indices
+ * are equal, the sublist is empty. The returned list will be
+ * unmodifiable, like this list. Changes to the elements of the
+ * returned list will be reflected in the underlying list. No structural
+ * modifications can take place in either list.
+ *
+ * @param fromIndex the index that the returned list should start from
+ * (inclusive).
+ * @param toIndex the index that the returned list should go to (exclusive).
+ * @return a List backed by a subsection of the underlying list.
+ * @throws IndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; size() || fromIndex &gt; toIndex.
+ */
+ public List<T> subList(int fromIndex, int toIndex)
+ {
+ return unmodifiableList(list.subList(fromIndex, toIndex));
+ }
+ } // class UnmodifiableList
+
+ /**
+ * The implementation of {@link #unmodifiableList(List)} for random-access
+ * lists. This class name is required for compatibility with Sun's JDK
+ * serializability.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static final class UnmodifiableRandomAccessList<T>
+ extends UnmodifiableList<T> implements RandomAccess
+ {
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = -2542308836966382001L;
+
+ /**
+ * Wrap a given list.
+ * @param l the list to wrap
+ * @throws NullPointerException if l is null
+ */
+ UnmodifiableRandomAccessList(List<? extends T> l)
+ {
+ super(l);
+ }
+ } // class UnmodifiableRandomAccessList
+
+ /**
+ * The implementation of {@link UnmodifiableList#listIterator()}.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static final class UnmodifiableListIterator<T>
+ extends UnmodifiableIterator<T> implements ListIterator<T>
+ {
+ /**
+ * The wrapped iterator, stored both here and in the superclass to
+ * avoid excessive casting.
+ */
+ private final ListIterator<T> li;
+
+ /**
+ * Only trusted code creates a wrapper.
+ * @param li the wrapped iterator
+ */
+ UnmodifiableListIterator(ListIterator<T> li)
+ {
+ super(li);
+ this.li = li;
+ }
+
+ /**
+ * Blocks the addition of an object to the list underlying this iterator.
+ * This method never returns, throwing an exception instead.
+ *
+ * @param o The object to add.
+ * @throws UnsupportedOperationException as the iterator of an unmodifiable
+ * list does not support the <code>add()</code> operation.
+ */
+ public void add(T o)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Tests whether there are still elements to be retrieved from the
+ * underlying collection by <code>previous()</code>. When this method
+ * returns <code>true</code>, an exception will not be thrown on calling
+ * <code>previous()</code>.
+ *
+ * @return <code>true</code> if there is at least one more element prior to the
+ * current position in the underlying list.
+ */
+ public boolean hasPrevious()
+ {
+ return li.hasPrevious();
+ }
+
+ /**
+ * Find the index of the element that would be returned by a call to next.
+ * If <code>hasNext()</code> returns <code>false</code>, this returns the list size.
+ *
+ * @return the index of the element that would be returned by
+ * <code>next()</code>.
+ */
+ public int nextIndex()
+ {
+ return li.nextIndex();
+ }
+
+ /**
+ * Obtains the previous element in the underlying list.
+ *
+ * @return the previous element in the list.
+ * @throws NoSuchElementException if there are no more prior elements.
+ */
+ public T previous()
+ {
+ return li.previous();
+ }
+
+ /**
+ * Find the index of the element that would be returned by a call to
+ * previous. If <code>hasPrevious()</code> returns <code>false</code>,
+ * this returns -1.
+ *
+ * @return the index of the element that would be returned by
+ * <code>previous()</code>.
+ */
+ public int previousIndex()
+ {
+ return li.previousIndex();
+ }
+
+ /**
+ * Blocks the replacement of an element in the list underlying this
+ * iterator. This method never returns, throwing an exception instead.
+ *
+ * @param o The new object to replace the existing one.
+ * @throws UnsupportedOperationException as the iterator of an unmodifiable
+ * list does not support the <code>set()</code> operation.
+ */
+ public void set(T o)
+ {
+ throw new UnsupportedOperationException();
+ }
+ } // class UnmodifiableListIterator
+
+ /**
+ * Returns an unmodifiable view of the given map. This allows "read-only"
+ * access, although changes in the backing map show up in this view.
+ * Attempts to modify the map directly, or via collection views or their
+ * iterators will fail with {@link UnsupportedOperationException}.
+ * Although this view prevents changes to the structure of the map and its
+ * entries, the values referenced by the objects in the map can still be
+ * modified.
+ * <p>
+ *
+ * The returned Map implements Serializable, but can only be serialized if
+ * the map it wraps is likewise Serializable.
+ *
+ * @param m the map to wrap
+ * @return a read-only view of the map
+ * @see Serializable
+ */
+ public static <K, V> Map<K, V> unmodifiableMap(Map<? extends K,
+ ? extends V> m)
+ {
+ return new UnmodifiableMap<K, V>(m);
+ }
+
+ /**
+ * The implementation of {@link #unmodifiableMap(Map)}. This
+ * class name is required for compatibility with Sun's JDK serializability.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static class UnmodifiableMap<K, V> implements Map<K, V>, Serializable
+ {
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = -1034234728574286014L;
+
+ /**
+ * The wrapped map.
+ * @serial the real map
+ */
+ private final Map<K, V> m;
+
+ /**
+ * Cache the entry set.
+ */
+ private transient Set<Map.Entry<K, V>> entries;
+
+ /**
+ * Cache the key set.
+ */
+ private transient Set<K> keys;
+
+ /**
+ * Cache the value collection.
+ */
+ private transient Collection<V> values;
+
+ /**
+ * Wrap a given map.
+ * @param m the map to wrap
+ * @throws NullPointerException if m is null
+ */
+ UnmodifiableMap(Map<? extends K, ? extends V> m)
+ {
+ this.m = (Map<K,V>) m;
+ if (m == null)
+ throw new NullPointerException();
+ }
+
+ /**
+ * Blocks the clearing of entries from the underlying map.
+ * This method never returns, throwing an exception instead.
+ *
+ * @throws UnsupportedOperationException as an unmodifiable
+ * map does not support the <code>clear()</code> operation.
+ */
+ public void clear()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns <code>true</code> if the underlying map contains a mapping for
+ * the given key.
+ *
+ * @param key the key to search for
+ * @return <code>true</code> if the map contains the key
+ * @throws ClassCastException if the key is of an inappropriate type
+ * @throws NullPointerException if key is <code>null</code> but the map
+ * does not permit null keys
+ */
+ public boolean containsKey(Object key)
+ {
+ return m.containsKey(key);
+ }
+
+ /**
+ * Returns <code>true</code> if the underlying map contains at least one mapping with
+ * the given value. In other words, it returns <code>true</code> if a value v exists where
+ * <code>(value == null ? v == null : value.equals(v))</code>. This usually
+ * requires linear time.
+ *
+ * @param value the value to search for
+ * @return <code>true</code> if the map contains the value
+ * @throws ClassCastException if the type of the value is not a valid type
+ * for this map.
+ * @throws NullPointerException if the value is null and the map doesn't
+ * support null values.
+ */
+ public boolean containsValue(Object value)
+ {
+ return m.containsValue(value);
+ }
+
+ /**
+ * Returns a unmodifiable set view of the entries in the underlying map.
+ * Each element in the set is a unmodifiable variant of <code>Map.Entry</code>.
+ * The set is backed by the map, so that changes in one show up in the other.
+ * Modifications made while an iterator is in progress cause undefined
+ * behavior. These modifications are again limited to the values of
+ * the objects.
+ *
+ * @return the unmodifiable set view of all mapping entries.
+ * @see Map.Entry
+ */
+ public Set<Map.Entry<K, V>> entrySet()
+ {
+ if (entries == null)
+ entries = new UnmodifiableEntrySet<K,V>(m.entrySet());
+ return entries;
+ }
+
+ /**
+ * The implementation of {@link UnmodifiableMap#entrySet()}. This class
+ * name is required for compatibility with Sun's JDK serializability.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static final class UnmodifiableEntrySet<K,V>
+ extends UnmodifiableSet<Map.Entry<K,V>>
+ implements Serializable
+ {
+ // Unmodifiable implementation of Map.Entry used as return value for
+ // UnmodifiableEntrySet accessors (iterator, toArray, toArray(Object[]))
+ private static final class UnmodifiableMapEntry<K,V>
+ implements Map.Entry<K,V>
+ {
+ private final Map.Entry<K,V> e;
+
+ private UnmodifiableMapEntry(Map.Entry<K,V> e)
+ {
+ super();
+ this.e = e;
+ }
+
+ /**
+ * Returns <code>true</code> if the object, o, is also a map entry
+ * with an identical key and value.
+ *
+ * @param o the object to compare.
+ * @return <code>true</code> if o is an equivalent map entry.
+ */
+ public boolean equals(Object o)
+ {
+ return e.equals(o);
+ }
+
+ /**
+ * Returns the key of this map entry.
+ *
+ * @return the key.
+ */
+ public K getKey()
+ {
+ return e.getKey();
+ }
+
+ /**
+ * Returns the value of this map entry.
+ *
+ * @return the value.
+ */
+ public V getValue()
+ {
+ return e.getValue();
+ }
+
+ /**
+ * Computes the hash code of this map entry. The computation is
+ * described in the <code>Map</code> interface documentation.
+ *
+ * @return the hash code of this entry.
+ * @see Map#hashCode()
+ */
+ public int hashCode()
+ {
+ return e.hashCode();
+ }
+
+ /**
+ * Blocks the alteration of the value of this map entry. This method
+ * never returns, throwing an exception instead.
+ *
+ * @param value The new value.
+ * @throws UnsupportedOperationException as an unmodifiable map entry
+ * does not support the <code>setValue()</code> operation.
+ */
+ public V setValue(V value)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns a textual representation of the map entry.
+ *
+ * @return The map entry as a <code>String</code>.
+ */
+ public String toString()
+ {
+ return e.toString();
+ }
+ }
+
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = 7854390611657943733L;
+
+ /**
+ * Wrap a given set.
+ * @param s the set to wrap
+ */
+ UnmodifiableEntrySet(Set<Map.Entry<K,V>> s)
+ {
+ super(s);
+ }
+
+ // The iterator must return unmodifiable map entries.
+ public Iterator<Map.Entry<K,V>> iterator()
+ {
+ return new UnmodifiableIterator<Map.Entry<K,V>>(c.iterator())
+ {
+ /**
+ * Obtains the next element from the underlying set of
+ * map entries.
+ *
+ * @return the next element in the collection.
+ * @throws NoSuchElementException if there are no more elements.
+ */
+ public Map.Entry<K,V> next()
+ {
+ final Map.Entry<K,V> e = super.next();
+ return new UnmodifiableMapEntry<K,V>(e);
+ }
+ };
+ }
+
+ // The array returned is an array of UnmodifiableMapEntry instead of
+ // Map.Entry
+ public Object[] toArray()
+ {
+ Object[] mapEntryResult = super.toArray();
+ UnmodifiableMapEntry<K,V> result[] = null;
+
+ if (mapEntryResult != null)
+ {
+ result = (UnmodifiableMapEntry<K,V>[])
+ new UnmodifiableMapEntry[mapEntryResult.length];
+ for (int i = 0; i < mapEntryResult.length; ++i)
+ result[i] = new UnmodifiableMapEntry<K,V>((Map.Entry<K,V>)mapEntryResult[i]);
+ }
+ return result;
+ }
+
+ // The array returned is an array of UnmodifiableMapEntry instead of
+ // Map.Entry
+ public <S> S[] toArray(S[] array)
+ {
+ S[] result = super.toArray(array);
+
+ if (result != null)
+ for (int i = 0; i < result.length; i++)
+ array[i] =
+ (S) new UnmodifiableMapEntry<K,V>((Map.Entry<K,V>) result[i]);
+ return array;
+ }
+
+
+ } // class UnmodifiableEntrySet
+
+ /**
+ * Returns <code>true</code> if the object, o, is also an instance
+ * of <code>Map</code> with an equal set of map entries.
+ *
+ * @param o The object to compare.
+ * @return <code>true</code> if o is an equivalent map.
+ */
+ public boolean equals(Object o)
+ {
+ return m.equals(o);
+ }
+
+ /**
+ * Returns the value associated with the supplied key or
+ * null if no such mapping exists. An ambiguity can occur
+ * if null values are accepted by the underlying map.
+ * In this case, <code>containsKey()</code> can be used
+ * to separate the two possible cases of a null result.
+ *
+ * @param key The key to look up.
+ * @return the value associated with the key, or null if key not in map.
+ * @throws ClassCastException if the key is an inappropriate type.
+ * @throws NullPointerException if this map does not accept null keys.
+ * @see #containsKey(Object)
+ */
+ public V get(Object key)
+ {
+ return m.get(key);
+ }
+
+ /**
+ * Blocks the addition of a new entry to the underlying map.
+ * This method never returns, throwing an exception instead.
+ *
+ * @param key The new key.
+ * @param value The new value.
+ * @return the previous value of the key, or null if there was no mapping.
+ * @throws UnsupportedOperationException as an unmodifiable
+ * map does not support the <code>put()</code> operation.
+ */
+ public V put(K key, V value)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Computes the hash code for the underlying map, as the sum
+ * of the hash codes of all entries.
+ *
+ * @return The hash code of the underlying map.
+ * @see Map.Entry#hashCode()
+ */
+ public int hashCode()
+ {
+ return m.hashCode();
+ }
+
+ /**
+ * Returns <code>true</code> if the underlying map contains no entries.
+ *
+ * @return <code>true</code> if the map is empty.
+ */
+ public boolean isEmpty()
+ {
+ return m.isEmpty();
+ }
+
+ /**
+ * Returns a unmodifiable set view of the keys in the underlying map.
+ * The set is backed by the map, so that changes in one show up in the other.
+ * Modifications made while an iterator is in progress cause undefined
+ * behavior. These modifications are again limited to the values of
+ * the keys.
+ *
+ * @return the set view of all keys.
+ */
+ public Set<K> keySet()
+ {
+ if (keys == null)
+ keys = new UnmodifiableSet<K>(m.keySet());
+ return keys;
+ }
+
+ /**
+ * Blocks the addition of the entries in the supplied map.
+ * This method never returns, throwing an exception instead.
+ *
+ * @param m The map, the entries of which should be added
+ * to the underlying map.
+ * @throws UnsupportedOperationException as an unmodifiable
+ * map does not support the <code>putAll</code> operation.
+ */
+ public void putAll(Map<? extends K, ? extends V> m)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Blocks the removal of an entry from the map.
+ * This method never returns, throwing an exception instead.
+ *
+ * @param o The key of the entry to remove.
+ * @return The value the key was associated with, or null
+ * if no such mapping existed. Null is also returned
+ * if the removed entry had a null key.
+ * @throws UnsupportedOperationException as an unmodifiable
+ * map does not support the <code>remove</code> operation.
+ */
+ public V remove(Object o)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+
+ /**
+ * Returns the number of key-value mappings in the underlying map.
+ * If there are more than Integer.MAX_VALUE mappings, Integer.MAX_VALUE
+ * is returned.
+ *
+ * @return the number of mappings.
+ */
+ public int size()
+ {
+ return m.size();
+ }
+
+ /**
+ * Returns a textual representation of the map.
+ *
+ * @return The map in the form of a <code>String</code>.
+ */
+ public String toString()
+ {
+ return m.toString();
+ }
+
+ /**
+ * Returns a unmodifiable collection view of the values in the underlying map.
+ * The collection is backed by the map, so that changes in one show up in the other.
+ * Modifications made while an iterator is in progress cause undefined
+ * behavior. These modifications are again limited to the values of
+ * the keys.
+ *
+ * @return the collection view of all values.
+ */
+ public Collection<V> values()
+ {
+ if (values == null)
+ values = new UnmodifiableCollection<V>(m.values());
+ return values;
+ }
+ } // class UnmodifiableMap
+
+ /**
+ * Returns an unmodifiable view of the given set. This allows
+ * "read-only" access, although changes in the backing set show up
+ * in this view. Attempts to modify the set directly or via iterators
+ * will fail with {@link UnsupportedOperationException}.
+ * Although this view prevents changes to the structure of the set and its
+ * entries, the values referenced by the objects in the set can still be
+ * modified.
+ * <p>
+ *
+ * The returned Set implements Serializable, but can only be serialized if
+ * the set it wraps is likewise Serializable.
+ *
+ * @param s the set to wrap
+ * @return a read-only view of the set
+ * @see Serializable
+ */
+ public static <T> Set<T> unmodifiableSet(Set<? extends T> s)
+ {
+ return new UnmodifiableSet<T>(s);
+ }
+
+ /**
+ * The implementation of {@link #unmodifiableSet(Set)}. This class
+ * name is required for compatibility with Sun's JDK serializability.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static class UnmodifiableSet<T> extends UnmodifiableCollection<T>
+ implements Set<T>
+ {
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = -9215047833775013803L;
+
+ /**
+ * Wrap a given set.
+ * @param s the set to wrap
+ * @throws NullPointerException if s is null
+ */
+ UnmodifiableSet(Set<? extends T> s)
+ {
+ super(s);
+ }
+
+ /**
+ * Returns <code>true</code> if the object, o, is also an instance of
+ * <code>Set</code> of the same size and with the same entries.
+ *
+ * @return <code>true</code> if o is an equivalent set.
+ */
+ public boolean equals(Object o)
+ {
+ return c.equals(o);
+ }
+
+ /**
+ * Computes the hash code of this set, as the sum of the
+ * hash codes of all elements within the set.
+ *
+ * @return the hash code of the set.
+ */
+ public int hashCode()
+ {
+ return c.hashCode();
+ }
+ } // class UnmodifiableSet
+
+ /**
+ * Returns an unmodifiable view of the given sorted map. This allows
+ * "read-only" access, although changes in the backing map show up in this
+ * view. Attempts to modify the map directly, via subviews, via collection
+ * views, or iterators, will fail with {@link UnsupportedOperationException}.
+ * Although this view prevents changes to the structure of the map and its
+ * entries, the values referenced by the objects in the map can still be
+ * modified.
+ * <p>
+ *
+ * The returned SortedMap implements Serializable, but can only be
+ * serialized if the map it wraps is likewise Serializable.
+ *
+ * @param m the map to wrap
+ * @return a read-only view of the map
+ * @see Serializable
+ */
+ public static <K, V> SortedMap<K, V> unmodifiableSortedMap(SortedMap<K,
+ ? extends V> m)
+ {
+ return new UnmodifiableSortedMap<K, V>(m);
+ }
+
+ /**
+ * The implementation of {@link #unmodifiableSortedMap(SortedMap)}. This
+ * class name is required for compatibility with Sun's JDK serializability.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static class UnmodifiableSortedMap<K, V>
+ extends UnmodifiableMap<K, V>
+ implements SortedMap<K, V>
+ {
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = -8806743815996713206L;
+
+ /**
+ * The wrapped map; stored both here and in the superclass to avoid
+ * excessive casting.
+ * @serial the wrapped map
+ */
+ private final SortedMap<K, V> sm;
+
+ /**
+ * Wrap a given map.
+ * @param sm the map to wrap
+ * @throws NullPointerException if sm is null
+ */
+ UnmodifiableSortedMap(SortedMap<K, ? extends V> sm)
+ {
+ super(sm);
+ this.sm = (SortedMap<K,V>) sm;
+ }
+
+ /**
+ * Returns the comparator used in sorting the underlying map,
+ * or null if it is the keys' natural ordering.
+ *
+ * @return the sorting comparator.
+ */
+ public Comparator<? super K> comparator()
+ {
+ return sm.comparator();
+ }
+
+ /**
+ * Returns the first (lowest sorted) key in the map.
+ *
+ * @return the first key.
+ * @throws NoSuchElementException if this map is empty.
+ */
+ public K firstKey()
+ {
+ return sm.firstKey();
+ }
+
+ /**
+ * Returns a unmodifiable view of the portion of the map strictly less
+ * than toKey. The view is backed by the underlying map, so changes in
+ * one show up in the other. The submap supports all optional operations
+ * of the original. This operation is equivalent to
+ * <code>subMap(firstKey(), toKey)</code>.
+ * <p>
+ *
+ * The returned map throws an IllegalArgumentException any time a key is
+ * used which is out of the range of toKey. Note that the endpoint, toKey,
+ * is not included; if you want this value to be included, pass its successor
+ * object in to toKey. For example, for Integers, you could request
+ * <code>headMap(new Integer(limit.intValue() + 1))</code>.
+ *
+ * @param toKey the exclusive upper range of the submap.
+ * @return the submap.
+ * @throws ClassCastException if toKey is not comparable to the map contents.
+ * @throws IllegalArgumentException if this is a subMap, and toKey is out
+ * of range.
+ * @throws NullPointerException if toKey is null but the map does not allow
+ * null keys.
+ */
+ public SortedMap<K, V> headMap(K toKey)
+ {
+ return new UnmodifiableSortedMap<K, V>(sm.headMap(toKey));
+ }
+
+ /**
+ * Returns the last (highest sorted) key in the map.
+ *
+ * @return the last key.
+ * @throws NoSuchElementException if this map is empty.
+ */
+ public K lastKey()
+ {
+ return sm.lastKey();
+ }
+
+ /**
+ * Returns a unmodifiable view of the portion of the map greater than or
+ * equal to fromKey, and strictly less than toKey. The view is backed by
+ * the underlying map, so changes in one show up in the other. The submap
+ * supports all optional operations of the original.
+ * <p>
+ *
+ * The returned map throws an IllegalArgumentException any time a key is
+ * used which is out of the range of fromKey and toKey. Note that the
+ * lower endpoint is included, but the upper is not; if you want to
+ * change the inclusion or exclusion of an endpoint, pass its successor
+ * object in instead. For example, for Integers, you could request
+ * <code>subMap(new Integer(lowlimit.intValue() + 1),
+ * new Integer(highlimit.intValue() + 1))</code> to reverse
+ * the inclusiveness of both endpoints.
+ *
+ * @param fromKey the inclusive lower range of the submap.
+ * @param toKey the exclusive upper range of the submap.
+ * @return the submap.
+ * @throws ClassCastException if fromKey or toKey is not comparable to
+ * the map contents.
+ * @throws IllegalArgumentException if this is a subMap, and fromKey or
+ * toKey is out of range.
+ * @throws NullPointerException if fromKey or toKey is null but the map
+ * does not allow null keys.
+ */
+ public SortedMap<K, V> subMap(K fromKey, K toKey)
+ {
+ return new UnmodifiableSortedMap<K, V>(sm.subMap(fromKey, toKey));
+ }
+
+ /**
+ * Returns a unmodifiable view of the portion of the map greater than or
+ * equal to fromKey. The view is backed by the underlying map, so changes
+ * in one show up in the other. The submap supports all optional operations
+ * of the original.
+ * <p>
+ *
+ * The returned map throws an IllegalArgumentException any time a key is
+ * used which is out of the range of fromKey. Note that the endpoint, fromKey, is
+ * included; if you do not want this value to be included, pass its successor object in
+ * to fromKey. For example, for Integers, you could request
+ * <code>tailMap(new Integer(limit.intValue() + 1))</code>.
+ *
+ * @param fromKey the inclusive lower range of the submap
+ * @return the submap
+ * @throws ClassCastException if fromKey is not comparable to the map
+ * contents
+ * @throws IllegalArgumentException if this is a subMap, and fromKey is out
+ * of range
+ * @throws NullPointerException if fromKey is null but the map does not allow
+ * null keys
+ */
+ public SortedMap<K, V> tailMap(K fromKey)
+ {
+ return new UnmodifiableSortedMap<K, V>(sm.tailMap(fromKey));
+ }
+ } // class UnmodifiableSortedMap
+
+ /**
+ * Returns an unmodifiable view of the given sorted set. This allows
+ * "read-only" access, although changes in the backing set show up
+ * in this view. Attempts to modify the set directly, via subsets, or via
+ * iterators, will fail with {@link UnsupportedOperationException}.
+ * Although this view prevents changes to the structure of the set and its
+ * entries, the values referenced by the objects in the set can still be
+ * modified.
+ * <p>
+ *
+ * The returns SortedSet implements Serializable, but can only be
+ * serialized if the set it wraps is likewise Serializable.
+ *
+ * @param s the set to wrap
+ * @return a read-only view of the set
+ * @see Serializable
+ */
+ public static <T> SortedSet<T> unmodifiableSortedSet(SortedSet<T> s)
+ {
+ return new UnmodifiableSortedSet<T>(s);
+ }
+
+ /**
+ * The implementation of {@link #synchronizedSortedMap(SortedMap)}. This
+ * class name is required for compatibility with Sun's JDK serializability.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static class UnmodifiableSortedSet<T> extends UnmodifiableSet<T>
+ implements SortedSet<T>
+ {
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = -4929149591599911165L;
+
+ /**
+ * The wrapped set; stored both here and in the superclass to avoid
+ * excessive casting.
+ * @serial the wrapped set
+ */
+ private SortedSet<T> ss;
+
+ /**
+ * Wrap a given set.
+ * @param ss the set to wrap
+ * @throws NullPointerException if ss is null
+ */
+ UnmodifiableSortedSet(SortedSet<T> ss)
+ {
+ super(ss);
+ this.ss = ss;
+ }
+
+ /**
+ * Returns the comparator used in sorting the underlying set,
+ * or null if it is the elements' natural ordering.
+ *
+ * @return the sorting comparator
+ */
+ public Comparator<? super T> comparator()
+ {
+ return ss.comparator();
+ }
+
+ /**
+ * Returns the first (lowest sorted) element in the underlying
+ * set.
+ *
+ * @return the first element.
+ * @throws NoSuchElementException if the set is empty.
+ */
+ public T first()
+ {
+ return ss.first();
+ }
+
+ /**
+ * Returns a unmodifiable view of the portion of the set strictly
+ * less than toElement. The view is backed by the underlying set,
+ * so changes in one show up in the other. The subset supports
+ * all optional operations of the original. This operation
+ * is equivalent to <code>subSet(first(), toElement)</code>.
+ * <p>
+ *
+ * The returned set throws an IllegalArgumentException any time an element is
+ * used which is out of the range of toElement. Note that the endpoint, toElement,
+ * is not included; if you want this value included, pass its successor object in to
+ * toElement. For example, for Integers, you could request
+ * <code>headSet(new Integer(limit.intValue() + 1))</code>.
+ *
+ * @param toElement the exclusive upper range of the subset
+ * @return the subset.
+ * @throws ClassCastException if toElement is not comparable to the set
+ * contents.
+ * @throws IllegalArgumentException if this is a subSet, and toElement is out
+ * of range.
+ * @throws NullPointerException if toElement is null but the set does not
+ * allow null elements.
+ */
+ public SortedSet<T> headSet(T toElement)
+ {
+ return new UnmodifiableSortedSet<T>(ss.headSet(toElement));
+ }
+
+ /**
+ * Returns the last (highest sorted) element in the underlying
+ * set.
+ *
+ * @return the last element.
+ * @throws NoSuchElementException if the set is empty.
+ */
+ public T last()
+ {
+ return ss.last();
+ }
+
+ /**
+ * Returns a unmodifiable view of the portion of the set greater than or
+ * equal to fromElement, and strictly less than toElement. The view is backed by
+ * the underlying set, so changes in one show up in the other. The subset
+ * supports all optional operations of the original.
+ * <p>
+ *
+ * The returned set throws an IllegalArgumentException any time an element is
+ * used which is out of the range of fromElement and toElement. Note that the
+ * lower endpoint is included, but the upper is not; if you want to
+ * change the inclusion or exclusion of an endpoint, pass its successor
+ * object in instead. For example, for Integers, you can request
+ * <code>subSet(new Integer(lowlimit.intValue() + 1),
+ * new Integer(highlimit.intValue() + 1))</code> to reverse
+ * the inclusiveness of both endpoints.
+ *
+ * @param fromElement the inclusive lower range of the subset.
+ * @param toElement the exclusive upper range of the subset.
+ * @return the subset.
+ * @throws ClassCastException if fromElement or toElement is not comparable
+ * to the set contents.
+ * @throws IllegalArgumentException if this is a subSet, and fromElement or
+ * toElement is out of range.
+ * @throws NullPointerException if fromElement or toElement is null but the
+ * set does not allow null elements.
+ */
+ public SortedSet<T> subSet(T fromElement, T toElement)
+ {
+ return new UnmodifiableSortedSet<T>(ss.subSet(fromElement, toElement));
+ }
+
+ /**
+ * Returns a unmodifiable view of the portion of the set greater than or equal to
+ * fromElement. The view is backed by the underlying set, so changes in one show up
+ * in the other. The subset supports all optional operations of the original.
+ * <p>
+ *
+ * The returned set throws an IllegalArgumentException any time an element is
+ * used which is out of the range of fromElement. Note that the endpoint,
+ * fromElement, is included; if you do not want this value to be included, pass its
+ * successor object in to fromElement. For example, for Integers, you could request
+ * <code>tailSet(new Integer(limit.intValue() + 1))</code>.
+ *
+ * @param fromElement the inclusive lower range of the subset
+ * @return the subset.
+ * @throws ClassCastException if fromElement is not comparable to the set
+ * contents.
+ * @throws IllegalArgumentException if this is a subSet, and fromElement is
+ * out of range.
+ * @throws NullPointerException if fromElement is null but the set does not
+ * allow null elements.
+ */
+ public SortedSet<T> tailSet(T fromElement)
+ {
+ return new UnmodifiableSortedSet<T>(ss.tailSet(fromElement));
+ }
+ } // class UnmodifiableSortedSet
+
+ /**
+ * <p>
+ * Returns a dynamically typesafe view of the given collection,
+ * where any modification is first checked to ensure that the type
+ * of the new data is appropriate. Although the addition of
+ * generics and parametrically-typed collections prevents an
+ * incorrect type of element being added to a collection at
+ * compile-time, via static type checking, this can be overridden by
+ * casting. In contrast, wrapping the collection within a
+ * dynamically-typesafe wrapper, using this and associated methods,
+ * <emph>guarantees</emph> that the collection will only contain
+ * elements of an appropriate type (provided it only contains such
+ * at the type of wrapping, and all subsequent access is via the
+ * wrapper). This can be useful for debugging the cause of a
+ * <code>ClassCastException</code> caused by erroneous casting, or
+ * for protecting collections from corruption by external libraries.
+ * </p>
+ * <p>
+ * Since the collection might be a List or a Set, and those
+ * have incompatible equals and hashCode requirements, this relies
+ * on Object's implementation rather than passing those calls on to
+ * the wrapped collection. The returned Collection implements
+ * Serializable, but can only be serialized if the collection it
+ * wraps is likewise Serializable.
+ * </p>
+ *
+ * @param c the collection to wrap in a dynamically typesafe wrapper
+ * @param type the type of elements the collection should hold.
+ * @return a dynamically typesafe view of the collection.
+ * @see Serializable
+ * @since 1.5
+ */
+ public static <E> Collection<E> checkedCollection(Collection<E> c,
+ Class<E> type)
+ {
+ return new CheckedCollection<E>(c, type);
+ }
+
+ /**
+ * The implementation of {@link #checkedCollection(Collection,Class)}. This
+ * class name is required for compatibility with Sun's JDK serializability.
+ *
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+ private static class CheckedCollection<E>
+ implements Collection<E>, Serializable
+ {
+ /**
+ * Compatible with JDK 1.5.
+ */
+ private static final long serialVersionUID = 1578914078182001775L;
+
+ /**
+ * The wrapped collection. Package visible for use by subclasses.
+ * @serial the real collection
+ */
+ final Collection<E> c;
+
+ /**
+ * The type of the elements of this collection.
+ * @serial the element type.
+ */
+ final Class<E> type;
+
+ /**
+ * Wrap a given collection.
+ * @param c the collection to wrap
+ * @param type the type to wrap
+ * @throws NullPointerException if c is null
+ */
+ CheckedCollection(Collection<E> c, Class<E> type)
+ {
+ this.c = c;
+ this.type = type;
+ if (c == null)
+ throw new NullPointerException();
+ }
+
+ /**
+ * Adds the supplied object to the collection, on the condition that
+ * it is of the correct type.
+ *
+ * @param o the object to add.
+ * @return <code>true</code> if the collection was modified as a result
+ * of this action.
+ * @throws ClassCastException if the object is not of the correct type.
+ */
+ public boolean add(E o)
+ {
+ if (type.isInstance(o))
+ return c.add(o);
+ else
+ throw new ClassCastException("The element is of the incorrect type.");
+ }
+
+ /**
+ * Adds the elements of the specified collection to the backing collection,
+ * provided they are all of the correct type.
+ *
+ * @param coll the collection to add.
+ * @return <code>true</code> if the collection was modified as a result
+ * of this action.
+ * @throws ClassCastException if <code>c</code> contained elements of an
+ * incorrect type.
+ */
+ public boolean addAll(Collection<? extends E> coll)
+ {
+ Collection<E> typedColl = (Collection<E>) c;
+ final Iterator<E> it = typedColl.iterator();
+ while (it.hasNext())
+ {
+ final E element = it.next();
+ if (!type.isInstance(element))
+ throw new ClassCastException("A member of the collection is not of the correct type.");
+ }
+ return c.addAll(typedColl);
+ }
+
+ /**
+ * Removes all elements from the underlying collection.
+ */
+ public void clear()
+ {
+ c.clear();
+ }
+
+ /**
+ * Test whether the underlying collection contains a given object as one
+ * of its elements.
+ *
+ * @param o the element to look for.
+ * @return <code>true</code> if the underlying collection contains at least
+ * one element e such that
+ * <code>o == null ? e == null : o.equals(e)</code>.
+ * @throws ClassCastException if the type of o is not a valid type for the
+ * underlying collection.
+ * @throws NullPointerException if o is null and the underlying collection
+ * doesn't support null values.
+ */
+ public boolean contains(Object o)
+ {
+ return c.contains(o);
+ }
+
+ /**
+ * Test whether the underlying collection contains every element in a given
+ * collection.
+ *
+ * @param coll the collection to test for.
+ * @return <code>true</code> if for every element o in c, contains(o) would
+ * return <code>true</code>.
+ * @throws ClassCastException if the type of any element in c is not a
+ * valid type for the underlying collection.
+ * @throws NullPointerException if some element of c is null and the
+ * underlying collection does not support
+ * null values.
+ * @throws NullPointerException if c itself is null.
+ */
+ public boolean containsAll(Collection<?> coll)
+ {
+ return c.containsAll(coll);
+ }
+
+ /**
+ * Tests whether the underlying collection is empty, that is,
+ * if size() == 0.
+ *
+ * @return <code>true</code> if this collection contains no elements.
+ */
+ public boolean isEmpty()
+ {
+ return c.isEmpty();
+ }
+
+ /**
+ * Obtain an Iterator over the underlying collection, which maintains
+ * its checked nature.
+ *
+ * @return a Iterator over the elements of the underlying
+ * collection, in any order.
+ */
+ public Iterator<E> iterator()
+ {
+ return new CheckedIterator<E>(c.iterator(), type);
+ }
+
+ /**
+ * Removes the supplied object from the collection, if it exists.
+ *
+ * @param o The object to remove.
+ * @return <code>true</code> if the object was removed (i.e. the underlying
+ * collection returned 1 or more instances of o).
+ */
+ public boolean remove(Object o)
+ {
+ return c.remove(o);
+ }
+
+ /**
+ * Removes all objects in the supplied collection from the backing
+ * collection, if they exist within it.
+ *
+ * @param coll the collection of objects to remove.
+ * @return <code>true</code> if the collection was modified.
+ */
+ public boolean removeAll(Collection<?> coll)
+ {
+ return c.removeAll(coll);
+ }
+
+ /**
+ * Retains all objects specified by the supplied collection which exist
+ * within the backing collection, and removes all others.
+ *
+ * @param coll the collection of objects to retain.
+ * @return <code>true</code> if the collection was modified.
+ */
+ public boolean retainAll(Collection<?> coll)
+ {
+ return c.retainAll(coll);
+ }
+
+ /**
+ * Retrieves the number of elements in the underlying collection.
+ *
+ * @return the number of elements in the collection.
+ */
+ public int size()
+ {
+ return c.size();
+ }
+
+ /**
+ * Copy the current contents of the underlying collection into an array.
+ *
+ * @return an array of type Object[] with a length equal to the size of the
+ * underlying collection and containing the elements currently in
+ * the underlying collection, in any order.
+ */
+ public Object[] toArray()
+ {
+ return c.toArray();
+ }
+
+ /**
+ * <p>
+ * Copy the current contents of the underlying collection into an array. If
+ * the array passed as an argument has length less than the size of the
+ * underlying collection, an array of the same run-time type as a, with a
+ * length equal to the size of the underlying collection, is allocated
+ * using reflection.
+ * </p>
+ * <p>
+ * Otherwise, a itself is used. The elements of the underlying collection
+ * are copied into it, and if there is space in the array, the following
+ * element is set to null. The resultant array is returned.
+ * </p>
+ * <p>
+ * <emph>Note</emph>: The fact that the following element is set to null
+ * is only useful if it is known that this collection does not contain
+ * any null elements.
+ *
+ * @param a the array to copy this collection into.
+ * @return an array containing the elements currently in the underlying
+ * collection, in any order.
+ * @throws ArrayStoreException if the type of any element of the
+ * collection is not a subtype of the element type of a.
+ */
+ public <S> S[] toArray(S[] a)
+ {
+ return c.toArray(a);
+ }
+
+ /**
+ * A textual representation of the unmodifiable collection.
+ *
+ * @return The checked collection in the form of a <code>String</code>.
+ */
+ public String toString()
+ {
+ return c.toString();
+ }
+ } // class CheckedCollection
+
+ /**
+ * The implementation of the various iterator methods in the
+ * checked classes.
+ *
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+ private static class CheckedIterator<E>
+ implements Iterator<E>
+ {
+ /**
+ * The wrapped iterator.
+ */
+ private final Iterator<E> i;
+
+ /**
+ * The type of the elements of this collection.
+ * @serial the element type.
+ */
+ final Class<E> type;
+
+ /**
+ * Only trusted code creates a wrapper.
+ * @param i the wrapped iterator
+ * @param type the type of the elements within the checked list.
+ */
+ CheckedIterator(Iterator<E> i, Class<E> type)
+ {
+ this.i = i;
+ this.type = type;
+ }
+
+ /**
+ * Obtains the next element in the underlying collection.
+ *
+ * @return the next element in the collection.
+ * @throws NoSuchElementException if there are no more elements.
+ */
+ public E next()
+ {
+ return i.next();
+ }
+
+ /**
+ * Tests whether there are still elements to be retrieved from the
+ * underlying collection by <code>next()</code>. When this method
+ * returns <code>true</code>, an exception will not be thrown on calling
+ * <code>next()</code>.
+ *
+ * @return <code>true</code> if there is at least one more element in the
+ * underlying collection.
+ */
+ public boolean hasNext()
+ {
+ return i.hasNext();
+ }
+
+ /**
+ * Removes the next element from the collection.
+ */
+ public void remove()
+ {
+ i.remove();
+ }
+ } // class CheckedIterator
+
+ /**
+ * <p>
+ * Returns a dynamically typesafe view of the given list,
+ * where any modification is first checked to ensure that the type
+ * of the new data is appropriate. Although the addition of
+ * generics and parametrically-typed collections prevents an
+ * incorrect type of element being added to a collection at
+ * compile-time, via static type checking, this can be overridden by
+ * casting. In contrast, wrapping the collection within a
+ * dynamically-typesafe wrapper, using this and associated methods,
+ * <emph>guarantees</emph> that the collection will only contain
+ * elements of an appropriate type (provided it only contains such
+ * at the type of wrapping, and all subsequent access is via the
+ * wrapper). This can be useful for debugging the cause of a
+ * <code>ClassCastException</code> caused by erroneous casting, or
+ * for protecting collections from corruption by external libraries.
+ * </p>
+ * <p>
+ * The returned List implements Serializable, but can only be serialized if
+ * the list it wraps is likewise Serializable. In addition, if the wrapped
+ * list implements RandomAccess, this does too.
+ * </p>
+ *
+ * @param l the list to wrap
+ * @param type the type of the elements within the checked list.
+ * @return a dynamically typesafe view of the list
+ * @see Serializable
+ * @see RandomAccess
+ */
+ public static <E> List<E> checkedList(List<E> l, Class<E> type)
+ {
+ if (l instanceof RandomAccess)
+ return new CheckedRandomAccessList<E>(l, type);
+ return new CheckedList<E>(l, type);
+ }
+
+ /**
+ * The implementation of {@link #checkedList(List,Class)} for sequential
+ * lists. This class name is required for compatibility with Sun's JDK
+ * serializability.
+ *
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+ private static class CheckedList<E>
+ extends CheckedCollection<E>
+ implements List<E>
+ {
+ /**
+ * Compatible with JDK 1.5.
+ */
+ private static final long serialVersionUID = 65247728283967356L;
+
+ /**
+ * The wrapped list; stored both here and in the superclass to avoid
+ * excessive casting. Package visible for use by subclass.
+ * @serial the wrapped list
+ */
+ final List<E> list;
+
+ /**
+ * Wrap a given list.
+ * @param l the list to wrap
+ * @param type the type of the elements within the checked list.
+ * @throws NullPointerException if l is null
+ */
+ CheckedList(List<E> l, Class<E> type)
+ {
+ super(l, type);
+ list = l;
+ }
+
+ /**
+ * Adds the supplied element to the underlying list at the specified
+ * index, provided it is of the right type.
+ *
+ * @param index The index at which to place the new element.
+ * @param o the object to add.
+ * @throws ClassCastException if the type of the object is not a
+ * valid type for the underlying collection.
+ */
+ public void add(int index, E o)
+ {
+ if (type.isInstance(o))
+ list.add(index, o);
+ else
+ throw new ClassCastException("The object is of the wrong type.");
+ }
+
+ /**
+ * Adds the members of the supplied collection to the underlying
+ * collection at the specified index, provided they are all of the
+ * correct type.
+ *
+ * @param index the index at which to place the new element.
+ * @param coll the collections of objects to add.
+ * @throws ClassCastException if the type of any element in c is not a
+ * valid type for the underlying collection.
+ */
+ public boolean addAll(int index, Collection<? extends E> coll)
+ {
+ Collection<E> typedColl = (Collection<E>) coll;
+ final Iterator<E> it = typedColl.iterator();
+ while (it.hasNext())
+ {
+ if (!type.isInstance(it.next()))
+ throw new ClassCastException("A member of the collection is not of the correct type.");
+ }
+ return list.addAll(index, coll);
+ }
+
+ /**
+ * Returns <code>true</code> if the object, o, is an instance of
+ * <code>List</code> with the same size and elements
+ * as the underlying list.
+ *
+ * @param o The object to compare.
+ * @return <code>true</code> if o is equivalent to the underlying list.
+ */
+ public boolean equals(Object o)
+ {
+ return list.equals(o);
+ }
+
+ /**
+ * Retrieves the element at a given index in the underlying list.
+ *
+ * @param index the index of the element to be returned
+ * @return the element at the specified index in the underlying list
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ */
+ public E get(int index)
+ {
+ return list.get(index);
+ }
+
+ /**
+ * Computes the hash code for the underlying list.
+ * The exact computation is described in the documentation
+ * of the <code>List</code> interface.
+ *
+ * @return The hash code of the underlying list.
+ * @see List#hashCode()
+ */
+ public int hashCode()
+ {
+ return list.hashCode();
+ }
+
+ /**
+ * Obtain the first index at which a given object is to be found in the
+ * underlying list.
+ *
+ * @param o the object to search for
+ * @return the least integer n such that <code>o == null ? get(n) == null :
+ * o.equals(get(n))</code>, or -1 if there is no such index.
+ * @throws ClassCastException if the type of o is not a valid
+ * type for the underlying list.
+ * @throws NullPointerException if o is null and the underlying
+ * list does not support null values.
+ */
+ public int indexOf(Object o)
+ {
+ return list.indexOf(o);
+ }
+
+ /**
+ * Obtain the last index at which a given object is to be found in the
+ * underlying list.
+ *
+ * @return the greatest integer n such that
+ * <code>o == null ? get(n) == null : o.equals(get(n))</code>,
+ * or -1 if there is no such index.
+ * @throws ClassCastException if the type of o is not a valid
+ * type for the underlying list.
+ * @throws NullPointerException if o is null and the underlying
+ * list does not support null values.
+ */
+ public int lastIndexOf(Object o)
+ {
+ return list.lastIndexOf(o);
+ }
+
+ /**
+ * Obtains a list iterator over the underlying list, starting at the
+ * beginning and maintaining the checked nature of this list.
+ *
+ * @return a <code>CheckedListIterator</code> over the elements of the
+ * underlying list, in order, starting at the beginning.
+ */
+ public ListIterator<E> listIterator()
+ {
+ return new CheckedListIterator<E>(list.listIterator(), type);
+ }
+
+ /**
+ * Obtains a list iterator over the underlying list, starting at the
+ * specified index and maintaining the checked nature of this list. An
+ * initial call to <code>next()</code> will retrieve the element at the
+ * specified index, and an initial call to <code>previous()</code> will
+ * retrieve the element at index - 1.
+ *
+ * @param index the position, between 0 and size() inclusive, to begin the
+ * iteration from.
+ * @return a <code>CheckedListIterator</code> over the elements of the
+ * underlying list, in order, starting at the specified index.
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ */
+ public ListIterator<E> listIterator(int index)
+ {
+ return new CheckedListIterator<E>(list.listIterator(index), type);
+ }
+
+ /**
+ * Removes the element at the specified index.
+ *
+ * @param index The index of the element to remove.
+ * @return the removed element.
+ */
+ public E remove(int index)
+ {
+ return list.remove(index);
+ }
+
+ /**
+ * Replaces the element at the specified index in the underlying list
+ * with that supplied.
+ *
+ * @param index the index of the element to replace.
+ * @param o the new object to place at the specified index.
+ * @return the replaced element.
+ */
+ public E set(int index, E o)
+ {
+ return list.set(index, o);
+ }
+
+ /**
+ * Obtain a List view of a subsection of the underlying list, from
+ * fromIndex (inclusive) to toIndex (exclusive). If the two indices
+ * are equal, the sublist is empty. The returned list will be
+ * checked, like this list. Changes to the elements of the
+ * returned list will be reflected in the underlying list. The effect
+ * of structural modifications is undefined.
+ *
+ * @param fromIndex the index that the returned list should start from
+ * (inclusive).
+ * @param toIndex the index that the returned list should go
+ * to (exclusive).
+ * @return a List backed by a subsection of the underlying list.
+ * @throws IndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; size() || fromIndex &gt; toIndex.
+ */
+ public List<E> subList(int fromIndex, int toIndex)
+ {
+ return checkedList(list.subList(fromIndex, toIndex), type);
+ }
+ } // class CheckedList
+
+ /**
+ * The implementation of {@link #checkedList(List)} for random-access
+ * lists. This class name is required for compatibility with Sun's JDK
+ * serializability.
+ *
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+ private static final class CheckedRandomAccessList<E>
+ extends CheckedList<E>
+ implements RandomAccess
+ {
+ /**
+ * Compatible with JDK 1.5.
+ */
+ private static final long serialVersionUID = 1638200125423088369L;
+
+ /**
+ * Wrap a given list.
+ * @param l the list to wrap
+ * @param type the type of the elements within the checked list.
+ * @throws NullPointerException if l is null
+ */
+ CheckedRandomAccessList(List<E> l, Class<E> type)
+ {
+ super(l, type);
+ }
+ } // class CheckedRandomAccessList
+
+ /**
+ * The implementation of {@link CheckedList#listIterator()}.
+ *
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+ private static final class CheckedListIterator<E>
+ extends CheckedIterator<E>
+ implements ListIterator<E>
+ {
+ /**
+ * The wrapped iterator, stored both here and in the superclass to
+ * avoid excessive casting.
+ */
+ private final ListIterator<E> li;
+
+ /**
+ * Only trusted code creates a wrapper.
+ * @param li the wrapped iterator
+ */
+ CheckedListIterator(ListIterator<E> li, Class<E> type)
+ {
+ super(li, type);
+ this.li = li;
+ }
+
+ /**
+ * Adds the supplied object at the current iterator position, provided
+ * it is of the correct type.
+ *
+ * @param o the object to add.
+ * @throws ClassCastException if the type of the object is not a
+ * valid type for the underlying collection.
+ */
+ public void add(E o)
+ {
+ if (type.isInstance(o))
+ li.add(o);
+ else
+ throw new ClassCastException("The object is of the wrong type.");
+ }
+
+ /**
+ * Tests whether there are still elements to be retrieved from the
+ * underlying collection by <code>previous()</code>. When this method
+ * returns <code>true</code>, an exception will not be thrown on calling
+ * <code>previous()</code>.
+ *
+ * @return <code>true</code> if there is at least one more element prior
+ * to the current position in the underlying list.
+ */
+ public boolean hasPrevious()
+ {
+ return li.hasPrevious();
+ }
+
+ /**
+ * Find the index of the element that would be returned by a call to next.
+ * If <code>hasNext()</code> returns <code>false</code>, this returns the
+ * list size.
+ *
+ * @return the index of the element that would be returned by
+ * <code>next()</code>.
+ */
+ public int nextIndex()
+ {
+ return li.nextIndex();
+ }
+
+ /**
+ * Obtains the previous element in the underlying list.
+ *
+ * @return the previous element in the list.
+ * @throws NoSuchElementException if there are no more prior elements.
+ */
+ public E previous()
+ {
+ return li.previous();
+ }
+
+ /**
+ * Find the index of the element that would be returned by a call to
+ * previous. If <code>hasPrevious()</code> returns <code>false</code>,
+ * this returns -1.
+ *
+ * @return the index of the element that would be returned by
+ * <code>previous()</code>.
+ */
+ public int previousIndex()
+ {
+ return li.previousIndex();
+ }
+
+ /**
+ * Sets the next element to that supplied, provided that it is of the
+ * correct type.
+ *
+ * @param o The new object to replace the existing one.
+ * @throws ClassCastException if the type of the object is not a
+ * valid type for the underlying collection.
+ */
+ public void set(E o)
+ {
+ if (type.isInstance(o))
+ li.set(o);
+ else
+ throw new ClassCastException("The object is of the wrong type.");
+ }
+ } // class CheckedListIterator
+
+ /**
+ * <p>
+ * Returns a dynamically typesafe view of the given map,
+ * where any modification is first checked to ensure that the type
+ * of the new data is appropriate. Although the addition of
+ * generics and parametrically-typed collections prevents an
+ * incorrect type of element being added to a collection at
+ * compile-time, via static type checking, this can be overridden by
+ * casting. In contrast, wrapping the collection within a
+ * dynamically-typesafe wrapper, using this and associated methods,
+ * <emph>guarantees</emph> that the collection will only contain
+ * elements of an appropriate type (provided it only contains such
+ * at the type of wrapping, and all subsequent access is via the
+ * wrapper). This can be useful for debugging the cause of a
+ * <code>ClassCastException</code> caused by erroneous casting, or
+ * for protecting collections from corruption by external libraries.
+ * </p>
+ * <p>
+ * The returned Map implements Serializable, but can only be serialized if
+ * the map it wraps is likewise Serializable.
+ * </p>
+ *
+ * @param m the map to wrap
+ * @param keyType the dynamic type of the map's keys.
+ * @param valueType the dynamic type of the map's values.
+ * @return a dynamically typesafe view of the map
+ * @see Serializable
+ */
+ public static <K, V> Map<K, V> checkedMap(Map<K, V> m, Class<K> keyType,
+ Class<V> valueType)
+ {
+ return new CheckedMap<K, V>(m, keyType, valueType);
+ }
+
+ /**
+ * The implementation of {@link #checkedMap(Map)}. This
+ * class name is required for compatibility with Sun's JDK serializability.
+ *
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+ private static class CheckedMap<K, V>
+ implements Map<K, V>, Serializable
+ {
+ /**
+ * Compatible with JDK 1.5.
+ */
+ private static final long serialVersionUID = 5742860141034234728L;
+
+ /**
+ * The wrapped map.
+ * @serial the real map
+ */
+ private final Map<K, V> m;
+
+ /**
+ * The type of the map's keys.
+ * @serial the key type.
+ */
+ final Class<K> keyType;
+
+ /**
+ * The type of the map's values.
+ * @serial the value type.
+ */
+ final Class<V> valueType;
+
+ /**
+ * Cache the entry set.
+ */
+ private transient Set<Map.Entry<K, V>> entries;
+
+ /**
+ * Cache the key set.
+ */
+ private transient Set<K> keys;
+
+ /**
+ * Cache the value collection.
+ */
+ private transient Collection<V> values;
+
+ /**
+ * Wrap a given map.
+ * @param m the map to wrap
+ * @param keyType the dynamic type of the map's keys.
+ * @param valueType the dynamic type of the map's values.
+ * @throws NullPointerException if m is null
+ */
+ CheckedMap(Map<K, V> m, Class<K> keyType, Class<V> valueType)
+ {
+ this.m = m;
+ this.keyType = keyType;
+ this.valueType = valueType;
+ if (m == null)
+ throw new NullPointerException();
+ }
+
+ /**
+ * Clears all pairs from the map.
+ */
+ public void clear()
+ {
+ m.clear();
+ }
+
+ /**
+ * Returns <code>true</code> if the underlying map contains a mapping for
+ * the given key.
+ *
+ * @param key the key to search for
+ * @return <code>true</code> if the map contains the key
+ * @throws ClassCastException if the key is of an inappropriate type
+ * @throws NullPointerException if key is <code>null</code> but the map
+ * does not permit null keys
+ */
+ public boolean containsKey(Object key)
+ {
+ return m.containsKey(key);
+ }
+
+ /**
+ * Returns <code>true</code> if the underlying map contains at least one
+ * mapping with the given value. In other words, it returns
+ * <code>true</code> if a value v exists where
+ * <code>(value == null ? v == null : value.equals(v))</code>.
+ * This usually requires linear time.
+ *
+ * @param value the value to search for
+ * @return <code>true</code> if the map contains the value
+ * @throws ClassCastException if the type of the value is not a valid type
+ * for this map.
+ * @throws NullPointerException if the value is null and the map doesn't
+ * support null values.
+ */
+ public boolean containsValue(Object value)
+ {
+ return m.containsValue(value);
+ }
+
+ /**
+ * <p>
+ * Returns a checked set view of the entries in the underlying map.
+ * Each element in the set is a unmodifiable variant of
+ * <code>Map.Entry</code>.
+ * </p>
+ * <p>
+ * The set is backed by the map, so that changes in one show up in the
+ * other. Modifications made while an iterator is in progress cause
+ * undefined behavior.
+ * </p>
+ *
+ * @return the checked set view of all mapping entries.
+ * @see Map.Entry
+ */
+ public Set<Map.Entry<K, V>> entrySet()
+ {
+ if (entries == null)
+ {
+ Class<Map.Entry<K,V>> klass =
+ (Class<Map.Entry<K,V>>) (Class) Map.Entry.class;
+ entries = new CheckedEntrySet<Map.Entry<K,V>,K,V>(m.entrySet(),
+ klass,
+ keyType,
+ valueType);
+ }
+ return entries;
+ }
+
+ /**
+ * The implementation of {@link CheckedMap#entrySet()}. This class
+ * is <emph>not</emph> serializable.
+ *
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+ private static final class CheckedEntrySet<E,SK,SV>
+ extends CheckedSet<E>
+ {
+ /**
+ * The type of the map's keys.
+ * @serial the key type.
+ */
+ private final Class<SK> keyType;
+
+ /**
+ * The type of the map's values.
+ * @serial the value type.
+ */
+ private final Class<SV> valueType;
+
+ /**
+ * Wrap a given set of map entries.
+ *
+ * @param s the set to wrap.
+ * @param type the type of the set's entries.
+ * @param keyType the type of the map's keys.
+ * @param valueType the type of the map's values.
+ */
+ CheckedEntrySet(Set<E> s, Class<E> type, Class<SK> keyType,
+ Class<SV> valueType)
+ {
+ super(s, type);
+ this.keyType = keyType;
+ this.valueType = valueType;
+ }
+
+ // The iterator must return checked map entries.
+ public Iterator<E> iterator()
+ {
+ return new CheckedIterator<E>(c.iterator(), type)
+ {
+ /**
+ * Obtains the next element from the underlying set of
+ * map entries.
+ *
+ * @return the next element in the collection.
+ * @throws NoSuchElementException if there are no more elements.
+ */
+ public E next()
+ {
+ final Map.Entry e = (Map.Entry) super.next();
+ return (E) new Map.Entry()
+ {
+ /**
+ * Returns <code>true</code> if the object, o, is also a map
+ * entry with an identical key and value.
+ *
+ * @param o the object to compare.
+ * @return <code>true</code> if o is an equivalent map entry.
+ */
+ public boolean equals(Object o)
+ {
+ return e.equals(o);
+ }
+
+ /**
+ * Returns the key of this map entry.
+ *
+ * @return the key.
+ */
+ public Object getKey()
+ {
+ return e.getKey();
+ }
+
+ /**
+ * Returns the value of this map entry.
+ *
+ * @return the value.
+ */
+ public Object getValue()
+ {
+ return e.getValue();
+ }
+
+ /**
+ * Computes the hash code of this map entry.
+ * The computation is described in the <code>Map</code>
+ * interface documentation.
+ *
+ * @return the hash code of this entry.
+ * @see Map#hashCode()
+ */
+ public int hashCode()
+ {
+ return e.hashCode();
+ }
+
+ /**
+ * Sets the value of this map entry, provided it is of the
+ * right type.
+ *
+ * @param value The new value.
+ * @throws ClassCastException if the type of the value is not
+ * a valid type for the underlying
+ * map.
+ */
+ public Object setValue(Object value)
+ {
+ if (valueType.isInstance(value))
+ return e.setValue(value);
+ else
+ throw new ClassCastException("The value is of the wrong type.");
+ }
+
+ /**
+ * Returns a textual representation of the map entry.
+ *
+ * @return The map entry as a <code>String</code>.
+ */
+ public String toString()
+ {
+ return e.toString();
+ }
+ };
+ }
+ };
+ }
+ } // class CheckedEntrySet
+
+ /**
+ * Returns <code>true</code> if the object, o, is also an instance
+ * of <code>Map</code> with an equal set of map entries.
+ *
+ * @param o The object to compare.
+ * @return <code>true</code> if o is an equivalent map.
+ */
+ public boolean equals(Object o)
+ {
+ return m.equals(o);
+ }
+
+ /**
+ * Returns the value associated with the supplied key or
+ * null if no such mapping exists. An ambiguity can occur
+ * if null values are accepted by the underlying map.
+ * In this case, <code>containsKey()</code> can be used
+ * to separate the two possible cases of a null result.
+ *
+ * @param key The key to look up.
+ * @return the value associated with the key, or null if key not in map.
+ * @throws ClassCastException if the key is an inappropriate type.
+ * @throws NullPointerException if this map does not accept null keys.
+ * @see #containsKey(Object)
+ */
+ public V get(Object key)
+ {
+ return m.get(key);
+ }
+
+ /**
+ * Adds a new pair to the map, provided both the key and the value are
+ * of the correct types.
+ *
+ * @param key The new key.
+ * @param value The new value.
+ * @return the previous value of the key, or null if there was no mapping.
+ * @throws ClassCastException if the type of the key or the value is
+ * not a valid type for the underlying map.
+ */
+ public V put(K key, V value)
+ {
+ if (keyType.isInstance(key))
+ {
+ if (valueType.isInstance(value))
+ return m.put(key,value);
+ else
+ throw new ClassCastException("The value is of the wrong type.");
+ }
+ throw new ClassCastException("The key is of the wrong type.");
+ }
+
+ /**
+ * Computes the hash code for the underlying map, as the sum
+ * of the hash codes of all entries.
+ *
+ * @return The hash code of the underlying map.
+ * @see Map.Entry#hashCode()
+ */
+ public int hashCode()
+ {
+ return m.hashCode();
+ }
+
+ /**
+ * Returns <code>true</code> if the underlying map contains no entries.
+ *
+ * @return <code>true</code> if the map is empty.
+ */
+ public boolean isEmpty()
+ {
+ return m.isEmpty();
+ }
+
+ /**
+ * <p>
+ * Returns a checked set view of the keys in the underlying map.
+ * The set is backed by the map, so that changes in one show up in the
+ * other.
+ * </p>
+ * <p>
+ * Modifications made while an iterator is in progress cause undefined
+ * behavior. These modifications are again limited to the values of
+ * the keys.
+ * </p>
+ *
+ * @return the set view of all keys.
+ */
+ public Set<K> keySet()
+ {
+ if (keys == null)
+ keys = new CheckedSet<K>(m.keySet(), keyType);
+ return keys;
+ }
+
+ /**
+ * Adds all pairs within the supplied map to the underlying map,
+ * provided they are all have the correct key and value types.
+ *
+ * @param map the map, the entries of which should be added
+ * to the underlying map.
+ * @throws ClassCastException if the type of a key or value is
+ * not a valid type for the underlying map.
+ */
+ public void putAll(Map<? extends K, ? extends V> map)
+ {
+ Map<K,V> typedMap = (Map<K,V>) map;
+ final Iterator<Map.Entry<K,V>> it = typedMap.entrySet().iterator();
+ while (it.hasNext())
+ {
+ final Map.Entry<K,V> entry = it.next();
+ if (!keyType.isInstance(entry.getKey()))
+ throw new ClassCastException("A key is of the wrong type.");
+ if (!valueType.isInstance(entry.getValue()))
+ throw new ClassCastException("A value is of the wrong type.");
+ }
+ m.putAll(typedMap);
+ }
+
+ /**
+ * Removes a pair from the map.
+ *
+ * @param o The key of the entry to remove.
+ * @return The value the key was associated with, or null
+ * if no such mapping existed. Null is also returned
+ * if the removed entry had a null key.
+ * @throws UnsupportedOperationException as an unmodifiable
+ * map does not support the <code>remove</code> operation.
+ */
+ public V remove(Object o)
+ {
+ return m.remove(o);
+ }
+
+
+ /**
+ * Returns the number of key-value mappings in the underlying map.
+ * If there are more than Integer.MAX_VALUE mappings, Integer.MAX_VALUE
+ * is returned.
+ *
+ * @return the number of mappings.
+ */
+ public int size()
+ {
+ return m.size();
+ }
+
+ /**
+ * Returns a textual representation of the map.
+ *
+ * @return The map in the form of a <code>String</code>.
+ */
+ public String toString()
+ {
+ return m.toString();
+ }
+
+ /**
+ * <p>
+ * Returns a unmodifiable collection view of the values in the underlying
+ * map. The collection is backed by the map, so that changes in one show
+ * up in the other.
+ * </p>
+ * <p>
+ * Modifications made while an iterator is in progress cause undefined
+ * behavior. These modifications are again limited to the values of
+ * the keys.
+ * </p>
+ *
+ * @return the collection view of all values.
+ */
+ public Collection<V> values()
+ {
+ if (values == null)
+ values = new CheckedCollection<V>(m.values(), valueType);
+ return values;
+ }
+ } // class CheckedMap
+
+ /**
+ * <p>
+ * Returns a dynamically typesafe view of the given set,
+ * where any modification is first checked to ensure that the type
+ * of the new data is appropriate. Although the addition of
+ * generics and parametrically-typed collections prevents an
+ * incorrect type of element being added to a collection at
+ * compile-time, via static type checking, this can be overridden by
+ * casting. In contrast, wrapping the collection within a
+ * dynamically-typesafe wrapper, using this and associated methods,
+ * <emph>guarantees</emph> that the collection will only contain
+ * elements of an appropriate type (provided it only contains such
+ * at the type of wrapping, and all subsequent access is via the
+ * wrapper). This can be useful for debugging the cause of a
+ * <code>ClassCastException</code> caused by erroneous casting, or
+ * for protecting collections from corruption by external libraries.
+ * </p>
+ * <p>
+ * The returned Set implements Serializable, but can only be serialized if
+ * the set it wraps is likewise Serializable.
+ * </p>
+ *
+ * @param s the set to wrap.
+ * @param type the type of the elements within the checked list.
+ * @return a dynamically typesafe view of the set
+ * @see Serializable
+ */
+ public static <E> Set<E> checkedSet(Set<E> s, Class<E> type)
+ {
+ return new CheckedSet<E>(s, type);
+ }
+
+ /**
+ * The implementation of {@link #checkedSet(Set)}. This class
+ * name is required for compatibility with Sun's JDK serializability.
+ *
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+ private static class CheckedSet<E>
+ extends CheckedCollection<E>
+ implements Set<E>
+ {
+ /**
+ * Compatible with JDK 1.5.
+ */
+ private static final long serialVersionUID = 4694047833775013803L;
+
+ /**
+ * Wrap a given set.
+ *
+ * @param s the set to wrap
+ * @throws NullPointerException if s is null
+ */
+ CheckedSet(Set<E> s, Class<E> type)
+ {
+ super(s, type);
+ }
+
+ /**
+ * Returns <code>true</code> if the object, o, is also an instance of
+ * <code>Set</code> of the same size and with the same entries.
+ *
+ * @return <code>true</code> if o is an equivalent set.
+ */
+ public boolean equals(Object o)
+ {
+ return c.equals(o);
+ }
+
+ /**
+ * Computes the hash code of this set, as the sum of the
+ * hash codes of all elements within the set.
+ *
+ * @return the hash code of the set.
+ */
+ public int hashCode()
+ {
+ return c.hashCode();
+ }
+ } // class CheckedSet
+
+ /**
+ * <p>
+ * Returns a dynamically typesafe view of the given sorted map,
+ * where any modification is first checked to ensure that the type
+ * of the new data is appropriate. Although the addition of
+ * generics and parametrically-typed collections prevents an
+ * incorrect type of element being added to a collection at
+ * compile-time, via static type checking, this can be overridden by
+ * casting. In contrast, wrapping the collection within a
+ * dynamically-typesafe wrapper, using this and associated methods,
+ * <emph>guarantees</emph> that the collection will only contain
+ * elements of an appropriate type (provided it only contains such
+ * at the type of wrapping, and all subsequent access is via the
+ * wrapper). This can be useful for debugging the cause of a
+ * <code>ClassCastException</code> caused by erroneous casting, or
+ * for protecting collections from corruption by external libraries.
+ * </p>
+ * <p>
+ * The returned SortedMap implements Serializable, but can only be
+ * serialized if the map it wraps is likewise Serializable.
+ * </p>
+ *
+ * @param m the map to wrap.
+ * @param keyType the dynamic type of the map's keys.
+ * @param valueType the dynamic type of the map's values.
+ * @return a dynamically typesafe view of the map
+ * @see Serializable
+ */
+ public static <K, V> SortedMap<K, V> checkedSortedMap(SortedMap<K, V> m,
+ Class<K> keyType,
+ Class<V> valueType)
+ {
+ return new CheckedSortedMap<K, V>(m, keyType, valueType);
+ }
+
+ /**
+ * The implementation of {@link #checkedSortedMap(SortedMap,Class,Class)}.
+ * This class name is required for compatibility with Sun's JDK
+ * serializability.
+ *
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ */
+ private static class CheckedSortedMap<K, V>
+ extends CheckedMap<K, V>
+ implements SortedMap<K, V>
+ {
+ /**
+ * Compatible with JDK 1.5.
+ */
+ private static final long serialVersionUID = 1599671320688067438L;
+
+ /**
+ * The wrapped map; stored both here and in the superclass to avoid
+ * excessive casting.
+ * @serial the wrapped map
+ */
+ private final SortedMap<K, V> sm;
+
+ /**
+ * Wrap a given map.
+ *
+ * @param sm the map to wrap
+ * @param keyType the dynamic type of the map's keys.
+ * @param valueType the dynamic type of the map's values.
+ * @throws NullPointerException if sm is null
+ */
+ CheckedSortedMap(SortedMap<K, V> sm, Class<K> keyType, Class<V> valueType)
+ {
+ super(sm, keyType, valueType);
+ this.sm = sm;
+ }
+
+ /**
+ * Returns the comparator used in sorting the underlying map,
+ * or null if it is the keys' natural ordering.
+ *
+ * @return the sorting comparator.
+ */
+ public Comparator<? super K> comparator()
+ {
+ return sm.comparator();
+ }
+
+ /**
+ * Returns the first (lowest sorted) key in the map.
+ *
+ * @return the first key.
+ * @throws NoSuchElementException if this map is empty.
+ */
+ public K firstKey()
+ {
+ return sm.firstKey();
+ }
+
+ /**
+ * <p>
+ * Returns a checked view of the portion of the map strictly less
+ * than toKey. The view is backed by the underlying map, so changes in
+ * one show up in the other. The submap supports all optional operations
+ * of the original. This operation is equivalent to
+ * <code>subMap(firstKey(), toKey)</code>.
+ * </p>
+ * <p>
+ * The returned map throws an IllegalArgumentException any time a key is
+ * used which is out of the range of toKey. Note that the endpoint, toKey,
+ * is not included; if you want this value to be included, pass its
+ * successor object in to toKey. For example, for Integers, you could
+ * request <code>headMap(new Integer(limit.intValue() + 1))</code>.
+ * </p>
+ *
+ * @param toKey the exclusive upper range of the submap.
+ * @return the submap.
+ * @throws ClassCastException if toKey is not comparable to the map
+ * contents.
+ * @throws IllegalArgumentException if this is a subMap, and toKey is out
+ * of range.
+ * @throws NullPointerException if toKey is null but the map does not allow
+ * null keys.
+ */
+ public SortedMap<K, V> headMap(K toKey)
+ {
+ return new CheckedSortedMap<K, V>(sm.headMap(toKey), keyType, valueType);
+ }
+
+ /**
+ * Returns the last (highest sorted) key in the map.
+ *
+ * @return the last key.
+ * @throws NoSuchElementException if this map is empty.
+ */
+ public K lastKey()
+ {
+ return sm.lastKey();
+ }
+
+ /**
+ * <p>
+ * Returns a checked view of the portion of the map greater than or
+ * equal to fromKey, and strictly less than toKey. The view is backed by
+ * the underlying map, so changes in one show up in the other. The submap
+ * supports all optional operations of the original.
+ * </p>
+ * <p>
+ * The returned map throws an IllegalArgumentException any time a key is
+ * used which is out of the range of fromKey and toKey. Note that the
+ * lower endpoint is included, but the upper is not; if you want to
+ * change the inclusion or exclusion of an endpoint, pass its successor
+ * object in instead. For example, for Integers, you could request
+ * <code>subMap(new Integer(lowlimit.intValue() + 1),
+ * new Integer(highlimit.intValue() + 1))</code> to reverse
+ * the inclusiveness of both endpoints.
+ * </p>
+ *
+ * @param fromKey the inclusive lower range of the submap.
+ * @param toKey the exclusive upper range of the submap.
+ * @return the submap.
+ * @throws ClassCastException if fromKey or toKey is not comparable to
+ * the map contents.
+ * @throws IllegalArgumentException if this is a subMap, and fromKey or
+ * toKey is out of range.
+ * @throws NullPointerException if fromKey or toKey is null but the map
+ * does not allow null keys.
+ */
+ public SortedMap<K, V> subMap(K fromKey, K toKey)
+ {
+ return new CheckedSortedMap<K, V>(sm.subMap(fromKey, toKey), keyType,
+ valueType);
+ }
+
+ /**
+ * <p>
+ * Returns a checked view of the portion of the map greater than or
+ * equal to fromKey. The view is backed by the underlying map, so changes
+ * in one show up in the other. The submap supports all optional operations
+ * of the original.
+ * </p>
+ * <p>
+ * The returned map throws an IllegalArgumentException any time a key is
+ * used which is out of the range of fromKey. Note that the endpoint,
+ * fromKey, is included; if you do not want this value to be included,
+ * pass its successor object in to fromKey. For example, for Integers,
+ * you could request
+ * <code>tailMap(new Integer(limit.intValue() + 1))</code>.
+ * </p>
+ *
+ * @param fromKey the inclusive lower range of the submap
+ * @return the submap
+ * @throws ClassCastException if fromKey is not comparable to the map
+ * contents
+ * @throws IllegalArgumentException if this is a subMap, and fromKey is out
+ * of range
+ * @throws NullPointerException if fromKey is null but the map does not
+ * allow null keys
+ */
+ public SortedMap<K, V> tailMap(K fromKey)
+ {
+ return new CheckedSortedMap<K, V>(sm.tailMap(fromKey), keyType,
+ valueType);
+ }
+ } // class CheckedSortedMap
+
+ /**
+ * <p>
+ * Returns a dynamically typesafe view of the given sorted set,
+ * where any modification is first checked to ensure that the type
+ * of the new data is appropriate. Although the addition of
+ * generics and parametrically-typed collections prevents an
+ * incorrect type of element being added to a collection at
+ * compile-time, via static type checking, this can be overridden by
+ * casting. In contrast, wrapping the collection within a
+ * dynamically-typesafe wrapper, using this and associated methods,
+ * <emph>guarantees</emph> that the collection will only contain
+ * elements of an appropriate type (provided it only contains such
+ * at the type of wrapping, and all subsequent access is via the
+ * wrapper). This can be useful for debugging the cause of a
+ * <code>ClassCastException</code> caused by erroneous casting, or
+ * for protecting collections from corruption by external libraries.
+ * </p>
+ * <p>
+ * The returned SortedSet implements Serializable, but can only be
+ * serialized if the set it wraps is likewise Serializable.
+ * </p>
+ *
+ * @param s the set to wrap.
+ * @param type the type of the set's elements.
+ * @return a dynamically typesafe view of the set
+ * @see Serializable
+ */
+ public static <E> SortedSet<E> checkedSortedSet(SortedSet<E> s,
+ Class<E> type)
+ {
+ return new CheckedSortedSet<E>(s, type);
+ }
+
+ /**
+ * The implementation of {@link #checkedSortedSet(SortedSet,Class)}. This
+ * class name is required for compatibility with Sun's JDK serializability.
+ *
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+ private static class CheckedSortedSet<E>
+ extends CheckedSet<E>
+ implements SortedSet<E>
+ {
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = 1599911165492914959L;
+
+ /**
+ * The wrapped set; stored both here and in the superclass to avoid
+ * excessive casting.
+ *
+ * @serial the wrapped set
+ */
+ private SortedSet<E> ss;
+
+ /**
+ * Wrap a given set.
+ *
+ * @param ss the set to wrap.
+ * @param type the type of the set's elements.
+ * @throws NullPointerException if ss is null
+ */
+ CheckedSortedSet(SortedSet<E> ss, Class<E> type)
+ {
+ super(ss, type);
+ this.ss = ss;
+ }
+
+ /**
+ * Returns the comparator used in sorting the underlying set,
+ * or null if it is the elements' natural ordering.
+ *
+ * @return the sorting comparator
+ */
+ public Comparator<? super E> comparator()
+ {
+ return ss.comparator();
+ }
+
+ /**
+ * Returns the first (lowest sorted) element in the underlying
+ * set.
+ *
+ * @return the first element.
+ * @throws NoSuchElementException if the set is empty.
+ */
+ public E first()
+ {
+ return ss.first();
+ }
+
+ /**
+ * <p>
+ * Returns a checked view of the portion of the set strictly
+ * less than toElement. The view is backed by the underlying set,
+ * so changes in one show up in the other. The subset supports
+ * all optional operations of the original. This operation
+ * is equivalent to <code>subSet(first(), toElement)</code>.
+ * </p>
+ * <p>
+ * The returned set throws an IllegalArgumentException any time an
+ * element is used which is out of the range of toElement. Note that
+ * the endpoint, toElement, is not included; if you want this value
+ * included, pass its successor object in to toElement. For example,
+ * for Integers, you could request
+ * <code>headSet(new Integer(limit.intValue() + 1))</code>.
+ * </p>
+ *
+ * @param toElement the exclusive upper range of the subset
+ * @return the subset.
+ * @throws ClassCastException if toElement is not comparable to the set
+ * contents.
+ * @throws IllegalArgumentException if this is a subSet, and toElement is
+ * out of range.
+ * @throws NullPointerException if toElement is null but the set does not
+ * allow null elements.
+ */
+ public SortedSet<E> headSet(E toElement)
+ {
+ return new CheckedSortedSet<E>(ss.headSet(toElement), type);
+ }
+
+ /**
+ * Returns the last (highest sorted) element in the underlying
+ * set.
+ *
+ * @return the last element.
+ * @throws NoSuchElementException if the set is empty.
+ */
+ public E last()
+ {
+ return ss.last();
+ }
+
+ /**
+ * <p>
+ * Returns a checked view of the portion of the set greater than or
+ * equal to fromElement, and strictly less than toElement. The view is
+ * backed by the underlying set, so changes in one show up in the other.
+ * The subset supports all optional operations of the original.
+ * </p>
+ * <p>
+ * The returned set throws an IllegalArgumentException any time an
+ * element is used which is out of the range of fromElement and toElement.
+ * Note that the lower endpoint is included, but the upper is not; if you
+ * want to change the inclusion or exclusion of an endpoint, pass its
+ * successor object in instead. For example, for Integers, you can request
+ * <code>subSet(new Integer(lowlimit.intValue() + 1),
+ * new Integer(highlimit.intValue() + 1))</code> to reverse
+ * the inclusiveness of both endpoints.
+ * </p>
+ *
+ * @param fromElement the inclusive lower range of the subset.
+ * @param toElement the exclusive upper range of the subset.
+ * @return the subset.
+ * @throws ClassCastException if fromElement or toElement is not comparable
+ * to the set contents.
+ * @throws IllegalArgumentException if this is a subSet, and fromElement or
+ * toElement is out of range.
+ * @throws NullPointerException if fromElement or toElement is null but the
+ * set does not allow null elements.
+ */
+ public SortedSet<E> subSet(E fromElement, E toElement)
+ {
+ return new CheckedSortedSet<E>(ss.subSet(fromElement, toElement), type);
+ }
+
+ /**
+ * <p>
+ * Returns a checked view of the portion of the set greater than or equal
+ * to fromElement. The view is backed by the underlying set, so changes in
+ * one show up in the other. The subset supports all optional operations
+ * of the original.
+ * </p>
+ * <p>
+ * The returned set throws an IllegalArgumentException any time an
+ * element is used which is out of the range of fromElement. Note that
+ * the endpoint, fromElement, is included; if you do not want this value
+ * to be included, pass its successor object in to fromElement. For
+ * example, for Integers, you could request
+ * <code>tailSet(new Integer(limit.intValue() + 1))</code>.
+ * </p>
+ *
+ * @param fromElement the inclusive lower range of the subset
+ * @return the subset.
+ * @throws ClassCastException if fromElement is not comparable to the set
+ * contents.
+ * @throws IllegalArgumentException if this is a subSet, and fromElement is
+ * out of range.
+ * @throws NullPointerException if fromElement is null but the set does not
+ * allow null elements.
+ */
+ public SortedSet<E> tailSet(E fromElement)
+ {
+ return new CheckedSortedSet<E>(ss.tailSet(fromElement), type);
+ }
+ } // class CheckedSortedSet
+
+ /**
+ * Returns a view of a {@link Deque} as a stack or LIFO (Last-In-First-Out)
+ * {@link Queue}. Each call to the LIFO queue corresponds to one
+ * equivalent method call to the underlying deque, with the exception
+ * of {@link Collection#addAll(Collection)}, which is emulated by a series
+ * of {@link Deque#push(E)} calls.
+ *
+ * @param deque the deque to convert to a LIFO queue.
+ * @return a LIFO queue.
+ * @since 1.6
+ */
+ public static <T> Queue<T> asLifoQueue(Deque<T> deque)
+ {
+ return new LIFOQueue<T>(deque);
+ }
+
+ /**
+ * Returns a set backed by the supplied map. The resulting set
+ * has the same performance, concurrency and ordering characteristics
+ * as the original map. The supplied map must be empty and should not
+ * be used after the set is created. Each call to the set corresponds
+ * to one equivalent method call to the underlying map, with the exception
+ * of {@link Set#addAll(Collection)} which is emulated by a series of
+ * calls to <code>put</code>.
+ *
+ * @param map the map to convert to a set.
+ * @return a set backed by the supplied map.
+ * @throws IllegalArgumentException if the map is not empty.
+ * @since 1.6
+ */
+ public static <E> Set<E> newSetFromMap(Map<E,Boolean> map)
+ {
+ return new MapSet<E>(map);
+ }
+
+ /**
+ * The implementation of {@link #asLIFOQueue(Deque)}.
+ *
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.6
+ */
+ private static class LIFOQueue<T>
+ extends AbstractQueue<T>
+ {
+
+ /**
+ * The backing deque.
+ */
+ private Deque<T> deque;
+
+ /**
+ * Constructs a new {@link LIFOQueue} with the specified
+ * backing {@link Deque}.
+ *
+ * @param deque the backing deque.
+ */
+ public LIFOQueue(Deque<T> deque)
+ {
+ this.deque = deque;
+ }
+
+ public boolean add(T e)
+ {
+ return deque.offerFirst(e);
+ }
+
+ public boolean addAll(Collection<? extends T> c)
+ {
+ boolean result = false;
+ final Iterator<? extends T> it = c.iterator();
+ while (it.hasNext())
+ result |= deque.offerFirst(it.next());
+ return result;
+ }
+
+ public void clear()
+ {
+ deque.clear();
+ }
+
+ public boolean isEmpty()
+ {
+ return deque.isEmpty();
+ }
+
+ public Iterator<T> iterator()
+ {
+ return deque.iterator();
+ }
+
+ public boolean offer(T e)
+ {
+ return deque.offerFirst(e);
+ }
+
+ public T peek()
+ {
+ return deque.peek();
+ }
+
+ public T poll()
+ {
+ return deque.poll();
+ }
+
+ public int size()
+ {
+ return deque.size();
+ }
+ } // class LIFOQueue
+
+ /**
+ * The implementation of {@link #newSetFromMap(Map)}.
+ *
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.6
+ */
+ private static class MapSet<E>
+ extends AbstractSet<E>
+ {
+
+ /**
+ * The backing map.
+ */
+ private Map<E,Boolean> map;
+
+ /**
+ * Constructs a new {@link MapSet} using the specified
+ * backing {@link Map}.
+ *
+ * @param map the backing map.
+ * @throws IllegalArgumentException if the map is not empty.
+ */
+ public MapSet(Map<E,Boolean> map)
+ {
+ if (!map.isEmpty())
+ throw new IllegalArgumentException("The map must be empty.");
+ this.map = map;
+ }
+
+ public boolean add(E e)
+ {
+ return map.put(e, true) == null;
+ }
+
+ public boolean addAll(Collection<? extends E> c)
+ {
+ boolean result = false;
+ final Iterator<? extends E> it = c.iterator();
+ while (it.hasNext())
+ result |= (map.put(it.next(), true) == null);
+ return result;
+ }
+
+ public void clear()
+ {
+ map.clear();
+ }
+
+ public boolean contains(Object o)
+ {
+ return map.containsKey(o);
+ }
+
+ public boolean isEmpty()
+ {
+ return map.isEmpty();
+ }
+
+ public Iterator<E> iterator()
+ {
+ return map.keySet().iterator();
+ }
+
+ public boolean remove(Object o)
+ {
+ return map.remove(o) != null;
+ }
+
+ public int size()
+ {
+ return map.size();
+ }
+ } // class MapSet
+
+} // class Collections
diff --git a/libjava/classpath/java/util/Comparator.java b/libjava/classpath/java/util/Comparator.java
new file mode 100644
index 000000000..ca414e7b1
--- /dev/null
+++ b/libjava/classpath/java/util/Comparator.java
@@ -0,0 +1,119 @@
+/* Comparator.java -- Interface for objects that specify an ordering
+ Copyright (C) 1998, 2001, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Interface for objects that specify an ordering between objects. The ordering
+ * should be <em>total</em>, such that any two objects of the correct type
+ * can be compared, and the comparison is reflexive, anti-symmetric, and
+ * transitive. It is also recommended that the comparator be <em>consistent
+ * with equals</em>, although this is not a strict requirement. A relation
+ * is consistent with equals if these two statements always have the same
+ * results (if no exceptions occur):<br>
+ * <code>compare((Object) e1, (Object) e2) == 0</code> and
+ * <code>e1.equals((Object) e2)</code><br>
+ * Comparators that violate consistency with equals may cause strange behavior
+ * in sorted lists and sets. For example, a case-sensitive dictionary order
+ * comparison of Strings is consistent with equals, but if it is
+ * case-insensitive it is not, because "abc" and "ABC" compare as equal even
+ * though "abc".equals("ABC") returns false.
+ * <P>
+ * In general, Comparators should be Serializable, because when they are passed
+ * to Serializable data structures such as SortedMap or SortedSet, the entire
+ * data structure will only serialize correctly if the comparator is
+ * Serializable.
+ *
+ * @author Original author unknown
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Comparable
+ * @see TreeMap
+ * @see TreeSet
+ * @see SortedMap
+ * @see SortedSet
+ * @see Arrays#sort(Object[], Comparator)
+ * @see java.io.Serializable
+ * @since 1.2
+ * @status updated to 1.4
+ */
+public interface Comparator<T>
+{
+ /**
+ * Return an integer that is negative, zero or positive depending on whether
+ * the first argument is less than, equal to or greater than the second
+ * according to this ordering. This method should obey the following
+ * contract:
+ * <ul>
+ * <li>if compare(a, b) &lt; 0 then compare(b, a) &gt; 0</li>
+ * <li>if compare(a, b) throws an exception, so does compare(b, a)</li>
+ * <li>if compare(a, b) &lt; 0 and compare(b, c) &lt; 0 then compare(a, c)
+ * &lt; 0</li>
+ * <li>if compare(a, b) == 0 then compare(a, c) and compare(b, c) must
+ * have the same sign</li>
+ * </ul>
+ * To be consistent with equals, the following additional constraint is
+ * in place:
+ * <ul>
+ * <li>if a.equals(b) or both a and b are null, then
+ * compare(a, b) == 0.</li>
+ * </ul><p>
+ *
+ * Although it is permissible for a comparator to provide an order
+ * inconsistent with equals, that should be documented.
+ *
+ * @param o1 the first object
+ * @param o2 the second object
+ * @return the comparison
+ * @throws ClassCastException if the elements are not of types that can be
+ * compared by this ordering.
+ */
+ int compare(T o1, T o2);
+
+ /**
+ * Return true if the object is equal to this object. To be
+ * considered equal, the argument object must satisfy the constraints
+ * of <code>Object.equals()</code>, be a Comparator, and impose the
+ * same ordering as this Comparator. The default implementation
+ * inherited from Object is usually adequate.
+ *
+ * @param obj The object
+ * @return true if it is a Comparator that imposes the same order
+ * @see Object#equals(Object)
+ */
+ boolean equals(Object obj);
+}
diff --git a/libjava/classpath/java/util/ConcurrentModificationException.java b/libjava/classpath/java/util/ConcurrentModificationException.java
new file mode 100644
index 000000000..3d7ae1084
--- /dev/null
+++ b/libjava/classpath/java/util/ConcurrentModificationException.java
@@ -0,0 +1,92 @@
+/* ConcurrentModificationException.java -- Data structure concurrently modified
+ Copyright (C) 1998, 1999, 2001, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/* 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.
+ */
+
+/**
+ * Exception that is thrown by the collections classes when it is detected that
+ * a modification has been made to a data structure when this is not allowed,
+ * such as when a collection is structurally modified while an Iterator is
+ * operating over it. In cases where this can be detected, a
+ * ConcurrentModificationException will be thrown. An Iterator that detects
+ * this condition is referred to as fail-fast. Notice that this can occur
+ * even in single-threaded designs, if you call methods out of order.
+ *
+ * @author Warren Levy (warrenl@cygnus.com)
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Collection
+ * @see Iterator
+ * @see ListIterator
+ * @see Vector
+ * @see LinkedList
+ * @see HashSet
+ * @see Hashtable
+ * @see TreeMap
+ * @see AbstractList
+ * @since 1.2
+ * @status updated to 1.4
+ */
+public class ConcurrentModificationException extends RuntimeException
+{
+ /**
+ * Compatible with JDK 1.2.
+ */
+ private static final long serialVersionUID = -3666751008965953603L;
+
+ /**
+ * Constructs a ConcurrentModificationException with no detail message.
+ */
+ public ConcurrentModificationException()
+ {
+ }
+
+ /**
+ * Constructs a ConcurrentModificationException with a detail message.
+ *
+ * @param detail the detail message for the exception
+ */
+ public ConcurrentModificationException(String detail)
+ {
+ super(detail);
+ }
+}
diff --git a/libjava/classpath/java/util/Currency.java b/libjava/classpath/java/util/Currency.java
new file mode 100644
index 000000000..d58082c17
--- /dev/null
+++ b/libjava/classpath/java/util/Currency.java
@@ -0,0 +1,471 @@
+/* Currency.java -- Representation of a currency
+ Copyright (C) 2003, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.locale.LocaleHelper;
+
+import java.io.IOException;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+
+import java.util.spi.CurrencyNameProvider;
+
+/**
+ * Representation of a currency for a particular locale. Each currency
+ * is identified by its ISO 4217 code, and only one instance of this
+ * class exists per currency. As a result, instances are created
+ * via the <code>getInstance()</code> methods rather than by using
+ * a constructor.
+ *
+ * @see java.util.Locale
+ * @author Guilhem Lavaux (guilhem.lavaux@free.fr)
+ * @author Dalibor Topic (robilad@kaffe.org)
+ * @author Bryce McKinlay (mckinlay@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.4
+ */
+public final class Currency
+ implements Serializable
+{
+ /**
+ * For compatability with Sun's JDK
+ */
+ static final long serialVersionUID = -158308464356906721L;
+
+ /**
+ * The set of properties which map a currency to
+ * the currency information such as the ISO 4217
+ * currency code and the number of decimal points.
+ *
+ * @see #getCurrencyCode()
+ * @serial ignored.
+ */
+ private static transient Properties properties;
+
+ /**
+ * The ISO 4217 currency code associated with this
+ * particular instance.
+ *
+ * @see #getCurrencyCode()
+ * @serial the ISO 4217 currency code
+ */
+ private String currencyCode;
+
+ /**
+ * The number of fraction digits associated with this
+ * particular instance.
+ *
+ * @see #getDefaultFractionDigits()
+ * @serial the number of fraction digits
+ */
+ private transient int fractionDigits;
+
+ /**
+ * A cached map of country codes
+ * instances to international currency code
+ * <code>String</code>s. Seperating this
+ * from the <code>Currency</code> instances
+ * ensures we have a common lookup between
+ * the two <code>getInstance()</code> methods.
+ *
+ * @see #getInstance(java.util.Locale)
+ * @serial ignored.
+ */
+ private static transient Map countryMap;
+
+ /**
+ * A cache of <code>Currency</code> instances to
+ * ensure the singleton nature of this class. The key
+ * is the international currency code.
+ *
+ * @see #getInstance(java.util.Locale)
+ * @see #getInstance(java.lang.String)
+ * @see #readResolve()
+ * @serial ignored.
+ */
+ private static transient Map cache;
+
+ /**
+ * Instantiates the cache and reads in the properties.
+ */
+ static
+ {
+ /* Create a hash map for the locale mappings */
+ countryMap = new HashMap();
+ /* Create a hash map for the cache */
+ cache = new HashMap();
+ /* Create the properties object */
+ properties = new Properties();
+ /* Try and load the properties from our iso4217.properties resource */
+ try
+ {
+ properties.load(Currency.class.getResourceAsStream("iso4217.properties"));
+ }
+ catch (IOException exception)
+ {
+ throw new InternalError("Failed to load currency resource: " + exception);
+ }
+ }
+
+ /**
+ * Default constructor for deserialization
+ */
+ private Currency()
+ {
+ }
+
+ /**
+ * Constructor to create a <code>Currency</code> object
+ * for a particular <code>Locale</code>.
+ * All components of the given locale, other than the
+ * country code, are ignored. The results of calling this
+ * method may vary over time, as the currency associated with
+ * a particular country changes. For countries without
+ * a given currency (e.g. Antarctica), the result is null.
+ *
+ * @param loc the locale for the new currency, or null if
+ * there is no country code specified or a currency
+ * for this country.
+ */
+ private Currency(Locale loc)
+ {
+ String countryCode;
+ String currencyKey;
+ String fractionDigitsKey;
+ int commaPosition;
+
+ /* Retrieve the country code from the locale */
+ countryCode = loc.getCountry();
+ /* If there is no country code, return */
+ if (countryCode.equals(""))
+ {
+ throw new
+ IllegalArgumentException("Invalid (empty) country code for locale:"
+ + loc);
+ }
+ /* Construct the key for the currency */
+ currencyKey = countryCode + ".currency";
+ /* Construct the key for the fraction digits */
+ fractionDigitsKey = countryCode + ".fractionDigits";
+ /* Retrieve the currency */
+ currencyCode = properties.getProperty(currencyKey);
+ /* Return if the currency code is null */
+ if (currencyCode == null)
+ {
+ return;
+ }
+ /* Split off the first currency code (we only use the first for now) */
+ commaPosition = currencyCode.indexOf(",");
+ if (commaPosition != -1)
+ {
+ currencyCode = currencyCode.substring(0, commaPosition);
+ }
+ /* Retrieve the fraction digits */
+ fractionDigits = Integer.parseInt(properties.getProperty(fractionDigitsKey));
+ }
+
+ /**
+ * Constructor for the "XXX" special case. This allows
+ * a Currency to be constructed from an assumed good
+ * currency code.
+ *
+ * @param code the code to use.
+ */
+ private Currency(String code)
+ {
+ currencyCode = code;
+ fractionDigits = -1; /* Pseudo currency */
+ }
+
+ /**
+ * Returns the ISO4217 currency code of this currency.
+ *
+ * @return a <code>String</code> containing currency code.
+ */
+ public String getCurrencyCode()
+ {
+ return currencyCode;
+ }
+
+ /**
+ * Returns the number of digits which occur after the decimal point
+ * for this particular currency. For example, currencies such
+ * as the U.S. dollar, the Euro and the Great British pound have two
+ * digits following the decimal point to indicate the value which exists
+ * in the associated lower-valued coinage (cents in the case of the first
+ * two, pennies in the latter). Some currencies such as the Japanese
+ * Yen have no digits after the decimal point. In the case of pseudo
+ * currencies, such as IMF Special Drawing Rights, -1 is returned.
+ *
+ * @return the number of digits after the decimal separator for this currency.
+ */
+ public int getDefaultFractionDigits()
+ {
+ return fractionDigits;
+ }
+
+ /**
+ * Builds a new currency instance for this locale.
+ * All components of the given locale, other than the
+ * country code, are ignored. The results of calling this
+ * method may vary over time, as the currency associated with
+ * a particular country changes. For countries without
+ * a given currency (e.g. Antarctica), the result is null.
+ *
+ * @param locale a <code>Locale</code> instance.
+ * @return a new <code>Currency</code> instance.
+ * @throws NullPointerException if the locale or its
+ * country code is null.
+ * @throws IllegalArgumentException if the country of
+ * the given locale is not a supported ISO3166 code.
+ */
+ public static Currency getInstance(Locale locale)
+ {
+ /**
+ * The new instance must be the only available instance
+ * for the currency it supports. We ensure this happens,
+ * while maintaining a suitable performance level, by
+ * creating the appropriate object on the first call to
+ * this method, and returning the cached instance on
+ * later calls.
+ */
+ Currency newCurrency;
+
+ String country = locale.getCountry();
+ if (locale == null || country == null)
+ {
+ throw new
+ NullPointerException("The locale or its country is null.");
+ }
+
+ /* Check that country of locale given is valid. */
+ if (country.length() != 2)
+ throw new IllegalArgumentException();
+
+ /* Attempt to get the currency from the cache */
+ String code = (String) countryMap.get(country);
+ if (code == null)
+ {
+ /* Create the currency for this locale */
+ newCurrency = new Currency(locale);
+ /*
+ * If the currency code is null, then creation failed
+ * and we return null.
+ */
+ code = newCurrency.getCurrencyCode();
+ if (code == null)
+ {
+ return null;
+ }
+ else
+ {
+ /* Cache it */
+ countryMap.put(country, code);
+ cache.put(code, newCurrency);
+ }
+ }
+ else
+ {
+ newCurrency = (Currency) cache.get(code);
+ }
+ /* Return the instance */
+ return newCurrency;
+ }
+
+ /**
+ * Builds the currency corresponding to the specified currency code.
+ *
+ * @param currencyCode a string representing a currency code.
+ * @return a new <code>Currency</code> instance.
+ * @throws NullPointerException if currencyCode is null.
+ * @throws IllegalArgumentException if the supplied currency code
+ * is not a supported ISO 4217 code.
+ */
+ public static Currency getInstance(String currencyCode)
+ {
+ Locale[] allLocales;
+
+ /*
+ * Throw a null pointer exception explicitly if currencyCode is null.
+ * One is not thrown otherwise. It results in an
+ * IllegalArgumentException.
+ */
+ if (currencyCode == null)
+ {
+ throw new NullPointerException("The supplied currency code is null.");
+ }
+ /* Nasty special case to allow an erroneous currency... blame Sun */
+ if (currencyCode.equals("XXX"))
+ return new Currency("XXX");
+ Currency newCurrency = (Currency) cache.get(currencyCode);
+ if (newCurrency == null)
+ {
+ /* Get all locales */
+ allLocales = Locale.getAvailableLocales();
+ /* Loop through each locale, looking for the code */
+ for (int i = 0;i < allLocales.length; i++)
+ {
+ try
+ {
+ Currency testCurrency = getInstance (allLocales[i]);
+ if (testCurrency != null &&
+ testCurrency.getCurrencyCode().equals(currencyCode))
+ {
+ return testCurrency;
+ }
+ }
+ catch (IllegalArgumentException exception)
+ {
+ /* Ignore locales without valid countries */
+ }
+ }
+ /*
+ * If we get this far, the code is not supported by any of
+ * our locales.
+ */
+ throw new IllegalArgumentException("The currency code, " + currencyCode +
+ ", is not supported.");
+ }
+ else
+ {
+ return newCurrency;
+ }
+ }
+
+ /**
+ * This method returns the symbol which precedes or follows a
+ * value in this particular currency in the default locale.
+ * In cases where there is no such symbol for the currency,
+ * the ISO 4217 currency code is returned.
+ *
+ * @return the currency symbol, or the ISO 4217 currency code if
+ * one doesn't exist.
+ */
+ public String getSymbol()
+ {
+ return getSymbol(Locale.getDefault());
+ }
+
+ /**
+ * <p>
+ * This method returns the symbol which precedes or follows a
+ * value in this particular currency. The returned value is
+ * the symbol used to denote the currency in the specified locale.
+ * </p>
+ * <p>
+ * For example, a supplied locale may specify a different symbol
+ * for the currency, due to conflicts with its own currency.
+ * This would be the case with the American currency, the dollar.
+ * Locales that also use a dollar-based currency (e.g. Canada, Australia)
+ * need to differentiate the American dollar using 'US$' rather than '$'.
+ * So, supplying one of these locales to <code>getSymbol()</code> would
+ * return this value, rather than the standard '$'.
+ * </p>
+ * <p>
+ * In cases where there is no such symbol for a particular currency,
+ * the ISO 4217 currency code is returned.
+ * </p>
+ *
+ * @param locale the locale to express the symbol in.
+ * @return the currency symbol, or the ISO 4217 currency code if
+ * one doesn't exist.
+ * @throws NullPointerException if the locale is null.
+ */
+ public String getSymbol(Locale locale)
+ {
+ String property = "currenciesSymbol." + currencyCode;
+ try
+ {
+ return ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
+ locale).getString(property);
+ }
+ catch (MissingResourceException exception)
+ {
+ /* This means runtime support for the locale
+ * is not available, so we check providers. */
+ }
+ for (CurrencyNameProvider p :
+ ServiceLoader.load(CurrencyNameProvider.class))
+ {
+ for (Locale loc : p.getAvailableLocales())
+ {
+ if (loc.equals(locale))
+ {
+ String localizedString = p.getSymbol(currencyCode,
+ locale);
+ if (localizedString != null)
+ return localizedString;
+ break;
+ }
+ }
+ }
+ if (locale.equals(Locale.ROOT)) // Base case
+ return currencyCode;
+ return getSymbol(LocaleHelper.getFallbackLocale(locale));
+ }
+
+ /**
+ * Returns the international ISO4217 currency code of this currency.
+ *
+ * @return a <code>String</code> containing the ISO4217 currency code.
+ */
+ public String toString()
+ {
+ return getCurrencyCode();
+ }
+
+ /**
+ * Resolves the deserialized object to the singleton instance for its
+ * particular currency. The currency code of the deserialized instance
+ * is used to return the correct instance.
+ *
+ * @return the singleton instance for the currency specified by the
+ * currency code of the deserialized object. This replaces
+ * the deserialized object as the returned object from
+ * deserialization.
+ * @throws ObjectStreamException if a problem occurs with deserializing
+ * the object.
+ */
+ private Object readResolve()
+ throws ObjectStreamException
+ {
+ return getInstance(currencyCode);
+ }
+
+}
diff --git a/libjava/classpath/java/util/Date.java b/libjava/classpath/java/util/Date.java
new file mode 100644
index 000000000..3f7ba6f59
--- /dev/null
+++ b/libjava/classpath/java/util/Date.java
@@ -0,0 +1,1256 @@
+/* java.util.Date
+ Copyright (C) 1998, 1999, 2000, 2001, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+
+/**
+ * <p>
+ * This class represents a specific time in milliseconds since the epoch.
+ * The epoch is 1970, January 1 00:00:00.0000 UTC.
+ * </p>
+ * <p>
+ * <code>Date</code> is intended to reflect universal time coordinate (UTC),
+ * but this depends on the underlying host environment. Most operating systems
+ * don't handle the leap second, which occurs about once every year or
+ * so. The leap second is added to the last minute of the day on either
+ * the 30th of June or the 31st of December, creating a minute 61 seconds
+ * in length.
+ * </p>
+ * <p>
+ * The representations of the date fields are as follows:
+ * <ul>
+ * <li>
+ * Years are specified as the difference between the year
+ * and 1900. Thus, the final year used is equal to
+ * 1900 + y, where y is the input value.
+ * </li>
+ * <li>
+ * Months are represented using zero-based indexing,
+ * making 0 January and 11 December.
+ * </li>
+ * <li>
+ * Dates are represented with the usual values of
+ * 1 through to 31.
+ * </li>
+ * <li>
+ * Hours are represented in the twenty-four hour clock,
+ * with integer values from 0 to 23. 12am is 0, and
+ * 12pm is 12.
+ * </li>
+ * <li>
+ * Minutes are again as usual, with values from 0 to 59.
+ * </li>
+ * <li>
+ * Seconds are represented with the values 0 through to 61,
+ * with 60 and 61 being leap seconds (as per the ISO C standard).
+ * </li>
+ * </ul>
+ * </p>
+ * <p>
+ * Prior to JDK 1.1, this class was the sole class handling date and time
+ * related functionality. However, this particular solution was not
+ * amenable to internationalization. The new <code>Calendar</code>
+ * class should now be used to handle dates and times, with <code>Date</code>
+ * being used only for values in milliseconds since the epoch. The
+ * <code>Calendar</code> class, and its concrete implementations, handle
+ * the interpretation of these values into minutes, hours, days, months
+ * and years. The formatting and parsing of dates is left to the
+ * <code>DateFormat</code> class, which is able to handle the different
+ * types of date format which occur in different locales.
+ * </p>
+ *
+ * @see Calendar
+ * @see GregorianCalendar
+ * @see java.text.DateFormat
+ * @author Jochen Hoenicke
+ * @author Per Bothner (bothner@cygnus.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ */
+public class Date
+ implements Cloneable, Comparable<Date>, Serializable
+{
+ /**
+ * This is the serialization UID for this class
+ * for compatability with Sun's JDK.
+ */
+ private static final long serialVersionUID = 7523967970034938905L;
+
+ /**
+ * The time in milliseconds since the epoch.
+ */
+ private transient long time;
+
+ /**
+ * An array of week names used to map names to integer values.
+ */
+ private static final String[] weekNames = { "Sun", "Mon", "Tue", "Wed",
+ "Thu", "Fri", "Sat" };
+ /**
+ * An array of month names used to map names to integer values.
+ */
+ private static final String[] monthNames = { "Jan", "Feb", "Mar", "Apr",
+ "May", "Jun", "Jul", "Aug",
+ "Sep", "Oct", "Nov", "Dec" };
+ /**
+ * Creates a new Date Object representing the current time.
+ */
+ public Date()
+ {
+ time = System.currentTimeMillis();
+ }
+
+ /**
+ * Creates a new Date Object representing the given time.
+ *
+ * @param time the time in milliseconds since the epoch.
+ */
+ public Date(long time)
+ {
+ this.time = time;
+ }
+
+ /**
+ * Creates a new Date Object representing the given time.
+ *
+ * @deprecated use <code>new GregorianCalendar(year+1900, month,
+ * day)</code> instead.
+ * @param year the difference between the required year and 1900.
+ * @param month the month as a value between 0 and 11.
+ * @param day the day as a value between 0 and 31.
+ */
+ public Date(int year, int month, int day)
+ {
+ this(year, month, day, 0, 0, 0);
+ }
+
+ /**
+ * Creates a new Date Object representing the given time.
+ *
+ * @deprecated use <code>new GregorianCalendar(year+1900, month,
+ * day, hour, min)</code> instead.
+ * @param year the difference between the required year and 1900.
+ * @param month the month as a value between 0 and 11.
+ * @param day the day as a value between 0 and 31.
+ * @param hour the hour as a value between 0 and 23, in 24-hour
+ * clock notation.
+ * @param min the minute as a value between 0 and 59.
+ */
+ public Date(int year, int month, int day, int hour, int min)
+ {
+ this(year, month, day, hour, min, 0);
+ }
+
+ /**
+ * Creates a new Date Object representing the given time.
+ *
+ * @deprecated use <code>new GregorianCalendar(year+1900, month,
+ * day, hour, min, sec)</code> instead.
+ * @param year the difference between the required year and 1900.
+ * @param month the month as a value between 0 and 11.
+ * @param day the day as a value between 0 and 31.
+ * @param hour the hour as a value between 0 and 23, in 24-hour
+ * clock notation.
+ * @param min the minute as a value between 0 and 59.
+ * @param sec the second as a value between 0 and 61 (with 60
+ * and 61 being leap seconds).
+ */
+ public Date(int year, int month, int day, int hour, int min, int sec)
+ {
+ GregorianCalendar cal =
+ new GregorianCalendar(year + 1900, month, day, hour, min, sec);
+ time = cal.getTimeInMillis();
+ }
+
+ /**
+ * Creates a new Date from the given string representation. This
+ * does the same as <code>new Date(Date.parse(s))</code>
+ * @see #parse
+ * @deprecated use <code>java.text.DateFormat.parse(s)</code> instead.
+ */
+ public Date(String s)
+ {
+ time = parse(s);
+ }
+
+ /**
+ * Returns a copy of this <code>Date</code> object.
+ *
+ * @return a copy, or null if the object couldn't be
+ * cloned.
+ * @see Object#clone()
+ */
+ public Object clone()
+ {
+ try
+ {
+ return super.clone();
+ }
+ catch (CloneNotSupportedException ex)
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the number of milliseconds since the epoch
+ * specified by the given arguments. The arguments are
+ * interpreted relative to UTC rather than the local
+ * time zone.
+ *
+ * @deprecated Use <code>Calendar</code> with a UTC
+ * <code>TimeZone</code> instead.
+ * @param year the difference between the required year and 1900.
+ * @param month the month as a value between 0 and 11.
+ * @param date the day as a value between 0 and 31.
+ * @param hrs the hour as a value between 0 and 23, in 24-hour
+ * clock notation.
+ * @param min the minute as a value between 0 and 59.
+ * @param sec the second as a value between 0 and 61 (with 60
+ * and 61 being leap seconds).
+ * @return the time in milliseconds since the epoch.
+ */
+ public static long UTC(int year, int month, int date,
+ int hrs, int min, int sec)
+ {
+ GregorianCalendar cal =
+ new GregorianCalendar(year + 1900, month, date, hrs, min, sec);
+ cal.set(Calendar.ZONE_OFFSET, 0);
+ cal.set(Calendar.DST_OFFSET, 0);
+ return cal.getTimeInMillis();
+ }
+
+ /**
+ * Gets the time represented by this object.
+ *
+ * @return the time in milliseconds since the epoch.
+ */
+ public long getTime()
+ {
+ return time;
+ }
+
+ /**
+ * Returns the number of minutes offset used with UTC to give the time
+ * represented by this object in the current time zone. The date information
+ * from this object is also used to determine whether or not daylight savings
+ * time is in effect. For example, the offset for the UK would be 0 if the
+ * month of the date object was January, and 1 if the month was August.
+ *
+ * @deprecated use
+ * <code>Calendar.get(Calendar.ZONE_OFFSET)+Calendar.get(Calendar.DST_OFFSET)</code>
+ * instead.
+ * @return The time zone offset in minutes of the local time zone
+ * relative to UTC. The time represented by this object is used to
+ * determine if we should use daylight savings.
+ */
+ public int getTimezoneOffset()
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(time);
+ return - (cal.get(Calendar.ZONE_OFFSET)
+ + cal.get(Calendar.DST_OFFSET)) / (60 * 1000);
+ }
+
+ /**
+ * Sets the time which this object should represent.
+ *
+ * @param time the time in milliseconds since the epoch.
+ */
+ public void setTime(long time)
+ {
+ this.time = time;
+ }
+
+ /**
+ * Tests if this date is after the specified date.
+ *
+ * @param when the other date
+ * @return true, if the date represented by this object is
+ * strictly later than the time represented by when.
+ */
+ public boolean after(Date when)
+ {
+ return time > when.time;
+ }
+
+ /**
+ * Tests if this date is before the specified date.
+ *
+ * @param when the other date
+ * @return true, if the date represented by when is strictly later
+ * than the time represented by this object.
+ */
+ public boolean before(Date when)
+ {
+ return time < when.time;
+ }
+
+ /**
+ * Compares two dates for equality.
+ *
+ * @param obj the object to compare.
+ * @return true, if obj is a Date object and the time represented
+ * by obj is exactly the same as the time represented by this
+ * object.
+ */
+ public boolean equals(Object obj)
+ {
+ return (obj instanceof Date && time == ((Date) obj).time);
+ }
+
+ /**
+ * Compares two dates.
+ *
+ * @param when the other date.
+ * @return 0, if the date represented
+ * by obj is exactly the same as the time represented by this
+ * object, a negative if this Date is before the other Date, and
+ * a positive value otherwise.
+ */
+ public int compareTo(Date when)
+ {
+ return (time < when.time) ? -1 : (time == when.time) ? 0 : 1;
+ }
+
+ /**
+ * Computes the hash code of this <code>Date</code> as the
+ * XOR of the most significant and the least significant
+ * 32 bits of the 64 bit milliseconds value.
+ *
+ * @return the hash code.
+ */
+ public int hashCode()
+ {
+ return (int) time ^ (int) (time >>> 32);
+ }
+
+ /**
+ * <p>
+ * Returns a string representation of this date using
+ * the following date format:
+ * </p>
+ * <p>
+ * <code>day mon dd hh:mm:ss zz yyyy</code>
+ * </p>
+ * <p>where the fields used here are:
+ * <ul>
+ * <li>
+ * <code>day</code> -- the day of the week
+ * (Sunday through to Saturday).
+ * </li>
+ * <li>
+ * <code>mon</code> -- the month (Jan to Dec).
+ * </li>
+ * <li>
+ * <code>dd</code> -- the day of the month
+ * as two decimal digits (01 to 31).
+ * </li>
+ * <li>
+ * <code>hh</code> -- the hour of the day
+ * as two decimal digits in 24-hour clock notation
+ * (01 to 23).
+ * </li>
+ * <li>
+ * <code>mm</code> -- the minute of the day
+ * as two decimal digits (01 to 59).
+ * </li>
+ * <li>
+ * <code>ss</code> -- the second of the day
+ * as two decimal digits (01 to 61).
+ * </li>
+ * <li>
+ * <code>zz</code> -- the time zone information if available.
+ * The possible time zones used include the abbreviations
+ * recognised by <code>parse()</code> (e.g. GMT, CET, etc.)
+ * and may reflect the fact that daylight savings time is in
+ * effect. The empty string is used if there is no time zone
+ * information.
+ * </li>
+ * <li>
+ * <code>yyyy</code> -- the year as four decimal digits.
+ * </li>
+ * </ul>
+ * <p>
+ * The <code>DateFormat</code> class should now be
+ * preferred over using this method.
+ * </p>
+ *
+ * @return A string of the form 'day mon dd hh:mm:ss zz yyyy'
+ * @see #parse(String)
+ * @see DateFormat
+ */
+ public String toString()
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(time);
+ String day = "0" + cal.get(Calendar.DATE);
+ String hour = "0" + cal.get(Calendar.HOUR_OF_DAY);
+ String min = "0" + cal.get(Calendar.MINUTE);
+ String sec = "0" + cal.get(Calendar.SECOND);
+ String year = "000" + cal.get(Calendar.YEAR);
+ return weekNames[cal.get(Calendar.DAY_OF_WEEK) - 1] + " "
+ + monthNames[cal.get(Calendar.MONTH)] + " "
+ + day.substring(day.length() - 2) + " "
+ + hour.substring(hour.length() - 2) + ":"
+ + min.substring(min.length() - 2) + ":"
+ + sec.substring(sec.length() - 2) + " "
+ +
+ cal.getTimeZone().getDisplayName(cal.getTimeZone().inDaylightTime(this),
+ TimeZone.SHORT) + " " +
+ year.substring(year.length() - 4);
+ }
+
+ /**
+ * Returns a locale-dependent string representation of this
+ * <code>Date</code> object.
+ *
+ * @deprecated Use DateFormat.format(Date)
+ * @return A locale-dependent string representation.
+ * @see #parse(String)
+ * @see DateFormat
+ */
+ public String toLocaleString()
+ {
+ return java.text.DateFormat.getInstance().format(this);
+ }
+
+ /**
+ * <p>
+ * Returns a string representation of this <code>Date</code>
+ * object using GMT rather than the local timezone.
+ * The following date format is used:
+ * </p>
+ * <p>
+ * <code>d mon yyyy hh:mm:ss GMT</code>
+ * </p>
+ * <p>where the fields used here are:
+ * <ul>
+ * <li>
+ * <code>d</code> -- the day of the month
+ * as one or two decimal digits (1 to 31).
+ * </li>
+ * <li>
+ * <code>mon</code> -- the month (Jan to Dec).
+ * </li>
+ * <li>
+ * <code>yyyy</code> -- the year as four decimal digits.
+ * </li>
+ * <li>
+ * <code>hh</code> -- the hour of the day
+ * as two decimal digits in 24-hour clock notation
+ * (01 to 23).
+ * </li>
+ * <li>
+ * <code>mm</code> -- the minute of the day
+ * as two decimal digits (01 to 59).
+ * </li>
+ * <li>
+ * <code>ss</code> -- the second of the day
+ * as two decimal digits (01 to 61).
+ * </li>
+ * <li>
+ * <code>GMT</code> -- the literal string "GMT"
+ * indicating Greenwich Mean Time as opposed to
+ * the local timezone.
+ * </li>
+ * </ul>
+ *
+ * @deprecated Use DateFormat.format(Date) with a GMT TimeZone.
+ * @return A string of the form 'd mon yyyy hh:mm:ss GMT' using
+ * GMT as opposed to the local timezone.
+ * @see #parse(String)
+ * @see DateFormat
+ */
+ public String toGMTString()
+ {
+ java.text.DateFormat format = java.text.DateFormat.getInstance();
+ format.setTimeZone(TimeZone.getTimeZone("GMT"));
+ return format.format(this);
+ }
+
+ /**
+ * Parses the time zone string.
+ *
+ * @param tok The token containing the time zone.
+ * @param sign The sign (+ or -) used by the time zone.
+ * @return An integer representing the number of minutes offset
+ * from GMT for the time zone.
+ */
+ private static int parseTz(String tok, char sign)
+ throws IllegalArgumentException
+ {
+ int num;
+
+ try
+ {
+ // parseInt doesn't handle '+' so strip off sign.
+ num = Integer.parseInt(tok.substring(1));
+ }
+ catch (NumberFormatException ex)
+ {
+ throw new IllegalArgumentException(tok);
+ }
+
+ // Convert hours to minutes.
+ if (num < 24)
+ num *= 60;
+ else
+ num = (num / 100) * 60 + num % 100;
+
+ return sign == '-' ? -num : num;
+ }
+
+ /**
+ * Parses the month string.
+ *
+ * @param tok the token containing the month.
+ * @return An integer between 0 and 11, representing
+ * a month from January (0) to December (11),
+ * or -1 if parsing failed.
+ */
+ private static int parseMonth(String tok)
+ {
+ // Initialize strings for month names.
+ // We could possibly use the fields of DateFormatSymbols but that is
+ // localized and thus might not match the English words specified.
+ String months[] = { "JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY",
+ "JUNE", "JULY", "AUGUST", "SEPTEMBER", "OCTOBER",
+ "NOVEMBER", "DECEMBER" };
+
+ int i;
+ for (i = 0; i < 12; i++)
+ if (months[i].startsWith(tok))
+ return i;
+
+ // Return -1 if not found.
+ return -1;
+ }
+
+ /**
+ * Parses the day of the week string.
+ *
+ * @param tok the token containing the day of the week.
+ * @return true if the token was parsed successfully.
+ */
+ private static boolean parseDayOfWeek(String tok)
+ {
+ // Initialize strings for days of the week names.
+ // We could possibly use the fields of DateFormatSymbols but that is
+ // localized and thus might not match the English words specified.
+ String daysOfWeek[] = { "SUNDAY", "MONDAY", "TUESDAY", "WEDNESDAY",
+ "THURSDAY", "FRIDAY", "SATURDAY" };
+
+ int i;
+ for (i = 0; i < 7; i++)
+ if (daysOfWeek[i].startsWith(tok))
+ return true;
+
+ return false;
+ }
+
+ /**
+ * <p>
+ * Parses a String and returns the time, in milliseconds since the
+ * epoch, it represents. Most syntaxes are handled, including
+ * the IETF date standard "day, dd mon yyyy hh:mm:ss zz" (see
+ * <code>toString()</code> for definitions of these fields).
+ * Standard U.S. time zone abbreviations are recognised, in
+ * addition to time zone offsets in positive or negative minutes.
+ * If a time zone is specified, the specified time is assumed to
+ * be in UTC and the appropriate conversion is applied, following
+ * parsing, to convert this to the local time zone. If no zone
+ * is specified, the time is assumed to already be in the local
+ * time zone.
+ * </p>
+ * <p>
+ * The method parses the string progressively from left to right.
+ * At the end of the parsing process, either a time is returned
+ * or an <code>IllegalArgumentException</code> is thrown to signify
+ * failure. The ASCII characters A-Z, a-z, 0-9, and ',', '+', '-',
+ * ':' and '/' are the only characters permitted within the string,
+ * besides whitespace and characters enclosed within parantheses
+ * '(' and ')'.
+ * </p>
+ * <p>
+ * A sequence of consecutive digits are recognised as a number,
+ * and interpreted as follows:
+ * <ul>
+ * <li>
+ * A number preceded by a sign (+ or -) is taken to be a time zone
+ * offset. The time zone offset can be specified in either hours
+ * or minutes. The former is assumed if the number is less than 24.
+ * Otherwise, the offset is assumed to be in minutes. A - indicates
+ * a time zone west of GMT, while a + represents a time zone to the
+ * east of GMT. The time zones are always assumed to be relative
+ * to GMT, and a (redundant) specification of this can be included
+ * with the time zone. For example, '-9', 'utc-9' and 'GMT-9' all
+ * represent a time zone nine hours west of GMT. Similarly,
+ * '+4', 'ut+4' and 'UTC+4' all give 4 hours east of GMT.
+ * </li>
+ * <li>
+ * A number equal to or greater than 70 is regarded as a year specification.
+ * Values lower than 70 are only assumed to indicate a year if both the
+ * day of the month and the month itself have already been recognised.
+ * Year values less than 100 are interpreted as being relative to the current
+ * century when the <code>Date</code> class is initialised.. Given a century,
+ * x, the year is assumed to be within the range x - 80 to x + 19. The value
+ * itself is then used as a match against the two last digits of one of these
+ * years. For example, take x to be 2004. A two-digit year is assumed to fall
+ * within the range x - 80 (1924) and x + 19 (2023). Thus, any intepreted value
+ * between 0 and 23 is assumed to be 2000 to 2023 and values between 24 and 99
+ * are taken as being 1924 to 1999. This only applies for the case of 2004.
+ * With a different year, the values will be interpreted differently. 2005
+ * will used 0 to 24 as 2000 to 2024 and 25 to 99 as 1925 to 1999, for example.
+ * This behaviour differs from that of <code>SimpleDateFormat</code> and is
+ * time-dependent (a two-digit year will be interpreted differently depending
+ * on the time the code is run).
+ * </li>
+ * <li>
+ * Numbers followed by a colon are interpreted by first an hour, and then
+ * as a minute, once an hour has been found.
+ * </li>
+ * <li>
+ * <li>
+ * Numbers followed by a slash are regarded first as a month, and then as
+ * a day of the month once the month has been found. This follows the
+ * U.S. date format of mm/dd, rather than the European dd/mm. Months
+ * are converted to the recognised value - 1 before storage, in order
+ * to put the number within the range 0 to 11.
+ * </li>
+ * <li>
+ * Numbers followed by commas, whitespace, hyphens or the end of the string
+ * are interpreted in the following order: hour, minute, second, day of month.
+ * The first type not already recognised in the current string being parsed is
+ * assumed.
+ * </li>
+ * </ul>
+ * </p>
+ * <p>
+ * A sequence of consecutive alphabetic characters is recognised as a word,
+ * and interpreted as follows, in a case-insentive fashion:
+ * <ul>
+ * <li>
+ * The characters 'AM' or 'PM' restrict the hour value to a value between 0
+ * and 12. In the latter case, 12 is added to the hour value before storage.
+ * </li>
+ * <li>
+ * Any words which match any prefix of one of the days of the week ('Monday',
+ * 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' and 'Sunday'),
+ * are simply ignored.
+ * </li>
+ * <li>
+ * Any words which match any prefix of one of the months of the year ('January',
+ * 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September',
+ * 'October', 'November', 'December') are recognised and interpreted as the
+ * appropriate value between 0 and 11. The first match made against a
+ * month is the one used, in the order specified here. For example, 'Ma' is
+ * intepreted as 'March' (2) and not as 'May' (4). Similarly, 'Ju' is 'June',
+ * and not 'July'.
+ * </li>
+ * <li>
+ * The words 'GMT', 'UT' and 'UTC' are interpreted as specifying UTC as the
+ * time zone in use for this date.
+ * </li>
+ * <li>
+ * The word pairs 'EST'/'EDT', 'CST'/'CDT', 'MST'/'MDT' and 'PST'/'PDT' are
+ * interpreted as the appropriate U.S. time zone abbreviation. Each pair
+ * is the standard and daylight savings time zone specification, respectively,
+ * for each zone within the U.S, these being Eastern Standard/Daylight Time
+ * (-5), Central Standard/Daylight Time (-6), Mountain Standard/Daylight Time
+ * (-7) and Pacific Standard/Daylight Time (-8).
+ * </li>
+ * </ul>
+ *
+ * @param string The String to parse.
+ * @return The time in milliseconds since the epoch.
+ * @throws IllegalArgumentException if the string fails to parse.
+ * @deprecated Use DateFormat.parse(String)
+ * @see #toString()
+ * @see SimpleDateFormat
+ */
+ public static long parse(String string)
+ {
+ // Initialize date/time fields before parsing begins.
+ int year = -1;
+ int month = -1;
+ int day = -1;
+ int hour = -1;
+ int minute = -1;
+ int second = -1;
+ int timezone = 0;
+ boolean localTimezone = true;
+
+ // Trim out any nested stuff in parentheses now to make parsing easier.
+ CPStringBuilder buf = new CPStringBuilder();
+ int parenNesting = 0;
+ int len = string.length();
+ for (int i = 0; i < len; i++)
+ {
+ char ch = string.charAt(i);
+ if (ch >= 'a' && ch <= 'z')
+ ch -= 'a' - 'A';
+ if (ch == '(')
+ parenNesting++;
+ else if (parenNesting == 0)
+ buf.append(ch);
+ else if (ch == ')')
+ parenNesting--;
+ }
+ int tmpMonth;
+
+ // Make all chars upper case to simplify comparisons later.
+ // Also ignore commas; treat them as delimiters.
+ StringTokenizer strtok = new StringTokenizer(buf.toString(), " \t\n\r,");
+
+ while (strtok.hasMoreTokens())
+ {
+ String tok = strtok.nextToken();
+ char firstch = tok.charAt(0);
+ if ((firstch == '+' || firstch == '-') && year >= 0)
+ {
+ timezone = parseTz(tok, firstch);
+ localTimezone = false;
+ }
+ else if (firstch >= '0' && firstch <= '9')
+ {
+ int lastPunct = -1;
+ while (tok != null && tok.length() > 0)
+ {
+ int punctOffset = tok.length();
+ int num = 0;
+ int punct;
+ for (int i = 0; ; i++)
+ {
+ if (i >= punctOffset)
+ {
+ punct = -1;
+ break;
+ }
+ else
+ {
+ punct = tok.charAt(i);
+ if (punct >= '0' && punct <= '9')
+ {
+ if (num > 999999999) // in case of overflow
+ throw new IllegalArgumentException(tok);
+ num = 10 * num + (punct - '0');
+ }
+ else
+ {
+ punctOffset = i;
+ break;
+ }
+ }
+
+ }
+
+ if (punct == ':')
+ {
+ if (hour < 0)
+ hour = num;
+ else
+ minute = num;
+ }
+ else if (lastPunct == ':' && hour >= 0 && (minute < 0 || second < 0))
+ {
+ if (minute < 0)
+ minute = num;
+ else
+ second = num;
+ }
+ else if ((num >= 70
+ && (punct == ' ' || punct == ','
+ || punct == '/' || punct < 0))
+ || (num < 70 && day >= 0 && month >= 0 && year < 0))
+ {
+ if (num >= 100)
+ year = num;
+ else
+ {
+ int curYear = 1900 + new Date().getYear();
+ int firstYear = curYear - 80;
+ year = firstYear / 100 * 100 + num;
+ if (year < firstYear)
+ year += 100;
+ }
+ }
+ else if (punct == '/')
+ {
+ if (month < 0)
+ month = num - 1;
+ else
+ day = num;
+ }
+ else if (hour >= 0 && minute < 0)
+ minute = num;
+ else if (minute >= 0 && second < 0)
+ second = num;
+ else if (day < 0)
+ day = num;
+ else
+ throw new IllegalArgumentException(tok);
+
+ // Advance string if there's more to process in this token.
+ if (punct < 0 || punctOffset + 1 >= tok.length())
+ tok = null;
+ else
+ tok = tok.substring(punctOffset + 1);
+ lastPunct = punct;
+ }
+ }
+ else if (firstch >= 'A' && firstch <= 'Z')
+ {
+ if (tok.equals("AM"))
+ {
+ if (hour < 1 || hour > 12)
+ throw new IllegalArgumentException(tok);
+ if (hour == 12)
+ hour = 0;
+ }
+ else if (tok.equals("PM"))
+ {
+ if (hour < 1 || hour > 12)
+ throw new IllegalArgumentException(tok);
+ if (hour < 12)
+ hour += 12;
+ }
+ else if (parseDayOfWeek(tok))
+ { /* Ignore it; throw the token away. */ }
+ else if (tok.equals("UT") || tok.equals("UTC") || tok.equals("GMT"))
+ localTimezone = false;
+ else if (tok.startsWith("UT") || tok.startsWith("GMT"))
+ {
+ int signOffset = 3;
+ if (tok.charAt(1) == 'T' && tok.charAt(2) != 'C')
+ signOffset = 2;
+
+ char sign = tok.charAt(signOffset);
+ if (sign != '+' && sign != '-')
+ throw new IllegalArgumentException(tok);
+
+ timezone = parseTz(tok.substring(signOffset), sign);
+ localTimezone = false;
+ }
+ else if ((tmpMonth = parseMonth(tok)) >= 0)
+ month = tmpMonth;
+ else if (tok.length() == 3 && tok.charAt(2) == 'T')
+ {
+ // Convert timezone offset from hours to minutes.
+ char ch = tok.charAt(0);
+ if (ch == 'E')
+ timezone = -5 * 60;
+ else if (ch == 'C')
+ timezone = -6 * 60;
+ else if (ch == 'M')
+ timezone = -7 * 60;
+ else if (ch == 'P')
+ timezone = -8 * 60;
+ else
+ throw new IllegalArgumentException(tok);
+
+ // Shift 60 minutes for Daylight Savings Time.
+ if (tok.charAt(1) == 'D')
+ timezone += 60;
+ else if (tok.charAt(1) != 'S')
+ throw new IllegalArgumentException(tok);
+
+ localTimezone = false;
+ }
+ else
+ throw new IllegalArgumentException(tok);
+ }
+ else
+ throw new IllegalArgumentException(tok);
+ }
+
+ // Unspecified hours, minutes, or seconds should default to 0.
+ if (hour < 0)
+ hour = 0;
+ if (minute < 0)
+ minute = 0;
+ if (second < 0)
+ second = 0;
+
+ // Throw exception if any other fields have not been recognized and set.
+ if (year < 0 || month < 0 || day < 0)
+ throw new IllegalArgumentException("Missing field");
+
+ // Return the time in either local time or relative to GMT as parsed.
+ // If no time-zone was specified, get the local one (in minutes) and
+ // convert to milliseconds before adding to the UTC.
+ GregorianCalendar cal
+ = new GregorianCalendar(year, month, day, hour, minute, second);
+ if (!localTimezone)
+ {
+ cal.set(Calendar.ZONE_OFFSET, timezone * 60 * 1000);
+ cal.set(Calendar.DST_OFFSET, 0);
+ }
+ return cal.getTimeInMillis();
+ }
+
+ /**
+ * Returns the difference between the year represented by this
+ * <code>Date</code> object and 1900.
+ *
+ * @return the year minus 1900 represented by this date object.
+ * @deprecated Use Calendar instead of Date, and use get(Calendar.YEAR)
+ * instead. Note the 1900 difference in the year.
+ * @see Calendar
+ * @see #setYear(int)
+ */
+ public int getYear()
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(time);
+ return cal.get(Calendar.YEAR) - 1900;
+ }
+
+ /**
+ * Sets the year to the specified year, plus 1900. The other
+ * fields are only altered as required to match the same date
+ * and time in the new year. Usually, this will mean that
+ * the fields are not changed at all, but in the case of
+ * a leap day or leap second, the fields will change in
+ * relation to the existence of such an event in the new year.
+ * For example, if the date specifies February the 29th, 2000,
+ * then this will become March the 1st if the year is changed
+ * to 2001, as 2001 is not a leap year. Similarly, a seconds
+ * value of 60 or 61 may result in the seconds becoming 0 and
+ * the minute increasing by 1, if the new time does not include
+ * a leap second.
+ *
+ * @param year the year minus 1900.
+ * @deprecated Use Calendar instead of Date, and use
+ * set(Calendar.YEAR, year) instead. Note about the 1900
+ * difference in year.
+ * @see #getYear()
+ * @see Calendar
+ */
+ public void setYear(int year)
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(time);
+ cal.set(Calendar.YEAR, 1900 + year);
+ time = cal.getTimeInMillis();
+ }
+
+ /**
+ * Returns the month represented by this <code>Date</code> object,
+ * as a value between 0 (January) and 11 (December).
+ *
+ * @return the month represented by this date object (zero based).
+ * @deprecated Use Calendar instead of Date, and use get(Calendar.MONTH)
+ * instead.
+ * @see #setMonth(int)
+ * @see Calendar
+ */
+ public int getMonth()
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(time);
+ return cal.get(Calendar.MONTH);
+ }
+
+ /**
+ * Sets the month to the given value. The other
+ * fields are only altered as necessary to match
+ * the same date and time in the new month. In most
+ * cases, the other fields won't change at all. However,
+ * in the case of a shorter month or a leap second, values
+ * may be adjusted. For example, if the day of the month
+ * is currently 31, and the month value is changed from
+ * January (0) to September (8), the date will become
+ * October the 1st, as September only has 30 days. Similarly,
+ * a seconds value of 60 or 61 (a leap second) may result
+ * in the seconds value being reset to 0 and the minutes
+ * value being incremented by 1, if the new time does
+ * not include a leap second.
+ *
+ * @param month the month, with a zero-based index
+ * from January.
+ * @deprecated Use Calendar instead of Date, and use
+ * set(Calendar.MONTH, month) instead.
+ * @see #getMonth()
+ * @see Calendar
+ */
+ public void setMonth(int month)
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(time);
+ cal.set(Calendar.MONTH, month);
+ time = cal.getTimeInMillis();
+ }
+
+ /**
+ * Returns the day of the month of this <code>Date</code>
+ * object, as a value between 0 and 31.
+ *
+ * @return the day of month represented by this date object.
+ * @deprecated Use Calendar instead of Date, and use get(Calendar.DATE)
+ * instead.
+ * @see Calendar
+ * @see #setDate(int)
+ */
+ public int getDate()
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(time);
+ return cal.get(Calendar.DATE);
+ }
+
+ /**
+ * Sets the date to the given value. The other
+ * fields are only altered as necessary to match
+ * the same date and time on the new day of the month. In most
+ * cases, the other fields won't change at all. However,
+ * in the case of a leap second or the day being out of
+ * the range of the current month, values
+ * may be adjusted. For example, if the day of the month
+ * is currently 30 and the month is June, a new day of the
+ * month value of 31 will cause the month to change to July,
+ * as June only has 30 days . Similarly,
+ * a seconds value of 60 or 61 (a leap second) may result
+ * in the seconds value being reset to 0 and the minutes
+ * value being incremented by 1, if the new time does
+ * not include a leap second.
+ *
+ * @param date the date.
+ * @deprecated Use Calendar instead of Date, and use
+ * set(Calendar.DATE, date) instead.
+ * @see Calendar
+ * @see #getDate()
+ */
+ public void setDate(int date)
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(time);
+ cal.set(Calendar.DATE, date);
+ time = cal.getTimeInMillis();
+ }
+
+ /**
+ * Returns the day represented by this <code>Date</code>
+ * object as an integer between 0 (Sunday) and 6 (Saturday).
+ *
+ * @return the day represented by this date object.
+ * @deprecated Use Calendar instead of Date, and use get(Calendar.DAY_OF_WEEK)
+ * instead.
+ * @see Calendar
+ */
+ public int getDay()
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(time);
+ // For Calendar, Sunday is 1. For Date, Sunday is 0.
+ return cal.get(Calendar.DAY_OF_WEEK) - 1;
+ }
+
+ /**
+ * Returns the hours represented by this <code>Date</code>
+ * object as an integer between 0 and 23.
+ *
+ * @return the hours represented by this date object.
+ * @deprecated Use Calendar instead of Date, and use get(Calendar.HOUR_OF_DAY)
+ * instead.
+ * @see Calendar
+ * @see #setHours(int)
+ */
+ public int getHours()
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(time);
+ return cal.get(Calendar.HOUR_OF_DAY);
+ }
+
+ /**
+ * Sets the hours to the given value. The other
+ * fields are only altered as necessary to match
+ * the same date and time in the new hour. In most
+ * cases, the other fields won't change at all. However,
+ * in the case of a leap second, values
+ * may be adjusted. For example,
+ * a seconds value of 60 or 61 (a leap second) may result
+ * in the seconds value being reset to 0 and the minutes
+ * value being incremented by 1 if the new hour does
+ * not contain a leap second.
+ *
+ * @param hours the hours.
+ * @deprecated Use Calendar instead of Date, and use
+ * set(Calendar.HOUR_OF_DAY, hours) instead.
+ * @see Calendar
+ * @see #getHours()
+ */
+ public void setHours(int hours)
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(time);
+ cal.set(Calendar.HOUR_OF_DAY, hours);
+ time = cal.getTimeInMillis();
+ }
+
+ /**
+ * Returns the number of minutes represented by the <code>Date</code>
+ * object, as an integer between 0 and 59.
+ *
+ * @return the minutes represented by this date object.
+ * @deprecated Use Calendar instead of Date, and use get(Calendar.MINUTE)
+ * instead.
+ * @see Calendar
+ * @see #setMinutes(int)
+ */
+ public int getMinutes()
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(time);
+ return cal.get(Calendar.MINUTE);
+ }
+
+ /**
+ * Sets the minutes to the given value. The other
+ * fields are only altered as necessary to match
+ * the same date and time in the new minute. In most
+ * cases, the other fields won't change at all. However,
+ * in the case of a leap second, values
+ * may be adjusted. For example,
+ * a seconds value of 60 or 61 (a leap second) may result
+ * in the seconds value being reset to 0 and the minutes
+ * value being incremented by 1 if the new minute does
+ * not contain a leap second.
+ *
+ * @param minutes the minutes.
+ * @deprecated Use Calendar instead of Date, and use
+ * set(Calendar.MINUTE, minutes) instead.
+ * @see Calendar
+ * @see #getMinutes()
+ */
+ public void setMinutes(int minutes)
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(time);
+ cal.set(Calendar.MINUTE, minutes);
+ time = cal.getTimeInMillis();
+ }
+
+ /**
+ * Returns the number of seconds represented by the <code>Date</code>
+ * object, as an integer between 0 and 61 (60 and 61 being leap seconds).
+ *
+ * @return the seconds represented by this date object.
+ * @deprecated Use Calendar instead of Date, and use get(Calendar.SECOND)
+ * instead.
+ * @see Calendar
+ * @see #setSeconds(int)
+ */
+ public int getSeconds()
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(time);
+ return cal.get(Calendar.SECOND);
+ }
+
+ /**
+ * Sets the seconds to the given value. The other
+ * fields are only altered as necessary to match
+ * the same date and time in the new minute. In most
+ * cases, the other fields won't change at all. However,
+ * in the case of a leap second, values
+ * may be adjusted. For example, setting the
+ * seconds value to 60 or 61 (a leap second) may result
+ * in the seconds value being reset to 0 and the minutes
+ * value being incremented by 1, if the current time does
+ * not contain a leap second.
+ *
+ * @param seconds the seconds.
+ * @deprecated Use Calendar instead of Date, and use
+ * set(Calendar.SECOND, seconds) instead.
+ * @see Calendar
+ * @see #getSeconds()
+ */
+ public void setSeconds(int seconds)
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(time);
+ cal.set(Calendar.SECOND, seconds);
+ time = cal.getTimeInMillis();
+ }
+
+ /**
+ * Deserializes a <code>Date</code> object from an
+ * input stream, setting the time (in milliseconds
+ * since the epoch) to the long value read from the
+ * stream.
+ *
+ * @param input the input stream.
+ * @throws IOException if an I/O error occurs in the stream.
+ * @throws ClassNotFoundException if the class of the
+ * serialized object could not be found.
+ */
+ private void readObject(ObjectInputStream input)
+ throws IOException, ClassNotFoundException
+ {
+ input.defaultReadObject();
+ time = input.readLong();
+ }
+
+ /**
+ * Serializes a <code>Date</code> object to an output stream,
+ * storing the time (in milliseconds since the epoch) as a long
+ * value in the stream.
+ *
+ * @serialdata A long value representing the offset from the epoch
+ * in milliseconds. This is the same value that is returned by the
+ * method getTime().
+ * @param output the output stream.
+ * @throws IOException if an I/O error occurs in the stream.
+ */
+ private void writeObject(ObjectOutputStream output)
+ throws IOException
+ {
+ output.defaultWriteObject();
+ output.writeLong(time);
+ }
+
+}
diff --git a/libjava/classpath/java/util/Dictionary.java b/libjava/classpath/java/util/Dictionary.java
new file mode 100644
index 000000000..acd90eb04
--- /dev/null
+++ b/libjava/classpath/java/util/Dictionary.java
@@ -0,0 +1,136 @@
+/* Dictionary.java -- an abstract (and essentially worthless)
+ class which is Hashtable's superclass
+ Copyright (C) 1998, 2001, 2002, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * A Dictionary maps keys to values; <i>how</i> it does that is
+ * implementation-specific.
+ *
+ * This is an abstract class which has really gone by the wayside.
+ * People at Javasoft are probably embarrassed by it. At this point,
+ * it might as well be an interface rather than a class, but it remains
+ * this poor, laughable skeleton for the sake of backwards compatibility.
+ * At any rate, this was what came before the {@link Map} interface
+ * in the Collections framework.
+ *
+ * @author Jon Zeppieri
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Map
+ * @see Hashtable
+ * @since 1.0
+ * @status updated to 1.4
+ */
+public abstract class Dictionary<K, V>
+{
+ // WARNING: Dictionary is a CORE class in the bootstrap cycle. See the
+ // comments in vm/reference/java/lang/Runtime for implications of this fact.
+
+ /**
+ * Sole constructor (often called implicitly).
+ */
+ public Dictionary()
+ {
+ }
+
+ /**
+ * Returns an Enumeration of the values in this Dictionary.
+ *
+ * @return an Enumeration of the values
+ * @see #keys()
+ */
+ public abstract Enumeration<V> elements();
+
+ /**
+ * Returns the value associated with the supplied key, or null
+ * if no such value exists. Since Dictionaries are not allowed null keys
+ * or elements, a null result always means the key is not present.
+ *
+ * @param key the key to use to fetch the value
+ * @return the mapped value
+ * @throws NullPointerException if key is null
+ * @see #put(Object, Object)
+ */
+ public abstract V get(Object key);
+
+ /**
+ * Returns true when there are no elements in this Dictionary.
+ *
+ * @return <code>size() == 0</code>
+ */
+ public abstract boolean isEmpty();
+
+ /**
+ * Returns an Enumeration of the keys in this Dictionary
+ *
+ * @return an Enumeration of the keys
+ * @see #elements()
+ */
+ public abstract Enumeration<K> keys();
+
+ /**
+ * Inserts a new value into this Dictionary, located by the
+ * supplied key. Dictionary does not support null keys or values, so
+ * a null return can safely be interpreted as adding a new key.
+ *
+ * @param key the key which locates the value
+ * @param value the value to put into the Dictionary
+ * @return the previous value of the key, or null if there was none
+ * @throws NullPointerException if key or value is null
+ * @see #get(Object)
+ */
+ public abstract V put(K key, V value);
+
+ /**
+ * Removes from the Dictionary the value located by the given key. A null
+ * return safely means that the key was not mapped in the Dictionary.
+ *
+ * @param key the key used to locate the value to be removed
+ * @return the value associated with the removed key
+ * @throws NullPointerException if key is null
+ */
+ public abstract V remove(Object key);
+
+ /**
+ * Returns the number of values currently in this Dictionary.
+ *
+ * @return the number of keys in the Dictionary
+ */
+ public abstract int size();
+} // class Dictionary
diff --git a/libjava/classpath/java/util/DuplicateFormatFlagsException.java b/libjava/classpath/java/util/DuplicateFormatFlagsException.java
new file mode 100644
index 000000000..38c37669d
--- /dev/null
+++ b/libjava/classpath/java/util/DuplicateFormatFlagsException.java
@@ -0,0 +1,88 @@
+/* DuplicateFormatFlagsException.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Thrown when the flags supplied to the {@link Formatter#format()}
+ * method of a {@link Formatter} contain duplicates.
+ *
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+public class DuplicateFormatFlagsException
+ extends IllegalFormatException
+{
+ private static final long serialVersionUID = 18890531L;
+
+ /**
+ * The flags which contain a duplicate.
+ *
+ * @serial the flags containing a duplicate.
+ */
+ // Note: name fixed by serialization.
+ private String flags;
+
+ /**
+ * Constructs a new <code>DuplicateFormatFlagsException</code>
+ * which specifies that the supplied set of flags contains a
+ * duplicate.
+ *
+ * @param flags the flags containing a duplicate.
+ * @throws NullPointerException if <code>flags</code> is null.
+ */
+ public DuplicateFormatFlagsException(String flags)
+ {
+ super("Duplicate flag passed in " + flags);
+ if (flags == null)
+ throw new
+ NullPointerException("Null flags value passed to constructor.");
+ this.flags = flags;
+ }
+
+ /**
+ * Returns the flags which contain a duplicate.
+ *
+ * @return the flags.
+ */
+ public String getFlags()
+ {
+ return flags;
+ }
+}
diff --git a/libjava/classpath/java/util/EmptyStackException.java b/libjava/classpath/java/util/EmptyStackException.java
new file mode 100644
index 000000000..e8b4509ee
--- /dev/null
+++ b/libjava/classpath/java/util/EmptyStackException.java
@@ -0,0 +1,69 @@
+/* EmptyStackException.java -- Attempt to pop from an empty stack
+ Copyright (C) 1998, 1999, 2001, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/* 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.
+ */
+
+/**
+ * This exception is thrown by the Stack class when an attempt is made to pop
+ * or otherwise access elements from an empty stack.
+ *
+ * @author Warren Levy (warrenl@cygnus.com)
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Stack
+ * @since 1.0
+ * @status updated to 1.4
+ */
+public class EmptyStackException extends RuntimeException
+{
+ /**
+ * Compatible with JDK 1.0.
+ */
+ private static final long serialVersionUID = 5084686378493302095L;
+
+ /**
+ * Constructs an EmptyStackException with no detail message.
+ */
+ public EmptyStackException()
+ {
+ }
+}
diff --git a/libjava/classpath/java/util/EnumMap.java b/libjava/classpath/java/util/EnumMap.java
new file mode 100644
index 000000000..78f05002f
--- /dev/null
+++ b/libjava/classpath/java/util/EnumMap.java
@@ -0,0 +1,405 @@
+/* EnumMap.java - Map where keys are enum constants
+ Copyright (C) 2004, 2005, 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.io.Serializable;
+
+/**
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+
+public class EnumMap<K extends Enum<K>, V>
+ extends AbstractMap<K, V>
+ implements Cloneable, Serializable
+{
+ private static final long serialVersionUID = 458661240069192865L;
+
+ V[] store;
+ int cardinality;
+ Class<K> enumClass;
+
+ /**
+ * The cache for {@link #entrySet()}.
+ */
+ transient Set<Map.Entry<K, V>> entries;
+
+ static final Object emptySlot = new Object();
+
+ public EnumMap(Class<K> keyType)
+ {
+ store = (V[]) new Object[keyType.getEnumConstants().length];
+ Arrays.fill(store, emptySlot);
+ cardinality = 0;
+ enumClass = keyType;
+ }
+
+ public EnumMap(EnumMap<K, ? extends V> map)
+ {
+ store = (V[]) map.store.clone();
+ cardinality = map.cardinality;
+ enumClass = map.enumClass;
+ }
+
+ public EnumMap(Map<K, ? extends V> map)
+ {
+ if (map instanceof EnumMap)
+ {
+ EnumMap<K, ? extends V> other = (EnumMap<K, ? extends V>) map;
+ store = (V[]) other.store.clone();
+ cardinality = other.cardinality;
+ enumClass = other.enumClass;
+ }
+ else
+ {
+ for (K key : map.keySet())
+ {
+ V value = map.get(key);
+ if (store == null)
+ {
+ enumClass = key.getDeclaringClass();
+ store = (V[]) new Object[enumClass.getEnumConstants().length];
+ }
+ int o = key.ordinal();
+ if (store[o] == emptySlot)
+ ++cardinality;
+ store[o] = value;
+ }
+ // There must be a single element.
+ if (store == null)
+ throw new IllegalArgumentException("no elements in map");
+ }
+ }
+
+ public int size()
+ {
+ return cardinality;
+ }
+
+ public boolean containsValue(Object value)
+ {
+ for (V i : store)
+ {
+ if (i != emptySlot && AbstractCollection.equals(i , value))
+ return true;
+ }
+ return false;
+ }
+
+ public boolean containsKey(Object key)
+ {
+ if (! (key instanceof Enum))
+ return false;
+ Enum<K> e = (Enum<K>) key;
+ if (e.getDeclaringClass() != enumClass)
+ return false;
+ return store[e.ordinal()] != emptySlot;
+ }
+
+ public V get(Object key)
+ {
+ if (! (key instanceof Enum))
+ return null;
+ Enum<K> e = (Enum<K>) key;
+ if (e.getDeclaringClass() != enumClass)
+ return null;
+ V o = store[e.ordinal()];
+ return o == emptySlot ? null : o;
+ }
+
+ public V put(K key, V value)
+ {
+ int o = key.ordinal();
+ V result;
+ if (store[o] == emptySlot)
+ {
+ result = null;
+ ++cardinality;
+ }
+ else
+ result = store[o];
+ store[o] = value;
+ return result;
+ }
+
+ public V remove(Object key)
+ {
+ if (! (key instanceof Enum))
+ return null;
+ Enum<K> e = (Enum<K>) key;
+ if (e.getDeclaringClass() != enumClass)
+ return null;
+ V result = store[e.ordinal()];
+ if (result == emptySlot)
+ result = null;
+ else
+ --cardinality;
+ store[e.ordinal()] = (V) emptySlot;
+ return result;
+ }
+
+ public void putAll(Map<? extends K, ? extends V> map)
+ {
+ for (K key : map.keySet())
+ {
+ V value = map.get(key);
+
+ int o = key.ordinal();
+ if (store[o] == emptySlot)
+ ++cardinality;
+ store[o] = value;
+ }
+ }
+
+ public void clear()
+ {
+ Arrays.fill(store, emptySlot);
+ cardinality = 0;
+ }
+
+ public Set<K> keySet()
+ {
+ if (keys == null)
+ {
+ keys = new AbstractSet<K>()
+ {
+ public int size()
+ {
+ return cardinality;
+ }
+
+ public Iterator<K> iterator()
+ {
+ return new Iterator<K>()
+ {
+ int count = 0;
+ int index = -1;
+
+ public boolean hasNext()
+ {
+ return count < cardinality;
+ }
+
+ public K next()
+ {
+ ++count;
+ for (++index; store[index] == emptySlot; ++index)
+ ;
+ return enumClass.getEnumConstants()[index];
+ }
+
+ public void remove()
+ {
+ --cardinality;
+ store[index] = (V) emptySlot;
+ }
+ };
+ }
+
+ public void clear()
+ {
+ EnumMap.this.clear();
+ }
+
+ public boolean contains(Object o)
+ {
+ return contains(o);
+ }
+
+ public boolean remove(Object o)
+ {
+ return EnumMap.this.remove(o) != null;
+ }
+ };
+ }
+ return keys;
+ }
+
+ public Collection<V> values()
+ {
+ if (values == null)
+ {
+ values = new AbstractCollection<V>()
+ {
+ public int size()
+ {
+ return cardinality;
+ }
+
+ public Iterator<V> iterator()
+ {
+ return new Iterator<V>()
+ {
+ int count = 0;
+ int index = -1;
+
+ public boolean hasNext()
+ {
+ return count < cardinality;
+ }
+
+ public V next()
+ {
+ ++count;
+ for (++index; store[index] == emptySlot; ++index)
+ ;
+ return store[index];
+ }
+
+ public void remove()
+ {
+ --cardinality;
+ store[index] = (V) emptySlot;
+ }
+ };
+ }
+
+ public void clear()
+ {
+ EnumMap.this.clear();
+ }
+ };
+ }
+ return values;
+ }
+
+ public Set<Map.Entry<K, V>> entrySet()
+ {
+ if (entries == null)
+ {
+ entries = new AbstractSet<Map.Entry<K, V>>()
+ {
+ public int size()
+ {
+ return cardinality;
+ }
+
+ public Iterator<Map.Entry<K, V>> iterator()
+ {
+ return new Iterator<Map.Entry<K, V>>()
+ {
+ int count = 0;
+ int index = -1;
+
+ public boolean hasNext()
+ {
+ return count < cardinality;
+ }
+
+ public Map.Entry<K,V> next()
+ {
+ ++count;
+ for (++index; store[index] == emptySlot; ++index)
+ ;
+ // FIXME: we could just return something that
+ // only knows the index. That would be cleaner.
+ return new AbstractMap.SimpleEntry<K, V>(enumClass.getEnumConstants()[index],
+ store[index])
+ {
+ public V setValue(V newVal)
+ {
+ value = newVal;
+ return put(key, newVal);
+ }
+ };
+ }
+
+ public void remove()
+ {
+ --cardinality;
+ store[index] = (V) emptySlot;
+ }
+ };
+ }
+
+ public void clear()
+ {
+ EnumMap.this.clear();
+ }
+
+ public boolean contains(Object o)
+ {
+ if (! (o instanceof Map.Entry))
+ return false;
+ Map.Entry<K, V> other = (Map.Entry<K, V>) o;
+ return (containsKey(other.getKey())
+ && AbstractCollection.equals(get(other.getKey()),
+ other.getValue()));
+ }
+
+ public boolean remove(Object o)
+ {
+ if (! (o instanceof Map.Entry))
+ return false;
+ Map.Entry<K, V> other = (Map.Entry<K, V>) o;
+ return EnumMap.this.remove(other.getKey()) != null;
+ }
+ };
+ }
+ return entries;
+ }
+
+ public boolean equals(Object o)
+ {
+ if (! (o instanceof EnumMap))
+ return false;
+ EnumMap<K, V> other = (EnumMap<K, V>) o;
+ if (other.enumClass != enumClass || other.cardinality != cardinality)
+ return false;
+ return Arrays.equals(store, other.store);
+ }
+
+ public EnumMap<K, V> clone()
+ {
+ EnumMap<K, V> result;
+ try
+ {
+ result = (EnumMap<K, V>) super.clone();
+ }
+ catch (CloneNotSupportedException ignore)
+ {
+ // Can't happen.
+ result = null;
+ }
+ result.store = (V[]) store.clone();
+ return result;
+ }
+
+}
diff --git a/libjava/classpath/java/util/EnumSet.java b/libjava/classpath/java/util/EnumSet.java
new file mode 100644
index 000000000..60d010654
--- /dev/null
+++ b/libjava/classpath/java/util/EnumSet.java
@@ -0,0 +1,518 @@
+/* EnumSet.java - Set of enum objects
+ Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.io.Serializable;
+
+/**
+ * <p>
+ * Provides an efficient mechanism for recording a set of enumeration
+ * constants. As enumerations have a known set of possible values, certain
+ * assumptions can be made when creating a set of constants. The maximum
+ * size of the set will always be equal to the number of constants, and each
+ * value will always be one of these constants. As a result, the set only needs
+ * to store whether a particular constant is present or not rather than the
+ * values themselves. Each constant can thus be represented by a single bit.
+ * </p>
+ * <p>
+ * This class is designed to provide an alternative to using integer bit flags
+ * by providing a typesafe {@link Collection} interface with an underlying
+ * implementation that utilises the assumptions above to give an equivalent level
+ * of efficiency. The values in a {@link EnumSet} must all be from the same
+ * {@link Enum} type, which allows the contents to be packed into a bit vector.
+ * A containment test is then simply a matter of inspecting the appropriate bit, while
+ * addition involves setting the same. Such basic operations take place in constant
+ * time.
+ * </p>
+ * <p>
+ * The {@link Iterator} implementation traverses the values in the natural order
+ * of the enumeration provided by each constant's {@link Enum#ordinal()}. It is
+ * <emph>weakly consistent</emph> and will not throw a {@link ConcurrentModificationException}.
+ * This means that concurrent changes to the set may or may not be noticeable during
+ * traversal.
+ * </p>
+ * <p>
+ * As is usual with most collections, the set is not synchronized by default. This
+ * can be remedied by using the {@link Collections#synchronizedSet(Set)} method. Null
+ * elements are not supported and attempts to add one will throw a {@link NullPointerException}.
+ * </p>
+ *
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @author Dalibor Topic (robilad@kaffe.org)
+ * @since 1.5
+ */
+
+// FIXME: serialization is special, uses SerializationProxy.
+// of(E e) is the 'bottom' method that creates a real EnumSet.
+public abstract class EnumSet<T extends Enum<T>>
+ extends AbstractSet<T>
+ implements Cloneable, Serializable
+{
+ private static final long serialVersionUID = 4782406773684236311L;
+
+ // These fields could go into the anonymous inner class in of(E),
+ // complementOf would need to be refactored then, though.
+ /**
+ * The store which maintains the bits used to represent
+ * the enumeration constants.
+ */
+ BitSet store;
+
+ /**
+ * The cardinality of the set (the current number
+ * of bits set).
+ */
+ int cardinality;
+
+ /**
+ * The enumeration used by this set.
+ */
+ Class<T> enumClass;
+
+ /**
+ * Empty package-private constructor
+ */
+ EnumSet()
+ {
+ }
+
+ /**
+ * Returns a clone of the set.
+ *
+ * @return a clone of the set.
+ */
+ public EnumSet<T> clone()
+ {
+ EnumSet<T> r;
+
+ try
+ {
+ r = (EnumSet<T>) super.clone();
+ }
+ catch (CloneNotSupportedException _)
+ {
+ /* Can't happen */
+ return null;
+ }
+ r.store = (BitSet) store.clone();
+ return r;
+ }
+
+ /**
+ * Returns a set for the given enumeration type where
+ * all the constants are present.
+ *
+ * @param eltType the type of enumeration to use for the set.
+ * @return an {@link EnumSet} with all the bits set.
+ * @throws NullPointerException if the element type is <code>null</code>.
+ */
+ public static <T extends Enum<T>> EnumSet<T> allOf(Class<T> eltType)
+ {
+ // create an EnumSet from the list of values of the type
+ return copyOf(Arrays.asList(eltType.getEnumConstants()));
+ }
+
+ /**
+ * Returns a set for the given enumeration type where
+ * none of the constants are present.
+ *
+ * @param eltType the type of enumeration to use for the set.
+ * @return an {@link EnumSet} with none of the bits set.
+ * @throws NullPointerException if the element type is <code>null</code>.
+ */
+ public static <T extends Enum<T>> EnumSet<T> noneOf(Class<T> eltType)
+ {
+ return complementOf(allOf(eltType));
+ }
+
+ /**
+ * Returns a clone of the given set.
+ *
+ * @param other the set to clone.
+ * @return an {@link EnumSet} that is a clone of the given set.
+ * @throws NullPointerException if <code>other</code> is <code>null</code>.
+ */
+ public static <T extends Enum<T>> EnumSet<T> copyOf(EnumSet<T> other)
+ {
+ return other.clone();
+ }
+
+ /**
+ * Creates an {@link EnumSet} using the contents of the given collection.
+ * If the collection is also an {@link EnumSet}, this method works the
+ * same as {@link #copyOf(EnumSet)}. Otherwise, the elements of the collection
+ * are inspected and used to populate the new set.
+ *
+ * @param other the collection to use to populate the new set.
+ * @return an {@link EnumSet} containing elements from the given collection.
+ * @throws NullPointerException if <code>other</code> is <code>null</code>.
+ * @throws IllegalArgumentException if the collection is empty.
+ */
+ public static <T extends Enum<T>> EnumSet<T> copyOf(Collection<T> other)
+ {
+ if (other instanceof EnumSet)
+ return copyOf((EnumSet<T>) other);
+ if (other.isEmpty())
+ throw new IllegalArgumentException("Collection is empty");
+
+ EnumSet<T> r = null;
+
+ for (T val : other)
+ {
+ if (r == null)
+ r = of(val);
+ else
+ r.add(val);
+ }
+
+ return r;
+ }
+
+ /**
+ * Returns a set which is the inverse of the supplied set.
+ * If a constant is present in the current set, it will not be
+ * present in the new set and vice versa.
+ *
+ * @param other the set to provide the complement of.
+ * @return an {@link EnumSet} which is the inverse of the current one.
+ * @throws NullPointerException if <code>other</code> is <code>null</code>.
+ */
+ public static <T extends Enum<T>> EnumSet<T> complementOf(EnumSet<T> other)
+ {
+ EnumSet<T> r = other.clone();
+ int numConstants = r.enumClass.getEnumConstants().length;
+ r.store.flip(0, numConstants);
+ r.cardinality = numConstants - other.cardinality;
+ return r;
+ }
+
+ /**
+ * Creates a new {@link EnumSet} populated with the given element.
+ *
+ * @param first the element to use to populate the new set.
+ * @return an {@link EnumSet} containing the element.
+ * @throws NullPointerException if <code>first</code> is <code>null</code>.
+ */
+ public static <T extends Enum<T>> EnumSet<T> of(T first)
+ {
+ EnumSet<T> r = new EnumSet<T>()
+ {
+ public boolean add(T val)
+ {
+ if (store.get(val.ordinal()))
+ return false;
+
+ store.set(val.ordinal());
+ ++cardinality;
+ return true;
+ }
+
+ public boolean addAll(Collection<? extends T> c)
+ {
+ boolean result = false;
+ if (c instanceof EnumSet)
+ {
+ EnumSet<T> other = (EnumSet<T>) c;
+ if (enumClass == other.enumClass)
+ {
+ store.or(other.store);
+ int save = cardinality;
+ cardinality = store.cardinality();
+ result = save != cardinality;
+ }
+ }
+ else
+ {
+ for (T val : c)
+ {
+ if (add (val))
+ result = true;
+ }
+ }
+ return result;
+ }
+
+ public void clear()
+ {
+ store.clear();
+ cardinality = 0;
+ }
+
+ public boolean contains(Object o)
+ {
+ if (! (o instanceof Enum))
+ return false;
+
+ Enum<T> e = (Enum<T>) o;
+ if (e.getDeclaringClass() != enumClass)
+ return false;
+
+ return store.get(e.ordinal());
+ }
+
+ public boolean containsAll(Collection<?> c)
+ {
+ if (c instanceof EnumSet)
+ {
+ EnumSet<T> other = (EnumSet<T>) c;
+ if (enumClass == other.enumClass)
+ return store.containsAll(other.store);
+
+ return false;
+ }
+ return super.containsAll(c);
+ }
+
+ public Iterator<T> iterator()
+ {
+ return new Iterator<T>()
+ {
+ int next = -1;
+ int count = 0;
+
+ public boolean hasNext()
+ {
+ return count < cardinality;
+ }
+
+ public T next()
+ {
+ next = store.nextSetBit(next + 1);
+ ++count;
+ return enumClass.getEnumConstants()[next];
+ }
+
+ public void remove()
+ {
+ if (! store.get(next))
+ {
+ store.clear(next);
+ --cardinality;
+ }
+ }
+ };
+ }
+
+ public boolean remove(Object o)
+ {
+ if (! (o instanceof Enum))
+ return false;
+
+ Enum<T> e = (Enum<T>) o;
+ if (e.getDeclaringClass() != enumClass)
+ return false;
+
+ store.clear(e.ordinal());
+ --cardinality;
+ return true;
+ }
+
+ public boolean removeAll(Collection<?> c)
+ {
+ if (c instanceof EnumSet)
+ {
+ EnumSet<T> other = (EnumSet<T>) c;
+ if (enumClass != other.enumClass)
+ return false;
+
+ store.andNot(other.store);
+ int save = cardinality;
+ cardinality = store.cardinality();
+ return save != cardinality;
+ }
+ return super.removeAll(c);
+ }
+
+ public boolean retainAll(Collection<?> c)
+ {
+ if (c instanceof EnumSet)
+ {
+ EnumSet<T> other = (EnumSet<T>) c;
+ if (enumClass != other.enumClass)
+ return false;
+
+ store.and(other.store);
+ int save = cardinality;
+ cardinality = store.cardinality();
+ return save != cardinality;
+ }
+ return super.retainAll(c);
+ }
+
+ public int size()
+ {
+ return cardinality;
+ }
+ };
+
+ // initialize the class
+ r.enumClass = first.getDeclaringClass();
+ r.store = new BitSet(r.enumClass.getEnumConstants().length);
+
+ r.add(first);
+ return r;
+ }
+
+ /**
+ * Creates a new {@link EnumSet} populated with the given two elements.
+ *
+ * @param first the first element to use to populate the new set.
+ * @param second the second element to use.
+ * @return an {@link EnumSet} containing the elements.
+ * @throws NullPointerException if any of the parameters are <code>null</code>.
+ */
+ public static <T extends Enum<T>> EnumSet<T> of(T first, T second)
+ {
+ EnumSet<T> r = of(first);
+ r.add(second);
+ return r;
+ }
+
+ /**
+ * Creates a new {@link EnumSet} populated with the given three elements.
+ *
+ * @param first the first element to use to populate the new set.
+ * @param second the second element to use.
+ * @param third the third element to use.
+ * @return an {@link EnumSet} containing the elements.
+ * @throws NullPointerException if any of the parameters are <code>null</code>.
+ */
+ public static <T extends Enum<T>> EnumSet<T> of(T first, T second, T third)
+ {
+ EnumSet<T> r = of(first, second);
+ r.add(third);
+ return r;
+ }
+
+ /**
+ * Creates a new {@link EnumSet} populated with the given four elements.
+ *
+ * @param first the first element to use to populate the new set.
+ * @param second the second element to use.
+ * @param third the third element to use.
+ * @param fourth the fourth element to use.
+ * @return an {@link EnumSet} containing the elements.
+ * @throws NullPointerException if any of the parameters are <code>null</code>.
+ */
+ public static <T extends Enum<T>> EnumSet<T> of(T first, T second, T third,
+ T fourth)
+ {
+ EnumSet<T> r = of(first, second, third);
+ r.add(fourth);
+ return r;
+ }
+
+ /**
+ * Creates a new {@link EnumSet} populated with the given five elements.
+ *
+ * @param first the first element to use to populate the new set.
+ * @param second the second element to use.
+ * @param third the third element to use.
+ * @param fourth the fourth element to use.
+ * @param fifth the fifth element to use.
+ * @return an {@link EnumSet} containing the elements.
+ * @throws NullPointerException if any of the parameters are <code>null</code>.
+ */
+ public static <T extends Enum<T>> EnumSet<T> of(T first, T second, T third,
+ T fourth, T fifth)
+ {
+ EnumSet<T> r = of(first, second, third, fourth);
+ r.add(fifth);
+ return r;
+ }
+
+ /**
+ * Creates a new {@link EnumSet} populated with the given elements.
+ *
+ * @param first the first element to use to populate the new set.
+ * @param rest the other elements to use.
+ * @return an {@link EnumSet} containing the elements.
+ * @throws NullPointerException if any of the parameters are <code>null</code>.
+ */
+ public static <T extends Enum<T>> EnumSet<T> of(T first, T... rest)
+ {
+ EnumSet<T> r = noneOf(first.getDeclaringClass());
+ r.add(first);
+ for (T val : rest)
+ r.add(val);
+ return r;
+ }
+
+ /**
+ * Creates a new {@link EnumSet} using the enumeration constants
+ * starting from {@code from} and ending at {@code to} inclusive.
+ * The two may be the same, but they must be in the correct order.
+ * So giving the first constant twice would give a set with just that
+ * constant set, while supplying the first and second constant will give
+ * a set with those two elements. However, specifying the second as
+ * the {@code from} element followed by an earlier element as the
+ * {@code to} element will result in an error.
+ *
+ * @param from the element to start from.
+ * @param to the element to end at (may be the same as {@code from}.
+ * @return an {@link EnumSet} containing the specified range of elements.
+ * @throws NullPointerException if any of the parameters are <code>null</code>.
+ * @throws IllegalArgumentException if {@code first.compareTo(last) > 0}.
+ */
+ public static <T extends Enum<T>> EnumSet<T> range(T from, T to)
+ {
+ if (from.compareTo(to) > 0)
+ throw new IllegalArgumentException();
+ Class<T> type = from.getDeclaringClass();
+ EnumSet<T> r = noneOf(type);
+
+ T[] values = type.getEnumConstants();
+ // skip over values until start of range is found
+ int i = 0;
+ while (from != values[i])
+ i++;
+
+ // add values until end of range is found
+ while (to != values[i]) {
+ r.add(values[i]);
+ i++;
+ }
+
+ // add end of range
+ r.add(to);
+
+ return r;
+ }
+}
diff --git a/libjava/classpath/java/util/Enumeration.java b/libjava/classpath/java/util/Enumeration.java
new file mode 100644
index 000000000..2aec31b6f
--- /dev/null
+++ b/libjava/classpath/java/util/Enumeration.java
@@ -0,0 +1,82 @@
+/* Enumeration.java -- Interface for enumerating lists of objects
+ Copyright (C) 1998, 1999, 2001, 2004, 2005
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
+ * "The Java Language Specification", ISBN 0-201-63451-1.
+ * Status: Believed complete and correct
+ */
+
+/**
+ * Interface for lists of objects that can be returned in sequence. Successive
+ * objects are obtained by the nextElement method.
+ * <p>
+ * As of Java 1.2, the Iterator interface provides the same functionality, but
+ * with shorter method names and a new optional method to remove items from the
+ * list. If writing for 1.2, consider using Iterator instead. Enumerations over
+ * the new collections classes, for use with legacy APIs that require them, can
+ * be obtained by the enumeration method in class Collections.
+ *
+ * @author Warren Levy (warrenl@cygnus.com)
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Iterator
+ * @see Hashtable
+ * @see Vector
+ * @since 1.0
+ * @status updated to 1.4
+ */
+public interface Enumeration<E>
+{
+ /**
+ * Tests whether there are elements remaining in the enumeration.
+ *
+ * @return true if there is at least one more element in the enumeration,
+ * that is, if the next call to nextElement will not throw a
+ * NoSuchElementException.
+ */
+ boolean hasMoreElements();
+
+ /**
+ * Obtain the next element in the enumeration.
+ *
+ * @return the next element in the enumeration
+ * @throws NoSuchElementException if there are no more elements
+ */
+ E nextElement();
+}
diff --git a/libjava/classpath/java/util/EventListener.java b/libjava/classpath/java/util/EventListener.java
new file mode 100644
index 000000000..c9a1795f9
--- /dev/null
+++ b/libjava/classpath/java/util/EventListener.java
@@ -0,0 +1,54 @@
+/* EventListener.java -- tagging interface for all event listeners
+ Copyright (C) 1998, 1999, 2001, 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Empty interface that is implemented by classes that need to receive
+ * events. Subinterfaces define methods that can be called to fire an
+ * event notification. Normally the name of these subinterfaces end in
+ * <code>Listener</code> and all method described by the subinterface
+ * take as argument an subclass of <code>EventObject</code>.
+ *
+ * @author Tom Tromey (tromey@cygnus.com)
+ * @see EventObject
+ * @status updated to 1.4
+ */
+public interface EventListener
+{
+}
diff --git a/libjava/classpath/java/util/EventListenerProxy.java b/libjava/classpath/java/util/EventListenerProxy.java
new file mode 100644
index 000000000..245c5ffb4
--- /dev/null
+++ b/libjava/classpath/java/util/EventListenerProxy.java
@@ -0,0 +1,75 @@
+/* EventListenerProxy.java -- abstract wrapper for event listeners
+ Copyright (C) 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * An abstract wrapper for event listeners. This allows subclasses to
+ * attach additional parameters to an existing event listener to create
+ * a new one. Subclasses are expected to add methods to set and retrieve
+ * any attached properties.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @since 1.4
+ * @status updated to 1.4
+ */
+public abstract class EventListenerProxy implements EventListener
+{
+ /** The listener that this proxy wraps. */
+ private final EventListener listener;
+
+ /**
+ * Construct a proxy event listener, given an existing one to augment.
+ *
+ * @param listener the listener to wrap
+ */
+ public EventListenerProxy(EventListener listener)
+ {
+ this.listener = listener;
+ }
+
+ /**
+ * Return the wrapped event listener.
+ *
+ * @return the listener associated with this proxy
+ */
+ public EventListener getListener()
+ {
+ return listener;
+ }
+} // class EventListenerProxy
diff --git a/libjava/classpath/java/util/EventObject.java b/libjava/classpath/java/util/EventObject.java
new file mode 100644
index 000000000..7ced18aa4
--- /dev/null
+++ b/libjava/classpath/java/util/EventObject.java
@@ -0,0 +1,101 @@
+/* EventObject.java -- represents an event on an object
+ Copyright (C) 1999, 2000, 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.io.Serializable;
+
+/**
+ * Represents Events fired by Objects.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see EventListener
+ * @since 1.1
+ * @status updated to 1.4
+ */
+public class EventObject implements Serializable
+{
+ /**
+ * Compatible with JDK 1.1+.
+ */
+ private static final long serialVersionUID = 5516075349620653480L;
+
+ /**
+ * The source object; in other words, the object which this event takes
+ * place on.
+ */
+ protected transient Object source;
+
+ /**
+ * Constructs an EventObject with the specified source.
+ *
+ * @param source the source of the event
+ * @throws IllegalArgumentException if source is null (This is not
+ * specified, but matches the behavior of the JDK)
+ */
+ public EventObject(Object source)
+ {
+ // This check for null is stupid, if you ask me, since source is
+ // protected and non-final, so a subclass can set it to null later on.
+ if (source == null)
+ throw new IllegalArgumentException();
+ this.source = source;
+ }
+
+ /**
+ * Returns the source of the event.
+ *
+ * @return the event source
+ */
+ public Object getSource()
+ {
+ return source;
+ }
+
+ /**
+ * Converts the event to a String. The format is not specified, but by
+ * observation, the JDK uses:
+ * <code>getClass().getName() + "[source=" + source + "]";</code>.
+ *
+ * @return String representation of the Event
+ */
+ public String toString()
+ {
+ return getClass().getName() + "[source=" + source + "]";
+ }
+} // class EventObject
diff --git a/libjava/classpath/java/util/FormatFlagsConversionMismatchException.java b/libjava/classpath/java/util/FormatFlagsConversionMismatchException.java
new file mode 100644
index 000000000..b28ded187
--- /dev/null
+++ b/libjava/classpath/java/util/FormatFlagsConversionMismatchException.java
@@ -0,0 +1,111 @@
+/* FormatFlagsConversionMismatchException.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Thrown when the flags supplied to the {@link Formatter#format()}
+ * method of a {@link Formatter} contains a flag that does not match
+ * the conversion character specified for it.
+ *
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+public class FormatFlagsConversionMismatchException
+ extends IllegalFormatException
+{
+ private static final long serialVersionUID = 19120414L;
+
+ /**
+ * The mismatching flag.
+ *
+ * @serial the mismatching flag.
+ */
+ // Note: name fixed by serialization.
+ private String f;
+
+ /**
+ * The conversion character which doesn't match the
+ * appropriate flag.
+ *
+ * @serial the conversion character which doesn't match its flag.
+ */
+ // Note: name fixed by serialization.
+ private char c;
+
+ /**
+ * Constructs a new <code>FormatFlagsConversionMismatchException</code>
+ * which specifies that the flag, <code>f</code>, does
+ * not match its appropriate conversion character, <code>c</code>.
+ *
+ * @param f the mismatching flag.
+ * @param c the conversion character which doesn't match its flag.
+ * @throws NullPointerException if <code>f</code> is null.
+ */
+ public FormatFlagsConversionMismatchException(String f, char c)
+ {
+ super("Invalid flag " + f + " for conversion " + c);
+ if (f == null)
+ throw new
+ NullPointerException("Null flag value passed to constructor.");
+ this.f = f;
+ this.c = c;
+ }
+
+ /**
+ * Returns the conversion character which doesn't
+ * match the flag.
+ *
+ * @return the conversion character.
+ */
+ public char getConversion()
+ {
+ return c;
+ }
+
+ /**
+ * Returns the mismatching flag.
+ *
+ * @return the mismatching flag.
+ */
+ public String getFlags()
+ {
+ return f;
+ }
+}
diff --git a/libjava/classpath/java/util/Formattable.java b/libjava/classpath/java/util/Formattable.java
new file mode 100644
index 000000000..6a83ed5d2
--- /dev/null
+++ b/libjava/classpath/java/util/Formattable.java
@@ -0,0 +1,92 @@
+/* Formattable.java -- Objects which can be passed to a Formatter
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * <p>
+ * The <code>Formattable</code> interface is used to provide customised
+ * formatting to arbitrary objects via the {@link Formatter}. The
+ * {@link #formatTo} method is called for <code>Formattable</code>
+ * objects used with the 's' conversion operator, allowing the object
+ * to provide its own formatting of its internal data.
+ * </p>
+ * <p>
+ * Thread safety is left up to the implementing class. Thus,
+ * {@link Formattable} objects are not guaranteed to be thread-safe,
+ * and users should make their own provisions for multiple thread access.
+ * </p>
+ *
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+public interface Formattable
+{
+
+ /**
+ * Formats the object using the supplied formatter to the specification
+ * provided by the given flags, width and precision.
+ *
+ * @param formatter the formatter to use for formatting the object.
+ * The formatter gives access to the output stream
+ * and locale via {@link Formatter#out()} and
+ * {@link Formatter#locale()} respectively.
+ * @param flags a bit mask constructed from the flags in the
+ * {@link FormattableFlags} class. When no flags
+ * are set, the implementing class should use its
+ * defaults.
+ * @param width the minimum number of characters to include.
+ * A value of -1 indicates no minimum. The remaining
+ * space is padded with ' ' either on the left
+ * (the default) or right (if left justification is
+ * specified by the flags).
+ * @param precision the maximum number of characters to include.
+ * A value of -1 indicates no maximum. This value
+ * is applied prior to the minimum (the width). Thus,
+ * a value may meet the minimum width initially, but
+ * not when the width value is applied, due to
+ * characters being removed by the precision value.
+ * @throws IllegalFormatException if there is a problem with
+ * the syntax of the format
+ * specification or a mismatch
+ * between it and the arguments.
+ */
+ public void formatTo(Formatter formatter, int flags, int width,
+ int precision);
+}
diff --git a/libjava/classpath/java/util/FormattableFlags.java b/libjava/classpath/java/util/FormattableFlags.java
new file mode 100644
index 000000000..2c5e199ba
--- /dev/null
+++ b/libjava/classpath/java/util/FormattableFlags.java
@@ -0,0 +1,123 @@
+/* FormattableFlags.java --
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * This class contains a set of flags used
+ * by the {@link Formattable#formatTo()} method.
+ * They are used to modify the output of the
+ * {@link Formattable}. The interpretation and
+ * validation of the flags is left to the
+ * particular {@link Formattable}.
+ *
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+public class FormattableFlags
+{
+
+ /**
+ * Requires the output to be left-justified. Any spaces
+ * required to meet the specified width will be added to
+ * the right of the output. The default output is
+ * right-justified, where spaces are added to the left.
+ * The output is as for the format specifier
+ * '-' ('\u002d').
+ */
+ public static final int LEFT_JUSTIFY = 1;
+
+ /**
+ * Requires the output to be in uppercase. The output
+ * should be the same as the result from calling
+ * {@link String#toUpperCase(java.util.Locale)} with
+ * the formatting locale. The output is as for the
+ * format specifier '^' ('\u005e').
+ */
+ public static final int UPPERCASE = 2;
+
+ /**
+ * Requires the use of an alternate form, as specified
+ * in the documentation of {@link Formattable}.
+ * The output is as for the format specifier
+ * '#' ('\u0023').
+ */
+ public static final int ALTERNATE = 4;
+
+ // Used internally by Formatter.
+ // Changes here must be reflected in the FLAGS string there.
+
+ /**
+ * Requires the output to always include a '+' sign.
+ * The output is as for the format specifier '+'.
+ */
+ static final int PLUS = 8;
+
+ /**
+ * Requires the output to include a leading space on
+ * positive value. The output is as for the format
+ * specifier ' '.
+ */
+ static final int SPACE = 16;
+
+ /**
+ * Requires the output to be zero-padded. The output
+ * is as for the format specifier '0'.
+ */
+ static final int ZERO = 32;
+
+ /**
+ * Requires the output to include locale-specific
+ * grouping operators. The output is as for the
+ * format specifier ','.
+ */
+ static final int COMMA = 64;
+
+ /**
+ * Requires the output to include negative numbers
+ * enclosed in parentheses. The output is as for
+ * the format specifier '('.
+ */
+ static final int PAREN = 128;
+
+ // Not instantiable.
+ private FormattableFlags()
+ {
+ }
+}
diff --git a/libjava/classpath/java/util/Formatter.java b/libjava/classpath/java/util/Formatter.java
new file mode 100644
index 000000000..04ae8058d
--- /dev/null
+++ b/libjava/classpath/java/util/Formatter.java
@@ -0,0 +1,1498 @@
+/* Formatter.java -- printf-style formatting
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.Flushable;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+import java.text.DateFormatSymbols;
+import java.text.DecimalFormatSymbols;
+
+import gnu.classpath.SystemProperties;
+
+/**
+ * <p>
+ * A Java formatter for <code>printf</code>-style format strings,
+ * as seen in the C programming language. This differs from the
+ * C interpretation of such strings by performing much stricter
+ * checking of format specifications and their corresponding
+ * arguments. While unknown conversions will be ignored in C,
+ * and invalid conversions will only produce compiler warnings,
+ * the Java version utilises a full range of run-time exceptions to
+ * handle these cases. The Java version is also more customisable
+ * by virtue of the provision of the {@link Formattable} interface,
+ * which allows an arbitrary class to be formatted by the formatter.
+ * </p>
+ * <p>
+ * The formatter is accessible by more convienient static methods.
+ * For example, streams now have appropriate format methods
+ * (the equivalent of <code>fprintf</code>) as do <code>String</code>
+ * objects (the equivalent of <code>sprintf</code>).
+ * </p>
+ * <p>
+ * <strong>Note</strong>: the formatter is not thread-safe. For
+ * multi-threaded access, external synchronization should be provided.
+ * </p>
+ *
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+public final class Formatter
+ implements Closeable, Flushable
+{
+
+ /**
+ * The output of the formatter.
+ */
+ private Appendable out;
+
+ /**
+ * The locale used by the formatter.
+ */
+ private Locale locale;
+
+ /**
+ * Whether or not the formatter is closed.
+ */
+ private boolean closed;
+
+ /**
+ * The last I/O exception thrown by the output stream.
+ */
+ private IOException ioException;
+
+ // Some state used when actually formatting.
+ /**
+ * The format string.
+ */
+ private String format;
+
+ /**
+ * The current index into the string.
+ */
+ private int index;
+
+ /**
+ * The length of the format string.
+ */
+ private int length;
+
+ /**
+ * The formatting locale.
+ */
+ private Locale fmtLocale;
+
+ // Note that we include '-' twice. The flags are ordered to
+ // correspond to the values in FormattableFlags, and there is no
+ // flag (in the sense of this field used when parsing) for
+ // UPPERCASE; the second '-' serves as a placeholder.
+ /**
+ * A string used to index into the formattable flags.
+ */
+ private static final String FLAGS = "--#+ 0,(";
+
+ /**
+ * The system line separator.
+ */
+ private static final String lineSeparator
+ = SystemProperties.getProperty("line.separator");
+
+ /**
+ * The type of numeric output format for a {@link BigDecimal}.
+ */
+ public enum BigDecimalLayoutForm
+ {
+ DECIMAL_FLOAT,
+ SCIENTIFIC
+ }
+
+ /**
+ * Constructs a new <code>Formatter</code> using the default
+ * locale and a {@link StringBuilder} as the output stream.
+ */
+ public Formatter()
+ {
+ this(null, Locale.getDefault());
+ }
+
+ /**
+ * Constructs a new <code>Formatter</code> using the specified
+ * locale and a {@link StringBuilder} as the output stream.
+ * If the locale is <code>null</code>, then no localization
+ * is applied.
+ *
+ * @param loc the locale to use.
+ */
+ public Formatter(Locale loc)
+ {
+ this(null, loc);
+ }
+
+ /**
+ * Constructs a new <code>Formatter</code> using the default
+ * locale and the specified output stream.
+ *
+ * @param app the output stream to use.
+ */
+ public Formatter(Appendable app)
+ {
+ this(app, Locale.getDefault());
+ }
+
+ /**
+ * Constructs a new <code>Formatter</code> using the specified
+ * locale and the specified output stream. If the locale is
+ * <code>null</code>, then no localization is applied.
+ *
+ * @param app the output stream to use.
+ * @param loc the locale to use.
+ */
+ public Formatter(Appendable app, Locale loc)
+ {
+ this.out = app == null ? new StringBuilder() : app;
+ this.locale = loc;
+ }
+
+ /**
+ * Constructs a new <code>Formatter</code> using the default
+ * locale and character set, with the specified file as the
+ * output stream.
+ *
+ * @param file the file to use for output.
+ * @throws FileNotFoundException if the file does not exist
+ * and can not be created.
+ * @throws SecurityException if a security manager is present
+ * and doesn't allow writing to the file.
+ */
+ public Formatter(File file)
+ throws FileNotFoundException
+ {
+ this(new OutputStreamWriter(new FileOutputStream(file)));
+ }
+
+ /**
+ * Constructs a new <code>Formatter</code> using the default
+ * locale, with the specified file as the output stream
+ * and the supplied character set.
+ *
+ * @param file the file to use for output.
+ * @param charset the character set to use for output.
+ * @throws FileNotFoundException if the file does not exist
+ * and can not be created.
+ * @throws SecurityException if a security manager is present
+ * and doesn't allow writing to the file.
+ * @throws UnsupportedEncodingException if the supplied character
+ * set is not supported.
+ */
+ public Formatter(File file, String charset)
+ throws FileNotFoundException, UnsupportedEncodingException
+ {
+ this(file, charset, Locale.getDefault());
+ }
+
+ /**
+ * Constructs a new <code>Formatter</code> using the specified
+ * file as the output stream with the supplied character set
+ * and locale. If the locale is <code>null</code>, then no
+ * localization is applied.
+ *
+ * @param file the file to use for output.
+ * @param charset the character set to use for output.
+ * @param loc the locale to use.
+ * @throws FileNotFoundException if the file does not exist
+ * and can not be created.
+ * @throws SecurityException if a security manager is present
+ * and doesn't allow writing to the file.
+ * @throws UnsupportedEncodingException if the supplied character
+ * set is not supported.
+ */
+ public Formatter(File file, String charset, Locale loc)
+ throws FileNotFoundException, UnsupportedEncodingException
+ {
+ this(new OutputStreamWriter(new FileOutputStream(file), charset),
+ loc);
+ }
+
+ /**
+ * Constructs a new <code>Formatter</code> using the default
+ * locale and character set, with the specified output stream.
+ *
+ * @param out the output stream to use.
+ */
+ public Formatter(OutputStream out)
+ {
+ this(new OutputStreamWriter(out));
+ }
+
+ /**
+ * Constructs a new <code>Formatter</code> using the default
+ * locale, with the specified file output stream and the
+ * supplied character set.
+ *
+ * @param out the output stream.
+ * @param charset the character set to use for output.
+ * @throws UnsupportedEncodingException if the supplied character
+ * set is not supported.
+ */
+ public Formatter(OutputStream out, String charset)
+ throws UnsupportedEncodingException
+ {
+ this(out, charset, Locale.getDefault());
+ }
+
+ /**
+ * Constructs a new <code>Formatter</code> using the specified
+ * output stream with the supplied character set and locale.
+ * If the locale is <code>null</code>, then no localization is
+ * applied.
+ *
+ * @param out the output stream.
+ * @param charset the character set to use for output.
+ * @param loc the locale to use.
+ * @throws UnsupportedEncodingException if the supplied character
+ * set is not supported.
+ */
+ public Formatter(OutputStream out, String charset, Locale loc)
+ throws UnsupportedEncodingException
+ {
+ this(new OutputStreamWriter(out, charset), loc);
+ }
+
+ /**
+ * Constructs a new <code>Formatter</code> using the default
+ * locale with the specified output stream. The character
+ * set used is that of the output stream.
+ *
+ * @param out the output stream to use.
+ */
+ public Formatter(PrintStream out)
+ {
+ this((Appendable) out);
+ }
+
+ /**
+ * Constructs a new <code>Formatter</code> using the default
+ * locale and character set, with the specified file as the
+ * output stream.
+ *
+ * @param file the file to use for output.
+ * @throws FileNotFoundException if the file does not exist
+ * and can not be created.
+ * @throws SecurityException if a security manager is present
+ * and doesn't allow writing to the file.
+ */
+ public Formatter(String file) throws FileNotFoundException
+ {
+ this(new OutputStreamWriter(new FileOutputStream(file)));
+ }
+
+ /**
+ * Constructs a new <code>Formatter</code> using the default
+ * locale, with the specified file as the output stream
+ * and the supplied character set.
+ *
+ * @param file the file to use for output.
+ * @param charset the character set to use for output.
+ * @throws FileNotFoundException if the file does not exist
+ * and can not be created.
+ * @throws SecurityException if a security manager is present
+ * and doesn't allow writing to the file.
+ * @throws UnsupportedEncodingException if the supplied character
+ * set is not supported.
+ */
+ public Formatter(String file, String charset)
+ throws FileNotFoundException, UnsupportedEncodingException
+ {
+ this(file, charset, Locale.getDefault());
+ }
+
+ /**
+ * Constructs a new <code>Formatter</code> using the specified
+ * file as the output stream with the supplied character set
+ * and locale. If the locale is <code>null</code>, then no
+ * localization is applied.
+ *
+ * @param file the file to use for output.
+ * @param charset the character set to use for output.
+ * @param loc the locale to use.
+ * @throws FileNotFoundException if the file does not exist
+ * and can not be created.
+ * @throws SecurityException if a security manager is present
+ * and doesn't allow writing to the file.
+ * @throws UnsupportedEncodingException if the supplied character
+ * set is not supported.
+ */
+ public Formatter(String file, String charset, Locale loc)
+ throws FileNotFoundException, UnsupportedEncodingException
+ {
+ this(new OutputStreamWriter(new FileOutputStream(file), charset),
+ loc);
+ }
+
+ /**
+ * Closes the formatter, so as to release used resources.
+ * If the underlying output stream supports the {@link Closeable}
+ * interface, then this is also closed. Attempts to use
+ * a formatter instance, via any method other than
+ * {@link #ioException()}, after closure results in a
+ * {@link FormatterClosedException}.
+ */
+ public void close()
+ {
+ if (closed)
+ return;
+ try
+ {
+ if (out instanceof Closeable)
+ ((Closeable) out).close();
+ }
+ catch (IOException _)
+ {
+ // FIXME: do we ignore these or do we set ioException?
+ // The docs seem to indicate that we should ignore.
+ }
+ closed = true;
+ }
+
+ /**
+ * Flushes the formatter, writing any cached data to the output
+ * stream. If the underlying output stream supports the
+ * {@link Flushable} interface, it is also flushed.
+ *
+ * @throws FormatterClosedException if the formatter is closed.
+ */
+ public void flush()
+ {
+ if (closed)
+ throw new FormatterClosedException();
+ try
+ {
+ if (out instanceof Flushable)
+ ((Flushable) out).flush();
+ }
+ catch (IOException _)
+ {
+ // FIXME: do we ignore these or do we set ioException?
+ // The docs seem to indicate that we should ignore.
+ }
+ }
+
+ /**
+ * Return the name corresponding to a flag.
+ *
+ * @param flags the flag to return the name of.
+ * @return the name of the flag.
+ */
+ private String getName(int flags)
+ {
+ // FIXME: do we want all the flags in here?
+ // Or should we redo how this is reported?
+ int bit = Integer.numberOfTrailingZeros(flags);
+ return FLAGS.substring(bit, bit + 1);
+ }
+
+ /**
+ * Verify the flags passed to a conversion.
+ *
+ * @param flags the flags to verify.
+ * @param allowed the allowed flags mask.
+ * @param conversion the conversion character.
+ */
+ private void checkFlags(int flags, int allowed, char conversion)
+ {
+ flags &= ~allowed;
+ if (flags != 0)
+ throw new FormatFlagsConversionMismatchException(getName(flags),
+ conversion);
+ }
+
+ /**
+ * Throw an exception if a precision was specified.
+ *
+ * @param precision the precision value (-1 indicates not specified).
+ */
+ private void noPrecision(int precision)
+ {
+ if (precision != -1)
+ throw new IllegalFormatPrecisionException(precision);
+ }
+
+ /**
+ * Apply the numeric localization algorithm to a StringBuilder.
+ *
+ * @param builder the builder to apply to.
+ * @param flags the formatting flags to use.
+ * @param width the width of the numeric value.
+ * @param isNegative true if the value is negative.
+ */
+ private void applyLocalization(CPStringBuilder builder, int flags, int width,
+ boolean isNegative)
+ {
+ DecimalFormatSymbols dfsyms;
+ if (fmtLocale == null)
+ dfsyms = new DecimalFormatSymbols();
+ else
+ dfsyms = new DecimalFormatSymbols(fmtLocale);
+
+ // First replace each digit.
+ char zeroDigit = dfsyms.getZeroDigit();
+ int decimalOffset = -1;
+ for (int i = builder.length() - 1; i >= 0; --i)
+ {
+ char c = builder.charAt(i);
+ if (c >= '0' && c <= '9')
+ builder.setCharAt(i, (char) (c - '0' + zeroDigit));
+ else if (c == '.')
+ {
+ assert decimalOffset == -1;
+ decimalOffset = i;
+ }
+ }
+
+ // Localize the decimal separator.
+ if (decimalOffset != -1)
+ {
+ builder.deleteCharAt(decimalOffset);
+ builder.insert(decimalOffset, dfsyms.getDecimalSeparator());
+ }
+
+ // Insert the grouping separators.
+ if ((flags & FormattableFlags.COMMA) != 0)
+ {
+ char groupSeparator = dfsyms.getGroupingSeparator();
+ int groupSize = 3; // FIXME
+ int offset = (decimalOffset == -1) ? builder.length() : decimalOffset;
+ // We use '>' because we don't want to insert a separator
+ // before the first digit.
+ for (int i = offset - groupSize; i > 0; i -= groupSize)
+ builder.insert(i, groupSeparator);
+ }
+
+ if ((flags & FormattableFlags.ZERO) != 0)
+ {
+ // Zero fill. Note that according to the algorithm we do not
+ // insert grouping separators here.
+ for (int i = width - builder.length(); i > 0; --i)
+ builder.insert(0, zeroDigit);
+ }
+
+ if (isNegative)
+ {
+ if ((flags & FormattableFlags.PAREN) != 0)
+ {
+ builder.insert(0, '(');
+ builder.append(')');
+ }
+ else
+ builder.insert(0, '-');
+ }
+ else if ((flags & FormattableFlags.PLUS) != 0)
+ builder.insert(0, '+');
+ else if ((flags & FormattableFlags.SPACE) != 0)
+ builder.insert(0, ' ');
+ }
+
+ /**
+ * A helper method that handles emitting a String after applying
+ * precision, width, justification, and upper case flags.
+ *
+ * @param arg the string to emit.
+ * @param flags the formatting flags to use.
+ * @param width the width to use.
+ * @param precision the precision to use.
+ * @throws IOException if the output stream throws an I/O error.
+ */
+ private void genericFormat(String arg, int flags, int width, int precision)
+ throws IOException
+ {
+ if ((flags & FormattableFlags.UPPERCASE) != 0)
+ {
+ if (fmtLocale == null)
+ arg = arg.toUpperCase();
+ else
+ arg = arg.toUpperCase(fmtLocale);
+ }
+
+ if (precision >= 0 && arg.length() > precision)
+ arg = arg.substring(0, precision);
+
+ boolean leftJustify = (flags & FormattableFlags.LEFT_JUSTIFY) != 0;
+ if (leftJustify && width == -1)
+ throw new MissingFormatWidthException("fixme");
+ if (! leftJustify && arg.length() < width)
+ {
+ for (int i = width - arg.length(); i > 0; --i)
+ out.append(' ');
+ }
+ out.append(arg);
+ if (leftJustify && arg.length() < width)
+ {
+ for (int i = width - arg.length(); i > 0; --i)
+ out.append(' ');
+ }
+ }
+
+ /**
+ * Emit a boolean.
+ *
+ * @param arg the boolean to emit.
+ * @param flags the formatting flags to use.
+ * @param width the width to use.
+ * @param precision the precision to use.
+ * @param conversion the conversion character.
+ * @throws IOException if the output stream throws an I/O error.
+ */
+ private void booleanFormat(Object arg, int flags, int width, int precision,
+ char conversion)
+ throws IOException
+ {
+ checkFlags(flags,
+ FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE,
+ conversion);
+ String result;
+ if (arg instanceof Boolean)
+ result = String.valueOf((Boolean) arg);
+ else
+ result = arg == null ? "false" : "true";
+ genericFormat(result, flags, width, precision);
+ }
+
+ /**
+ * Emit a hash code.
+ *
+ * @param arg the hash code to emit.
+ * @param flags the formatting flags to use.
+ * @param width the width to use.
+ * @param precision the precision to use.
+ * @param conversion the conversion character.
+ * @throws IOException if the output stream throws an I/O error.
+ */
+ private void hashCodeFormat(Object arg, int flags, int width, int precision,
+ char conversion)
+ throws IOException
+ {
+ checkFlags(flags,
+ FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE,
+ conversion);
+ genericFormat(arg == null ? "null" : Integer.toHexString(arg.hashCode()),
+ flags, width, precision);
+ }
+
+ /**
+ * Emit a String or Formattable conversion.
+ *
+ * @param arg the String or Formattable to emit.
+ * @param flags the formatting flags to use.
+ * @param width the width to use.
+ * @param precision the precision to use.
+ * @param conversion the conversion character.
+ * @throws IOException if the output stream throws an I/O error.
+ */
+ private void stringFormat(Object arg, int flags, int width, int precision,
+ char conversion)
+ throws IOException
+ {
+ if (arg instanceof Formattable)
+ {
+ checkFlags(flags,
+ (FormattableFlags.LEFT_JUSTIFY
+ | FormattableFlags.UPPERCASE
+ | FormattableFlags.ALTERNATE),
+ conversion);
+ Formattable fmt = (Formattable) arg;
+ fmt.formatTo(this, flags, width, precision);
+ }
+ else
+ {
+ checkFlags(flags,
+ FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE,
+ conversion);
+ genericFormat(arg == null ? "null" : arg.toString(), flags, width,
+ precision);
+ }
+ }
+
+ /**
+ * Emit a character.
+ *
+ * @param arg the character to emit.
+ * @param flags the formatting flags to use.
+ * @param width the width to use.
+ * @param precision the precision to use.
+ * @param conversion the conversion character.
+ * @throws IOException if the output stream throws an I/O error.
+ */
+ private void characterFormat(Object arg, int flags, int width, int precision,
+ char conversion)
+ throws IOException
+ {
+ checkFlags(flags,
+ FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE,
+ conversion);
+ noPrecision(precision);
+
+ int theChar;
+ if (arg instanceof Character)
+ theChar = ((Character) arg).charValue();
+ else if (arg instanceof Byte)
+ theChar = (char) (((Byte) arg).byteValue ());
+ else if (arg instanceof Short)
+ theChar = (char) (((Short) arg).shortValue ());
+ else if (arg instanceof Integer)
+ {
+ theChar = ((Integer) arg).intValue();
+ if (! Character.isValidCodePoint(theChar))
+ throw new IllegalFormatCodePointException(theChar);
+ }
+ else
+ throw new IllegalFormatConversionException(conversion, arg.getClass());
+ String result = new String(Character.toChars(theChar));
+ genericFormat(result, flags, width, precision);
+ }
+
+ /**
+ * Emit a '%'.
+ *
+ * @param flags the formatting flags to use.
+ * @param width the width to use.
+ * @param precision the precision to use.
+ * @throws IOException if the output stream throws an I/O error.
+ */
+ private void percentFormat(int flags, int width, int precision)
+ throws IOException
+ {
+ checkFlags(flags, FormattableFlags.LEFT_JUSTIFY, '%');
+ noPrecision(precision);
+ genericFormat("%", flags, width, precision);
+ }
+
+ /**
+ * Emit a newline.
+ *
+ * @param flags the formatting flags to use.
+ * @param width the width to use.
+ * @param precision the precision to use.
+ * @throws IOException if the output stream throws an I/O error.
+ */
+ private void newLineFormat(int flags, int width, int precision)
+ throws IOException
+ {
+ checkFlags(flags, 0, 'n');
+ noPrecision(precision);
+ if (width != -1)
+ throw new IllegalFormatWidthException(width);
+ genericFormat(lineSeparator, flags, width, precision);
+ }
+
+ /**
+ * Helper method to do initial formatting and checking for integral
+ * conversions.
+ *
+ * @param arg the formatted argument.
+ * @param flags the formatting flags to use.
+ * @param width the width to use.
+ * @param precision the precision to use.
+ * @param radix the radix of the number.
+ * @param conversion the conversion character.
+ * @return the result.
+ */
+ private CPStringBuilder basicIntegralConversion(Object arg, int flags,
+ int width, int precision,
+ int radix, char conversion)
+ {
+ assert radix == 8 || radix == 10 || radix == 16;
+ noPrecision(precision);
+
+ // Some error checking.
+ if ((flags & FormattableFlags.PLUS) != 0
+ && (flags & FormattableFlags.SPACE) != 0)
+ throw new IllegalFormatFlagsException(getName(flags));
+
+ if ((flags & FormattableFlags.LEFT_JUSTIFY) != 0 && width == -1)
+ throw new MissingFormatWidthException("fixme");
+
+ // Do the base translation of the value to a string.
+ String result;
+ int basicFlags = (FormattableFlags.LEFT_JUSTIFY
+ // We already handled any possible error when
+ // parsing.
+ | FormattableFlags.UPPERCASE
+ | FormattableFlags.ZERO);
+ if (radix == 10)
+ basicFlags |= (FormattableFlags.PLUS
+ | FormattableFlags.SPACE
+ | FormattableFlags.COMMA
+ | FormattableFlags.PAREN);
+ else
+ basicFlags |= FormattableFlags.ALTERNATE;
+
+ if (arg instanceof BigInteger)
+ {
+ checkFlags(flags,
+ (basicFlags
+ | FormattableFlags.PLUS
+ | FormattableFlags.SPACE
+ | FormattableFlags.PAREN),
+ conversion);
+ BigInteger bi = (BigInteger) arg;
+ result = bi.toString(radix);
+ }
+ else if (arg instanceof Number
+ && ! (arg instanceof Float)
+ && ! (arg instanceof Double))
+ {
+ checkFlags(flags, basicFlags, conversion);
+ long value = ((Number) arg).longValue ();
+ if (radix == 8)
+ result = Long.toOctalString(value);
+ else if (radix == 16)
+ result = Long.toHexString(value);
+ else
+ result = Long.toString(value);
+ }
+ else
+ throw new IllegalFormatConversionException(conversion, arg.getClass());
+
+ return new CPStringBuilder(result);
+ }
+
+ /**
+ * Emit a hex or octal value.
+ *
+ * @param arg the hexadecimal or octal value.
+ * @param flags the formatting flags to use.
+ * @param width the width to use.
+ * @param precision the precision to use.
+ * @param radix the radix of the number.
+ * @param conversion the conversion character.
+ * @throws IOException if the output stream throws an I/O error.
+ */
+ private void hexOrOctalConversion(Object arg, int flags, int width,
+ int precision, int radix,
+ char conversion)
+ throws IOException
+ {
+ assert radix == 8 || radix == 16;
+
+ CPStringBuilder builder = basicIntegralConversion(arg, flags, width,
+ precision, radix,
+ conversion);
+ int insertPoint = 0;
+
+ // Insert the sign.
+ if (builder.charAt(0) == '-')
+ {
+ // Already inserted. Note that we don't insert a sign, since
+ // the only case where it is needed it BigInteger, and it has
+ // already been inserted by toString.
+ ++insertPoint;
+ }
+ else if ((flags & FormattableFlags.PLUS) != 0)
+ {
+ builder.insert(insertPoint, '+');
+ ++insertPoint;
+ }
+ else if ((flags & FormattableFlags.SPACE) != 0)
+ {
+ builder.insert(insertPoint, ' ');
+ ++insertPoint;
+ }
+
+ // Insert the radix prefix.
+ if ((flags & FormattableFlags.ALTERNATE) != 0)
+ {
+ builder.insert(insertPoint, radix == 8 ? "0" : "0x");
+ insertPoint += radix == 8 ? 1 : 2;
+ }
+
+ // Now justify the result.
+ int resultWidth = builder.length();
+ if (resultWidth < width)
+ {
+ char fill = ((flags & FormattableFlags.ZERO) != 0) ? '0' : ' ';
+ if ((flags & FormattableFlags.LEFT_JUSTIFY) != 0)
+ {
+ // Left justify.
+ if (fill == ' ')
+ insertPoint = builder.length();
+ }
+ else
+ {
+ // Right justify. Insert spaces before the radix prefix
+ // and sign.
+ insertPoint = 0;
+ }
+ while (resultWidth++ < width)
+ builder.insert(insertPoint, fill);
+ }
+
+ String result = builder.toString();
+ if ((flags & FormattableFlags.UPPERCASE) != 0)
+ {
+ if (fmtLocale == null)
+ result = result.toUpperCase();
+ else
+ result = result.toUpperCase(fmtLocale);
+ }
+
+ out.append(result);
+ }
+
+ /**
+ * Emit a decimal value.
+ *
+ * @param arg the hexadecimal or octal value.
+ * @param flags the formatting flags to use.
+ * @param width the width to use.
+ * @param precision the precision to use.
+ * @param conversion the conversion character.
+ * @throws IOException if the output stream throws an I/O error.
+ */
+ private void decimalConversion(Object arg, int flags, int width,
+ int precision, char conversion)
+ throws IOException
+ {
+ CPStringBuilder builder = basicIntegralConversion(arg, flags, width,
+ precision, 10,
+ conversion);
+ boolean isNegative = false;
+ if (builder.charAt(0) == '-')
+ {
+ // Sign handling is done during localization.
+ builder.deleteCharAt(0);
+ isNegative = true;
+ }
+
+ applyLocalization(builder, flags, width, isNegative);
+ genericFormat(builder.toString(), flags, width, precision);
+ }
+
+ /**
+ * Emit a single date or time conversion to a StringBuilder.
+ *
+ * @param builder the builder to write to.
+ * @param cal the calendar to use in the conversion.
+ * @param conversion the formatting character to specify the type of data.
+ * @param syms the date formatting symbols.
+ */
+ private void singleDateTimeConversion(CPStringBuilder builder, Calendar cal,
+ char conversion,
+ DateFormatSymbols syms)
+ {
+ int oldLen = builder.length();
+ int digits = -1;
+ switch (conversion)
+ {
+ case 'H':
+ builder.append(cal.get(Calendar.HOUR_OF_DAY));
+ digits = 2;
+ break;
+ case 'I':
+ builder.append(cal.get(Calendar.HOUR));
+ digits = 2;
+ break;
+ case 'k':
+ builder.append(cal.get(Calendar.HOUR_OF_DAY));
+ break;
+ case 'l':
+ builder.append(cal.get(Calendar.HOUR));
+ break;
+ case 'M':
+ builder.append(cal.get(Calendar.MINUTE));
+ digits = 2;
+ break;
+ case 'S':
+ builder.append(cal.get(Calendar.SECOND));
+ digits = 2;
+ break;
+ case 'N':
+ // FIXME: nanosecond ...
+ digits = 9;
+ break;
+ case 'p':
+ {
+ int ampm = cal.get(Calendar.AM_PM);
+ builder.append(syms.getAmPmStrings()[ampm]);
+ }
+ break;
+ case 'z':
+ {
+ int zone = cal.get(Calendar.ZONE_OFFSET) / (1000 * 60);
+ builder.append(zone);
+ digits = 4;
+ // Skip the '-' sign.
+ if (zone < 0)
+ ++oldLen;
+ }
+ break;
+ case 'Z':
+ {
+ // FIXME: DST?
+ int zone = cal.get(Calendar.ZONE_OFFSET) / (1000 * 60 * 60);
+ String[][] zs = syms.getZoneStrings();
+ builder.append(zs[zone + 12][1]);
+ }
+ break;
+ case 's':
+ {
+ long val = cal.getTime().getTime();
+ builder.append(val / 1000);
+ }
+ break;
+ case 'Q':
+ {
+ long val = cal.getTime().getTime();
+ builder.append(val);
+ }
+ break;
+ case 'B':
+ {
+ int month = cal.get(Calendar.MONTH);
+ builder.append(syms.getMonths()[month]);
+ }
+ break;
+ case 'b':
+ case 'h':
+ {
+ int month = cal.get(Calendar.MONTH);
+ builder.append(syms.getShortMonths()[month]);
+ }
+ break;
+ case 'A':
+ {
+ int day = cal.get(Calendar.DAY_OF_WEEK);
+ builder.append(syms.getWeekdays()[day]);
+ }
+ break;
+ case 'a':
+ {
+ int day = cal.get(Calendar.DAY_OF_WEEK);
+ builder.append(syms.getShortWeekdays()[day]);
+ }
+ break;
+ case 'C':
+ builder.append(cal.get(Calendar.YEAR) / 100);
+ digits = 2;
+ break;
+ case 'Y':
+ builder.append(cal.get(Calendar.YEAR));
+ digits = 4;
+ break;
+ case 'y':
+ builder.append(cal.get(Calendar.YEAR) % 100);
+ digits = 2;
+ break;
+ case 'j':
+ builder.append(cal.get(Calendar.DAY_OF_YEAR));
+ digits = 3;
+ break;
+ case 'm':
+ builder.append(cal.get(Calendar.MONTH) + 1);
+ digits = 2;
+ break;
+ case 'd':
+ builder.append(cal.get(Calendar.DAY_OF_MONTH));
+ digits = 2;
+ break;
+ case 'e':
+ builder.append(cal.get(Calendar.DAY_OF_MONTH));
+ break;
+ case 'R':
+ singleDateTimeConversion(builder, cal, 'H', syms);
+ builder.append(':');
+ singleDateTimeConversion(builder, cal, 'M', syms);
+ break;
+ case 'T':
+ singleDateTimeConversion(builder, cal, 'H', syms);
+ builder.append(':');
+ singleDateTimeConversion(builder, cal, 'M', syms);
+ builder.append(':');
+ singleDateTimeConversion(builder, cal, 'S', syms);
+ break;
+ case 'r':
+ singleDateTimeConversion(builder, cal, 'I', syms);
+ builder.append(':');
+ singleDateTimeConversion(builder, cal, 'M', syms);
+ builder.append(':');
+ singleDateTimeConversion(builder, cal, 'S', syms);
+ builder.append(' ');
+ singleDateTimeConversion(builder, cal, 'p', syms);
+ break;
+ case 'D':
+ singleDateTimeConversion(builder, cal, 'm', syms);
+ builder.append('/');
+ singleDateTimeConversion(builder, cal, 'd', syms);
+ builder.append('/');
+ singleDateTimeConversion(builder, cal, 'y', syms);
+ break;
+ case 'F':
+ singleDateTimeConversion(builder, cal, 'Y', syms);
+ builder.append('-');
+ singleDateTimeConversion(builder, cal, 'm', syms);
+ builder.append('-');
+ singleDateTimeConversion(builder, cal, 'd', syms);
+ break;
+ case 'c':
+ singleDateTimeConversion(builder, cal, 'a', syms);
+ builder.append(' ');
+ singleDateTimeConversion(builder, cal, 'b', syms);
+ builder.append(' ');
+ singleDateTimeConversion(builder, cal, 'd', syms);
+ builder.append(' ');
+ singleDateTimeConversion(builder, cal, 'T', syms);
+ builder.append(' ');
+ singleDateTimeConversion(builder, cal, 'Z', syms);
+ builder.append(' ');
+ singleDateTimeConversion(builder, cal, 'Y', syms);
+ break;
+ default:
+ throw new UnknownFormatConversionException(String.valueOf(conversion));
+ }
+
+ if (digits > 0)
+ {
+ int newLen = builder.length();
+ int delta = newLen - oldLen;
+ while (delta++ < digits)
+ builder.insert(oldLen, '0');
+ }
+ }
+
+ /**
+ * Emit a date or time value.
+ *
+ * @param arg the date or time value.
+ * @param flags the formatting flags to use.
+ * @param width the width to use.
+ * @param precision the precision to use.
+ * @param conversion the conversion character.
+ * @param subConversion the sub conversion character.
+ * @throws IOException if the output stream throws an I/O error.
+ */
+ private void dateTimeConversion(Object arg, int flags, int width,
+ int precision, char conversion,
+ char subConversion)
+ throws IOException
+ {
+ noPrecision(precision);
+ checkFlags(flags,
+ FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE,
+ conversion);
+
+ Calendar cal;
+ if (arg instanceof Calendar)
+ cal = (Calendar) arg;
+ else
+ {
+ Date date;
+ if (arg instanceof Date)
+ date = (Date) arg;
+ else if (arg instanceof Long)
+ date = new Date(((Long) arg).longValue());
+ else
+ throw new IllegalFormatConversionException(conversion,
+ arg.getClass());
+ if (fmtLocale == null)
+ cal = Calendar.getInstance();
+ else
+ cal = Calendar.getInstance(fmtLocale);
+ cal.setTime(date);
+ }
+
+ // We could try to be more efficient by computing this lazily.
+ DateFormatSymbols syms;
+ if (fmtLocale == null)
+ syms = new DateFormatSymbols();
+ else
+ syms = new DateFormatSymbols(fmtLocale);
+
+ CPStringBuilder result = new CPStringBuilder();
+ singleDateTimeConversion(result, cal, subConversion, syms);
+
+ genericFormat(result.toString(), flags, width, precision);
+ }
+
+ /**
+ * Advance the internal parsing index, and throw an exception
+ * on overrun.
+ *
+ * @throws IllegalArgumentException on overrun.
+ */
+ private void advance()
+ {
+ ++index;
+ if (index >= length)
+ {
+ // FIXME: what exception here?
+ throw new IllegalArgumentException();
+ }
+ }
+
+ /**
+ * Parse an integer appearing in the format string. Will return -1
+ * if no integer was found.
+ *
+ * @return the parsed integer.
+ */
+ private int parseInt()
+ {
+ int start = index;
+ while (Character.isDigit(format.charAt(index)))
+ advance();
+ if (start == index)
+ return -1;
+ return Integer.decode(format.substring(start, index));
+ }
+
+ /**
+ * Parse the argument index. Returns -1 if there was no index, 0 if
+ * we should re-use the previous index, and a positive integer to
+ * indicate an absolute index.
+ *
+ * @return the parsed argument index.
+ */
+ private int parseArgumentIndex()
+ {
+ int result = -1;
+ int start = index;
+ if (format.charAt(index) == '<')
+ {
+ result = 0;
+ advance();
+ }
+ else if (Character.isDigit(format.charAt(index)))
+ {
+ result = parseInt();
+ if (format.charAt(index) == '$')
+ advance();
+ else
+ {
+ // Reset.
+ index = start;
+ result = -1;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Parse a set of flags and return a bit mask of values from
+ * FormattableFlags. Will throw an exception if a flag is
+ * duplicated.
+ *
+ * @return the parsed flags.
+ */
+ private int parseFlags()
+ {
+ int value = 0;
+ int start = index;
+ while (true)
+ {
+ int x = FLAGS.indexOf(format.charAt(index));
+ if (x == -1)
+ break;
+ int newValue = 1 << x;
+ if ((value & newValue) != 0)
+ throw new DuplicateFormatFlagsException(format.substring(start,
+ index + 1));
+ value |= newValue;
+ advance();
+ }
+ return value;
+ }
+
+ /**
+ * Parse the width part of a format string. Returns -1 if no width
+ * was specified.
+ *
+ * @return the parsed width.
+ */
+ private int parseWidth()
+ {
+ return parseInt();
+ }
+
+ /**
+ * If the current character is '.', parses the precision part of a
+ * format string. Returns -1 if no precision was specified.
+ *
+ * @return the parsed precision.
+ */
+ private int parsePrecision()
+ {
+ if (format.charAt(index) != '.')
+ return -1;
+ advance();
+ int precision = parseInt();
+ if (precision == -1)
+ // FIXME
+ throw new IllegalArgumentException();
+ return precision;
+ }
+
+ /**
+ * Outputs a formatted string based on the supplied specification,
+ * <code>fmt</code>, and its arguments using the specified locale.
+ * The locale of the formatter does not change as a result; the
+ * specified locale is just used for this particular formatting
+ * operation. If the locale is <code>null</code>, then no
+ * localization is applied.
+ *
+ * @param loc the locale to use for this format.
+ * @param fmt the format specification.
+ * @param args the arguments to apply to the specification.
+ * @throws IllegalFormatException if there is a problem with
+ * the syntax of the format
+ * specification or a mismatch
+ * between it and the arguments.
+ * @throws FormatterClosedException if the formatter is closed.
+ */
+ public Formatter format(Locale loc, String fmt, Object... args)
+ {
+ if (closed)
+ throw new FormatterClosedException();
+
+ // Note the arguments are indexed starting at 1.
+ int implicitArgumentIndex = 1;
+ int previousArgumentIndex = 0;
+
+ try
+ {
+ fmtLocale = loc;
+ format = fmt;
+ length = format.length();
+ for (index = 0; index < length; ++index)
+ {
+ char c = format.charAt(index);
+ if (c != '%')
+ {
+ out.append(c);
+ continue;
+ }
+
+ int start = index;
+ advance();
+
+ // We do the needed post-processing of this later, when we
+ // determine whether an argument is actually needed by
+ // this conversion.
+ int argumentIndex = parseArgumentIndex();
+
+ int flags = parseFlags();
+ int width = parseWidth();
+ int precision = parsePrecision();
+ char origConversion = format.charAt(index);
+ char conversion = origConversion;
+ if (Character.isUpperCase(conversion))
+ {
+ flags |= FormattableFlags.UPPERCASE;
+ conversion = Character.toLowerCase(conversion);
+ }
+
+ Object argument = null;
+ if (conversion == '%' || conversion == 'n')
+ {
+ if (argumentIndex != -1)
+ {
+ // FIXME: not sure about this.
+ throw new UnknownFormatConversionException("FIXME");
+ }
+ }
+ else
+ {
+ if (argumentIndex == -1)
+ argumentIndex = implicitArgumentIndex++;
+ else if (argumentIndex == 0)
+ argumentIndex = previousArgumentIndex;
+ // Argument indices start at 1 but array indices at 0.
+ --argumentIndex;
+ if (argumentIndex < 0 || argumentIndex >= args.length)
+ throw new MissingFormatArgumentException(format.substring(start, index));
+ argument = args[argumentIndex];
+ }
+
+ switch (conversion)
+ {
+ case 'b':
+ booleanFormat(argument, flags, width, precision,
+ origConversion);
+ break;
+ case 'h':
+ hashCodeFormat(argument, flags, width, precision,
+ origConversion);
+ break;
+ case 's':
+ stringFormat(argument, flags, width, precision,
+ origConversion);
+ break;
+ case 'c':
+ characterFormat(argument, flags, width, precision,
+ origConversion);
+ break;
+ case 'd':
+ checkFlags(flags & FormattableFlags.UPPERCASE, 0, 'd');
+ decimalConversion(argument, flags, width, precision,
+ origConversion);
+ break;
+ case 'o':
+ checkFlags(flags & FormattableFlags.UPPERCASE, 0, 'o');
+ hexOrOctalConversion(argument, flags, width, precision, 8,
+ origConversion);
+ break;
+ case 'x':
+ hexOrOctalConversion(argument, flags, width, precision, 16,
+ origConversion);
+ case 'e':
+ // scientificNotationConversion();
+ break;
+ case 'f':
+ // floatingDecimalConversion();
+ break;
+ case 'g':
+ // smartFloatingConversion();
+ break;
+ case 'a':
+ // hexFloatingConversion();
+ break;
+ case 't':
+ advance();
+ char subConversion = format.charAt(index);
+ dateTimeConversion(argument, flags, width, precision,
+ origConversion, subConversion);
+ break;
+ case '%':
+ percentFormat(flags, width, precision);
+ break;
+ case 'n':
+ newLineFormat(flags, width, precision);
+ break;
+ default:
+ throw new UnknownFormatConversionException(String.valueOf(origConversion));
+ }
+ }
+ }
+ catch (IOException exc)
+ {
+ ioException = exc;
+ }
+ return this;
+ }
+
+ /**
+ * Outputs a formatted string based on the supplied specification,
+ * <code>fmt</code>, and its arguments using the formatter's locale.
+ *
+ * @param format the format specification.
+ * @param args the arguments to apply to the specification.
+ * @throws IllegalFormatException if there is a problem with
+ * the syntax of the format
+ * specification or a mismatch
+ * between it and the arguments.
+ * @throws FormatterClosedException if the formatter is closed.
+ */
+ public Formatter format(String format, Object... args)
+ {
+ return format(locale, format, args);
+ }
+
+ /**
+ * Returns the last I/O exception thrown by the
+ * <code>append()</code> operation of the underlying
+ * output stream.
+ *
+ * @return the last I/O exception.
+ */
+ public IOException ioException()
+ {
+ return ioException;
+ }
+
+ /**
+ * Returns the locale used by this formatter.
+ *
+ * @return the formatter's locale.
+ * @throws FormatterClosedException if the formatter is closed.
+ */
+ public Locale locale()
+ {
+ if (closed)
+ throw new FormatterClosedException();
+ return locale;
+ }
+
+ /**
+ * Returns the output stream used by this formatter.
+ *
+ * @return the formatter's output stream.
+ * @throws FormatterClosedException if the formatter is closed.
+ */
+ public Appendable out()
+ {
+ if (closed)
+ throw new FormatterClosedException();
+ return out;
+ }
+
+ /**
+ * Returns the result of applying {@link Object#toString()}
+ * to the underlying output stream. The results returned
+ * depend on the particular {@link Appendable} being used.
+ * For example, a {@link StringBuilder} will return the
+ * formatted output but an I/O stream will not.
+ *
+ * @throws FormatterClosedException if the formatter is closed.
+ */
+ public String toString()
+ {
+ if (closed)
+ throw new FormatterClosedException();
+ return out.toString();
+ }
+}
diff --git a/libjava/classpath/java/util/FormatterClosedException.java b/libjava/classpath/java/util/FormatterClosedException.java
new file mode 100644
index 000000000..620684938
--- /dev/null
+++ b/libjava/classpath/java/util/FormatterClosedException.java
@@ -0,0 +1,60 @@
+/* FormatterClosedException.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Thrown when a method is called on a {@link Formatter} but
+ * it has already been closed.
+ *
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+public class FormatterClosedException
+ extends IllegalStateException
+{
+ private static final long serialVersionUID = 18111216L;
+
+ /**
+ * Constructs a new <code>FormatterClosedException</code>.
+ */
+ public FormatterClosedException()
+ {
+ }
+}
diff --git a/libjava/classpath/java/util/GregorianCalendar.java b/libjava/classpath/java/util/GregorianCalendar.java
new file mode 100644
index 000000000..b5d9e8c47
--- /dev/null
+++ b/libjava/classpath/java/util/GregorianCalendar.java
@@ -0,0 +1,1365 @@
+/* java.util.GregorianCalendar
+ Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004, 2007
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+
+/**
+ * <p>
+ * This class represents the Gregorian calendar, that is used in most
+ * countries all over the world. It does also handle the Julian calendar
+ * for dates smaller than the date of the change to the Gregorian calendar.
+ * The Gregorian calendar differs from the Julian calendar by a different
+ * leap year rule (no leap year every 100 years, except if year is divisible
+ * by 400).
+ * </p>
+ * <p>
+ * This change date is different from country to country, and can be changed with
+ * <code>setGregorianChange</code>. The first countries to adopt the Gregorian
+ * calendar did so on the 15th of October, 1582. This date followed October
+ * the 4th, 1582 in the Julian calendar system. The non-existant days that were
+ * omitted when the change took place are interpreted as Gregorian dates.
+ * </p>
+ * <p>
+ * Prior to the changeover date, New Year's Day occurred on the 25th of March.
+ * However, this class always takes New Year's Day as being the 1st of January.
+ * Client code should manually adapt the year value, if required, for dates
+ * between January the 1st and March the 24th in years prior to the changeover.
+ * </p>
+ * <p>
+ * Any date infinitely forwards or backwards in time can be represented by
+ * this class. A <em>proleptic</em> calendar system is used, which allows
+ * future dates to be created via the existing rules. This allows meaningful
+ * and consistent dates to be produced for all years. However, dates are only
+ * historically accurate following March the 1st, 4AD when the Julian calendar
+ * system was adopted. Prior to this, leap year rules were applied erraticly.
+ * </p>
+ * <p>
+ * There are two eras available for the Gregorian calendar, namely BC and AD.
+ * </p>
+ * <p>
+ * Weeks are defined as a period of seven days, beginning on the first day
+ * of the week, as returned by <code>getFirstDayOfWeek()</code>, and ending
+ * on the day prior to this.
+ * </p>
+ * <p>
+ * The weeks of the year are numbered from 1 to a possible 53. The first week
+ * of the year is defined as the first week that contains at least the minimum
+ * number of days of the first week in the new year (retrieved via
+ * <code>getMinimalDaysInFirstWeek()</code>). All weeks after this are numbered
+ * from 2 onwards.
+ * </p>
+ * <p>
+ * For example, take the year 2004. It began on a Thursday. The first week
+ * of 2004 depends both on where a week begins and how long it must minimally
+ * last. Let's say that the week begins on a Monday and must have a minimum
+ * of 5 days. In this case, the first week begins on Monday, the 5th of January.
+ * The first 4 days (Thursday to Sunday) are not eligible, as they are too few
+ * to make up the minimum number of days of the first week which must be in
+ * the new year. If the minimum was lowered to 4 days, then the first week
+ * would instead begin on Monday, the 29th of December, 2003. This first week
+ * has 4 of its days in the new year, and is now eligible.
+ * </p>
+ * <p>
+ * The weeks of the month are numbered from 0 to a possible 6. The first week
+ * of the month (numbered 1) is a set of days, prior to the first day of the week,
+ * which number at least the minimum number of days in a week. Unlike the first
+ * week of the year, the first week of the month only uses days from that particular
+ * month. As a consequence, it may have a variable number of days (from the minimum
+ * number required up to a full week of 7) and it need not start on the first day of
+ * the week. It must, however, be following by the first day of the week, as this
+ * marks the beginning of week 2. Any days of the month which occur prior to the
+ * first week (because the first day of the week occurs before the minimum number
+ * of days is met) are seen as week 0.
+ * </p>
+ * <p>
+ * Again, we will take the example of the year 2004 to demonstrate this. September
+ * 2004 begins on a Wednesday. Taking our first day of the week as Monday, and the
+ * minimum length of the first week as 6, we find that week 1 runs from Monday,
+ * the 6th of September to Sunday the 12th. Prior to the 6th, there are only
+ * 5 days (Wednesday through to Sunday). This is too small a number to meet the
+ * minimum, so these are classed as being days in week 0. Week 2 begins on the
+ * 13th, and so on. This changes if we reduce the minimum to 5. In this case,
+ * week 1 is a truncated week from Wednesday the 1st to Sunday the 5th, and week
+ * 0 doesn't exist. The first seven day week is week 2, starting on the 6th.
+ * </p>
+ * <p>
+ * On using the <code>clear()</code> method, the Gregorian calendar returns
+ * to its default value of the 1st of January, 1970 AD 00:00:00 (the epoch).
+ * The day of the week is set to the correct day for that particular time.
+ * The day is also the first of the month, and the date is in week 0.
+ * </p>
+ *
+ * @see Calendar
+ * @see TimeZone
+ * @see Calendar#getFirstDayOfWeek()
+ * @see Calendar#getMinimalDaysInFirstWeek()
+ */
+public class GregorianCalendar extends Calendar
+{
+ /**
+ * Constant representing the era BC (Before Christ).
+ */
+ public static final int BC = 0;
+
+ /**
+ * Constant representing the era AD (Anno Domini).
+ */
+ public static final int AD = 1;
+
+ /**
+ * The point at which the Gregorian calendar rules were used.
+ * This may be changed by using setGregorianChange;
+ * The default is midnight (UTC) on October 5, 1582 (Julian),
+ * or October 15, 1582 (Gregorian).
+ *
+ * @serial the changeover point from the Julian calendar
+ * system to the Gregorian.
+ */
+ private long gregorianCutover = (new Date((24 * 60 * 60 * 1000L) * (((1582 * (365 * 4
+ + 1)) / 4
+ + (java.util.Calendar.OCTOBER * (31
+ + 30 + 31 + 30 + 31) - 9) / 5 + 5)
+ - ((1970 * (365 * 4 + 1)) / 4 + 1
+ - 13)))).getTime();
+
+ /**
+ * For compatability with Sun's JDK.
+ */
+ static final long serialVersionUID = -8125100834729963327L;
+
+ /**
+ * Days in the epoch. Relative Jan 1, year '0' which is not a leap year.
+ * (although there is no year zero, this does not matter.)
+ * This is consistent with the formula:
+ * = (year-1)*365L + ((year-1) >> 2)
+ *
+ * Plus the gregorian correction:
+ * Math.floor((year-1) / 400.) - Math.floor((year-1) / 100.);
+ * For a correct julian date, the correction is -2 instead.
+ *
+ * The gregorian cutover in 1582 was 10 days, so by calculating the
+ * correction from year zero, we have 15 non-leap days (even centuries)
+ * minus 3 leap days (year 400,800,1200) = 12. Subtracting two corrects
+ * this to the correct number 10.
+ */
+ private static final int EPOCH_DAYS = 719162;
+
+ /**
+ * Constructs a new GregorianCalender representing the current
+ * time, using the default time zone and the default locale.
+ */
+ public GregorianCalendar()
+ {
+ this(TimeZone.getDefault(), Locale.getDefault());
+ }
+
+ /**
+ * Constructs a new GregorianCalender representing the current
+ * time, using the specified time zone and the default locale.
+ *
+ * @param zone a time zone.
+ */
+ public GregorianCalendar(TimeZone zone)
+ {
+ this(zone, Locale.getDefault());
+ }
+
+ /**
+ * Constructs a new GregorianCalender representing the current
+ * time, using the default time zone and the specified locale.
+ *
+ * @param locale a locale.
+ */
+ public GregorianCalendar(Locale locale)
+ {
+ this(TimeZone.getDefault(), locale);
+ }
+
+ /**
+ * Constructs a new GregorianCalender representing the current
+ * time with the given time zone and the given locale.
+ *
+ * @param zone a time zone.
+ * @param locale a locale.
+ */
+ public GregorianCalendar(TimeZone zone, Locale locale)
+ {
+ this(zone, locale, false);
+ setTimeInMillis(System.currentTimeMillis());
+ }
+
+ /**
+ * Common constructor that all constructors should call.
+ * @param zone a time zone.
+ * @param locale a locale.
+ * @param unused unused parameter to make the signature differ from
+ * the public constructor (TimeZone, Locale).
+ */
+ private GregorianCalendar(TimeZone zone, Locale locale, boolean unused)
+ {
+ super(zone, locale);
+ }
+
+ /**
+ * Constructs a new GregorianCalendar representing midnight on the
+ * given date with the default time zone and locale.
+ *
+ * @param year corresponds to the YEAR time field.
+ * @param month corresponds to the MONTH time field.
+ * @param day corresponds to the DAY time field.
+ */
+ public GregorianCalendar(int year, int month, int day)
+ {
+ this(TimeZone.getDefault(), Locale.getDefault(), false);
+ set(year, month, day);
+ }
+
+ /**
+ * Constructs a new GregorianCalendar representing midnight on the
+ * given date with the default time zone and locale.
+ *
+ * @param year corresponds to the YEAR time field.
+ * @param month corresponds to the MONTH time field.
+ * @param day corresponds to the DAY time field.
+ * @param hour corresponds to the HOUR_OF_DAY time field.
+ * @param minute corresponds to the MINUTE time field.
+ */
+ public GregorianCalendar(int year, int month, int day, int hour, int minute)
+ {
+ this(TimeZone.getDefault(), Locale.getDefault(), false);
+ set(year, month, day, hour, minute);
+ }
+
+ /**
+ * Constructs a new GregorianCalendar representing midnight on the
+ * given date with the default time zone and locale.
+ *
+ * @param year corresponds to the YEAR time field.
+ * @param month corresponds to the MONTH time field.
+ * @param day corresponds to the DAY time field.
+ * @param hour corresponds to the HOUR_OF_DAY time field.
+ * @param minute corresponds to the MINUTE time field.
+ * @param second corresponds to the SECOND time field.
+ */
+ public GregorianCalendar(int year, int month, int day, int hour, int minute,
+ int second)
+ {
+ this(TimeZone.getDefault(), Locale.getDefault(), false);
+ set(year, month, day, hour, minute, second);
+ }
+
+ /**
+ * Sets the date of the switch from Julian dates to Gregorian dates.
+ * You can use <code>new Date(Long.MAX_VALUE)</code> to use a pure
+ * Julian calendar, or <code>Long.MIN_VALUE</code> for a pure Gregorian
+ * calendar.
+ *
+ * @param date the date of the change.
+ */
+ public void setGregorianChange(Date date)
+ {
+ gregorianCutover = date.getTime();
+ }
+
+ /**
+ * Gets the date of the switch from Julian dates to Gregorian dates.
+ *
+ * @return the date of the change.
+ */
+ public final Date getGregorianChange()
+ {
+ return new Date(gregorianCutover);
+ }
+
+ /**
+ * <p>
+ * Determines if the given year is a leap year. The result is
+ * undefined if the Gregorian change took place in 1800, so that
+ * the end of February is skipped, and that year is specified.
+ * (well...).
+ * </p>
+ * <p>
+ * To specify a year in the BC era, use a negative value calculated
+ * as 1 - y, where y is the required year in BC. So, 1 BC is 0,
+ * 2 BC is -1, 3 BC is -2, etc.
+ * </p>
+ *
+ * @param year a year (use a negative value for BC).
+ * @return true, if the given year is a leap year, false otherwise.
+ */
+ public boolean isLeapYear(int year)
+ {
+ // Only years divisible by 4 can be leap years
+ if ((year & 3) != 0)
+ return false;
+
+ // Is the leap-day a Julian date? Then it's a leap year
+ if (! isGregorian(year, 31 + 29 - 1))
+ return true;
+
+ // Apply gregorian rules otherwise
+ return ((year % 100) != 0 || (year % 400) == 0);
+ }
+
+ /**
+ * Retrieves the day of the week corresponding to the specified
+ * day of the specified year.
+ *
+ * @param year the year in which the dayOfYear occurs.
+ * @param dayOfYear the day of the year (an integer between 0 and
+ * and 366)
+ */
+ private int getWeekDay(int year, int dayOfYear)
+ {
+ boolean greg = isGregorian(year, dayOfYear);
+ int day = (int) getLinearDay(year, dayOfYear, greg);
+
+ // The epoch was a thursday.
+ int weekday = (day + THURSDAY) % 7;
+ if (weekday <= 0)
+ weekday += 7;
+ return weekday;
+ }
+
+ /**
+ * Returns the day of the week for the first day of a given month (0..11)
+ */
+ private int getFirstDayOfMonth(int year, int month)
+ {
+ int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+
+ if (month > 11)
+ {
+ year += (month / 12);
+ month = month % 12;
+ }
+
+ if (month < 0)
+ {
+ year += (int) month / 12;
+ month = month % 12;
+ if (month < 0)
+ {
+ month += 12;
+ year--;
+ }
+ }
+
+ int dayOfYear = dayCount[month] + 1;
+ if (month > 1)
+ if (isLeapYear(year))
+ dayOfYear++;
+
+ boolean greg = isGregorian(year, dayOfYear);
+ int day = (int) getLinearDay(year, dayOfYear, greg);
+
+ // The epoch was a thursday.
+ int weekday = (day + THURSDAY) % 7;
+ if (weekday <= 0)
+ weekday += 7;
+ return weekday;
+ }
+
+ /**
+ * Takes a year, and a (zero based) day of year and determines
+ * if it is gregorian or not.
+ */
+ private boolean isGregorian(int year, int dayOfYear)
+ {
+ int relativeDay = (year - 1) * 365 + ((year - 1) >> 2) + dayOfYear
+ - EPOCH_DAYS; // gregorian days from 1 to epoch.
+ int gregFactor = (int) Math.floor((double) (year - 1) / 400.)
+ - (int) Math.floor((double) (year - 1) / 100.);
+
+ return ((relativeDay + gregFactor) * 60L * 60L * 24L * 1000L >= gregorianCutover);
+ }
+
+ /**
+ * Check set fields for validity, without leniency.
+ *
+ * @throws IllegalArgumentException if a field is invalid
+ */
+ private void nonLeniencyCheck() throws IllegalArgumentException
+ {
+ int[] month_days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+ int year = fields[YEAR];
+ int month = fields[MONTH];
+ int leap = isLeapYear(year) ? 1 : 0;
+
+ if (isSet[ERA] && fields[ERA] != AD && fields[ERA] != BC)
+ throw new IllegalArgumentException("Illegal ERA.");
+ if (isSet[YEAR] && fields[YEAR] < 1)
+ throw new IllegalArgumentException("Illegal YEAR.");
+ if (isSet[MONTH] && (month < 0 || month > 11))
+ throw new IllegalArgumentException("Illegal MONTH.");
+ if (isSet[WEEK_OF_YEAR])
+ {
+ int daysInYear = 365 + leap;
+ daysInYear += (getFirstDayOfMonth(year, 0) - 1); // pad first week
+ int last = getFirstDayOfMonth(year, 11) + 4;
+ if (last > 7)
+ last -= 7;
+ daysInYear += 7 - last;
+ int weeks = daysInYear / 7;
+ if (fields[WEEK_OF_YEAR] < 1 || fields[WEEK_OF_YEAR] > weeks)
+ throw new IllegalArgumentException("Illegal WEEK_OF_YEAR.");
+ }
+
+ if (isSet[WEEK_OF_MONTH])
+ {
+ int weeks = (month == 1 && leap == 0) ? 5 : 6;
+ if (fields[WEEK_OF_MONTH] < 1 || fields[WEEK_OF_MONTH] > weeks)
+ throw new IllegalArgumentException("Illegal WEEK_OF_MONTH.");
+ }
+
+ if (isSet[DAY_OF_MONTH])
+ if (fields[DAY_OF_MONTH] < 1
+ || fields[DAY_OF_MONTH] > month_days[month]
+ + ((month == 1) ? leap : 0))
+ throw new IllegalArgumentException("Illegal DAY_OF_MONTH.");
+
+ if (isSet[DAY_OF_YEAR]
+ && (fields[DAY_OF_YEAR] < 1 || fields[DAY_OF_YEAR] > 365 + leap))
+ throw new IllegalArgumentException("Illegal DAY_OF_YEAR.");
+
+ if (isSet[DAY_OF_WEEK]
+ && (fields[DAY_OF_WEEK] < 1 || fields[DAY_OF_WEEK] > 7))
+ throw new IllegalArgumentException("Illegal DAY_OF_WEEK.");
+
+ if (isSet[DAY_OF_WEEK_IN_MONTH])
+ {
+ int weeks = (month == 1 && leap == 0) ? 4 : 5;
+ if (fields[DAY_OF_WEEK_IN_MONTH] < -weeks
+ || fields[DAY_OF_WEEK_IN_MONTH] > weeks)
+ throw new IllegalArgumentException("Illegal DAY_OF_WEEK_IN_MONTH.");
+ }
+
+ if (isSet[AM_PM] && fields[AM_PM] != AM && fields[AM_PM] != PM)
+ throw new IllegalArgumentException("Illegal AM_PM.");
+ if (isSet[HOUR] && (fields[HOUR] < 0 || fields[HOUR] > 11))
+ throw new IllegalArgumentException("Illegal HOUR.");
+ if (isSet[HOUR_OF_DAY]
+ && (fields[HOUR_OF_DAY] < 0 || fields[HOUR_OF_DAY] > 23))
+ throw new IllegalArgumentException("Illegal HOUR_OF_DAY.");
+ if (isSet[MINUTE] && (fields[MINUTE] < 0 || fields[MINUTE] > 59))
+ throw new IllegalArgumentException("Illegal MINUTE.");
+ if (isSet[SECOND] && (fields[SECOND] < 0 || fields[SECOND] > 59))
+ throw new IllegalArgumentException("Illegal SECOND.");
+ if (isSet[MILLISECOND]
+ && (fields[MILLISECOND] < 0 || fields[MILLISECOND] > 999))
+ throw new IllegalArgumentException("Illegal MILLISECOND.");
+ if (isSet[ZONE_OFFSET]
+ && (fields[ZONE_OFFSET] < -12 * 60 * 60 * 1000L
+ || fields[ZONE_OFFSET] > 12 * 60 * 60 * 1000L))
+ throw new IllegalArgumentException("Illegal ZONE_OFFSET.");
+ if (isSet[DST_OFFSET]
+ && (fields[DST_OFFSET] < -12 * 60 * 60 * 1000L
+ || fields[DST_OFFSET] > 12 * 60 * 60 * 1000L))
+ throw new IllegalArgumentException("Illegal DST_OFFSET.");
+ }
+
+ /**
+ * Converts the time field values (<code>fields</code>) to
+ * milliseconds since the epoch UTC (<code>time</code>).
+ *
+ * @throws IllegalArgumentException if any calendar fields
+ * are invalid.
+ */
+ protected synchronized void computeTime()
+ {
+ int millisInDay = 0;
+ int era = fields[ERA];
+ int year = fields[YEAR];
+ int month = fields[MONTH];
+ int day = fields[DAY_OF_MONTH];
+
+ int minute = fields[MINUTE];
+ int second = fields[SECOND];
+ int millis = fields[MILLISECOND];
+ int[] month_days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+ int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+ int hour = 0;
+
+ if (! isLenient())
+ nonLeniencyCheck();
+
+ if (! isSet[MONTH] && (! isSet[DAY_OF_WEEK] || isSet[WEEK_OF_YEAR]))
+ {
+ // 5: YEAR + DAY_OF_WEEK + WEEK_OF_YEAR
+ if (isSet[WEEK_OF_YEAR])
+ {
+ int first = getFirstDayOfMonth(year, 0);
+ int offs = 1;
+ int daysInFirstWeek = getFirstDayOfWeek() - first;
+ if (daysInFirstWeek <= 0)
+ daysInFirstWeek += 7;
+
+ if (daysInFirstWeek < getMinimalDaysInFirstWeek())
+ offs += daysInFirstWeek;
+ else
+ offs -= 7 - daysInFirstWeek;
+ month = 0;
+ day = offs + 7 * (fields[WEEK_OF_YEAR] - 1);
+ offs = fields[DAY_OF_WEEK] - getFirstDayOfWeek();
+
+ if (offs < 0)
+ offs += 7;
+ day += offs;
+ }
+ else
+ {
+ // 4: YEAR + DAY_OF_YEAR
+ month = 0;
+ day = fields[DAY_OF_YEAR];
+ }
+ }
+ else
+ {
+ if (isSet[DAY_OF_WEEK])
+ {
+ int first = getFirstDayOfMonth(year, month);
+
+ // 3: YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
+ if (isSet[DAY_OF_WEEK_IN_MONTH])
+ {
+ if (fields[DAY_OF_WEEK_IN_MONTH] < 0)
+ {
+ month++;
+ first = getFirstDayOfMonth(year, month);
+ day = 1 + 7 * (fields[DAY_OF_WEEK_IN_MONTH]);
+ }
+ else
+ day = 1 + 7 * (fields[DAY_OF_WEEK_IN_MONTH] - 1);
+
+ int offs = fields[DAY_OF_WEEK] - first;
+ if (offs < 0)
+ offs += 7;
+ day += offs;
+ }
+ else
+ { // 2: YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
+ int offs = 1;
+ int daysInFirstWeek = getFirstDayOfWeek() - first;
+ if (daysInFirstWeek <= 0)
+ daysInFirstWeek += 7;
+
+ if (daysInFirstWeek < getMinimalDaysInFirstWeek())
+ offs += daysInFirstWeek;
+ else
+ offs -= 7 - daysInFirstWeek;
+
+ day = offs + 7 * (fields[WEEK_OF_MONTH] - 1);
+ offs = fields[DAY_OF_WEEK] - getFirstDayOfWeek();
+ if (offs < 0)
+ offs += 7;
+ day += offs;
+ }
+ }
+
+ // 1: YEAR + MONTH + DAY_OF_MONTH
+ }
+ if (era == BC && year > 0)
+ year = 1 - year;
+
+ // rest of code assumes day/month/year set
+ // should negative BC years be AD?
+ // get the hour (but no check for validity)
+ if (isSet[HOUR])
+ {
+ hour = fields[HOUR];
+ if (fields[AM_PM] == PM)
+ hour += 12;
+ }
+ else
+ hour = fields[HOUR_OF_DAY];
+
+ // Read the era,year,month,day fields and convert as appropriate.
+ // Calculate number of milliseconds into the day
+ // This takes care of both h, m, s, ms over/underflows.
+ long allMillis = (((hour * 60L) + minute) * 60L + second) * 1000L + millis;
+ day += allMillis / (24 * 60 * 60 * 1000L);
+ millisInDay = (int) (allMillis % (24 * 60 * 60 * 1000L));
+
+ if (month < 0)
+ {
+ year += (int) month / 12;
+ month = month % 12;
+ if (month < 0)
+ {
+ month += 12;
+ year--;
+ }
+ }
+ if (month > 11)
+ {
+ year += (month / 12);
+ month = month % 12;
+ }
+
+ month_days[1] = isLeapYear(year) ? 29 : 28;
+
+ while (day <= 0)
+ {
+ if (month == 0)
+ {
+ year--;
+ month_days[1] = isLeapYear(year) ? 29 : 28;
+ }
+ month = (month + 11) % 12;
+ day += month_days[month];
+ }
+ while (day > month_days[month])
+ {
+ day -= (month_days[month]);
+ month = (month + 1) % 12;
+ if (month == 0)
+ {
+ year++;
+ month_days[1] = isLeapYear(year) ? 29 : 28;
+ }
+ }
+
+ // ok, by here we have valid day,month,year,era and millisinday
+ int dayOfYear = dayCount[month] + day - 1; // (day starts on 1)
+ if (isLeapYear(year) && month > 1)
+ dayOfYear++;
+
+ int relativeDay = (year - 1) * 365 + ((year - 1) >> 2) + dayOfYear
+ - EPOCH_DAYS; // gregorian days from 1 to epoch.
+ int gregFactor = (int) Math.floor((double) (year - 1) / 400.)
+ - (int) Math.floor((double) (year - 1) / 100.);
+
+ if ((relativeDay + gregFactor) * 60L * 60L * 24L * 1000L >= gregorianCutover)
+ relativeDay += gregFactor;
+ else
+ relativeDay -= 2;
+
+ time = relativeDay * (24 * 60 * 60 * 1000L) + millisInDay;
+
+ // the epoch was a Thursday.
+ int weekday = (int) (relativeDay + THURSDAY) % 7;
+ if (weekday <= 0)
+ weekday += 7;
+ fields[DAY_OF_WEEK] = weekday;
+
+ // Time zone corrections.
+ TimeZone zone = getTimeZone();
+ int rawOffset = isSet[ZONE_OFFSET] ? fields[ZONE_OFFSET]
+ : zone.getRawOffset();
+
+ int dstOffset = isSet[DST_OFFSET] ? fields[DST_OFFSET]
+ : (zone.getOffset((year < 0) ? BC : AD,
+ (year < 0) ? 1 - year
+ : year,
+ month, day, weekday,
+ millisInDay)
+ - zone.getRawOffset());
+
+ time -= rawOffset + dstOffset;
+
+ isTimeSet = true;
+ }
+
+ /**
+ * Get the linear day in days since the epoch, using the
+ * Julian or Gregorian calendar as specified. If you specify a
+ * nonpositive year it is interpreted as BC as following: 0 is 1
+ * BC, -1 is 2 BC and so on.
+ *
+ * @param year the year of the date.
+ * @param dayOfYear the day of year of the date; 1 based.
+ * @param gregorian <code>true</code>, if we should use the Gregorian rules.
+ * @return the days since the epoch, may be negative.
+ */
+ private long getLinearDay(int year, int dayOfYear, boolean gregorian)
+ {
+ // The 13 is the number of days, that were omitted in the Gregorian
+ // Calender until the epoch.
+ // We shift right by 2 instead of dividing by 4, to get correct
+ // results for negative years (and this is even more efficient).
+ long julianDay = (year - 1) * 365L + ((year - 1) >> 2) + (dayOfYear - 1)
+ - EPOCH_DAYS; // gregorian days from 1 to epoch.
+
+ if (gregorian)
+ {
+ // subtract the days that are missing in gregorian calendar
+ // with respect to julian calendar.
+ //
+ // Okay, here we rely on the fact that the gregorian
+ // calendar was introduced in the AD era. This doesn't work
+ // with negative years.
+ //
+ // The additional leap year factor accounts for the fact that
+ // a leap day is not seen on Jan 1 of the leap year.
+ int gregOffset = (int) Math.floor((double) (year - 1) / 400.)
+ - (int) Math.floor((double) (year - 1) / 100.);
+
+ return julianDay + gregOffset;
+ }
+ else
+ julianDay -= 2;
+ return julianDay;
+ }
+
+ /**
+ * Converts the given linear day into era, year, month,
+ * day_of_year, day_of_month, day_of_week, and writes the result
+ * into the fields array.
+ *
+ * @param day the linear day.
+ * @param gregorian true, if we should use Gregorian rules.
+ */
+ private void calculateDay(int[] fields, long day, boolean gregorian)
+ {
+ // the epoch was a Thursday.
+ int weekday = (int) (day + THURSDAY) % 7;
+ if (weekday <= 0)
+ weekday += 7;
+ fields[DAY_OF_WEEK] = weekday;
+
+ // get a first approximation of the year. This may be one
+ // year too big.
+ int year = 1970
+ + (int) (gregorian
+ ? ((day - 100L) * 400L) / (365L * 400L + 100L - 4L
+ + 1L) : ((day - 100L) * 4L) / (365L * 4L + 1L));
+ if (day >= 0)
+ year++;
+
+ long firstDayOfYear = getLinearDay(year, 1, gregorian);
+
+ // Now look in which year day really lies.
+ if (day < firstDayOfYear)
+ {
+ year--;
+ firstDayOfYear = getLinearDay(year, 1, gregorian);
+ }
+
+ day -= firstDayOfYear - 1; // day of year, one based.
+
+ fields[DAY_OF_YEAR] = (int) day;
+ if (year <= 0)
+ {
+ fields[ERA] = BC;
+ fields[YEAR] = 1 - year;
+ }
+ else
+ {
+ fields[ERA] = AD;
+ fields[YEAR] = year;
+ }
+
+ int leapday = isLeapYear(year) ? 1 : 0;
+ if (day <= 31 + 28 + leapday)
+ {
+ fields[MONTH] = (int) day / 32; // 31->JANUARY, 32->FEBRUARY
+ fields[DAY_OF_MONTH] = (int) day - 31 * fields[MONTH];
+ }
+ else
+ {
+ // A few more magic formulas
+ int scaledDay = ((int) day - leapday) * 5 + 8;
+ fields[MONTH] = scaledDay / (31 + 30 + 31 + 30 + 31);
+ fields[DAY_OF_MONTH] = (scaledDay % (31 + 30 + 31 + 30 + 31)) / 5 + 1;
+ }
+ }
+
+ /**
+ * Converts the milliseconds since the epoch UTC
+ * (<code>time</code>) to time fields
+ * (<code>fields</code>).
+ */
+ protected synchronized void computeFields()
+ {
+ boolean gregorian = (time >= gregorianCutover);
+
+ TimeZone zone = getTimeZone();
+ fields[ZONE_OFFSET] = zone.getRawOffset();
+ long localTime = time + fields[ZONE_OFFSET];
+
+ long day = localTime / (24 * 60 * 60 * 1000L);
+ int millisInDay = (int) (localTime % (24 * 60 * 60 * 1000L));
+
+ if (millisInDay < 0)
+ {
+ millisInDay += (24 * 60 * 60 * 1000);
+ day--;
+ }
+
+ calculateDay(fields, day, gregorian);
+ fields[DST_OFFSET] = zone.getOffset(fields[ERA], fields[YEAR],
+ fields[MONTH], fields[DAY_OF_MONTH],
+ fields[DAY_OF_WEEK], millisInDay)
+ - fields[ZONE_OFFSET];
+
+ millisInDay += fields[DST_OFFSET];
+ if (millisInDay >= 24 * 60 * 60 * 1000)
+ {
+ millisInDay -= 24 * 60 * 60 * 1000;
+ calculateDay(fields, ++day, gregorian);
+ }
+
+ fields[DAY_OF_WEEK_IN_MONTH] = (fields[DAY_OF_MONTH] + 6) / 7;
+
+ // which day of the week are we (0..6), relative to getFirstDayOfWeek
+ int relativeWeekday = (7 + fields[DAY_OF_WEEK] - getFirstDayOfWeek()) % 7;
+
+ // which day of the week is the first of this month?
+ // nb 35 is the smallest multiple of 7 that ensures that
+ // the left hand side of the modulo operator is positive.
+ int relativeWeekdayOfFirst = (relativeWeekday - fields[DAY_OF_MONTH]
+ + 1 + 35) % 7;
+
+ // which week of the month is the first of this month in?
+ int minDays = getMinimalDaysInFirstWeek();
+ int weekOfFirst = ((7 - relativeWeekdayOfFirst) >= minDays) ? 1 : 0;
+
+ // which week of the month is this day in?
+ fields[WEEK_OF_MONTH] = (fields[DAY_OF_MONTH]
+ + relativeWeekdayOfFirst - 1) / 7 + weekOfFirst;
+
+ int weekOfYear = (fields[DAY_OF_YEAR] - relativeWeekday + 6) / 7;
+
+ // Do the Correction: getMinimalDaysInFirstWeek() is always in the
+ // first week.
+ int firstWeekday = (7 + getWeekDay(fields[YEAR], minDays)
+ - getFirstDayOfWeek()) % 7;
+ if (minDays - firstWeekday < 1)
+ weekOfYear++;
+ fields[WEEK_OF_YEAR] = weekOfYear;
+
+ int hourOfDay = millisInDay / (60 * 60 * 1000);
+ fields[AM_PM] = (hourOfDay < 12) ? AM : PM;
+ int hour = hourOfDay % 12;
+ fields[HOUR] = hour;
+ fields[HOUR_OF_DAY] = hourOfDay;
+ millisInDay %= (60 * 60 * 1000);
+ fields[MINUTE] = millisInDay / (60 * 1000);
+ millisInDay %= (60 * 1000);
+ fields[SECOND] = millisInDay / (1000);
+ fields[MILLISECOND] = millisInDay % 1000;
+
+ areFieldsSet = isSet[ERA] = isSet[YEAR] = isSet[MONTH] = isSet[WEEK_OF_YEAR] = isSet[WEEK_OF_MONTH] = isSet[DAY_OF_MONTH] = isSet[DAY_OF_YEAR] = isSet[DAY_OF_WEEK] = isSet[DAY_OF_WEEK_IN_MONTH] = isSet[AM_PM] = isSet[HOUR] = isSet[HOUR_OF_DAY] = isSet[MINUTE] = isSet[SECOND] = isSet[MILLISECOND] = isSet[ZONE_OFFSET] = isSet[DST_OFFSET] = true;
+ }
+
+ /**
+ * Return a hash code for this object, following the general contract
+ * specified by {@link Object#hashCode()}.
+ * @return the hash code
+ */
+ public int hashCode()
+ {
+ int val = (int) ((gregorianCutover >>> 32) ^ (gregorianCutover & 0xffffffff));
+ return super.hashCode() ^ val;
+ }
+
+ /**
+ * Compares the given calendar with this. An object, o, is
+ * equivalent to this if it is also a <code>GregorianCalendar</code>
+ * with the same time since the epoch under the same conditions
+ * (same change date and same time zone).
+ *
+ * @param o the object to that we should compare.
+ * @return true, if the given object is a calendar, that represents
+ * the same time (but doesn't necessarily have the same fields).
+ * @throws IllegalArgumentException if one of the fields
+ * <code>ZONE_OFFSET</code> or <code>DST_OFFSET</code> is
+ * specified, if an unknown field is specified or if one
+ * of the calendar fields receives an illegal value when
+ * leniancy is not enabled.
+ */
+ public boolean equals(Object o)
+ {
+ if (! (o instanceof GregorianCalendar))
+ return false;
+
+ GregorianCalendar cal = (GregorianCalendar) o;
+ return (cal.gregorianCutover == gregorianCutover
+ && super.equals(o));
+ }
+
+ /**
+ * Adds the specified amount of time to the given time field. The
+ * amount may be negative to subtract the time. If the field overflows
+ * it does what you expect: Jan, 25 + 10 Days is Feb, 4.
+ * @param field one of the time field constants.
+ * @param amount the amount of time to add.
+ * @exception IllegalArgumentException if <code>field</code> is
+ * <code>ZONE_OFFSET</code>, <code>DST_OFFSET</code>, or invalid; or
+ * if <code>amount</code> contains an out-of-range value and the calendar
+ * is not in lenient mode.
+ */
+ public void add(int field, int amount)
+ {
+ switch (field)
+ {
+ case YEAR:
+ complete();
+ fields[YEAR] += amount;
+ isTimeSet = false;
+ break;
+ case MONTH:
+ complete();
+ int months = fields[MONTH] + amount;
+ fields[YEAR] += months / 12;
+ fields[MONTH] = months % 12;
+ if (fields[MONTH] < 0)
+ {
+ fields[MONTH] += 12;
+ fields[YEAR]--;
+ }
+ int maxDay = getActualMaximum(DAY_OF_MONTH);
+ if (fields[DAY_OF_MONTH] > maxDay)
+ fields[DAY_OF_MONTH] = maxDay;
+ set(YEAR, fields[YEAR]);
+ set(MONTH, fields[MONTH]);
+ break;
+ case DAY_OF_MONTH:
+ case DAY_OF_YEAR:
+ case DAY_OF_WEEK:
+ if (! isTimeSet)
+ computeTime();
+ time += amount * (24 * 60 * 60 * 1000L);
+ areFieldsSet = false;
+ break;
+ case WEEK_OF_YEAR:
+ case WEEK_OF_MONTH:
+ case DAY_OF_WEEK_IN_MONTH:
+ if (! isTimeSet)
+ computeTime();
+ time += amount * (7 * 24 * 60 * 60 * 1000L);
+ areFieldsSet = false;
+ break;
+ case AM_PM:
+ if (! isTimeSet)
+ computeTime();
+ time += amount * (12 * 60 * 60 * 1000L);
+ areFieldsSet = false;
+ break;
+ case HOUR:
+ case HOUR_OF_DAY:
+ if (! isTimeSet)
+ computeTime();
+ time += amount * (60 * 60 * 1000L);
+ areFieldsSet = false;
+ break;
+ case MINUTE:
+ if (! isTimeSet)
+ computeTime();
+ time += amount * (60 * 1000L);
+ areFieldsSet = false;
+ break;
+ case SECOND:
+ if (! isTimeSet)
+ computeTime();
+ time += amount * (1000L);
+ areFieldsSet = false;
+ break;
+ case MILLISECOND:
+ if (! isTimeSet)
+ computeTime();
+ time += amount;
+ areFieldsSet = false;
+ break;
+ case ZONE_OFFSET:
+ case DST_OFFSET:default:
+ throw new IllegalArgumentException("Invalid or unknown field");
+ }
+ }
+
+ /**
+ * Rolls the specified time field up or down. This means add one
+ * to the specified field, but don't change the other fields. If
+ * the maximum for this field is reached, start over with the
+ * minimum value.
+ *
+ * <strong>Note:</strong> There may be situation, where the other
+ * fields must be changed, e.g rolling the month on May, 31.
+ * The date June, 31 is automatically converted to July, 1.
+ * This requires lenient settings.
+ *
+ * @param field the time field. One of the time field constants.
+ * @param up the direction, true for up, false for down.
+ * @throws IllegalArgumentException if one of the fields
+ * <code>ZONE_OFFSET</code> or <code>DST_OFFSET</code> is
+ * specified, if an unknown field is specified or if one
+ * of the calendar fields receives an illegal value when
+ * leniancy is not enabled.
+ */
+ public void roll(int field, boolean up)
+ {
+ roll(field, up ? 1 : -1);
+ }
+
+ /**
+ * Checks that the fields are still within their legal bounds,
+ * following use of the <code>roll()</code> method.
+ *
+ * @param field the field to check.
+ * @param delta multipler for alterations to the <code>time</code>.
+ * @see #roll(int, boolean)
+ * @see #roll(int, int)
+ */
+ private void cleanUpAfterRoll(int field, int delta)
+ {
+ switch (field)
+ {
+ case ERA:
+ case YEAR:
+ case MONTH:
+ // check that day of month is still in correct range
+ if (fields[DAY_OF_MONTH] > getActualMaximum(DAY_OF_MONTH))
+ fields[DAY_OF_MONTH] = getActualMaximum(DAY_OF_MONTH);
+ isTimeSet = false;
+ isSet[WEEK_OF_MONTH] = false;
+ isSet[DAY_OF_WEEK] = false;
+ isSet[DAY_OF_WEEK_IN_MONTH] = false;
+ isSet[DAY_OF_YEAR] = false;
+ isSet[WEEK_OF_YEAR] = false;
+ break;
+ case DAY_OF_MONTH:
+ isSet[WEEK_OF_MONTH] = false;
+ isSet[DAY_OF_WEEK] = false;
+ isSet[DAY_OF_WEEK_IN_MONTH] = false;
+ isSet[DAY_OF_YEAR] = false;
+ isSet[WEEK_OF_YEAR] = false;
+ time += delta * (24 * 60 * 60 * 1000L);
+ break;
+ case WEEK_OF_MONTH:
+ isSet[DAY_OF_MONTH] = false;
+ isSet[DAY_OF_WEEK_IN_MONTH] = false;
+ isSet[DAY_OF_YEAR] = false;
+ isSet[WEEK_OF_YEAR] = false;
+ time += delta * (7 * 24 * 60 * 60 * 1000L);
+ break;
+ case DAY_OF_WEEK_IN_MONTH:
+ isSet[DAY_OF_MONTH] = false;
+ isSet[WEEK_OF_MONTH] = false;
+ isSet[DAY_OF_YEAR] = false;
+ isSet[WEEK_OF_YEAR] = false;
+ time += delta * (7 * 24 * 60 * 60 * 1000L);
+ break;
+ case DAY_OF_YEAR:
+ isSet[MONTH] = false;
+ isSet[DAY_OF_MONTH] = false;
+ isSet[WEEK_OF_MONTH] = false;
+ isSet[DAY_OF_WEEK_IN_MONTH] = false;
+ isSet[DAY_OF_WEEK] = false;
+ isSet[WEEK_OF_YEAR] = false;
+ time += delta * (24 * 60 * 60 * 1000L);
+ break;
+ case WEEK_OF_YEAR:
+ isSet[MONTH] = false;
+ isSet[DAY_OF_MONTH] = false;
+ isSet[WEEK_OF_MONTH] = false;
+ isSet[DAY_OF_WEEK_IN_MONTH] = false;
+ isSet[DAY_OF_YEAR] = false;
+ time += delta * (7 * 24 * 60 * 60 * 1000L);
+ break;
+ case AM_PM:
+ isSet[HOUR_OF_DAY] = false;
+ time += delta * (12 * 60 * 60 * 1000L);
+ break;
+ case HOUR:
+ isSet[HOUR_OF_DAY] = false;
+ time += delta * (60 * 60 * 1000L);
+ break;
+ case HOUR_OF_DAY:
+ isSet[HOUR] = false;
+ isSet[AM_PM] = false;
+ time += delta * (60 * 60 * 1000L);
+ break;
+ case MINUTE:
+ time += delta * (60 * 1000L);
+ break;
+ case SECOND:
+ time += delta * (1000L);
+ break;
+ case MILLISECOND:
+ time += delta;
+ break;
+ }
+ }
+
+ /**
+ * Rolls the specified time field by the given amount. This means
+ * add amount to the specified field, but don't change the other
+ * fields. If the maximum for this field is reached, start over
+ * with the minimum value and vice versa for negative amounts.
+ *
+ * <strong>Note:</strong> There may be situation, where the other
+ * fields must be changed, e.g rolling the month on May, 31.
+ * The date June, 31 is automatically corrected to June, 30.
+ *
+ * @param field the time field. One of the time field constants.
+ * @param amount the amount by which we should roll.
+ * @throws IllegalArgumentException if one of the fields
+ * <code>ZONE_OFFSET</code> or <code>DST_OFFSET</code> is
+ * specified, if an unknown field is specified or if one
+ * of the calendar fields receives an illegal value when
+ * leniancy is not enabled.
+ */
+ public void roll(int field, int amount)
+ {
+ switch (field)
+ {
+ case DAY_OF_WEEK:
+ // day of week is special: it rolls automatically
+ add(field, amount);
+ return;
+ case ZONE_OFFSET:
+ case DST_OFFSET:
+ throw new IllegalArgumentException("Can't roll time zone");
+ }
+ complete();
+ int min = getActualMinimum(field);
+ int range = getActualMaximum(field) - min + 1;
+ int oldval = fields[field];
+ int newval = (oldval - min + range + amount) % range + min;
+ if (newval < min)
+ newval += range;
+ fields[field] = newval;
+ cleanUpAfterRoll(field, newval - oldval);
+ }
+
+ /**
+ * The minimum values for the calendar fields.
+ */
+ private static final int[] minimums =
+ {
+ BC, 1, 0, 0, 1, 1, 1, SUNDAY, 1, AM,
+ 1, 0, 0, 0, 0, -(12 * 60 * 60 * 1000),
+ 0
+ };
+
+ /**
+ * The maximum values for the calendar fields.
+ */
+ private static final int[] maximums =
+ {
+ AD, 5000000, 11, 53, 6, 31, 366,
+ SATURDAY, 5, PM, 12, 23, 59, 59, 999,
+ +(12 * 60 * 60 * 1000),
+ (12 * 60 * 60 * 1000)
+ };
+
+ /**
+ * Gets the smallest value that is allowed for the specified field.
+ *
+ * @param field one of the time field constants.
+ * @return the smallest value for the specified field.
+ */
+ public int getMinimum(int field)
+ {
+ return minimums[field];
+ }
+
+ /**
+ * Gets the biggest value that is allowed for the specified field.
+ *
+ * @param field one of the time field constants.
+ * @return the biggest value.
+ */
+ public int getMaximum(int field)
+ {
+ return maximums[field];
+ }
+
+ /**
+ * Gets the greatest minimum value that is allowed for the specified field.
+ * This is the largest value returned by the <code>getActualMinimum(int)</code>
+ * method.
+ *
+ * @param field the time field. One of the time field constants.
+ * @return the greatest minimum value.
+ * @see #getActualMinimum(int)
+ */
+ public int getGreatestMinimum(int field)
+ {
+ if (field == WEEK_OF_YEAR)
+ return 1;
+ return minimums[field];
+ }
+
+ /**
+ * Gets the smallest maximum value that is allowed for the
+ * specified field. This is the smallest value returned
+ * by the <code>getActualMaximum(int)</code>. For example,
+ * this is 28 for DAY_OF_MONTH (as all months have at least
+ * 28 days).
+ *
+ * @param field the time field. One of the time field constants.
+ * @return the least maximum value.
+ * @see #getActualMaximum(int)
+ * @since 1.2
+ */
+ public int getLeastMaximum(int field)
+ {
+ switch (field)
+ {
+ case WEEK_OF_YEAR:
+ return 52;
+ case DAY_OF_MONTH:
+ return 28;
+ case DAY_OF_YEAR:
+ return 365;
+ case DAY_OF_WEEK_IN_MONTH:
+ case WEEK_OF_MONTH:
+ return 4;
+ default:
+ return maximums[field];
+ }
+ }
+
+ /**
+ * Gets the actual minimum value that is allowed for the specified field.
+ * This value is dependent on the values of the other fields. Note that
+ * this calls <code>complete()</code> if not enough fields are set. This
+ * can have ugly side effects. The value given depends on the current
+ * time used by this instance.
+ *
+ * @param field the time field. One of the time field constants.
+ * @return the actual minimum value.
+ * @since 1.2
+ */
+ public int getActualMinimum(int field)
+ {
+ if (field == WEEK_OF_YEAR)
+ {
+ int min = getMinimalDaysInFirstWeek();
+ if (min == 0)
+ return 1;
+ if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR])
+ complete();
+
+ int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
+ int weekday = getWeekDay(year, min);
+ if ((7 + weekday - getFirstDayOfWeek()) % 7 >= min - 1)
+ return 1;
+ return 0;
+ }
+ return minimums[field];
+ }
+
+ /**
+ * Gets the actual maximum value that is allowed for the specified field.
+ * This value is dependent on the values of the other fields. Note that
+ * this calls <code>complete()</code> if not enough fields are set. This
+ * can have ugly side effects. The value given depends on the current time
+ * used by this instance; thus, leap years have a maximum day of month value of
+ * 29, rather than 28.
+ *
+ * @param field the time field. One of the time field constants.
+ * @return the actual maximum value.
+ */
+ public int getActualMaximum(int field)
+ {
+ switch (field)
+ {
+ case WEEK_OF_YEAR:
+ {
+ if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR])
+ complete();
+
+ // This is wrong for the year that contains the gregorian change.
+ // I.e it gives the weeks in the julian year or in the gregorian
+ // year in that case.
+ int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
+ int lastDay = isLeapYear(year) ? 366 : 365;
+ int weekday = getWeekDay(year, lastDay);
+ int week = (lastDay + 6 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7;
+
+ int minimalDays = getMinimalDaysInFirstWeek();
+ int firstWeekday = getWeekDay(year, minimalDays);
+ /*
+ * Is there a set of days at the beginning of the year, before the
+ * first day of the week, equal to or greater than the minimum number
+ * of days required in the first week?
+ */
+ if (minimalDays - (7 + firstWeekday - getFirstDayOfWeek()) % 7 < 1)
+ return week + 1; /* Add week 1: firstWeekday through to firstDayOfWeek */
+ }
+ case DAY_OF_MONTH:
+ {
+ if (! areFieldsSet || ! isSet[MONTH])
+ complete();
+ int month = fields[MONTH];
+
+ // If you change this, you should also change
+ // SimpleTimeZone.getDaysInMonth();
+ if (month == FEBRUARY)
+ {
+ if (! isSet[YEAR] || ! isSet[ERA])
+ complete();
+ int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
+ return isLeapYear(year) ? 29 : 28;
+ }
+ else if (month < AUGUST)
+ return 31 - (month & 1);
+ else
+ return 30 + (month & 1);
+ }
+ case DAY_OF_YEAR:
+ {
+ if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR])
+ complete();
+ int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
+ return isLeapYear(year) ? 366 : 365;
+ }
+ case DAY_OF_WEEK_IN_MONTH:
+ {
+ // This is wrong for the month that contains the gregorian change.
+ int daysInMonth = getActualMaximum(DAY_OF_MONTH);
+
+ // That's black magic, I know
+ return (daysInMonth - (fields[DAY_OF_MONTH] - 1) % 7 + 6) / 7;
+ }
+ case WEEK_OF_MONTH:
+ {
+ int daysInMonth = getActualMaximum(DAY_OF_MONTH);
+ int weekday = (daysInMonth - fields[DAY_OF_MONTH]
+ + fields[DAY_OF_WEEK] - SUNDAY) % 7 + SUNDAY;
+ return (daysInMonth + 6 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7;
+ }
+ default:
+ return maximums[field];
+ }
+ }
+}
diff --git a/libjava/classpath/java/util/HashMap.java b/libjava/classpath/java/util/HashMap.java
new file mode 100644
index 000000000..55d81c620
--- /dev/null
+++ b/libjava/classpath/java/util/HashMap.java
@@ -0,0 +1,907 @@
+/* HashMap.java -- a class providing a basic hashtable data structure,
+ mapping Object --> Object
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+// NOTE: This implementation is very similar to that of Hashtable. If you fix
+// a bug in here, chances are you should make a similar change to the Hashtable
+// code.
+
+// NOTE: This implementation has some nasty coding style in order to
+// support LinkedHashMap, which extends this.
+
+/**
+ * This class provides a hashtable-backed implementation of the
+ * Map interface.
+ * <p>
+ *
+ * It uses a hash-bucket approach; that is, hash collisions are handled
+ * by linking the new node off of the pre-existing node (or list of
+ * nodes). In this manner, techniques such as linear probing (which
+ * can cause primary clustering) and rehashing (which does not fit very
+ * well with Java's method of precomputing hash codes) are avoided.
+ * <p>
+ *
+ * Under ideal circumstances (no collisions), HashMap offers O(1)
+ * 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>
+ *
+ * HashMap is part of the JDK1.2 Collections API. It differs from
+ * Hashtable in that it accepts the null key and null values, and it
+ * does not support "Enumeration views." Also, it is not synchronized;
+ * if you plan to use it in multiple threads, consider using:<br>
+ * <code>Map m = Collections.synchronizedMap(new HashMap(...));</code>
+ * <p>
+ *
+ * The iterators are <i>fail-fast</i>, meaning that any structural
+ * modification, except for <code>remove()</code> called on the iterator
+ * itself, cause the iterator to throw a
+ * <code>ConcurrentModificationException</code> rather than exhibit
+ * non-deterministic behavior.
+ *
+ * @author Jon Zeppieri
+ * @author Jochen Hoenicke
+ * @author Bryce McKinlay
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Object#hashCode()
+ * @see Collection
+ * @see Map
+ * @see TreeMap
+ * @see LinkedHashMap
+ * @see IdentityHashMap
+ * @see Hashtable
+ * @since 1.2
+ * @status updated to 1.4
+ */
+public class HashMap<K, V> extends AbstractMap<K, V>
+ implements Map<K, V>, Cloneable, Serializable
+{
+ /**
+ * Default number of buckets. This is the value the JDK 1.3 uses. Some
+ * early documentation specified this value as 101. That is incorrect.
+ * Package visible for use by HashSet.
+ */
+ static final int DEFAULT_CAPACITY = 11;
+
+ /**
+ * The default load factor; this is explicitly specified by the spec.
+ * Package visible for use by HashSet.
+ */
+ static final float DEFAULT_LOAD_FACTOR = 0.75f;
+
+ /**
+ * Compatible with JDK 1.2.
+ */
+ private static final long serialVersionUID = 362498820763181265L;
+
+ /**
+ * The rounded product of the capacity and the load factor; when the number
+ * of elements exceeds the threshold, the HashMap calls
+ * <code>rehash()</code>.
+ * @serial the threshold for rehashing
+ */
+ private int threshold;
+
+ /**
+ * Load factor of this HashMap: used in computing the threshold.
+ * Package visible for use by HashSet.
+ * @serial the load factor
+ */
+ final float loadFactor;
+
+ /**
+ * Array containing the actual key-value mappings.
+ * Package visible for use by nested and subclasses.
+ */
+ transient HashEntry<K, V>[] buckets;
+
+ /**
+ * Counts the number of modifications this HashMap has undergone, used
+ * by Iterators to know when to throw ConcurrentModificationExceptions.
+ * Package visible for use by nested and subclasses.
+ */
+ transient int modCount;
+
+ /**
+ * The size of this HashMap: denotes the number of key-value pairs.
+ * Package visible for use by nested and subclasses.
+ */
+ transient int size;
+
+ /**
+ * The cache for {@link #entrySet()}.
+ */
+ private transient Set<Map.Entry<K, V>> entries;
+
+ /**
+ * Class to represent an entry in the hash table. Holds a single key-value
+ * pair. Package visible for use by subclass.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ static class HashEntry<K, V> extends AbstractMap.SimpleEntry<K, V>
+ {
+ /**
+ * The next entry in the linked list. Package visible for use by subclass.
+ */
+ HashEntry<K, V> next;
+
+ /**
+ * Simple constructor.
+ * @param key the key
+ * @param value the value
+ */
+ HashEntry(K key, V value)
+ {
+ super(key, value);
+ }
+
+ /**
+ * Called when this entry is accessed via {@link #put(Object, Object)}.
+ * This version does nothing, but in LinkedHashMap, it must do some
+ * bookkeeping for access-traversal mode.
+ */
+ void access()
+ {
+ }
+
+ /**
+ * Called when this entry is removed from the map. This version simply
+ * returns the value, but in LinkedHashMap, it must also do bookkeeping.
+ *
+ * @return the value of this key as it is removed
+ */
+ V cleanup()
+ {
+ return value;
+ }
+ }
+
+ /**
+ * Construct a new HashMap with the default capacity (11) and the default
+ * load factor (0.75).
+ */
+ public HashMap()
+ {
+ this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR);
+ }
+
+ /**
+ * Construct a new HashMap from the given Map, with initial capacity
+ * the greater of the size of <code>m</code> or the default of 11.
+ * <p>
+ *
+ * Every element in Map m will be put into this new HashMap.
+ *
+ * @param m a Map whose key / value pairs will be put into the new HashMap.
+ * <b>NOTE: key / value pairs are not cloned in this constructor.</b>
+ * @throws NullPointerException if m is null
+ */
+ public HashMap(Map<? extends K, ? extends V> m)
+ {
+ this(Math.max(m.size() * 2, DEFAULT_CAPACITY), DEFAULT_LOAD_FACTOR);
+ putAll(m);
+ }
+
+ /**
+ * Construct a new HashMap with a specific inital capacity and
+ * default load factor of 0.75.
+ *
+ * @param initialCapacity the initial capacity of this HashMap (&gt;=0)
+ * @throws IllegalArgumentException if (initialCapacity &lt; 0)
+ */
+ public HashMap(int initialCapacity)
+ {
+ this(initialCapacity, DEFAULT_LOAD_FACTOR);
+ }
+
+ /**
+ * Construct a new HashMap with a specific inital capacity and load factor.
+ *
+ * @param initialCapacity the initial capacity (&gt;=0)
+ * @param loadFactor the load factor (&gt; 0, not NaN)
+ * @throws IllegalArgumentException if (initialCapacity &lt; 0) ||
+ * ! (loadFactor &gt; 0.0)
+ */
+ public HashMap(int initialCapacity, float loadFactor)
+ {
+ if (initialCapacity < 0)
+ throw new IllegalArgumentException("Illegal Capacity: "
+ + initialCapacity);
+ if (! (loadFactor > 0)) // check for NaN too
+ throw new IllegalArgumentException("Illegal Load: " + loadFactor);
+
+ if (initialCapacity == 0)
+ initialCapacity = 1;
+ buckets = (HashEntry<K, V>[]) new HashEntry[initialCapacity];
+ this.loadFactor = loadFactor;
+ threshold = (int) (initialCapacity * loadFactor);
+ }
+
+ /**
+ * Returns the number of kay-value mappings currently in this Map.
+ *
+ * @return the size
+ */
+ public int size()
+ {
+ return size;
+ }
+
+ /**
+ * Returns true if there are no key-value mappings currently in this Map.
+ *
+ * @return <code>size() == 0</code>
+ */
+ public boolean isEmpty()
+ {
+ return size == 0;
+ }
+
+ /**
+ * Return the value in this HashMap associated with the supplied key,
+ * or <code>null</code> if the key maps to nothing. NOTE: Since the value
+ * could also be null, you must use containsKey to see if this key
+ * actually maps to something.
+ *
+ * @param key the key for which to fetch an associated value
+ * @return what the key maps to, if present
+ * @see #put(Object, Object)
+ * @see #containsKey(Object)
+ */
+ public V get(Object key)
+ {
+ int idx = hash(key);
+ HashEntry<K, V> e = buckets[idx];
+ while (e != null)
+ {
+ if (equals(key, e.key))
+ return e.value;
+ e = e.next;
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if the supplied object <code>equals()</code> a key
+ * in this HashMap.
+ *
+ * @param key the key to search for in this HashMap
+ * @return true if the key is in the table
+ * @see #containsValue(Object)
+ */
+ public boolean containsKey(Object key)
+ {
+ int idx = hash(key);
+ HashEntry<K, V> e = buckets[idx];
+ while (e != null)
+ {
+ if (equals(key, e.key))
+ return true;
+ e = e.next;
+ }
+ return false;
+ }
+
+ /**
+ * Puts the supplied value into the Map, mapped by the supplied key.
+ * The value may be retrieved by any object which <code>equals()</code>
+ * this key. NOTE: Since the prior value could also be null, you must
+ * first use containsKey if you want to see if you are replacing the
+ * key's mapping.
+ *
+ * @param key the key used to locate the value
+ * @param value the value to be stored in the HashMap
+ * @return the prior mapping of the key, or null if there was none
+ * @see #get(Object)
+ * @see Object#equals(Object)
+ */
+ public V put(K key, V value)
+ {
+ int idx = hash(key);
+ HashEntry<K, V> e = buckets[idx];
+
+ while (e != null)
+ {
+ if (equals(key, e.key))
+ {
+ e.access(); // Must call this for bookkeeping in LinkedHashMap.
+ V r = e.value;
+ e.value = value;
+ return r;
+ }
+ else
+ e = e.next;
+ }
+
+ // At this point, we know we need to add a new entry.
+ modCount++;
+ if (++size > threshold)
+ {
+ rehash();
+ // Need a new hash value to suit the bigger table.
+ idx = hash(key);
+ }
+
+ // LinkedHashMap cannot override put(), hence this call.
+ addEntry(key, value, idx, true);
+ return null;
+ }
+
+ /**
+ * Copies all elements of the given map into this hashtable. If this table
+ * already has a mapping for a key, the new mapping replaces the current
+ * one.
+ *
+ * @param m the map to be hashed into this
+ */
+ public void putAll(Map<? extends K, ? extends V> m)
+ {
+ final Map<K,V> addMap = (Map<K,V>) m;
+ final Iterator<Map.Entry<K,V>> it = addMap.entrySet().iterator();
+ while (it.hasNext())
+ {
+ final Map.Entry<K,V> e = it.next();
+ // Optimize in case the Entry is one of our own.
+ if (e instanceof AbstractMap.SimpleEntry)
+ {
+ AbstractMap.SimpleEntry<? extends K, ? extends V> entry
+ = (AbstractMap.SimpleEntry<? extends K, ? extends V>) e;
+ put(entry.key, entry.value);
+ }
+ else
+ put(e.getKey(), e.getValue());
+ }
+ }
+
+ /**
+ * Removes from the HashMap and returns the value which is mapped by the
+ * supplied key. If the key maps to nothing, then the HashMap remains
+ * unchanged, and <code>null</code> is returned. NOTE: Since the value
+ * could also be null, you must use containsKey to see if you are
+ * actually removing a mapping.
+ *
+ * @param key the key used to locate the value to remove
+ * @return whatever the key mapped to, if present
+ */
+ public V remove(Object key)
+ {
+ int idx = hash(key);
+ HashEntry<K, V> e = buckets[idx];
+ HashEntry<K, V> last = null;
+
+ while (e != null)
+ {
+ if (equals(key, e.key))
+ {
+ modCount++;
+ if (last == null)
+ buckets[idx] = e.next;
+ else
+ last.next = e.next;
+ size--;
+ // Method call necessary for LinkedHashMap to work correctly.
+ return e.cleanup();
+ }
+ last = e;
+ e = e.next;
+ }
+ return null;
+ }
+
+ /**
+ * Clears the Map so it has no keys. This is O(1).
+ */
+ public void clear()
+ {
+ if (size != 0)
+ {
+ modCount++;
+ Arrays.fill(buckets, null);
+ size = 0;
+ }
+ }
+
+ /**
+ * Returns true 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
+ * @see #containsKey(Object)
+ */
+ public boolean containsValue(Object value)
+ {
+ for (int i = buckets.length - 1; i >= 0; i--)
+ {
+ HashEntry<K, V> e = buckets[i];
+ while (e != null)
+ {
+ if (equals(value, e.value))
+ return true;
+ e = e.next;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns a shallow clone of this HashMap. The Map itself is cloned,
+ * but its contents are not. This is O(n).
+ *
+ * @return the clone
+ */
+ public Object clone()
+ {
+ HashMap<K, V> copy = null;
+ try
+ {
+ copy = (HashMap<K, V>) super.clone();
+ }
+ catch (CloneNotSupportedException x)
+ {
+ // This is impossible.
+ }
+ copy.buckets = (HashEntry<K, V>[]) new HashEntry[buckets.length];
+ copy.putAllInternal(this);
+ // Clear the entry cache. AbstractMap.clone() does the others.
+ copy.entries = null;
+ return copy;
+ }
+
+ /**
+ * Returns a "set view" of this HashMap's keys. The set is backed by the
+ * HashMap, so changes in one show up in the other. The set supports
+ * element removal, but not element addition.
+ *
+ * @return a set view of the keys
+ * @see #values()
+ * @see #entrySet()
+ */
+ public Set<K> keySet()
+ {
+ if (keys == null)
+ // Create an AbstractSet with custom implementations of those methods
+ // that can be overridden easily and efficiently.
+ keys = new AbstractSet<K>()
+ {
+ public int size()
+ {
+ return size;
+ }
+
+ public Iterator<K> iterator()
+ {
+ // Cannot create the iterator directly, because of LinkedHashMap.
+ return HashMap.this.iterator(KEYS);
+ }
+
+ public void clear()
+ {
+ HashMap.this.clear();
+ }
+
+ public boolean contains(Object o)
+ {
+ return containsKey(o);
+ }
+
+ public boolean remove(Object o)
+ {
+ // Test against the size of the HashMap to determine if anything
+ // really got removed. This is necessary because the return value
+ // of HashMap.remove() is ambiguous in the null case.
+ int oldsize = size;
+ HashMap.this.remove(o);
+ return oldsize != size;
+ }
+ };
+ return keys;
+ }
+
+ /**
+ * Returns a "collection view" (or "bag view") of this HashMap's values.
+ * The collection is backed by the HashMap, so changes in one show up
+ * in the other. The collection supports element removal, but not element
+ * addition.
+ *
+ * @return a bag view of the values
+ * @see #keySet()
+ * @see #entrySet()
+ */
+ public Collection<V> values()
+ {
+ if (values == null)
+ // We don't bother overriding many of the optional methods, as doing so
+ // wouldn't provide any significant performance advantage.
+ values = new AbstractCollection<V>()
+ {
+ public int size()
+ {
+ return size;
+ }
+
+ public Iterator<V> iterator()
+ {
+ // Cannot create the iterator directly, because of LinkedHashMap.
+ return HashMap.this.iterator(VALUES);
+ }
+
+ public void clear()
+ {
+ HashMap.this.clear();
+ }
+ };
+ return values;
+ }
+
+ /**
+ * Returns a "set view" of this HashMap's entries. The set is backed by
+ * the HashMap, so changes in one show up in the other. The set supports
+ * element removal, but not element addition.<p>
+ *
+ * Note that the iterators for all three views, from keySet(), entrySet(),
+ * and values(), traverse the HashMap in the same sequence.
+ *
+ * @return a set view of the entries
+ * @see #keySet()
+ * @see #values()
+ * @see Map.Entry
+ */
+ public Set<Map.Entry<K, V>> entrySet()
+ {
+ if (entries == null)
+ // Create an AbstractSet with custom implementations of those methods
+ // that can be overridden easily and efficiently.
+ entries = new AbstractSet<Map.Entry<K, V>>()
+ {
+ public int size()
+ {
+ return size;
+ }
+
+ public Iterator<Map.Entry<K, V>> iterator()
+ {
+ // Cannot create the iterator directly, because of LinkedHashMap.
+ return HashMap.this.iterator(ENTRIES);
+ }
+
+ public void clear()
+ {
+ HashMap.this.clear();
+ }
+
+ public boolean contains(Object o)
+ {
+ return getEntry(o) != null;
+ }
+
+ public boolean remove(Object o)
+ {
+ HashEntry<K, V> e = getEntry(o);
+ if (e != null)
+ {
+ HashMap.this.remove(e.key);
+ return true;
+ }
+ return false;
+ }
+ };
+ return entries;
+ }
+
+ /**
+ * Helper method for put, that creates and adds a new Entry. This is
+ * overridden in LinkedHashMap for bookkeeping purposes.
+ *
+ * @param key the key of the new Entry
+ * @param value the value
+ * @param idx the index in buckets where the new Entry belongs
+ * @param callRemove whether to call the removeEldestEntry method
+ * @see #put(Object, Object)
+ */
+ void addEntry(K key, V value, int idx, boolean callRemove)
+ {
+ HashEntry<K, V> e = new HashEntry<K, V>(key, value);
+ e.next = buckets[idx];
+ buckets[idx] = e;
+ }
+
+ /**
+ * Helper method for entrySet(), which matches both key and value
+ * simultaneously.
+ *
+ * @param o the entry to match
+ * @return the matching entry, if found, or null
+ * @see #entrySet()
+ */
+ // Package visible, for use in nested classes.
+ final HashEntry<K, V> getEntry(Object o)
+ {
+ if (! (o instanceof Map.Entry))
+ return null;
+ Map.Entry<K, V> me = (Map.Entry<K, V>) o;
+ K key = me.getKey();
+ int idx = hash(key);
+ HashEntry<K, V> e = buckets[idx];
+ while (e != null)
+ {
+ if (equals(e.key, key))
+ return equals(e.value, me.getValue()) ? e : null;
+ e = e.next;
+ }
+ return null;
+ }
+
+ /**
+ * Helper method that returns an index in the buckets array for `key'
+ * based on its hashCode(). Package visible for use by subclasses.
+ *
+ * @param key the key
+ * @return the bucket number
+ */
+ final int hash(Object key)
+ {
+ return key == null ? 0 : Math.abs(key.hashCode() % buckets.length);
+ }
+
+ /**
+ * Generates a parameterized iterator. Must be overrideable, since
+ * LinkedHashMap iterates in a different order.
+ *
+ * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
+ * @return the appropriate iterator
+ */
+ <T> Iterator<T> iterator(int type)
+ {
+ // FIXME: bogus cast here.
+ return new HashIterator<T>(type);
+ }
+
+ /**
+ * A simplified, more efficient internal implementation of putAll(). clone()
+ * should not call putAll or put, in order to be compatible with the JDK
+ * implementation with respect to subclasses.
+ *
+ * @param m the map to initialize this from
+ */
+ void putAllInternal(Map<? extends K, ? extends V> m)
+ {
+ final Map<K,V> addMap = (Map<K,V>) m;
+ final Iterator<Map.Entry<K,V>> it = addMap.entrySet().iterator();
+ size = 0;
+ while (it.hasNext())
+ {
+ final Map.Entry<K,V> e = it.next();
+ size++;
+ K key = e.getKey();
+ int idx = hash(key);
+ addEntry(key, e.getValue(), idx, false);
+ }
+ }
+
+ /**
+ * 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.
+ *
+ * <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()
+ {
+ HashEntry<K, V>[] oldBuckets = buckets;
+
+ int newcapacity = (buckets.length * 2) + 1;
+ threshold = (int) (newcapacity * loadFactor);
+ buckets = (HashEntry<K, V>[]) new HashEntry[newcapacity];
+
+ for (int i = oldBuckets.length - 1; i >= 0; i--)
+ {
+ HashEntry<K, V> e = oldBuckets[i];
+ while (e != null)
+ {
+ int idx = hash(e.key);
+ HashEntry<K, V> dest = buckets[idx];
+ HashEntry<K, V> next = e.next;
+ e.next = buckets[idx];
+ buckets[idx] = e;
+ e = next;
+ }
+ }
+ }
+
+ /**
+ * Serializes this object to the given stream.
+ *
+ * @param s the stream to write to
+ * @throws IOException if the underlying stream fails
+ * @serialData the <i>capacity</i>(int) that is the length of the
+ * bucket array, the <i>size</i>(int) of the hash map
+ * are emitted first. They are followed by size entries,
+ * each consisting of a key (Object) and a value (Object).
+ */
+ private void writeObject(ObjectOutputStream s) throws IOException
+ {
+ // Write the threshold and loadFactor fields.
+ s.defaultWriteObject();
+
+ s.writeInt(buckets.length);
+ s.writeInt(size);
+ // Avoid creating a wasted Set by creating the iterator directly.
+ Iterator<HashEntry<K, V>> it = iterator(ENTRIES);
+ while (it.hasNext())
+ {
+ HashEntry<K, V> entry = it.next();
+ s.writeObject(entry.key);
+ s.writeObject(entry.value);
+ }
+ }
+
+ /**
+ * Deserializes this object from the given stream.
+ *
+ * @param s the stream to read from
+ * @throws ClassNotFoundException if the underlying stream fails
+ * @throws IOException if the underlying stream fails
+ * @serialData the <i>capacity</i>(int) that is the length of the
+ * bucket array, the <i>size</i>(int) of the hash map
+ * are emitted first. They are followed by size entries,
+ * each consisting of a key (Object) and a value (Object).
+ */
+ private void readObject(ObjectInputStream s)
+ throws IOException, ClassNotFoundException
+ {
+ // Read the threshold and loadFactor fields.
+ s.defaultReadObject();
+
+ // Read and use capacity, followed by key/value pairs.
+ buckets = (HashEntry<K, V>[]) new HashEntry[s.readInt()];
+ int len = s.readInt();
+ size = len;
+ while (len-- > 0)
+ {
+ Object key = s.readObject();
+ addEntry((K) key, (V) s.readObject(), hash(key), false);
+ }
+ }
+
+ /**
+ * Iterate over HashMap's entries.
+ * This implementation is parameterized to give a sequential view of
+ * keys, values, or entries.
+ *
+ * @author Jon Zeppieri
+ */
+ private final class HashIterator<T> implements Iterator<T>
+ {
+ /**
+ * The type of this Iterator: {@link #KEYS}, {@link #VALUES},
+ * or {@link #ENTRIES}.
+ */
+ private final int type;
+ /**
+ * The number of modifications to the backing HashMap that we know about.
+ */
+ private int knownMod = modCount;
+ /** The number of elements remaining to be returned by next(). */
+ private int count = size;
+ /** Current index in the physical hash table. */
+ private int idx = buckets.length;
+ /** The last Entry returned by a next() call. */
+ private HashEntry last;
+ /**
+ * The next entry that should be returned by next(). It is set to something
+ * if we're iterating through a bucket that contains multiple linked
+ * entries. It is null if next() needs to find a new bucket.
+ */
+ private HashEntry next;
+
+ /**
+ * Construct a new HashIterator with the supplied type.
+ * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
+ */
+ HashIterator(int type)
+ {
+ this.type = type;
+ }
+
+ /**
+ * Returns true if the Iterator has more elements.
+ * @return true if there are more elements
+ */
+ public boolean hasNext()
+ {
+ return count > 0;
+ }
+
+ /**
+ * Returns the next element in the Iterator's sequential view.
+ * @return the next element
+ * @throws ConcurrentModificationException if the HashMap was modified
+ * @throws NoSuchElementException if there is none
+ */
+ public T next()
+ {
+ if (knownMod != modCount)
+ throw new ConcurrentModificationException();
+ if (count == 0)
+ throw new NoSuchElementException();
+ count--;
+ HashEntry e = next;
+
+ while (e == null)
+ e = buckets[--idx];
+
+ next = e.next;
+ last = e;
+ if (type == VALUES)
+ return (T) e.value;
+ if (type == KEYS)
+ return (T) e.key;
+ return (T) e;
+ }
+
+ /**
+ * Removes from the backing HashMap the last element which was fetched
+ * 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()
+ {
+ if (knownMod != modCount)
+ throw new ConcurrentModificationException();
+ if (last == null)
+ throw new IllegalStateException();
+
+ HashMap.this.remove(last.key);
+ last = null;
+ knownMod++;
+ }
+ }
+}
diff --git a/libjava/classpath/java/util/HashSet.java b/libjava/classpath/java/util/HashSet.java
new file mode 100644
index 000000000..c08b6db5a
--- /dev/null
+++ b/libjava/classpath/java/util/HashSet.java
@@ -0,0 +1,293 @@
+/* HashSet.java -- a class providing a HashMap-backed Set
+ Copyright (C) 1998, 1999, 2001, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+/**
+ * This class provides a HashMap-backed implementation of the Set interface.
+ * <p>
+ *
+ * Most operations are O(1), assuming no hash collisions. In the worst
+ * case (where all hashes collide), operations are O(n). Setting the
+ * initial capacity too low will force many resizing operations, but
+ * setting the initial capacity too high (or loadfactor too low) leads
+ * to wasted memory and slower iteration.
+ * <p>
+ *
+ * HashSet accepts the null key and null values. It is not synchronized,
+ * so if you need multi-threaded access, consider using:<br>
+ * <code>Set s = Collections.synchronizedSet(new HashSet(...));</code>
+ * <p>
+ *
+ * The iterators are <i>fail-fast</i>, meaning that any structural
+ * modification, except for <code>remove()</code> called on the iterator
+ * itself, cause the iterator to throw a
+ * {@link ConcurrentModificationException} rather than exhibit
+ * non-deterministic behavior.
+ *
+ * @author Jon Zeppieri
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Collection
+ * @see Set
+ * @see TreeSet
+ * @see Collections#synchronizedSet(Set)
+ * @see HashMap
+ * @see LinkedHashSet
+ * @since 1.2
+ * @status updated to 1.4
+ */
+public class HashSet<T> extends AbstractSet<T>
+ implements Set<T>, Cloneable, Serializable
+{
+ /**
+ * Compatible with JDK 1.2.
+ */
+ private static final long serialVersionUID = -5024744406713321676L;
+
+ /**
+ * The HashMap which backs this Set.
+ */
+ private transient HashMap<T, String> map;
+
+ /**
+ * Construct a new, empty HashSet whose backing HashMap has the default
+ * capacity (11) and loadFacor (0.75).
+ */
+ public HashSet()
+ {
+ this(HashMap.DEFAULT_CAPACITY, HashMap.DEFAULT_LOAD_FACTOR);
+ }
+
+ /**
+ * Construct a new, empty HashSet whose backing HashMap has the supplied
+ * capacity and the default load factor (0.75).
+ *
+ * @param initialCapacity the initial capacity of the backing HashMap
+ * @throws IllegalArgumentException if the capacity is negative
+ */
+ public HashSet(int initialCapacity)
+ {
+ this(initialCapacity, HashMap.DEFAULT_LOAD_FACTOR);
+ }
+
+ /**
+ * Construct a new, empty HashSet whose backing HashMap has the supplied
+ * capacity and load factor.
+ *
+ * @param initialCapacity the initial capacity of the backing HashMap
+ * @param loadFactor the load factor of the backing HashMap
+ * @throws IllegalArgumentException if either argument is negative, or
+ * if loadFactor is POSITIVE_INFINITY or NaN
+ */
+ public HashSet(int initialCapacity, float loadFactor)
+ {
+ map = init(initialCapacity, loadFactor);
+ }
+
+ /**
+ * Construct a new HashSet with the same elements as are in the supplied
+ * collection (eliminating any duplicates, of course). The backing storage
+ * has twice the size of the collection, or the default size of 11,
+ * whichever is greater; and the default load factor (0.75).
+ *
+ * @param c a collection of initial set elements
+ * @throws NullPointerException if c is null
+ */
+ public HashSet(Collection<? extends T> c)
+ {
+ this(Math.max(2 * c.size(), HashMap.DEFAULT_CAPACITY));
+ addAll(c);
+ }
+
+ /**
+ * Adds the given Object to the set if it is not already in the Set.
+ * This set permits a null element.
+ *
+ * @param o the Object to add to this Set
+ * @return true if the set did not already contain o
+ */
+ public boolean add(T o)
+ {
+ return map.put(o, "") == null;
+ }
+
+ /**
+ * Empties this Set of all elements; this takes constant time.
+ */
+ public void clear()
+ {
+ map.clear();
+ }
+
+ /**
+ * Returns a shallow copy of this Set. The Set itself is cloned; its
+ * elements are not.
+ *
+ * @return a shallow clone of the set
+ */
+ public Object clone()
+ {
+ HashSet<T> copy = null;
+ try
+ {
+ copy = (HashSet<T>) super.clone();
+ }
+ catch (CloneNotSupportedException x)
+ {
+ // Impossible to get here.
+ }
+ copy.map = (HashMap<T, String>) map.clone();
+ return copy;
+ }
+
+ /**
+ * Returns true if the supplied element is in this Set.
+ *
+ * @param o the Object to look for
+ * @return true if it is in the set
+ */
+ public boolean contains(Object o)
+ {
+ return map.containsKey(o);
+ }
+
+ /**
+ * Returns true if this set has no elements in it.
+ *
+ * @return <code>size() == 0</code>.
+ */
+ public boolean isEmpty()
+ {
+ return map.size == 0;
+ }
+
+ /**
+ * Returns an Iterator over the elements of this Set, which visits the
+ * elements in no particular order. For this class, the Iterator allows
+ * removal of elements. The iterator is fail-fast, and will throw a
+ * ConcurrentModificationException if the set is modified externally.
+ *
+ * @return a set iterator
+ * @see ConcurrentModificationException
+ */
+ public Iterator<T> iterator()
+ {
+ // Avoid creating intermediate keySet() object by using non-public API.
+ return map.iterator(HashMap.KEYS);
+ }
+
+ /**
+ * Removes the supplied Object from this Set if it is in the Set.
+ *
+ * @param o the object to remove
+ * @return true if an element was removed
+ */
+ public boolean remove(Object o)
+ {
+ return (map.remove(o) != null);
+ }
+
+ /**
+ * Returns the number of elements in this Set (its cardinality).
+ *
+ * @return the size of the set
+ */
+ public int size()
+ {
+ return map.size;
+ }
+
+ /**
+ * Helper method which initializes the backing Map. Overridden by
+ * LinkedHashSet for correct semantics.
+ *
+ * @param capacity the initial capacity
+ * @param load the initial load factor
+ * @return the backing HashMap
+ */
+ HashMap init(int capacity, float load)
+ {
+ return new HashMap(capacity, load);
+ }
+
+ /**
+ * Serializes this object to the given stream.
+ *
+ * @param s the stream to write to
+ * @throws IOException if the underlying stream fails
+ * @serialData the <i>capacity</i> (int) and <i>loadFactor</i> (float)
+ * of the backing store, followed by the set size (int),
+ * then a listing of its elements (Object) in no order
+ */
+ private void writeObject(ObjectOutputStream s) throws IOException
+ {
+ s.defaultWriteObject();
+ // Avoid creating intermediate keySet() object by using non-public API.
+ Iterator<T> it = map.iterator(HashMap.KEYS);
+ s.writeInt(map.buckets.length);
+ s.writeFloat(map.loadFactor);
+ s.writeInt(map.size);
+ while (it.hasNext())
+ s.writeObject(it.next());
+ }
+
+ /**
+ * Deserializes this object from the given stream.
+ *
+ * @param s the stream to read from
+ * @throws ClassNotFoundException if the underlying stream fails
+ * @throws IOException if the underlying stream fails
+ * @serialData the <i>capacity</i> (int) and <i>loadFactor</i> (float)
+ * of the backing store, followed by the set size (int),
+ * then a listing of its elements (Object) in no order
+ */
+ private void readObject(ObjectInputStream s)
+ throws IOException, ClassNotFoundException
+ {
+ s.defaultReadObject();
+
+ map = init(s.readInt(), s.readFloat());
+ for (int size = s.readInt(); size > 0; size--)
+ map.put((T) s.readObject(), "");
+ }
+}
diff --git a/libjava/classpath/java/util/Hashtable.java b/libjava/classpath/java/util/Hashtable.java
new file mode 100644
index 000000000..8f08e96ab
--- /dev/null
+++ b/libjava/classpath/java/util/Hashtable.java
@@ -0,0 +1,1397 @@
+/* Hashtable.java -- a class providing a basic hashtable data structure,
+ mapping Object --> Object
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+// NOTE: This implementation is very similar to that of HashMap. If you fix
+// a bug in here, chances are you should make a similar change to the HashMap
+// code.
+
+/**
+ * A class which implements a hashtable data structure.
+ * <p>
+ *
+ * This implementation of Hashtable uses a hash-bucket approach. That is:
+ * linear probing and rehashing is avoided; instead, each hashed value maps
+ * to a simple linked-list which, in the best case, only has one node.
+ * Assuming a large enough table, low enough load factor, and / or well
+ * implemented hashCode() methods, Hashtable should provide O(1)
+ * insertion, deletion, and searching of keys. Hashtable is O(n) in
+ * the worst case for all of these (if all keys hash to the same bucket).
+ * <p>
+ *
+ * This is a JDK-1.2 compliant implementation of Hashtable. As such, it
+ * belongs, partially, to the Collections framework (in that it implements
+ * Map). For backwards compatibility, it inherits from the obsolete and
+ * utterly useless Dictionary class.
+ * <p>
+ *
+ * Being a hybrid of old and new, Hashtable has methods which provide redundant
+ * capability, but with subtle and even crucial differences.
+ * For example, one can iterate over various aspects of a Hashtable with
+ * either an Iterator (which is the JDK-1.2 way of doing things) or with an
+ * Enumeration. The latter can end up in an undefined state if the Hashtable
+ * changes while the Enumeration is open.
+ * <p>
+ *
+ * Unlike HashMap, Hashtable does not accept `null' as a key value. Also,
+ * all accesses are synchronized: in a single thread environment, this is
+ * expensive, but in a multi-thread environment, this saves you the effort
+ * of extra synchronization. However, the old-style enumerators are not
+ * synchronized, because they can lead to unspecified behavior even if
+ * they were synchronized. You have been warned.
+ * <p>
+ *
+ * The iterators are <i>fail-fast</i>, meaning that any structural
+ * modification, except for <code>remove()</code> called on the iterator
+ * itself, cause the iterator to throw a
+ * <code>ConcurrentModificationException</code> rather than exhibit
+ * non-deterministic behavior.
+ *
+ * @author Jon Zeppieri
+ * @author Warren Levy
+ * @author Bryce McKinlay
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see HashMap
+ * @see TreeMap
+ * @see IdentityHashMap
+ * @see LinkedHashMap
+ * @since 1.0
+ * @status updated to 1.4
+ */
+public class Hashtable<K, V> extends Dictionary<K, V>
+ implements Map<K, V>, 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.
+ */
+ private static final int DEFAULT_CAPACITY = 11;
+
+ /**
+ * The default load factor; this is explicitly specified by the spec.
+ */
+ private static final float DEFAULT_LOAD_FACTOR = 0.75f;
+
+ /**
+ * Compatible with JDK 1.0+.
+ */
+ private static final long serialVersionUID = 1421746759512286392L;
+
+ /**
+ * The rounded product of the capacity and the load factor; when the number
+ * of elements exceeds the threshold, the Hashtable calls
+ * <code>rehash()</code>.
+ * @serial
+ */
+ private int threshold;
+
+ /**
+ * Load factor of this Hashtable: used in computing the threshold.
+ * @serial
+ */
+ private final float loadFactor;
+
+ /**
+ * Array containing the actual key-value mappings.
+ */
+ // Package visible for use by nested classes.
+ transient HashEntry<K, V>[] buckets;
+
+ /**
+ * Counts the number of modifications this Hashtable has undergone, used
+ * by Iterators to know when to throw ConcurrentModificationExceptions.
+ */
+ // Package visible for use by nested classes.
+ transient int modCount;
+
+ /**
+ * The size of this Hashtable: denotes the number of key-value pairs.
+ */
+ // Package visible for use by nested classes.
+ transient int size;
+
+ /**
+ * The cache for {@link #keySet()}.
+ */
+ private transient Set<K> keys;
+
+ /**
+ * The cache for {@link #values()}.
+ */
+ private transient Collection<V> values;
+
+ /**
+ * The cache for {@link #entrySet()}.
+ */
+ private transient Set<Map.Entry<K, V>> entries;
+
+ /**
+ * Class to represent an entry in the hash table. Holds a single key-value
+ * 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<K, V>
+ extends AbstractMap.SimpleEntry<K, V>
+ {
+ /** The next entry in the linked list. */
+ HashEntry<K, V> next;
+
+ /**
+ * Simple constructor.
+ * @param key the key, already guaranteed non-null
+ * @param value the value, already guaranteed non-null
+ */
+ HashEntry(K key, V value)
+ {
+ super(key, value);
+ }
+
+ /**
+ * Resets the value.
+ * @param newVal the new value
+ * @return the prior value
+ * @throws NullPointerException if <code>newVal</code> is null
+ */
+ public V setValue(V newVal)
+ {
+ if (newVal == null)
+ throw new NullPointerException();
+ return super.setValue(newVal);
+ }
+ }
+
+ /**
+ * Construct a new Hashtable with the default capacity (11) and the default
+ * load factor (0.75).
+ */
+ public Hashtable()
+ {
+ this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR);
+ }
+
+ /**
+ * Construct a new Hashtable from the given Map, with initial capacity
+ * the greater of the size of <code>m</code> or the default of 11.
+ * <p>
+ *
+ * Every element in Map m will be put into this new Hashtable.
+ *
+ * @param m a Map whose key / value pairs will be put into
+ * the new Hashtable. <b>NOTE: key / value pairs
+ * are not cloned in this constructor.</b>
+ * @throws NullPointerException if m is null, or if m contains a mapping
+ * to or from `null'.
+ * @since 1.2
+ */
+ public Hashtable(Map<? extends K, ? extends V> m)
+ {
+ this(Math.max(m.size() * 2, DEFAULT_CAPACITY), DEFAULT_LOAD_FACTOR);
+ putAll(m);
+ }
+
+ /**
+ * Construct a new Hashtable with a specific inital capacity and
+ * default load factor of 0.75.
+ *
+ * @param initialCapacity the initial capacity of this Hashtable (&gt;= 0)
+ * @throws IllegalArgumentException if (initialCapacity &lt; 0)
+ */
+ public Hashtable(int initialCapacity)
+ {
+ this(initialCapacity, DEFAULT_LOAD_FACTOR);
+ }
+
+ /**
+ * Construct a new Hashtable with a specific initial capacity and
+ * load factor.
+ *
+ * @param initialCapacity the initial capacity (&gt;= 0)
+ * @param loadFactor the load factor (&gt; 0, not NaN)
+ * @throws IllegalArgumentException if (initialCapacity &lt; 0) ||
+ * ! (loadFactor &gt; 0.0)
+ */
+ public Hashtable(int initialCapacity, float loadFactor)
+ {
+ if (initialCapacity < 0)
+ throw new IllegalArgumentException("Illegal Capacity: "
+ + initialCapacity);
+ if (! (loadFactor > 0)) // check for NaN too
+ throw new IllegalArgumentException("Illegal Load: " + loadFactor);
+
+ if (initialCapacity == 0)
+ initialCapacity = 1;
+ buckets = (HashEntry<K, V>[]) new HashEntry[initialCapacity];
+ this.loadFactor = loadFactor;
+ threshold = (int) (initialCapacity * loadFactor);
+ }
+
+ /**
+ * Returns the number of key-value mappings currently in this hashtable.
+ * @return the size
+ */
+ public synchronized int size()
+ {
+ return size;
+ }
+
+ /**
+ * Returns true if there are no key-value mappings currently in this table.
+ * @return <code>size() == 0</code>
+ */
+ public synchronized boolean isEmpty()
+ {
+ return size == 0;
+ }
+
+ /**
+ * Return an enumeration of the keys of this table. There's no point
+ * in synchronizing this, as you have already been warned that the
+ * enumeration is not specified to be thread-safe.
+ *
+ * @return the keys
+ * @see #elements()
+ * @see #keySet()
+ */
+ public Enumeration<K> keys()
+ {
+ return new KeyEnumerator();
+ }
+
+ /**
+ * Return an enumeration of the values of this table. There's no point
+ * in synchronizing this, as you have already been warned that the
+ * enumeration is not specified to be thread-safe.
+ *
+ * @return the values
+ * @see #keys()
+ * @see #values()
+ */
+ public Enumeration<V> elements()
+ {
+ return new ValueEnumerator();
+ }
+
+ /**
+ * Returns true if this Hashtable contains a value <code>o</code>,
+ * such that <code>o.equals(value)</code>. This is the same as
+ * <code>containsValue()</code>, and is O(n).
+ * <p>
+ *
+ * @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 #containsValue(Object)
+ * @see #containsKey(Object)
+ */
+ public synchronized boolean contains(Object value)
+ {
+ if (value == null)
+ throw new NullPointerException();
+
+ for (int i = buckets.length - 1; i >= 0; i--)
+ {
+ HashEntry<K, V> e = buckets[i];
+ while (e != null)
+ {
+ if (e.value.equals(value))
+ return true;
+ e = e.next;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if this Hashtable contains a value <code>o</code>, such that
+ * <code>o.equals(value)</code>. This is the new API for the old
+ * <code>contains()</code>.
+ *
+ * @param value the value to search for in this Hashtable
+ * @return true if at least one key maps to the value
+ * @see #contains(Object)
+ * @see #containsKey(Object)
+ * @throws NullPointerException if <code>value</code> is null
+ * @since 1.2
+ */
+ public boolean containsValue(Object value)
+ {
+ // Delegate to older method to make sure code overriding it continues
+ // to work.
+ return contains(value);
+ }
+
+ /**
+ * Returns true if the supplied object <code>equals()</code> a key
+ * in this Hashtable.
+ *
+ * @param key the key to search for in this Hashtable
+ * @return true if the key is in the table
+ * @throws NullPointerException if key is null
+ * @see #containsValue(Object)
+ */
+ public synchronized boolean containsKey(Object key)
+ {
+ int idx = hash(key);
+ HashEntry<K, V> e = buckets[idx];
+ while (e != null)
+ {
+ if (e.key.equals(key))
+ return true;
+ e = e.next;
+ }
+ return false;
+ }
+
+ /**
+ * Return the value in this Hashtable associated with the supplied key,
+ * or <code>null</code> if the key maps to nothing.
+ *
+ * @param key the key for which to fetch an associated value
+ * @return what the key maps to, if present
+ * @throws NullPointerException if key is null
+ * @see #put(Object, Object)
+ * @see #containsKey(Object)
+ */
+ public synchronized V get(Object key)
+ {
+ int idx = hash(key);
+ HashEntry<K, V> e = buckets[idx];
+ while (e != null)
+ {
+ if (e.key.equals(key))
+ return e.value;
+ e = e.next;
+ }
+ return null;
+ }
+
+ /**
+ * Puts the supplied value into the Map, mapped by the supplied key.
+ * Neither parameter may be null. The value may be retrieved by any
+ * object which <code>equals()</code> this key.
+ *
+ * @param key the key used to locate the value
+ * @param value the value to be stored in the table
+ * @return the prior mapping of the key, or null if there was none
+ * @throws NullPointerException if key or value is null
+ * @see #get(Object)
+ * @see Object#equals(Object)
+ */
+ public synchronized V put(K key, V value)
+ {
+ int idx = hash(key);
+ HashEntry<K, V> e = buckets[idx];
+
+ // Check if value is null since it is not permitted.
+ if (value == null)
+ throw new NullPointerException();
+
+ while (e != null)
+ {
+ if (e.key.equals(key))
+ {
+ // Bypass e.setValue, since we already know value is non-null.
+ V r = e.value;
+ e.value = value;
+ return r;
+ }
+ else
+ {
+ e = e.next;
+ }
+ }
+
+ // At this point, we know we need to add a new entry.
+ modCount++;
+ if (++size > threshold)
+ {
+ rehash();
+ // Need a new hash value to suit the bigger table.
+ idx = hash(key);
+ }
+
+ e = new HashEntry<K, V>(key, value);
+
+ e.next = buckets[idx];
+ buckets[idx] = e;
+
+ return null;
+ }
+
+ /**
+ * Removes from the table and returns the value which is mapped by the
+ * supplied key. If the key maps to nothing, then the table remains
+ * unchanged, and <code>null</code> is returned.
+ *
+ * @param key the key used to locate the value to remove
+ * @return whatever the key mapped to, if present
+ */
+ public synchronized V remove(Object key)
+ {
+ int idx = hash(key);
+ HashEntry<K, V> e = buckets[idx];
+ HashEntry<K, V> last = null;
+
+ while (e != null)
+ {
+ if (e.key.equals(key))
+ {
+ modCount++;
+ if (last == null)
+ buckets[idx] = e.next;
+ else
+ last.next = e.next;
+ size--;
+ return e.value;
+ }
+ last = e;
+ e = e.next;
+ }
+ return null;
+ }
+
+ /**
+ * Copies all elements of the given map into this hashtable. However, no
+ * mapping can contain null as key or value. If this table already has
+ * a mapping for a key, the new mapping replaces the current one.
+ *
+ * @param m the map to be hashed into this
+ * @throws NullPointerException if m is null, or contains null keys or values
+ */
+ public synchronized void putAll(Map<? extends K, ? extends V> m)
+ {
+ final Map<K,V> addMap = (Map<K,V>) m;
+ final Iterator<Map.Entry<K,V>> it = addMap.entrySet().iterator();
+ while (it.hasNext())
+ {
+ final Map.Entry<K,V> e = it.next();
+ // Optimize in case the Entry is one of our own.
+ if (e instanceof AbstractMap.SimpleEntry)
+ {
+ AbstractMap.SimpleEntry<? extends K, ? extends V> entry
+ = (AbstractMap.SimpleEntry<? extends K, ? extends V>) e;
+ put(entry.key, entry.value);
+ }
+ else
+ {
+ put(e.getKey(), e.getValue());
+ }
+ }
+ }
+
+ /**
+ * Clears the hashtable so it has no keys. This is O(1).
+ */
+ public synchronized void clear()
+ {
+ if (size > 0)
+ {
+ modCount++;
+ Arrays.fill(buckets, null);
+ size = 0;
+ }
+ }
+
+ /**
+ * Returns a shallow clone of this Hashtable. The Map itself is cloned,
+ * but its contents are not. This is O(n).
+ *
+ * @return the clone
+ */
+ public synchronized Object clone()
+ {
+ Hashtable<K, V> copy = null;
+ try
+ {
+ copy = (Hashtable<K, V>) super.clone();
+ }
+ catch (CloneNotSupportedException x)
+ {
+ // This is impossible.
+ }
+ copy.buckets = (HashEntry<K, V>[]) new HashEntry[buckets.length];
+ copy.putAllInternal(this);
+ // Clear the caches.
+ copy.keys = null;
+ copy.values = null;
+ copy.entries = null;
+ return copy;
+ }
+
+ /**
+ * Converts this Hashtable to a String, surrounded by braces, and with
+ * key/value pairs listed with an equals sign between, separated by a
+ * comma and space. For example, <code>"{a=1, b=2}"</code>.<p>
+ *
+ * NOTE: if the <code>toString()</code> method of any key or value
+ * throws an exception, this will fail for the same reason.
+ *
+ * @return the string representation
+ */
+ public synchronized String toString()
+ {
+ // Since we are already synchronized, and entrySet().iterator()
+ // would repeatedly re-lock/release the monitor, we directly use the
+ // unsynchronized EntryIterator instead.
+ Iterator<Map.Entry<K, V>> entries = new EntryIterator();
+ CPStringBuilder r = new CPStringBuilder("{");
+ for (int pos = size; pos > 0; pos--)
+ {
+ r.append(entries.next());
+ if (pos > 1)
+ r.append(", ");
+ }
+ r.append("}");
+ return r.toString();
+ }
+
+ /**
+ * Returns a "set view" of this Hashtable's keys. The set is backed by
+ * the hashtable, so changes in one show up in the other. The set supports
+ * element removal, but not element addition. The set is properly
+ * synchronized on the original hashtable. Sun has not documented the
+ * proper interaction of null with this set, but has inconsistent behavior
+ * in the JDK. Therefore, in this implementation, contains, remove,
+ * containsAll, retainAll, removeAll, and equals just ignore a null key
+ * rather than throwing a {@link NullPointerException}.
+ *
+ * @return a set view of the keys
+ * @see #values()
+ * @see #entrySet()
+ * @since 1.2
+ */
+ public Set<K> keySet()
+ {
+ if (keys == null)
+ {
+ // Create a synchronized AbstractSet with custom implementations of
+ // those methods that can be overridden easily and efficiently.
+ Set<K> r = new AbstractSet<K>()
+ {
+ public int size()
+ {
+ return size;
+ }
+
+ public Iterator<K> iterator()
+ {
+ return new KeyIterator();
+ }
+
+ public void clear()
+ {
+ Hashtable.this.clear();
+ }
+
+ public boolean contains(Object o)
+ {
+ if (o == null)
+ return false;
+ return containsKey(o);
+ }
+
+ public boolean remove(Object o)
+ {
+ return Hashtable.this.remove(o) != null;
+ }
+ };
+ // We must specify the correct object to synchronize upon, hence the
+ // use of a non-public API
+ keys = new Collections.SynchronizedSet<K>(this, r);
+ }
+ return keys;
+ }
+
+ /**
+ * Returns a "collection view" (or "bag view") of this Hashtable's values.
+ * The collection is backed by the hashtable, so changes in one show up
+ * in the other. The collection supports element removal, but not element
+ * addition. The collection is properly synchronized on the original
+ * hashtable. Sun has not documented the proper interaction of null with
+ * this set, but has inconsistent behavior in the JDK. Therefore, in this
+ * implementation, contains, remove, containsAll, retainAll, removeAll, and
+ * equals just ignore a null value rather than throwing a
+ * {@link NullPointerException}.
+ *
+ * @return a bag view of the values
+ * @see #keySet()
+ * @see #entrySet()
+ * @since 1.2
+ */
+ public Collection<V> values()
+ {
+ if (values == null)
+ {
+ // We don't bother overriding many of the optional methods, as doing so
+ // wouldn't provide any significant performance advantage.
+ Collection<V> r = new AbstractCollection<V>()
+ {
+ public int size()
+ {
+ return size;
+ }
+
+ public Iterator<V> iterator()
+ {
+ return new ValueIterator();
+ }
+
+ public void clear()
+ {
+ Hashtable.this.clear();
+ }
+ };
+ // We must specify the correct object to synchronize upon, hence the
+ // use of a non-public API
+ values = new Collections.SynchronizedCollection<V>(this, r);
+ }
+ return values;
+ }
+
+ /**
+ * Returns a "set view" of this Hashtable's entries. The set is backed by
+ * the hashtable, so changes in one show up in the other. The set supports
+ * element removal, but not element addition. The set is properly
+ * synchronized on the original hashtable. Sun has not documented the
+ * proper interaction of null with this set, but has inconsistent behavior
+ * in the JDK. Therefore, in this implementation, contains, remove,
+ * containsAll, retainAll, removeAll, and equals just ignore a null entry,
+ * or an entry with a null key or value, rather than throwing a
+ * {@link NullPointerException}. However, calling entry.setValue(null)
+ * will fail.
+ * <p>
+ *
+ * Note that the iterators for all three views, from keySet(), entrySet(),
+ * and values(), traverse the hashtable in the same sequence.
+ *
+ * @return a set view of the entries
+ * @see #keySet()
+ * @see #values()
+ * @see Map.Entry
+ * @since 1.2
+ */
+ public Set<Map.Entry<K, V>> entrySet()
+ {
+ if (entries == null)
+ {
+ // Create an AbstractSet with custom implementations of those methods
+ // that can be overridden easily and efficiently.
+ Set<Map.Entry<K, V>> r = new AbstractSet<Map.Entry<K, V>>()
+ {
+ public int size()
+ {
+ return size;
+ }
+
+ public Iterator<Map.Entry<K, V>> iterator()
+ {
+ return new EntryIterator();
+ }
+
+ public void clear()
+ {
+ Hashtable.this.clear();
+ }
+
+ public boolean contains(Object o)
+ {
+ return getEntry(o) != null;
+ }
+
+ public boolean remove(Object o)
+ {
+ HashEntry<K, V> e = getEntry(o);
+ if (e != null)
+ {
+ Hashtable.this.remove(e.key);
+ return true;
+ }
+ return false;
+ }
+ };
+ // We must specify the correct object to synchronize upon, hence the
+ // use of a non-public API
+ entries = new Collections.SynchronizedSet<Map.Entry<K, V>>(this, r);
+ }
+ return entries;
+ }
+
+ /**
+ * Returns true if this Hashtable equals the supplied Object <code>o</code>.
+ * As specified by Map, this is:
+ * <code>
+ * (o instanceof Map) && entrySet().equals(((Map) o).entrySet());
+ * </code>
+ *
+ * @param o the object to compare to
+ * @return true if o is an equal map
+ * @since 1.2
+ */
+ public boolean equals(Object o)
+ {
+ // no need to synchronize, entrySet().equals() does that.
+ if (o == this)
+ return true;
+ if (!(o instanceof Map))
+ return false;
+
+ return entrySet().equals(((Map) o).entrySet());
+ }
+
+ /**
+ * Returns the hashCode for this Hashtable. As specified by Map, this is
+ * the sum of the hashCodes of all of its Map.Entry objects
+ *
+ * @return the sum of the hashcodes of the entries
+ * @since 1.2
+ */
+ public synchronized int hashCode()
+ {
+ // Since we are already synchronized, and entrySet().iterator()
+ // would repeatedly re-lock/release the monitor, we directly use the
+ // unsynchronized EntryIterator instead.
+ Iterator<Map.Entry<K, V>> itr = new EntryIterator();
+ int hashcode = 0;
+ for (int pos = size; pos > 0; pos--)
+ hashcode += itr.next().hashCode();
+
+ return hashcode;
+ }
+
+ /**
+ * Helper method that returns an index in the buckets array for `key'
+ * based on its hashCode().
+ *
+ * @param key the key
+ * @return the bucket number
+ * @throws NullPointerException if key is null
+ */
+ private int hash(Object key)
+ {
+ // 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;
+ }
+
+ /**
+ * Helper method for entrySet(), which matches both key and value
+ * simultaneously. Ignores null, as mentioned in entrySet().
+ *
+ * @param o the entry to match
+ * @return the matching entry, if found, or null
+ * @see #entrySet()
+ */
+ // Package visible, for use in nested classes.
+ HashEntry<K, V> getEntry(Object o)
+ {
+ if (! (o instanceof Map.Entry))
+ return null;
+ K key = ((Map.Entry<K, V>) o).getKey();
+ if (key == null)
+ return null;
+
+ int idx = hash(key);
+ HashEntry<K, V> e = buckets[idx];
+ while (e != null)
+ {
+ if (e.equals(o))
+ return e;
+ e = e.next;
+ }
+ return null;
+ }
+
+ /**
+ * A simplified, more efficient internal implementation of putAll(). clone()
+ * should not call putAll or put, in order to be compatible with the JDK
+ * implementation with respect to subclasses.
+ *
+ * @param m the map to initialize this from
+ */
+ void putAllInternal(Map<? extends K, ? extends V> m)
+ {
+ final Map<K,V> addMap = (Map<K,V>) m;
+ final Iterator<Map.Entry<K,V>> it = addMap.entrySet().iterator();
+ size = 0;
+ while (it.hasNext())
+ {
+ final Map.Entry<K,V> e = it.next();
+ size++;
+ K key = e.getKey();
+ int idx = hash(key);
+ HashEntry<K, V> he = new HashEntry<K, V>(key, e.getValue());
+ he.next = buckets[idx];
+ buckets[idx] = he;
+ }
+ }
+
+ /**
+ * 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() &gt; threshold. Note that the existing Entry objects are reused in
+ * the new hash table.
+ * <p>
+ *
+ * This is not specified, but the new size is twice the current size plus
+ * one; this number is not always prime, unfortunately. This implementation
+ * is not synchronized, as it is only invoked from synchronized methods.
+ */
+ protected void rehash()
+ {
+ HashEntry<K, V>[] oldBuckets = buckets;
+
+ int newcapacity = (buckets.length * 2) + 1;
+ threshold = (int) (newcapacity * loadFactor);
+ buckets = (HashEntry<K, V>[]) new HashEntry[newcapacity];
+
+ for (int i = oldBuckets.length - 1; i >= 0; i--)
+ {
+ HashEntry<K, V> e = oldBuckets[i];
+ while (e != null)
+ {
+ int idx = hash(e.key);
+ HashEntry<K, V> dest = buckets[idx];
+
+ if (dest != null)
+ {
+ HashEntry next = dest.next;
+ while (next != null)
+ {
+ dest = next;
+ next = dest.next;
+ }
+ dest.next = e;
+ }
+ else
+ {
+ buckets[idx] = e;
+ }
+
+ HashEntry<K, V> next = e.next;
+ e.next = null;
+ e = next;
+ }
+ }
+ }
+
+ /**
+ * Serializes this object to the given stream.
+ *
+ * @param s the stream to write to
+ * @throws IOException if the underlying stream fails
+ * @serialData the <i>capacity</i> (int) that is the length of the
+ * bucket array, the <i>size</i> (int) of the hash map
+ * are emitted first. They are followed by size entries,
+ * each consisting of a key (Object) and a value (Object).
+ */
+ private synchronized void writeObject(ObjectOutputStream s)
+ throws IOException
+ {
+ // Write the threshold and loadFactor fields.
+ s.defaultWriteObject();
+
+ s.writeInt(buckets.length);
+ s.writeInt(size);
+ // Since we are already synchronized, and entrySet().iterator()
+ // would repeatedly re-lock/release the monitor, we directly use the
+ // unsynchronized EntryIterator instead.
+ Iterator<Map.Entry<K, V>> it = new EntryIterator();
+ while (it.hasNext())
+ {
+ HashEntry<K, V> entry = (HashEntry<K, V>) it.next();
+ s.writeObject(entry.key);
+ s.writeObject(entry.value);
+ }
+ }
+
+ /**
+ * Deserializes this object from the given stream.
+ *
+ * @param s the stream to read from
+ * @throws ClassNotFoundException if the underlying stream fails
+ * @throws IOException if the underlying stream fails
+ * @serialData the <i>capacity</i> (int) that is the length of the
+ * bucket array, the <i>size</i> (int) of the hash map
+ * are emitted first. They are followed by size entries,
+ * each consisting of a key (Object) and a value (Object).
+ */
+ private void readObject(ObjectInputStream s)
+ throws IOException, ClassNotFoundException
+ {
+ // Read the threshold and loadFactor fields.
+ s.defaultReadObject();
+
+ // Read and use capacity.
+ buckets = (HashEntry<K, V>[]) new HashEntry[s.readInt()];
+ int len = s.readInt();
+
+ // Read and use key/value pairs.
+ // TODO: should we be defensive programmers, and check for illegal nulls?
+ while (--len >= 0)
+ put((K) s.readObject(), (V) s.readObject());
+ }
+
+ /**
+ * A class which implements the Iterator interface and is used for
+ * iterating over Hashtables.
+ * This implementation iterates entries. Subclasses are used to
+ * iterate key and values. It also allows the removal of elements,
+ * as per the Javasoft spec. Note that it is not synchronized; this
+ * is a performance enhancer since it is never exposed externally
+ * and is only used within synchronized blocks above.
+ *
+ * @author Jon Zeppieri
+ * @author Fridjof Siebert
+ */
+ private class EntryIterator
+ implements Iterator<Entry<K,V>>
+ {
+ /**
+ * The number of modifications to the backing Hashtable that we know about.
+ */
+ int knownMod = modCount;
+ /** The number of elements remaining to be returned by next(). */
+ int count = size;
+ /** Current index in the physical hash table. */
+ int idx = buckets.length;
+ /** The last Entry returned by a next() call. */
+ HashEntry<K, V> last;
+ /**
+ * The next entry that should be returned by next(). It is set to something
+ * if we're iterating through a bucket that contains multiple linked
+ * entries. It is null if next() needs to find a new bucket.
+ */
+ HashEntry<K, V> next;
+
+ /**
+ * Construct a new EntryIterator
+ */
+ EntryIterator()
+ {
+ }
+
+
+ /**
+ * Returns true if the Iterator has more elements.
+ * @return true if there are more elements
+ */
+ public boolean hasNext()
+ {
+ return count > 0;
+ }
+
+ /**
+ * Returns the next element in the Iterator's sequential view.
+ * @return the next element
+ * @throws ConcurrentModificationException if the hashtable was modified
+ * @throws NoSuchElementException if there is none
+ */
+ public Map.Entry<K,V> next()
+ {
+ if (knownMod != modCount)
+ throw new ConcurrentModificationException();
+ if (count == 0)
+ throw new NoSuchElementException();
+ count--;
+ HashEntry<K, V> e = next;
+
+ while (e == null)
+ if (idx <= 0)
+ return null;
+ else
+ e = buckets[--idx];
+
+ next = e.next;
+ last = e;
+ return e;
+ }
+
+ /**
+ * Removes from the backing Hashtable the last element which was fetched
+ * with the <code>next()</code> method.
+ * @throws ConcurrentModificationException if the hashtable was modified
+ * @throws IllegalStateException if called when there is no last element
+ */
+ public void remove()
+ {
+ if (knownMod != modCount)
+ throw new ConcurrentModificationException();
+ if (last == null)
+ throw new IllegalStateException();
+
+ Hashtable.this.remove(last.key);
+ last = null;
+ knownMod++;
+ }
+ } // class EntryIterator
+
+ /**
+ * A class which implements the Iterator interface and is used for
+ * iterating over keys in Hashtables. This class uses an
+ * <code>EntryIterator</code> to obtain the keys of each entry.
+ *
+ * @author Fridtjof Siebert
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ */
+ private class KeyIterator
+ implements Iterator<K>
+ {
+
+ /**
+ * This entry iterator is used for most operations. Only
+ * <code>next()</code> gives a different result, by returning just
+ * the key rather than the whole element.
+ */
+ private final EntryIterator iterator;
+
+ /**
+ * Construct a new KeyIterator
+ */
+ KeyIterator()
+ {
+ iterator = new EntryIterator();
+ }
+
+
+ /**
+ * Returns true if the entry iterator has more elements.
+ *
+ * @return true if there are more elements
+ * @throws ConcurrentModificationException if the hashtable was modified
+ */
+ public boolean hasNext()
+ {
+ return iterator.hasNext();
+ }
+
+ /**
+ * Returns the next element in the Iterator's sequential view.
+ *
+ * @return the next element
+ *
+ * @throws ConcurrentModificationException if the hashtable was modified
+ * @throws NoSuchElementException if there is none
+ */
+ public K next()
+ {
+ return ((HashEntry<K,V>) iterator.next()).key;
+ }
+
+ /**
+ * Removes the last element used by the <code>next()</code> method
+ * using the entry iterator.
+ *
+ * @throws ConcurrentModificationException if the hashtable was modified
+ * @throws IllegalStateException if called when there is no last element
+ */
+ public void remove()
+ {
+ iterator.remove();
+ }
+ } // class KeyIterator
+
+ /**
+ * A class which implements the Iterator interface and is used for
+ * iterating over values in Hashtables. This class uses an
+ * <code>EntryIterator</code> to obtain the values of each entry.
+ *
+ * @author Fridtjof Siebert
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ */
+ private class ValueIterator
+ implements Iterator<V>
+ {
+
+ /**
+ * This entry iterator is used for most operations. Only
+ * <code>next()</code> gives a different result, by returning just
+ * the value rather than the whole element.
+ */
+ private final EntryIterator iterator;
+
+ /**
+ * Construct a new KeyIterator
+ */
+ ValueIterator()
+ {
+ iterator = new EntryIterator();
+ }
+
+
+ /**
+ * Returns true if the entry iterator has more elements.
+ *
+ * @return true if there are more elements
+ * @throws ConcurrentModificationException if the hashtable was modified
+ */
+ public boolean hasNext()
+ {
+ return iterator.hasNext();
+ }
+
+ /**
+ * Returns the value of the next element in the iterator's sequential view.
+ *
+ * @return the next value
+ *
+ * @throws ConcurrentModificationException if the hashtable was modified
+ * @throws NoSuchElementException if there is none
+ */
+ public V next()
+ {
+ return ((HashEntry<K,V>) iterator.next()).value;
+ }
+
+ /**
+ * Removes the last element used by the <code>next()</code> method
+ * using the entry iterator.
+ *
+ * @throws ConcurrentModificationException if the hashtable was modified
+ * @throws IllegalStateException if called when there is no last element
+ */
+ public void remove()
+ {
+ iterator.remove();
+ }
+
+ } // class ValueIterator
+
+ /**
+ * Enumeration view of the entries in this Hashtable, providing
+ * sequential access to its elements.
+ *
+ * <b>NOTE</b>: Enumeration is not safe if new elements are put in the table
+ * as this could cause a rehash and we'd completely lose our place. Even
+ * without a rehash, it is undetermined if a new element added would
+ * appear in the enumeration. The spec says nothing about this, but
+ * the "Java Class Libraries" book implies that modifications to the
+ * hashtable during enumeration causes indeterminate results. Don't do it!
+ *
+ * @author Jon Zeppieri
+ * @author Fridjof Siebert
+ */
+ private class EntryEnumerator
+ implements Enumeration<Entry<K,V>>
+ {
+ /** The number of elements remaining to be returned by next(). */
+ int count = size;
+ /** Current index in the physical hash table. */
+ int idx = buckets.length;
+ /**
+ * Entry which will be returned by the next nextElement() call. It is
+ * set if we are iterating through a bucket with multiple entries, or null
+ * if we must look in the next bucket.
+ */
+ HashEntry<K, V> next;
+
+ /**
+ * Construct the enumeration.
+ */
+ EntryEnumerator()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Checks whether more elements remain in the enumeration.
+ * @return true if nextElement() will not fail.
+ */
+ public boolean hasMoreElements()
+ {
+ return count > 0;
+ }
+
+ /**
+ * Returns the next element.
+ * @return the next element
+ * @throws NoSuchElementException if there is none.
+ */
+ public Map.Entry<K,V> nextElement()
+ {
+ if (count == 0)
+ throw new NoSuchElementException("Hashtable Enumerator");
+ count--;
+ HashEntry<K, V> e = next;
+
+ while (e == null)
+ if (idx <= 0)
+ return null;
+ else
+ e = buckets[--idx];
+
+ next = e.next;
+ return e;
+ }
+ } // class EntryEnumerator
+
+
+ /**
+ * Enumeration view of this Hashtable, providing sequential access to its
+ * elements.
+ *
+ * <b>NOTE</b>: Enumeration is not safe if new elements are put in the table
+ * as this could cause a rehash and we'd completely lose our place. Even
+ * without a rehash, it is undetermined if a new element added would
+ * appear in the enumeration. The spec says nothing about this, but
+ * the "Java Class Libraries" book implies that modifications to the
+ * hashtable during enumeration causes indeterminate results. Don't do it!
+ *
+ * @author Jon Zeppieri
+ * @author Fridjof Siebert
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ */
+ private final class KeyEnumerator
+ implements Enumeration<K>
+ {
+ /**
+ * This entry enumerator is used for most operations. Only
+ * <code>nextElement()</code> gives a different result, by returning just
+ * the key rather than the whole element.
+ */
+ private final EntryEnumerator enumerator;
+
+ /**
+ * Construct a new KeyEnumerator
+ */
+ KeyEnumerator()
+ {
+ enumerator = new EntryEnumerator();
+ }
+
+
+ /**
+ * Returns true if the entry enumerator has more elements.
+ *
+ * @return true if there are more elements
+ * @throws ConcurrentModificationException if the hashtable was modified
+ */
+ public boolean hasMoreElements()
+ {
+ return enumerator.hasMoreElements();
+ }
+
+ /**
+ * Returns the next element.
+ * @return the next element
+ * @throws NoSuchElementException if there is none.
+ */
+ public K nextElement()
+ {
+ HashEntry<K,V> entry = (HashEntry<K,V>) enumerator.nextElement();
+ K retVal = null;
+ if (entry != null)
+ retVal = entry.key;
+ return retVal;
+ }
+ } // class KeyEnumerator
+
+
+ /**
+ * Enumeration view of this Hashtable, providing sequential access to its
+ * values.
+ *
+ * <b>NOTE</b>: Enumeration is not safe if new elements are put in the table
+ * as this could cause a rehash and we'd completely lose our place. Even
+ * without a rehash, it is undetermined if a new element added would
+ * appear in the enumeration. The spec says nothing about this, but
+ * the "Java Class Libraries" book implies that modifications to the
+ * hashtable during enumeration causes indeterminate results. Don't do it!
+ *
+ * @author Jon Zeppieri
+ * @author Fridjof Siebert
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ */
+ private final class ValueEnumerator
+ implements Enumeration<V>
+ {
+ /**
+ * This entry enumerator is used for most operations. Only
+ * <code>nextElement()</code> gives a different result, by returning just
+ * the value rather than the whole element.
+ */
+ private final EntryEnumerator enumerator;
+
+ /**
+ * Construct a new ValueEnumerator
+ */
+ ValueEnumerator()
+ {
+ enumerator = new EntryEnumerator();
+ }
+
+
+ /**
+ * Returns true if the entry enumerator has more elements.
+ *
+ * @return true if there are more elements
+ * @throws ConcurrentModificationException if the hashtable was modified
+ */
+ public boolean hasMoreElements()
+ {
+ return enumerator.hasMoreElements();
+ }
+
+ /**
+ * Returns the next element.
+ * @return the next element
+ * @throws NoSuchElementException if there is none.
+ */
+ public V nextElement()
+ {
+ HashEntry<K,V> entry = (HashEntry<K,V>) enumerator.nextElement();
+ V retVal = null;
+ if (entry != null)
+ retVal = entry.value;
+ return retVal;
+ }
+ } // class ValueEnumerator
+
+} // class Hashtable
diff --git a/libjava/classpath/java/util/IdentityHashMap.java b/libjava/classpath/java/util/IdentityHashMap.java
new file mode 100644
index 000000000..89a75259e
--- /dev/null
+++ b/libjava/classpath/java/util/IdentityHashMap.java
@@ -0,0 +1,990 @@
+/* IdentityHashMap.java -- a class providing a hashtable data structure,
+ mapping Object --> Object, which uses object identity for hashing.
+ Copyright (C) 2001, 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+/**
+ * This class provides a hashtable-backed implementation of the
+ * Map interface, but uses object identity to do its hashing. In fact,
+ * it uses object identity for comparing values, as well. It uses a
+ * linear-probe hash table, which may have faster performance
+ * than the chaining employed by HashMap.
+ * <p>
+ *
+ * <em>WARNING: This is not a general purpose map. Because it uses
+ * System.identityHashCode and ==, instead of hashCode and equals, for
+ * comparison, it violated Map's general contract, and may cause
+ * undefined behavior when compared to other maps which are not
+ * IdentityHashMaps. This is designed only for the rare cases when
+ * identity semantics are needed.</em> An example use is
+ * topology-preserving graph transformations, such as deep cloning,
+ * or as proxy object mapping such as in debugging.
+ * <p>
+ *
+ * This map permits <code>null</code> keys and values, and does not
+ * guarantee that elements will stay in the same order over time. The
+ * basic operations (<code>get</code> and <code>put</code>) take
+ * constant time, provided System.identityHashCode is decent. You can
+ * tune the behavior by specifying the expected maximum size. As more
+ * elements are added, the map may need to allocate a larger table,
+ * which can be expensive.
+ * <p>
+ *
+ * This implementation is unsynchronized. If you want multi-thread
+ * access to be consistent, you must synchronize it, perhaps by using
+ * <code>Collections.synchronizedMap(new IdentityHashMap(...));</code>.
+ * The iterators are <i>fail-fast</i>, meaning that a structural modification
+ * made to the map outside of an iterator's remove method cause the
+ * iterator, and in the case of the entrySet, the Map.Entry, to
+ * fail with a {@link ConcurrentModificationException}.
+ *
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see System#identityHashCode(Object)
+ * @see Collection
+ * @see Map
+ * @see HashMap
+ * @see TreeMap
+ * @see LinkedHashMap
+ * @see WeakHashMap
+ * @since 1.4
+ * @status updated to 1.4
+ */
+public class IdentityHashMap<K,V> extends AbstractMap<K,V>
+ implements Map<K,V>, Serializable, Cloneable
+{
+ /** The default capacity. */
+ private static final int DEFAULT_CAPACITY = 21;
+
+ /**
+ * This object is used to mark a slot whose key or value is 'null'.
+ * This is more efficient than using a special value to mark an empty
+ * slot, because null entries are rare, empty slots are common, and
+ * the JVM will clear new arrays for us.
+ * Package visible for use by nested classes.
+ */
+ static final Object nullslot = new Object();
+
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = 8188218128353913216L;
+
+ /**
+ * The number of mappings in the table. Package visible for use by nested
+ * classes.
+ * @serial
+ */
+ int size;
+
+ /**
+ * The table itself. Package visible for use by nested classes.
+ */
+ transient Object[] table;
+
+ /**
+ * The number of structural modifications made so far. Package visible for
+ * use by nested classes.
+ */
+ transient int modCount;
+
+ /**
+ * The cache for {@link #entrySet()}.
+ */
+ private transient Set<Map.Entry<K,V>> entries;
+
+ /**
+ * The threshold for rehashing, which is 75% of (table.length / 2).
+ */
+ private transient int threshold;
+
+ /**
+ * Create a new IdentityHashMap with the default capacity (21 entries).
+ */
+ public IdentityHashMap()
+ {
+ this(DEFAULT_CAPACITY);
+ }
+
+ /**
+ * Create a new IdentityHashMap with the indicated number of
+ * entries. If the number of elements added to this hash map
+ * exceeds this maximum, the map will grow itself; however, that
+ * incurs a performance penalty.
+ *
+ * @param max initial size
+ * @throws IllegalArgumentException if max is negative
+ */
+ public IdentityHashMap(int max)
+ {
+ if (max < 0)
+ throw new IllegalArgumentException();
+ // Need at least two slots, or hash() will break.
+ if (max < 2)
+ max = 2;
+ table = new Object[max << 1];
+ threshold = (max >> 2) * 3;
+ }
+
+ /**
+ * Create a new IdentityHashMap whose contents are taken from the
+ * given Map.
+ *
+ * @param m The map whose elements are to be put in this map
+ * @throws NullPointerException if m is null
+ */
+ public IdentityHashMap(Map<? extends K, ? extends V> m)
+ {
+ this(Math.max(m.size() << 1, DEFAULT_CAPACITY));
+ putAll(m);
+ }
+
+ /**
+ * Remove all mappings from this map.
+ */
+ public void clear()
+ {
+ if (size != 0)
+ {
+ modCount++;
+ Arrays.fill(table, null);
+ size = 0;
+ }
+ }
+
+ /**
+ * Creates a shallow copy where keys and values are not cloned.
+ */
+ public Object clone()
+ {
+ try
+ {
+ IdentityHashMap copy = (IdentityHashMap) super.clone();
+ copy.table = (Object[]) table.clone();
+ copy.entries = null; // invalidate the cache
+ return copy;
+ }
+ catch (CloneNotSupportedException e)
+ {
+ // Can't happen.
+ return null;
+ }
+ }
+
+ /**
+ * Tests whether the specified key is in this map. Unlike normal Maps,
+ * this test uses <code>entry == key</code> instead of
+ * <code>entry == null ? key == null : entry.equals(key)</code>.
+ *
+ * @param key the key to look for
+ * @return true if the key is contained in the map
+ * @see #containsValue(Object)
+ * @see #get(Object)
+ */
+ public boolean containsKey(Object key)
+ {
+ key = xform(key);
+ return key == table[hash(key)];
+ }
+
+ /**
+ * Returns true if this HashMap contains the value. Unlike normal maps,
+ * this test uses <code>entry == value</code> instead of
+ * <code>entry == null ? value == null : entry.equals(value)</code>.
+ *
+ * @param value the value to search for in this HashMap
+ * @return true if at least one key maps to the value
+ * @see #containsKey(Object)
+ */
+ public boolean containsValue(Object value)
+ {
+ value = xform(value);
+ for (int i = table.length - 1; i > 0; i -= 2)
+ if (table[i] == value)
+ return true;
+ return false;
+ }
+
+ /**
+ * Returns a "set view" of this Map's entries. The set is backed by
+ * the Map, so changes in one show up in the other. The set supports
+ * element removal, but not element addition.
+ * <p>
+ *
+ * <em>The semantics of this set, and of its contained entries, are
+ * different from the contract of Set and Map.Entry in order to make
+ * IdentityHashMap work. This means that while you can compare these
+ * objects between IdentityHashMaps, comparing them with regular sets
+ * or entries is likely to have undefined behavior.</em> The entries
+ * in this set are reference-based, rather than the normal object
+ * equality. Therefore, <code>e1.equals(e2)</code> returns
+ * <code>e1.getKey() == e2.getKey() && e1.getValue() == e2.getValue()</code>,
+ * and <code>e.hashCode()</code> returns
+ * <code>System.identityHashCode(e.getKey()) ^
+ * System.identityHashCode(e.getValue())</code>.
+ * <p>
+ *
+ * Note that the iterators for all three views, from keySet(), entrySet(),
+ * and values(), traverse the Map in the same sequence.
+ *
+ * @return a set view of the entries
+ * @see #keySet()
+ * @see #values()
+ * @see Map.Entry
+ */
+ public Set<Map.Entry<K,V>> entrySet()
+ {
+ if (entries == null)
+ entries = new AbstractSet<Map.Entry<K,V>>()
+ {
+ public int size()
+ {
+ return size;
+ }
+
+ public Iterator<Map.Entry<K,V>> iterator()
+ {
+ return new IdentityIterator<Map.Entry<K,V>>(ENTRIES);
+ }
+
+ public void clear()
+ {
+ IdentityHashMap.this.clear();
+ }
+
+ public boolean contains(Object o)
+ {
+ if (! (o instanceof Map.Entry))
+ return false;
+ Map.Entry m = (Map.Entry) o;
+ Object value = xform(m.getValue());
+ Object key = xform(m.getKey());
+ return value == table[hash(key) + 1];
+ }
+
+ public int hashCode()
+ {
+ return IdentityHashMap.this.hashCode();
+ }
+
+ public boolean remove(Object o)
+ {
+ if (! (o instanceof Map.Entry))
+ return false;
+ Object key = xform(((Map.Entry) o).getKey());
+ int h = hash(key);
+ if (table[h] == key)
+ {
+ size--;
+ modCount++;
+ IdentityHashMap.this.removeAtIndex(h);
+ return true;
+ }
+ return false;
+ }
+ };
+ return entries;
+ }
+
+ /**
+ * Compares two maps for equality. This returns true only if both maps
+ * have the same reference-identity comparisons. While this returns
+ * <code>this.entrySet().equals(m.entrySet())</code> as specified by Map,
+ * this will not work with normal maps, since the entry set compares
+ * with == instead of .equals.
+ *
+ * @param o the object to compare to
+ * @return true if it is equal
+ */
+ public boolean equals(Object o)
+ {
+ // Why did Sun specify this one? The superclass does the right thing.
+ return super.equals(o);
+ }
+
+ /**
+ * Return the value in this Map associated with the supplied key, or
+ * <code>null</code> if the key maps to nothing.
+ *
+ * <p>NOTE: Since the value could also be null, you must use
+ * containsKey to see if this key actually maps to something.
+ * Unlike normal maps, this tests for the key with <code>entry ==
+ * key</code> instead of <code>entry == null ? key == null :
+ * entry.equals(key)</code>.
+ *
+ * @param key the key for which to fetch an associated value
+ * @return what the key maps to, if present
+ * @see #put(Object, Object)
+ * @see #containsKey(Object)
+ */
+ public V get(Object key)
+ {
+ key = xform(key);
+ int h = hash(key);
+ return (V) (table[h] == key ? unxform(table[h + 1]) : null);
+ }
+
+ /**
+ * Returns the hashcode of this map. This guarantees that two
+ * IdentityHashMaps that compare with equals() will have the same hash code,
+ * but may break with comparison to normal maps since it uses
+ * System.identityHashCode() instead of hashCode().
+ *
+ * @return the hash code
+ */
+ public int hashCode()
+ {
+ int hash = 0;
+ for (int i = table.length - 2; i >= 0; i -= 2)
+ {
+ Object key = table[i];
+ if (key == null)
+ continue;
+ // FIXME: this is a lame computation.
+ hash += (System.identityHashCode(unxform(key))
+ ^ System.identityHashCode(unxform(table[i + 1])));
+ }
+ return hash;
+ }
+
+ /**
+ * Returns true if there are no key-value mappings currently in this Map
+ * @return <code>size() == 0</code>
+ */
+ public boolean isEmpty()
+ {
+ return size == 0;
+ }
+
+ /**
+ * Returns a "set view" of this Map's keys. The set is backed by the
+ * Map, so changes in one show up in the other. The set supports
+ * element removal, but not element addition.
+ * <p>
+ *
+ * <em>The semantics of this set are different from the contract of Set
+ * in order to make IdentityHashMap work. This means that while you can
+ * compare these objects between IdentityHashMaps, comparing them with
+ * regular sets is likely to have undefined behavior.</em> The hashCode
+ * of the set is the sum of the identity hash codes, instead of the
+ * regular hashCodes, and equality is determined by reference instead
+ * of by the equals method.
+ * <p>
+ *
+ * @return a set view of the keys
+ * @see #values()
+ * @see #entrySet()
+ */
+ public Set<K> keySet()
+ {
+ if (keys == null)
+ keys = new AbstractSet<K>()
+ {
+ public int size()
+ {
+ return size;
+ }
+
+ public Iterator<K> iterator()
+ {
+ return new IdentityIterator<K>(KEYS);
+ }
+
+ public void clear()
+ {
+ IdentityHashMap.this.clear();
+ }
+
+ public boolean contains(Object o)
+ {
+ return containsKey(o);
+ }
+
+ public int hashCode()
+ {
+ int hash = 0;
+ for (int i = table.length - 2; i >= 0; i -= 2)
+ {
+ Object key = table[i];
+ if (key == null)
+ continue;
+ hash += System.identityHashCode(unxform(key));
+ }
+ return hash;
+ }
+
+ public boolean remove(Object o)
+ {
+ o = xform(o);
+ int h = hash(o);
+ if (table[h] == o)
+ {
+ size--;
+ modCount++;
+ removeAtIndex(h);
+ return true;
+ }
+ return false;
+ }
+ };
+ return keys;
+ }
+
+ /**
+ * Puts the supplied value into the Map, mapped by the supplied key.
+ * The value may be retrieved by any object which <code>equals()</code>
+ * this key. NOTE: Since the prior value could also be null, you must
+ * first use containsKey if you want to see if you are replacing the
+ * key's mapping. Unlike normal maps, this tests for the key
+ * with <code>entry == key</code> instead of
+ * <code>entry == null ? key == null : entry.equals(key)</code>.
+ *
+ * @param key the key used to locate the value
+ * @param value the value to be stored in the HashMap
+ * @return the prior mapping of the key, or null if there was none
+ * @see #get(Object)
+ */
+ public V put(K key, V value)
+ {
+ key = (K) xform(key);
+ value = (V) xform(value);
+
+ // We don't want to rehash if we're overwriting an existing slot.
+ int h = hash(key);
+ if (table[h] == key)
+ {
+ V r = (V) unxform(table[h + 1]);
+ table[h + 1] = value;
+ return r;
+ }
+
+ // Rehash if the load factor is too high.
+ if (size > threshold)
+ {
+ Object[] old = table;
+ // This isn't necessarily prime, but it is an odd number of key/value
+ // slots, which has a higher probability of fewer collisions.
+ table = new Object[(old.length * 2) + 2];
+ size = 0;
+ threshold = (table.length >>> 3) * 3;
+
+ for (int i = old.length - 2; i >= 0; i -= 2)
+ {
+ K oldkey = (K) old[i];
+ if (oldkey != null)
+ {
+ h = hash(oldkey);
+ table[h] = oldkey;
+ table[h + 1] = old[i + 1];
+ ++size;
+ // No need to update modCount here, we'll do it
+ // just after the loop.
+ }
+ }
+
+ // Now that we've resize, recompute the hash value.
+ h = hash(key);
+ }
+
+ // At this point, we add a new mapping.
+ modCount++;
+ size++;
+ table[h] = key;
+ table[h + 1] = value;
+ return null;
+ }
+
+ /**
+ * Copies all of the mappings from the specified map to this. If a key
+ * is already in this map, its value is replaced.
+ *
+ * @param m the map to copy
+ * @throws NullPointerException if m is null
+ */
+ public void putAll(Map<? extends K, ? extends V> m)
+ {
+ // Why did Sun specify this one? The superclass does the right thing.
+ super.putAll(m);
+ }
+
+ /**
+ * Remove the element at index and update the table to compensate.
+ * This is package-private for use by inner classes.
+ * @param i index of the removed element
+ */
+ final void removeAtIndex(int i)
+ {
+ // This is Algorithm R from Knuth, section 6.4.
+ // Variable names are taken directly from the text.
+ while (true)
+ {
+ table[i] = null;
+ table[i + 1] = null;
+ int j = i;
+ int r;
+ do
+ {
+ i -= 2;
+ if (i < 0)
+ i = table.length - 2;
+ Object key = table[i];
+ if (key == null)
+ return;
+ r = Math.abs(System.identityHashCode(key)
+ % (table.length >> 1)) << 1;
+ }
+ while ((i <= r && r < j)
+ || (r < j && j < i)
+ || (j < i && i <= r));
+ table[j] = table[i];
+ table[j + 1] = table[i + 1];
+ }
+ }
+
+ /**
+ * Removes from the HashMap and returns the value which is mapped by
+ * the supplied key. If the key maps to nothing, then the HashMap
+ * remains unchanged, and <code>null</code> is returned.
+ *
+ * NOTE: Since the value could also be null, you must use
+ * containsKey to see if you are actually removing a mapping.
+ * Unlike normal maps, this tests for the key with <code>entry ==
+ * key</code> instead of <code>entry == null ? key == null :
+ * entry.equals(key)</code>.
+ *
+ * @param key the key used to locate the value to remove
+ * @return whatever the key mapped to, if present
+ */
+ public V remove(Object key)
+ {
+ key = xform(key);
+ int h = hash(key);
+ if (table[h] == key)
+ {
+ modCount++;
+ size--;
+ Object r = unxform(table[h + 1]);
+ removeAtIndex(h);
+ return (V) r;
+ }
+ return null;
+ }
+
+ /**
+ * Returns the number of kay-value mappings currently in this Map
+ * @return the size
+ */
+ public int size()
+ {
+ return size;
+ }
+
+ /**
+ * Returns a "collection view" (or "bag view") of this Map's values.
+ * The collection is backed by the Map, so changes in one show up
+ * in the other. The collection supports element removal, but not element
+ * addition.
+ * <p>
+ *
+ * <em>The semantics of this set are different from the contract of
+ * Collection in order to make IdentityHashMap work. This means that
+ * while you can compare these objects between IdentityHashMaps, comparing
+ * them with regular sets is likely to have undefined behavior.</em>
+ * Likewise, contains and remove go by == instead of equals().
+ * <p>
+ *
+ * @return a bag view of the values
+ * @see #keySet()
+ * @see #entrySet()
+ */
+ public Collection<V> values()
+ {
+ if (values == null)
+ values = new AbstractCollection<V>()
+ {
+ public int size()
+ {
+ return size;
+ }
+
+ public Iterator<V> iterator()
+ {
+ return new IdentityIterator<V>(VALUES);
+ }
+
+ public void clear()
+ {
+ IdentityHashMap.this.clear();
+ }
+
+ public boolean remove(Object o)
+ {
+ o = xform(o);
+ // This approach may look strange, but it is ok.
+ for (int i = table.length - 1; i > 0; i -= 2)
+ if (table[i] == o)
+ {
+ modCount++;
+ size--;
+ IdentityHashMap.this.removeAtIndex(i - 1);
+ return true;
+ }
+ return false;
+ }
+ };
+ return values;
+ }
+
+ /**
+ * Transform a reference from its external form to its internal form.
+ * This is package-private for use by inner classes.
+ */
+ final Object xform(Object o)
+ {
+ if (o == null)
+ o = nullslot;
+ return o;
+ }
+
+ /**
+ * Transform a reference from its internal form to its external form.
+ * This is package-private for use by inner classes.
+ */
+ final Object unxform(Object o)
+ {
+ if (o == nullslot)
+ o = null;
+ return o;
+ }
+
+ /**
+ * Helper method which computes the hash code, then traverses the table
+ * until it finds the key, or the spot where the key would go. the key
+ * must already be in its internal form.
+ *
+ * @param key the key to check
+ * @return the index where the key belongs
+ * @see #IdentityHashMap(int)
+ * @see #put(Object, Object)
+ */
+ // Package visible for use by nested classes.
+ final int hash(Object key)
+ {
+ int h = Math.abs(System.identityHashCode(key) % (table.length >> 1)) << 1;
+
+ while (true)
+ {
+ // By requiring at least 2 key/value slots, and rehashing at 75%
+ // capacity, we guarantee that there will always be either an empty
+ // slot somewhere in the table.
+ if (table[h] == key || table[h] == null)
+ return h;
+ // We use linear probing as it is friendlier to the cache and
+ // it lets us efficiently remove entries.
+ h -= 2;
+ if (h < 0)
+ h = table.length - 2;
+ }
+ }
+
+ /**
+ * This class allows parameterized iteration over IdentityHashMaps. Based
+ * on its construction, it returns the key or value of a mapping, or
+ * creates the appropriate Map.Entry object with the correct fail-fast
+ * semantics and identity comparisons.
+ *
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private class IdentityIterator<I> implements Iterator<I>
+ {
+ /**
+ * The type of this Iterator: {@link #KEYS}, {@link #VALUES},
+ * or {@link #ENTRIES}.
+ */
+ final int type;
+ /** The number of modifications to the backing Map that we know about. */
+ int knownMod = modCount;
+ /** The number of elements remaining to be returned by next(). */
+ int count = size;
+ /** Location in the table. */
+ int loc = table.length;
+
+ /**
+ * Construct a new Iterator with the supplied type.
+ * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
+ */
+ IdentityIterator(int type)
+ {
+ this.type = type;
+ }
+
+ /**
+ * Returns true if the Iterator has more elements.
+ * @return true if there are more elements
+ */
+ public boolean hasNext()
+ {
+ return count > 0;
+ }
+
+ /**
+ * Returns the next element in the Iterator's sequential view.
+ * @return the next element
+ * @throws ConcurrentModificationException if the Map was modified
+ * @throws NoSuchElementException if there is none
+ */
+ public I next()
+ {
+ if (knownMod != modCount)
+ throw new ConcurrentModificationException();
+ if (count == 0)
+ throw new NoSuchElementException();
+ count--;
+
+ Object key;
+ do
+ {
+ loc -= 2;
+ key = table[loc];
+ }
+ while (key == null);
+
+ return (I) (type == KEYS ? unxform(key)
+ : (type == VALUES ? unxform(table[loc + 1])
+ : new IdentityEntry(loc)));
+ }
+
+ /**
+ * Removes from the backing Map the last element which was fetched
+ * with the <code>next()</code> method.
+ *
+ * @throws ConcurrentModificationException if the Map was modified
+ * @throws IllegalStateException if called when there is no last element
+ */
+ public void remove()
+ {
+ if (knownMod != modCount)
+ throw new ConcurrentModificationException();
+ if (loc == table.length)
+ throw new IllegalStateException();
+ modCount++;
+ size--;
+ removeAtIndex(loc);
+ knownMod++;
+ }
+ } // class IdentityIterator
+
+ /**
+ * This class provides Map.Entry objects for IdentityHashMaps. The entry
+ * is fail-fast, and will throw a ConcurrentModificationException if
+ * the underlying map is modified, or if remove is called on the iterator
+ * that generated this object. It is identity based, so it violates
+ * the general contract of Map.Entry, and is probably unsuitable for
+ * comparison to normal maps; but it works among other IdentityHashMaps.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private final class IdentityEntry<EK,EV> implements Map.Entry<EK,EV>
+ {
+ /** The location of this entry. */
+ final int loc;
+ /** The number of modifications to the backing Map that we know about. */
+ final int knownMod = modCount;
+
+ /**
+ * Constructs the Entry.
+ *
+ * @param loc the location of this entry in table
+ */
+ IdentityEntry(int loc)
+ {
+ this.loc = loc;
+ }
+
+ /**
+ * Compares the specified object with this entry, using identity
+ * semantics. Note that this can lead to undefined results with
+ * Entry objects created by normal maps.
+ *
+ * @param o the object to compare
+ * @return true if it is equal
+ * @throws ConcurrentModificationException if the entry was invalidated
+ * by modifying the Map or calling Iterator.remove()
+ */
+ public boolean equals(Object o)
+ {
+ if (knownMod != modCount)
+ throw new ConcurrentModificationException();
+ if (! (o instanceof Map.Entry))
+ return false;
+ Map.Entry e = (Map.Entry) o;
+ return table[loc] == xform(e.getKey())
+ && table[loc + 1] == xform(e.getValue());
+ }
+
+ /**
+ * Returns the key of this entry.
+ *
+ * @return the key
+ * @throws ConcurrentModificationException if the entry was invalidated
+ * by modifying the Map or calling Iterator.remove()
+ */
+ public EK getKey()
+ {
+ if (knownMod != modCount)
+ throw new ConcurrentModificationException();
+ return (EK) unxform(table[loc]);
+ }
+
+ /**
+ * Returns the value of this entry.
+ *
+ * @return the value
+ * @throws ConcurrentModificationException if the entry was invalidated
+ * by modifying the Map or calling Iterator.remove()
+ */
+ public EV getValue()
+ {
+ if (knownMod != modCount)
+ throw new ConcurrentModificationException();
+ return (EV) unxform(table[loc + 1]);
+ }
+
+ /**
+ * Returns the hashcode of the entry, using identity semantics.
+ * Note that this can lead to undefined results with Entry objects
+ * created by normal maps.
+ *
+ * @return the hash code
+ * @throws ConcurrentModificationException if the entry was invalidated
+ * by modifying the Map or calling Iterator.remove()
+ */
+ public int hashCode()
+ {
+ if (knownMod != modCount)
+ throw new ConcurrentModificationException();
+ return (System.identityHashCode(unxform(table[loc]))
+ ^ System.identityHashCode(unxform(table[loc + 1])));
+ }
+
+ /**
+ * Replaces the value of this mapping, and returns the old value.
+ *
+ * @param value the new value
+ * @return the old value
+ * @throws ConcurrentModificationException if the entry was invalidated
+ * by modifying the Map or calling Iterator.remove()
+ */
+ public EV setValue(EV value)
+ {
+ if (knownMod != modCount)
+ throw new ConcurrentModificationException();
+ EV r = (EV) unxform(table[loc + 1]);
+ table[loc + 1] = xform(value);
+ 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
+ * @throws ConcurrentModificationException if the entry was invalidated
+ * by modifying the Map or calling Iterator.remove()
+ */
+ public String toString()
+ {
+ if (knownMod != modCount)
+ throw new ConcurrentModificationException();
+ return unxform(table[loc]) + "=" + unxform(table[loc + 1]);
+ }
+ } // class IdentityEntry
+
+ /**
+ * Reads the object from a serial stream.
+ *
+ * @param s the stream to read from
+ * @throws ClassNotFoundException if the underlying stream fails
+ * @throws IOException if the underlying stream fails
+ * @serialData expects the size (int), followed by that many key (Object)
+ * and value (Object) pairs, with the pairs in no particular
+ * order
+ */
+ private void readObject(ObjectInputStream s)
+ throws IOException, ClassNotFoundException
+ {
+ s.defaultReadObject();
+
+ int num = s.readInt();
+ table = new Object[Math.max(num << 1, DEFAULT_CAPACITY) << 1];
+ // Read key/value pairs.
+ while (--num >= 0)
+ put((K) s.readObject(), (V) s.readObject());
+ }
+
+ /**
+ * Writes the object to a serial stream.
+ *
+ * @param s the stream to write to
+ * @throws IOException if the underlying stream fails
+ * @serialData outputs the size (int), followed by that many key (Object)
+ * and value (Object) pairs, with the pairs in no particular
+ * order
+ */
+ private void writeObject(ObjectOutputStream s)
+ throws IOException
+ {
+ s.defaultWriteObject();
+ s.writeInt(size);
+ for (int i = table.length - 2; i >= 0; i -= 2)
+ {
+ Object key = table[i];
+ if (key != null)
+ {
+ s.writeObject(unxform(key));
+ s.writeObject(unxform(table[i + 1]));
+ }
+ }
+ }
+}
diff --git a/libjava/classpath/java/util/IllegalFormatCodePointException.java b/libjava/classpath/java/util/IllegalFormatCodePointException.java
new file mode 100644
index 000000000..f6d5c2fef
--- /dev/null
+++ b/libjava/classpath/java/util/IllegalFormatCodePointException.java
@@ -0,0 +1,85 @@
+/* IllegalFormatCodePointException.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Thrown when a {@link Formatter} receives a character with an
+ * invalid Unicode codepoint, as defined by
+ * {@link Character#isValidCodePoint(int)}.
+ *
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+public class IllegalFormatCodePointException
+ extends IllegalFormatException
+{
+ private static final long serialVersionUID = 19080630L;
+
+ /**
+ * The character which is an invalid Unicode code point.
+ *
+ * @serial the invalid character.
+ */
+ // Note: name fixed by serialization.
+ int c;
+
+ /**
+ * Constructs a new <code>IllegalFormatCodePointException</code>
+ * which specifies that the character, <code>c</code>, passed to
+ * a {@link Formatter} is an invalid Unicode code point.
+ *
+ * @param c the invalid character.
+ */
+ public IllegalFormatCodePointException(int c)
+ {
+ super("An invalid Unicode code point was supplied.");
+ this.c = c;
+ }
+
+ /**
+ * Returns the invalid character.
+ *
+ * @return the invalid character.
+ */
+ public int getCodePoint()
+ {
+ return c;
+ }
+}
diff --git a/libjava/classpath/java/util/IllegalFormatConversionException.java b/libjava/classpath/java/util/IllegalFormatConversionException.java
new file mode 100644
index 000000000..58aa91833
--- /dev/null
+++ b/libjava/classpath/java/util/IllegalFormatConversionException.java
@@ -0,0 +1,110 @@
+/* IllegalFormatConversionException.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Thrown when the type of an argument supplied to the
+ * {@link Formatter#format()} method of a {@link Formatter}
+ * does not match the conversion character specified for it.
+ *
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+public class IllegalFormatConversionException
+ extends IllegalFormatException
+{
+ private static final long serialVersionUID = 17000126L;
+
+ /**
+ * The conversion character which doesn't match
+ * the type of the argument.
+ *
+ * @serial the conversion character.
+ */
+ // Note: name fixed by serialization.
+ char c;
+
+ /**
+ * The type of the mismatching argument.
+ *
+ * @serial the mismatching argument type.
+ */
+ // Note: name fixed by serialization.
+ Class<?> arg;
+
+ /**
+ * Constructs a new <code>IllegalFormatConversionException</code>
+ * which specifies that the argument of type <code>arg</code> does
+ * not match the conversion character, <code>c</code>.
+ *
+ * @param c the conversion character.
+ * @param arg the type which doesn't match the conversion character.
+ * @throws NullPointerException if <code>arg</code> is null.
+ */
+ public IllegalFormatConversionException(char c, Class<?> arg)
+ {
+ super("The type, " + arg + ", is invalid for the conversion character, " +
+ c + ".");
+ if (arg == null)
+ throw new NullPointerException("The supplied type was null.");
+ this.c = c;
+ this.arg = arg;
+ }
+
+ /**
+ * Returns the conversion character.
+ *
+ * @return the conversion character.
+ */
+ public char getConversion()
+ {
+ return c;
+ }
+
+ /**
+ * Returns the type of the mismatched argument.
+ *
+ * @return the type of the mismatched argument.
+ */
+ public Class<?> getArgumentClass()
+ {
+ return arg;
+ }
+}
diff --git a/libjava/classpath/java/util/IllegalFormatException.java b/libjava/classpath/java/util/IllegalFormatException.java
new file mode 100644
index 000000000..b5c740a8f
--- /dev/null
+++ b/libjava/classpath/java/util/IllegalFormatException.java
@@ -0,0 +1,75 @@
+/* IllegalFormatException.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * A general exception thrown when a format string is supplied
+ * to a {@link Formatter} that contains either invalid syntax
+ * or a mismatch between the format specification and the
+ * supplied arguments. This class is never instantiated;
+ * instead one of its subclasses is used to throw a more
+ * specific exception.
+ *
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+public class IllegalFormatException
+ extends IllegalArgumentException
+{
+ private static final long serialVersionUID = 18830826L;
+
+ /**
+ * Constructs a new <code>IllegalFormatException</code>.
+ */
+ IllegalFormatException()
+ {
+ }
+
+ /**
+ * Constructs a new <code>IllegalFormatException</code>
+ * with the specified message.
+ *
+ * @param msg the error message for this exception.
+ */
+ IllegalFormatException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/libjava/classpath/java/util/IllegalFormatFlagsException.java b/libjava/classpath/java/util/IllegalFormatFlagsException.java
new file mode 100644
index 000000000..b53bfa50a
--- /dev/null
+++ b/libjava/classpath/java/util/IllegalFormatFlagsException.java
@@ -0,0 +1,86 @@
+/* IllegalFormatFlagsException.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Thrown when the flags supplied to the {@link Formatter#format()}
+ * method of a {@link Formatter} form an illegal combination.
+ *
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+public class IllegalFormatFlagsException
+ extends IllegalFormatException
+{
+ private static final long serialVersionUID = 790824L;
+
+ /**
+ * The set of flags which forms an illegal combination.
+ *
+ * @serial the illegal set of flags.
+ */
+ // Note: name fixed by serialization.
+ private String flags;
+
+ /**
+ * Constructs a new <code>IllegalFormatFlagsException</code>
+ * for the specified flags.
+ *
+ * @param flags the illegal set of flags.
+ * @throws NullPointerException if <code>flags</code> is null.
+ */
+ public IllegalFormatFlagsException(String flags)
+ {
+ super("An illegal set of flags, " + flags + ", was supplied.");
+ if (flags == null)
+ throw new NullPointerException("The supplied flags are null.");
+ this.flags = flags;
+ }
+
+ /**
+ * Returns the illegal flags.
+ *
+ * @return the illegal flags.
+ */
+ public String getFlags()
+ {
+ return flags;
+ }
+}
diff --git a/libjava/classpath/java/util/IllegalFormatPrecisionException.java b/libjava/classpath/java/util/IllegalFormatPrecisionException.java
new file mode 100644
index 000000000..a216dc185
--- /dev/null
+++ b/libjava/classpath/java/util/IllegalFormatPrecisionException.java
@@ -0,0 +1,85 @@
+/* IllegalFormatPrecisionException.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Thrown when the specified precision for a {@link Formatter}
+ * argument is illegal. This may be because the number is
+ * a negative number (other than -1), the argument does not
+ * accept a precision or for some other reason.
+ *
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+public class IllegalFormatPrecisionException
+ extends IllegalFormatException
+{
+ private static final long serialVersionUID = 18711008L;
+
+ /**
+ * The illegal precision value.
+ *
+ * @serial the illegal precision.
+ */
+ // Note: name fixed by serialization.
+ private int p;
+
+ /**
+ * Constructs a new <code>IllegalFormatPrecisionException</code>
+ * for the precision, <code>p</code>.
+ *
+ * @param p the illegal precision.
+ */
+ public IllegalFormatPrecisionException(int p)
+ {
+ super("The precision, " + p + ", is illegal.");
+ this.p = p;
+ }
+
+ /**
+ * Returns the illegal precision.
+ *
+ * @return the illegal precision.
+ */
+ public int getPrecision()
+ {
+ return p;
+ }
+}
diff --git a/libjava/classpath/java/util/IllegalFormatWidthException.java b/libjava/classpath/java/util/IllegalFormatWidthException.java
new file mode 100644
index 000000000..7f2a6257e
--- /dev/null
+++ b/libjava/classpath/java/util/IllegalFormatWidthException.java
@@ -0,0 +1,84 @@
+/* IllegalFormatWidthException.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Thrown when the specified width for a {@link Formatter}
+ * argument is illegal. This may be because the number is
+ * a negative number (other than -1) or for some other reason.
+ *
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+public class IllegalFormatWidthException
+ extends IllegalFormatException
+{
+ private static final long serialVersionUID = 16660902L;
+
+ /**
+ * The illegal width value.
+ *
+ * @serial the illegal width.
+ */
+ // Note: name fixed by serialization.
+ private int w;
+
+ /**
+ * Constructs a new <code>IllegalFormatWidthException</code>
+ * with the specified width, <code>w</code>.
+ *
+ * @param w the illegal width.
+ */
+ public IllegalFormatWidthException(int w)
+ {
+ super("The width, " + w + ", is illegal.");
+ this.w = w;
+ }
+
+ /**
+ * Returns the illegal width.
+ *
+ * @return the illegal width.
+ */
+ public int getWidth()
+ {
+ return w;
+ }
+}
diff --git a/libjava/classpath/java/util/InputMismatchException.java b/libjava/classpath/java/util/InputMismatchException.java
new file mode 100644
index 000000000..ee29e4653
--- /dev/null
+++ b/libjava/classpath/java/util/InputMismatchException.java
@@ -0,0 +1,73 @@
+/* InputMismatchException.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Thrown when a {@link Scanner} instance encounters a mismatch
+ * between the input data and the pattern it is trying to match it
+ * against. This could be because the input data represents an
+ * out-of-range value for the type the pattern represents, or simply
+ * because the data does not fit that particular type.
+ *
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+public class InputMismatchException
+ extends NoSuchElementException
+{
+ /**
+ * Constructs a new <code>InputMismatchException</code>
+ * with a <code>null</code> message.
+ */
+ public InputMismatchException()
+ {
+ }
+
+ /**
+ * Constructs a new <code>InputMismatchException</code>
+ * with the supplied error message.
+ *
+ * @param s the error message to report back to the user.
+ */
+ public InputMismatchException(String s)
+ {
+ super(s);
+ }
+}
diff --git a/libjava/classpath/java/util/InvalidPropertiesFormatException.java b/libjava/classpath/java/util/InvalidPropertiesFormatException.java
new file mode 100644
index 000000000..aaa6c4eb4
--- /dev/null
+++ b/libjava/classpath/java/util/InvalidPropertiesFormatException.java
@@ -0,0 +1,72 @@
+/* InvalidPropertiesFormatException.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+/** @since 1.5 */
+public class InvalidPropertiesFormatException extends IOException
+{
+ // This class won't serialize, but we have a UID to placate the compiler.
+ private static final long serialVersionUID = 7763056076009360219L;
+
+ public InvalidPropertiesFormatException(String message)
+ {
+ super(message);
+ }
+
+ public InvalidPropertiesFormatException(Throwable cause)
+ {
+ super();
+ initCause(cause);
+ }
+
+ private void writeObject(ObjectOutputStream out) throws IOException
+ {
+ throw new NotSerializableException("objects of this type are not serializable");
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException
+ {
+ throw new NotSerializableException("objects of this type are not serializable");
+ }
+}
diff --git a/libjava/classpath/java/util/Iterator.java b/libjava/classpath/java/util/Iterator.java
new file mode 100644
index 000000000..41111a52d
--- /dev/null
+++ b/libjava/classpath/java/util/Iterator.java
@@ -0,0 +1,87 @@
+/* Iterator.java -- Interface for iterating over collections
+ Copyright (C) 1998, 2001, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * An object which iterates over a collection. An Iterator is used to return
+ * the items once only, in sequence, by successive calls to the next method.
+ * It is also possible to remove elements from the underlying collection by
+ * using the optional remove method. Iterator is intended as a replacement
+ * for the Enumeration interface of previous versions of Java, which did not
+ * have the remove method and had less conveniently named methods.
+ *
+ * @author Original author unknown
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Collection
+ * @see ListIterator
+ * @see Enumeration
+ * @since 1.2
+ * @status updated to 1.4
+ */
+public interface Iterator<E>
+{
+ /**
+ * Tests whether there are elements remaining in the collection. In other
+ * words, calling <code>next()</code> will not throw an exception.
+ *
+ * @return true if there is at least one more element in the collection
+ */
+ boolean hasNext();
+
+ /**
+ * Obtain the next element in the collection.
+ *
+ * @return the next element in the collection
+ * @throws NoSuchElementException if there are no more elements
+ */
+ E next();
+
+ /**
+ * Remove from the underlying collection the last element returned by next
+ * (optional operation). This method can be called only once after each
+ * call to <code>next()</code>. It does not affect what will be returned
+ * by subsequent calls to next.
+ *
+ * @throws IllegalStateException if next has not yet been called or remove
+ * has already been called since the last call to next.
+ * @throws UnsupportedOperationException if this Iterator does not support
+ * the remove operation.
+ */
+ void remove();
+}
diff --git a/libjava/classpath/java/util/LinkedHashMap.java b/libjava/classpath/java/util/LinkedHashMap.java
new file mode 100644
index 000000000..7701d7763
--- /dev/null
+++ b/libjava/classpath/java/util/LinkedHashMap.java
@@ -0,0 +1,500 @@
+/* LinkedHashMap.java -- a class providing hashtable data structure,
+ mapping Object --> Object, with linked list traversal
+ Copyright (C) 2001, 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * This class provides a hashtable-backed implementation of the
+ * Map interface, with predictable traversal order.
+ * <p>
+ *
+ * It uses a hash-bucket approach; that is, hash collisions are handled
+ * by linking the new node off of the pre-existing node (or list of
+ * nodes). In this manner, techniques such as linear probing (which
+ * can cause primary clustering) and rehashing (which does not fit very
+ * well with Java's method of precomputing hash codes) are avoided. In
+ * addition, this maintains a doubly-linked list which tracks either
+ * insertion or access order.
+ * <p>
+ *
+ * In insertion order, calling <code>put</code> adds the key to the end of
+ * traversal, unless the key was already in the map; changing traversal order
+ * requires removing and reinserting a key. On the other hand, in access
+ * order, all calls to <code>put</code> and <code>get</code> cause the
+ * accessed key to move to the end of the traversal list. Note that any
+ * accesses to the map's contents via its collection views and iterators do
+ * not affect the map's traversal order, since the collection views do not
+ * call <code>put</code> or <code>get</code>.
+ * <p>
+ *
+ * One of the nice features of tracking insertion order is that you can
+ * copy a hashtable, and regardless of the implementation of the original,
+ * produce the same results when iterating over the copy. This is possible
+ * without needing the overhead of <code>TreeMap</code>.
+ * <p>
+ *
+ * When using this {@link #LinkedHashMap(int, float, boolean) constructor},
+ * you can build an access-order mapping. This can be used to implement LRU
+ * caches, for example. By overriding {@link #removeEldestEntry(Map.Entry)},
+ * you can also control the removal of the oldest entry, and thereby do
+ * things like keep the map at a fixed size.
+ * <p>
+ *
+ * Under ideal circumstances (no collisions), LinkedHashMap offers O(1)
+ * 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). Traversal is
+ * faster than in HashMap (proportional to the map size, and not the space
+ * allocated for the map), but other operations may be slower because of the
+ * overhead of the maintaining the traversal order list.
+ * <p>
+ *
+ * LinkedHashMap accepts the null key and null values. It is not
+ * synchronized, so if you need multi-threaded access, consider using:<br>
+ * <code>Map m = Collections.synchronizedMap(new LinkedHashMap(...));</code>
+ * <p>
+ *
+ * The iterators are <i>fail-fast</i>, meaning that any structural
+ * modification, except for <code>remove()</code> called on the iterator
+ * itself, cause the iterator to throw a
+ * {@link ConcurrentModificationException} rather than exhibit
+ * non-deterministic behavior.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @see Object#hashCode()
+ * @see Collection
+ * @see Map
+ * @see HashMap
+ * @see TreeMap
+ * @see Hashtable
+ * @since 1.4
+ * @status updated to 1.4
+ */
+public class LinkedHashMap<K,V> extends HashMap<K,V>
+{
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = 3801124242820219131L;
+
+ /**
+ * The oldest Entry to begin iteration at.
+ */
+ transient LinkedHashEntry root;
+
+ /**
+ * The iteration order of this linked hash map: <code>true</code> for
+ * access-order, <code>false</code> for insertion-order.
+ *
+ * @serial true for access order traversal
+ */
+ final boolean accessOrder;
+
+ /**
+ * Class to represent an entry in the hash table. Holds a single key-value
+ * pair and the doubly-linked insertion order list.
+ */
+ class LinkedHashEntry<K,V> extends HashEntry<K,V>
+ {
+ /**
+ * The predecessor in the iteration list. If this entry is the root
+ * (eldest), pred points to the newest entry.
+ */
+ LinkedHashEntry<K,V> pred;
+
+ /** The successor in the iteration list, null if this is the newest. */
+ LinkedHashEntry<K,V> succ;
+
+ /**
+ * Simple constructor.
+ *
+ * @param key the key
+ * @param value the value
+ */
+ LinkedHashEntry(K key, V value)
+ {
+ super(key, value);
+ if (root == null)
+ {
+ root = this;
+ pred = this;
+ }
+ else
+ {
+ pred = root.pred;
+ pred.succ = this;
+ root.pred = this;
+ }
+ }
+
+ /**
+ * Called when this entry is accessed via put or get. This version does
+ * the necessary bookkeeping to keep the doubly-linked list in order,
+ * after moving this element to the newest position in access order.
+ */
+ void access()
+ {
+ if (accessOrder && succ != null)
+ {
+ modCount++;
+ if (this == root)
+ {
+ root = succ;
+ pred.succ = this;
+ succ = null;
+ }
+ else
+ {
+ pred.succ = succ;
+ succ.pred = pred;
+ succ = null;
+ pred = root.pred;
+ pred.succ = this;
+ root.pred = this;
+ }
+ }
+ }
+
+ /**
+ * Called when this entry is removed from the map. This version does
+ * the necessary bookkeeping to keep the doubly-linked list in order.
+ *
+ * @return the value of this key as it is removed
+ */
+ V cleanup()
+ {
+ if (this == root)
+ {
+ root = succ;
+ if (succ != null)
+ succ.pred = pred;
+ }
+ else if (succ == null)
+ {
+ pred.succ = null;
+ root.pred = pred;
+ }
+ else
+ {
+ pred.succ = succ;
+ succ.pred = pred;
+ }
+ return value;
+ }
+ } // class LinkedHashEntry
+
+ /**
+ * Construct a new insertion-ordered LinkedHashMap with the default
+ * capacity (11) and the default load factor (0.75).
+ */
+ public LinkedHashMap()
+ {
+ super();
+ accessOrder = false;
+ }
+
+ /**
+ * Construct a new insertion-ordered LinkedHashMap from the given Map,
+ * with initial capacity the greater of the size of <code>m</code> or
+ * the default of 11.
+ * <p>
+ *
+ * Every element in Map m will be put into this new HashMap, in the
+ * order of m's iterator.
+ *
+ * @param m a Map whose key / value pairs will be put into
+ * the new HashMap. <b>NOTE: key / value pairs
+ * are not cloned in this constructor.</b>
+ * @throws NullPointerException if m is null
+ */
+ public LinkedHashMap(Map<? extends K, ? extends V> m)
+ {
+ super(m);
+ accessOrder = false;
+ }
+
+ /**
+ * Construct a new insertion-ordered LinkedHashMap with a specific
+ * inital capacity and default load factor of 0.75.
+ *
+ * @param initialCapacity the initial capacity of this HashMap (&gt;= 0)
+ * @throws IllegalArgumentException if (initialCapacity &lt; 0)
+ */
+ public LinkedHashMap(int initialCapacity)
+ {
+ super(initialCapacity);
+ accessOrder = false;
+ }
+
+ /**
+ * Construct a new insertion-orderd LinkedHashMap with a specific
+ * inital capacity and load factor.
+ *
+ * @param initialCapacity the initial capacity (&gt;= 0)
+ * @param loadFactor the load factor (&gt; 0, not NaN)
+ * @throws IllegalArgumentException if (initialCapacity &lt; 0) ||
+ * ! (loadFactor &gt; 0.0)
+ */
+ public LinkedHashMap(int initialCapacity, float loadFactor)
+ {
+ super(initialCapacity, loadFactor);
+ accessOrder = false;
+ }
+
+ /**
+ * Construct a new LinkedHashMap with a specific inital capacity, load
+ * factor, and ordering mode.
+ *
+ * @param initialCapacity the initial capacity (&gt;=0)
+ * @param loadFactor the load factor (&gt;0, not NaN)
+ * @param accessOrder true for access-order, false for insertion-order
+ * @throws IllegalArgumentException if (initialCapacity &lt; 0) ||
+ * ! (loadFactor &gt; 0.0)
+ */
+ public LinkedHashMap(int initialCapacity, float loadFactor,
+ boolean accessOrder)
+ {
+ super(initialCapacity, loadFactor);
+ this.accessOrder = accessOrder;
+ }
+
+ /**
+ * Clears the Map so it has no keys. This is O(1).
+ */
+ public void clear()
+ {
+ super.clear();
+ root = null;
+ }
+
+ /**
+ * 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 <code>true</code> if at least one key maps to the value
+ */
+ public boolean containsValue(Object value)
+ {
+ LinkedHashEntry e = root;
+ while (e != null)
+ {
+ if (equals(value, e.value))
+ return true;
+ e = e.succ;
+ }
+ return false;
+ }
+
+ /**
+ * Return the value in this Map associated with the supplied key,
+ * 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
+ * see if this key actually maps to something.
+ *
+ * @param key the key for which to fetch an associated value
+ * @return what the key maps to, if present
+ * @see #put(Object, Object)
+ * @see #containsKey(Object)
+ */
+ public V get(Object key)
+ {
+ int idx = hash(key);
+ HashEntry<K,V> e = buckets[idx];
+ while (e != null)
+ {
+ if (equals(key, e.key))
+ {
+ e.access();
+ return e.value;
+ }
+ e = e.next;
+ }
+ return null;
+ }
+
+ /**
+ * Returns <code>true</code> if this map should remove the eldest entry.
+ * This method is invoked by all calls to <code>put</code> and
+ * <code>putAll</code> which place a new entry in the map, providing
+ * the implementer an opportunity to remove the eldest entry any time
+ * a new one is added. This can be used to save memory usage of the
+ * hashtable, as well as emulating a cache, by deleting stale entries.
+ * <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() &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.
+ * However, if you override it to modify the map, you must return false
+ * (indicating that <code>put</code> should leave the modified map alone),
+ * or you face unspecified behavior. Remember that in access-order mode,
+ * even calling <code>get</code> is a structural modification, but using
+ * the collections views (such as <code>keySet</code>) is not.
+ * <p>
+ *
+ * This method is called after the eldest entry has been inserted, so
+ * if <code>put</code> was called on a previously empty map, the eldest
+ * entry is the one you just put in! The default implementation just
+ * returns <code>false</code>, so that this map always behaves like
+ * a normal one with unbounded growth.
+ *
+ * @param eldest the eldest element which would be removed if this
+ * 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<K,V> eldest)
+ {
+ return false;
+ }
+
+ /**
+ * Helper method called by <code>put</code>, which creates and adds a
+ * new Entry, followed by performing bookkeeping (like removeEldestEntry).
+ *
+ * @param key the key of the new Entry
+ * @param value the value
+ * @param idx the index in buckets where the new Entry belongs
+ * @param callRemove whether to call the removeEldestEntry method
+ * @see #put(Object, Object)
+ * @see #removeEldestEntry(Map.Entry)
+ * @see LinkedHashEntry#LinkedHashEntry(Object, Object)
+ */
+ void addEntry(K key, V value, int idx, boolean callRemove)
+ {
+ LinkedHashEntry e = new LinkedHashEntry(key, value);
+ e.next = buckets[idx];
+ buckets[idx] = e;
+ if (callRemove && removeEldestEntry(root))
+ remove(root.key);
+ }
+
+ /**
+ * Helper method, called by clone() to reset the doubly-linked list.
+ *
+ * @param m the map to add entries from
+ * @see #clone()
+ */
+ void putAllInternal(Map m)
+ {
+ root = null;
+ super.putAllInternal(m);
+ }
+
+ /**
+ * Generates a parameterized iterator. This allows traversal to follow
+ * the doubly-linked list instead of the random bin order of HashMap.
+ *
+ * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
+ * @return the appropriate iterator
+ */
+ Iterator iterator(final int type)
+ {
+ return new Iterator()
+ {
+ /** The current Entry. */
+ LinkedHashEntry current = root;
+
+ /** The previous Entry returned by next(). */
+ LinkedHashEntry last;
+
+ /** The number of known modifications to the backing Map. */
+ int knownMod = modCount;
+
+ /**
+ * Returns true if the Iterator has more elements.
+ *
+ * @return true if there are more elements
+ */
+ public boolean hasNext()
+ {
+ return current != null;
+ }
+
+ /**
+ * Returns the next element in the Iterator's sequential view.
+ *
+ * @return the next element
+ * @throws ConcurrentModificationException if the HashMap was modified
+ * @throws NoSuchElementException if there is none
+ */
+ public Object next()
+ {
+ if (knownMod != modCount)
+ throw new ConcurrentModificationException();
+ if (current == null)
+ throw new NoSuchElementException();
+ last = current;
+ current = current.succ;
+ return type == VALUES ? last.value : type == KEYS ? last.key : last;
+ }
+
+ /**
+ * Removes from the backing HashMap the last element which was fetched
+ * 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()
+ {
+ if (knownMod != modCount)
+ throw new ConcurrentModificationException();
+ if (last == null)
+ throw new IllegalStateException();
+ LinkedHashMap.this.remove(last.key);
+ last = null;
+ knownMod++;
+ }
+ };
+ }
+} // class LinkedHashMap
diff --git a/libjava/classpath/java/util/LinkedHashSet.java b/libjava/classpath/java/util/LinkedHashSet.java
new file mode 100644
index 000000000..2caebaf93
--- /dev/null
+++ b/libjava/classpath/java/util/LinkedHashSet.java
@@ -0,0 +1,159 @@
+/* LinkedHashSet.java -- a set backed by a LinkedHashMap, for linked
+ list traversal.
+ Copyright (C) 2001, 2004, 2005, 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.io.Serializable;
+
+/**
+ * This class provides a hashtable-backed implementation of the
+ * Set interface, with predictable traversal order.
+ * <p>
+ *
+ * It uses a hash-bucket approach; that is, hash collisions are handled
+ * by linking the new node off of the pre-existing node (or list of
+ * nodes). In this manner, techniques such as linear probing (which
+ * can cause primary clustering) and rehashing (which does not fit very
+ * well with Java's method of precomputing hash codes) are avoided. In
+ * addition, this maintains a doubly-linked list which tracks insertion
+ * order. Note that the insertion order is not modified if an
+ * <code>add</code> simply reinserts an element in the set.
+ * <p>
+ *
+ * One of the nice features of tracking insertion order is that you can
+ * copy a set, and regardless of the implementation of the original,
+ * produce the same results when iterating over the copy. This is possible
+ * without needing the overhead of <code>TreeSet</code>.
+ * <p>
+ *
+ * Under ideal circumstances (no collisions), LinkedHashSet offers O(1)
+ * performance on most operations. In the worst case (all elements map
+ * to the same hash code -- very unlikely), most operations are O(n).
+ * <p>
+ *
+ * LinkedHashSet accepts the null entry. It is not synchronized, so if
+ * you need multi-threaded access, consider using:<br>
+ * <code>Set s = Collections.synchronizedSet(new LinkedHashSet(...));</code>
+ * <p>
+ *
+ * The iterators are <i>fail-fast</i>, meaning that any structural
+ * modification, except for <code>remove()</code> called on the iterator
+ * itself, cause the iterator to throw a
+ * {@link ConcurrentModificationException} rather than exhibit
+ * non-deterministic behavior.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Object#hashCode()
+ * @see Collection
+ * @see Set
+ * @see HashSet
+ * @see TreeSet
+ * @see Collections#synchronizedSet(Set)
+ * @since 1.4
+ * @status updated to 1.4
+ */
+public class LinkedHashSet<T> extends HashSet<T>
+ implements Set<T>, Cloneable, Serializable
+{
+ /**
+ * Compatible with JDK 1.4.
+ */
+ private static final long serialVersionUID = -2851667679971038690L;
+
+ /**
+ * Construct a new, empty HashSet whose backing HashMap has the default
+ * capacity (11) and loadFactor (0.75).
+ */
+ public LinkedHashSet()
+ {
+ super();
+ }
+
+ /**
+ * Construct a new, empty HashSet whose backing HashMap has the supplied
+ * capacity and the default load factor (0.75).
+ *
+ * @param initialCapacity the initial capacity of the backing HashMap
+ * @throws IllegalArgumentException if the capacity is negative
+ */
+ public LinkedHashSet(int initialCapacity)
+ {
+ super(initialCapacity);
+ }
+
+ /**
+ * Construct a new, empty HashSet whose backing HashMap has the supplied
+ * capacity and load factor.
+ *
+ * @param initialCapacity the initial capacity of the backing HashMap
+ * @param loadFactor the load factor of the backing HashMap
+ * @throws IllegalArgumentException if either argument is negative, or
+ * if loadFactor is POSITIVE_INFINITY or NaN
+ */
+ public LinkedHashSet(int initialCapacity, float loadFactor)
+ {
+ super(initialCapacity, loadFactor);
+ }
+
+ /**
+ * Construct a new HashSet with the same elements as are in the supplied
+ * collection (eliminating any duplicates, of course). The backing storage
+ * has twice the size of the collection, or the default size of 11,
+ * whichever is greater; and the default load factor (0.75).
+ *
+ * @param c a collection of initial set elements
+ * @throws NullPointerException if c is null
+ */
+ public LinkedHashSet(Collection<? extends T> c)
+ {
+ super(c);
+ }
+
+ /**
+ * Helper method which initializes the backing Map.
+ *
+ * @param capacity the initial capacity
+ * @param load the initial load factor
+ * @return the backing HashMap
+ */
+ HashMap<T, String> init(int capacity, float load)
+ {
+ return new LinkedHashMap<T, String>(capacity, load);
+ }
+}
diff --git a/libjava/classpath/java/util/LinkedList.java b/libjava/classpath/java/util/LinkedList.java
new file mode 100644
index 000000000..813d099da
--- /dev/null
+++ b/libjava/classpath/java/util/LinkedList.java
@@ -0,0 +1,1260 @@
+/* LinkedList.java -- Linked list implementation of the List interface
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Array;
+
+/**
+ * Linked list implementation of the List interface. In addition to the
+ * methods of the List interface, this class provides access to the first
+ * and last list elements in O(1) time for easy stack, queue, or double-ended
+ * queue (deque) creation. The list is doubly-linked, with traversal to a
+ * given index starting from the end closest to the element.<p>
+ *
+ * LinkedList is not synchronized, so if you need multi-threaded access,
+ * consider using:<br>
+ * <code>List l = Collections.synchronizedList(new LinkedList(...));</code>
+ * <p>
+ *
+ * The iterators are <i>fail-fast</i>, meaning that any structural
+ * modification, except for <code>remove()</code> called on the iterator
+ * itself, cause the iterator to throw a
+ * {@link ConcurrentModificationException} rather than exhibit
+ * non-deterministic behavior.
+ *
+ * @author Original author unknown
+ * @author Bryce McKinlay
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @see List
+ * @see ArrayList
+ * @see Vector
+ * @see Collections#synchronizedList(List)
+ * @since 1.2
+ * @status Complete to 1.6
+ */
+public class LinkedList<T> extends AbstractSequentialList<T>
+ implements List<T>, Deque<T>, Cloneable, Serializable
+{
+ /**
+ * Compatible with JDK 1.2.
+ */
+ private static final long serialVersionUID = 876323262645176354L;
+
+ /**
+ * The first element in the list.
+ */
+ transient Entry<T> first;
+
+ /**
+ * The last element in the list.
+ */
+ transient Entry<T> last;
+
+ /**
+ * The current length of the list.
+ */
+ transient int size = 0;
+
+ /**
+ * Class to represent an entry in the list. Holds a single element.
+ */
+ private static final class Entry<T>
+ {
+ /** The element in the list. */
+ T data;
+
+ /** The next list entry, null if this is last. */
+ Entry<T> next;
+
+ /** The previous list entry, null if this is first. */
+ Entry<T> previous;
+
+ /**
+ * Construct an entry.
+ * @param data the list element
+ */
+ Entry(T data)
+ {
+ this.data = data;
+ }
+ } // class Entry
+
+ /**
+ * Obtain the Entry at a given position in a list. This method of course
+ * takes linear time, but it is intelligent enough to take the shorter of the
+ * paths to get to the Entry required. This implies that the first or last
+ * 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 &lt; 0) or (n &gt;= size).
+ *
+ * @param n the number of the entry to get
+ * @return the entry at position n
+ */
+ // Package visible for use in nested classes.
+ Entry<T> getEntry(int n)
+ {
+ Entry<T> e;
+ if (n < size / 2)
+ {
+ e = first;
+ // n less than size/2, iterate from start
+ while (n-- > 0)
+ e = e.next;
+ }
+ else
+ {
+ e = last;
+ // n greater than size/2, iterate from end
+ while (++n < size)
+ e = e.previous;
+ }
+ return e;
+ }
+
+ /**
+ * Remove an entry from the list. This will adjust size and deal with
+ * `first' and `last' appropriatly.
+ *
+ * @param e the entry to remove
+ */
+ // Package visible for use in nested classes.
+ void removeEntry(Entry<T> e)
+ {
+ modCount++;
+ size--;
+ if (size == 0)
+ first = last = null;
+ else
+ {
+ if (e == first)
+ {
+ first = e.next;
+ e.next.previous = null;
+ }
+ else if (e == last)
+ {
+ last = e.previous;
+ e.previous.next = null;
+ }
+ else
+ {
+ e.next.previous = e.previous;
+ e.previous.next = e.next;
+ }
+ }
+ }
+
+ /**
+ * Checks that the index is in the range of possible elements (inclusive).
+ *
+ * @param index the index to check
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size
+ */
+ private void checkBoundsInclusive(int index)
+ {
+ if (index < 0 || index > size)
+ throw new IndexOutOfBoundsException("Index: " + index + ", Size:"
+ + size);
+ }
+
+ /**
+ * Checks that the index is in the range of existing elements (exclusive).
+ *
+ * @param index the index to check
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size
+ */
+ private void checkBoundsExclusive(int index)
+ {
+ if (index < 0 || index >= size)
+ throw new IndexOutOfBoundsException("Index: " + index + ", Size:"
+ + size);
+ }
+
+ /**
+ * Create an empty linked list.
+ */
+ public LinkedList()
+ {
+ }
+
+ /**
+ * Create a linked list containing the elements, in order, of a given
+ * collection.
+ *
+ * @param c the collection to populate this list from
+ * @throws NullPointerException if c is null
+ */
+ public LinkedList(Collection<? extends T> c)
+ {
+ addAll(c);
+ }
+
+ /**
+ * Returns the first element in the list.
+ *
+ * @return the first list element
+ * @throws NoSuchElementException if the list is empty
+ */
+ public T getFirst()
+ {
+ if (size == 0)
+ throw new NoSuchElementException();
+ return first.data;
+ }
+
+ /**
+ * Returns the last element in the list.
+ *
+ * @return the last list element
+ * @throws NoSuchElementException if the list is empty
+ */
+ public T getLast()
+ {
+ if (size == 0)
+ throw new NoSuchElementException();
+ return last.data;
+ }
+
+ /**
+ * Remove and return the first element in the list.
+ *
+ * @return the former first element in the list
+ * @throws NoSuchElementException if the list is empty
+ */
+ public T removeFirst()
+ {
+ if (size == 0)
+ throw new NoSuchElementException();
+ modCount++;
+ size--;
+ T r = first.data;
+
+ if (first.next != null)
+ first.next.previous = null;
+ else
+ last = null;
+
+ first = first.next;
+
+ return r;
+ }
+
+ /**
+ * Remove and return the last element in the list.
+ *
+ * @return the former last element in the list
+ * @throws NoSuchElementException if the list is empty
+ */
+ public T removeLast()
+ {
+ if (size == 0)
+ throw new NoSuchElementException();
+ modCount++;
+ size--;
+ T r = last.data;
+
+ if (last.previous != null)
+ last.previous.next = null;
+ else
+ first = null;
+
+ last = last.previous;
+
+ return r;
+ }
+
+ /**
+ * Insert an element at the first of the list.
+ *
+ * @param o the element to insert
+ */
+ public void addFirst(T o)
+ {
+ Entry<T> e = new Entry(o);
+
+ modCount++;
+ if (size == 0)
+ first = last = e;
+ else
+ {
+ e.next = first;
+ first.previous = e;
+ first = e;
+ }
+ size++;
+ }
+
+ /**
+ * Insert an element at the last of the list.
+ *
+ * @param o the element to insert
+ */
+ public void addLast(T o)
+ {
+ addLastEntry(new Entry<T>(o));
+ }
+
+ /**
+ * Inserts an element at the end of the list.
+ *
+ * @param e the entry to add
+ */
+ private void addLastEntry(Entry<T> e)
+ {
+ modCount++;
+ if (size == 0)
+ first = last = e;
+ else
+ {
+ e.previous = last;
+ last.next = e;
+ last = e;
+ }
+ size++;
+ }
+
+ /**
+ * Returns true if the list contains the given object. Comparison is done by
+ * <code>o == null ? e = null : o.equals(e)</code>.
+ *
+ * @param o the element to look for
+ * @return true if it is found
+ */
+ public boolean contains(Object o)
+ {
+ Entry<T> e = first;
+ while (e != null)
+ {
+ if (equals(o, e.data))
+ return true;
+ e = e.next;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the size of the list.
+ *
+ * @return the list size
+ */
+ public int size()
+ {
+ return size;
+ }
+
+ /**
+ * Adds an element to the end of the list.
+ *
+ * @param o the entry to add
+ * @return true, as it always succeeds
+ */
+ public boolean add(T o)
+ {
+ addLastEntry(new Entry<T>(o));
+ return true;
+ }
+
+ /**
+ * Removes the entry at the lowest index in the list that matches the given
+ * object, comparing by <code>o == null ? e = null : o.equals(e)</code>.
+ *
+ * @param o the object to remove
+ * @return true if an instance of the object was removed
+ */
+ public boolean remove(Object o)
+ {
+ Entry<T> e = first;
+ while (e != null)
+ {
+ if (equals(o, e.data))
+ {
+ removeEntry(e);
+ return true;
+ }
+ e = e.next;
+ }
+ return false;
+ }
+
+ /**
+ * Append the elements of the collection in iteration order to the end of
+ * this list. If this list is modified externally (for example, if this
+ * list is the collection), behavior is unspecified.
+ *
+ * @param c the collection to append
+ * @return true if the list was modified
+ * @throws NullPointerException if c is null
+ */
+ public boolean addAll(Collection<? extends T> c)
+ {
+ return addAll(size, c);
+ }
+
+ /**
+ * Insert the elements of the collection in iteration order at the given
+ * index of this list. If this list is modified externally (for example,
+ * if this list is the collection), behavior is unspecified.
+ *
+ * @param c the collection to append
+ * @return true if the list was modified
+ * @throws NullPointerException if c is null
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ */
+ public boolean addAll(int index, Collection<? extends T> c)
+ {
+ checkBoundsInclusive(index);
+ int csize = c.size();
+
+ if (csize == 0)
+ return false;
+
+ Iterator<? extends T> itr = c.iterator();
+
+ // Get the entries just before and after index. If index is at the start
+ // of the list, BEFORE is null. If index is at the end of the list, AFTER
+ // is null. If the list is empty, both are null.
+ Entry<T> after = null;
+ Entry<T> before = null;
+ if (index != size)
+ {
+ after = getEntry(index);
+ before = after.previous;
+ }
+ else
+ before = last;
+
+ // Create the first new entry. We do not yet set the link from `before'
+ // to the first entry, in order to deal with the case where (c == this).
+ // [Actually, we don't have to handle this case to fufill the
+ // contract for addAll(), but Sun's implementation appears to.]
+ Entry<T> e = new Entry<T>(itr.next());
+ e.previous = before;
+ Entry<T> prev = e;
+ Entry<T> firstNew = e;
+
+ // Create and link all the remaining entries.
+ for (int pos = 1; pos < csize; pos++)
+ {
+ e = new Entry<T>(itr.next());
+ e.previous = prev;
+ prev.next = e;
+ prev = e;
+ }
+
+ // Link the new chain of entries into the list.
+ modCount++;
+ size += csize;
+ prev.next = after;
+ if (after != null)
+ after.previous = e;
+ else
+ last = e;
+
+ if (before != null)
+ before.next = firstNew;
+ else
+ first = firstNew;
+ return true;
+ }
+
+ /**
+ * Remove all elements from this list.
+ */
+ public void clear()
+ {
+ if (size > 0)
+ {
+ modCount++;
+ first = null;
+ last = null;
+ size = 0;
+ }
+ }
+
+ /**
+ * Return the element at index.
+ *
+ * @param index the place to look
+ * @return the element at index
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ */
+ public T get(int index)
+ {
+ checkBoundsExclusive(index);
+ return getEntry(index).data;
+ }
+
+ /**
+ * Replace the element at the given location in the list.
+ *
+ * @param index which index to change
+ * @param o the new element
+ * @return the prior element
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ */
+ public T set(int index, T o)
+ {
+ checkBoundsExclusive(index);
+ Entry<T> e = getEntry(index);
+ T old = e.data;
+ e.data = o;
+ return old;
+ }
+
+ /**
+ * Inserts an element in the given position in the list.
+ *
+ * @param index where to insert the element
+ * @param o the element to insert
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ */
+ public void add(int index, T o)
+ {
+ checkBoundsInclusive(index);
+ Entry<T> e = new Entry<T>(o);
+
+ if (index < size)
+ {
+ modCount++;
+ Entry<T> after = getEntry(index);
+ e.next = after;
+ e.previous = after.previous;
+ if (after.previous == null)
+ first = e;
+ else
+ after.previous.next = e;
+ after.previous = e;
+ size++;
+ }
+ else
+ addLastEntry(e);
+ }
+
+ /**
+ * Removes the element at the given position from the list.
+ *
+ * @param index the location of the element to remove
+ * @return the removed element
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ */
+ public T remove(int index)
+ {
+ checkBoundsExclusive(index);
+ Entry<T> e = getEntry(index);
+ removeEntry(e);
+ return e.data;
+ }
+
+ /**
+ * Returns the first index where the element is located in the list, or -1.
+ *
+ * @param o the element to look for
+ * @return its position, or -1 if not found
+ */
+ public int indexOf(Object o)
+ {
+ int index = 0;
+ Entry<T> e = first;
+ while (e != null)
+ {
+ if (equals(o, e.data))
+ return index;
+ index++;
+ e = e.next;
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the last index where the element is located in the list, or -1.
+ *
+ * @param o the element to look for
+ * @return its position, or -1 if not found
+ */
+ public int lastIndexOf(Object o)
+ {
+ int index = size - 1;
+ Entry<T> e = last;
+ while (e != null)
+ {
+ if (equals(o, e.data))
+ return index;
+ index--;
+ e = e.previous;
+ }
+ return -1;
+ }
+
+ /**
+ * Obtain a ListIterator over this list, starting at a given index. The
+ * ListIterator returned by this method supports the add, remove and set
+ * methods.
+ *
+ * @param index the index of the element to be returned by the first call to
+ * next(), or size() to be initially positioned at the end of the list
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ */
+ public ListIterator<T> listIterator(int index)
+ {
+ checkBoundsInclusive(index);
+ return new LinkedListItr<T>(index);
+ }
+
+ /**
+ * Create a shallow copy of this LinkedList (the elements are not cloned).
+ *
+ * @return an object of the same class as this object, containing the
+ * same elements in the same order
+ */
+ public Object clone()
+ {
+ LinkedList<T> copy = null;
+ try
+ {
+ copy = (LinkedList<T>) super.clone();
+ }
+ catch (CloneNotSupportedException ex)
+ {
+ }
+ copy.clear();
+ copy.addAll(this);
+ return copy;
+ }
+
+ /**
+ * Returns an array which contains the elements of the list in order.
+ *
+ * @return an array containing the list elements
+ */
+ public Object[] toArray()
+ {
+ Object[] array = new Object[size];
+ Entry<T> e = first;
+ for (int i = 0; i < size; i++)
+ {
+ array[i] = e.data;
+ e = e.next;
+ }
+ return array;
+ }
+
+ /**
+ * Returns an Array whose component type is the runtime component type of
+ * the passed-in Array. The returned Array is populated with all of the
+ * elements in this LinkedList. If the passed-in Array is not large enough
+ * to store all of the elements in this List, a new Array will be created
+ * and returned; if the passed-in Array is <i>larger</i> than the size
+ * of this List, then size() index will be set to null.
+ *
+ * @param a the passed-in Array
+ * @return an array representation of this list
+ * @throws ArrayStoreException if the runtime type of a does not allow
+ * an element in this list
+ * @throws NullPointerException if a is null
+ */
+ public <S> S[] toArray(S[] a)
+ {
+ if (a.length < size)
+ a = (S[]) Array.newInstance(a.getClass().getComponentType(), size);
+ else if (a.length > size)
+ a[size] = null;
+ Entry<T> e = first;
+ for (int i = 0; i < size; i++)
+ {
+ a[i] = (S) e.data;
+ e = e.next;
+ }
+ return a;
+ }
+
+ /**
+ * Adds the specified element to the end of the list.
+ *
+ * @param value the value to add.
+ * @return true.
+ * @since 1.5
+ */
+ public boolean offer(T value)
+ {
+ return add(value);
+ }
+
+ /**
+ * Returns the first element of the list without removing
+ * it.
+ *
+ * @return the first element of the list.
+ * @throws NoSuchElementException if the list is empty.
+ * @since 1.5
+ */
+ public T element()
+ {
+ return getFirst();
+ }
+
+ /**
+ * Returns the first element of the list without removing
+ * it.
+ *
+ * @return the first element of the list, or <code>null</code>
+ * if the list is empty.
+ * @since 1.5
+ */
+ public T peek()
+ {
+ if (size == 0)
+ return null;
+ return getFirst();
+ }
+
+ /**
+ * Removes and returns the first element of the list.
+ *
+ * @return the first element of the list, or <code>null</code>
+ * if the list is empty.
+ * @since 1.5
+ */
+ public T poll()
+ {
+ if (size == 0)
+ return null;
+ return removeFirst();
+ }
+
+ /**
+ * Removes and returns the first element of the list.
+ *
+ * @return the first element of the list.
+ * @throws NoSuchElementException if the list is empty.
+ * @since 1.5
+ */
+ public T remove()
+ {
+ return removeFirst();
+ }
+
+ /**
+ * Serializes this object to the given stream.
+ *
+ * @param s the stream to write to
+ * @throws IOException if the underlying stream fails
+ * @serialData the size of the list (int), followed by all the elements
+ * (Object) in proper order
+ */
+ private void writeObject(ObjectOutputStream s) throws IOException
+ {
+ s.defaultWriteObject();
+ s.writeInt(size);
+ Entry<T> e = first;
+ while (e != null)
+ {
+ s.writeObject(e.data);
+ e = e.next;
+ }
+ }
+
+ /**
+ * Deserializes this object from the given stream.
+ *
+ * @param s the stream to read from
+ * @throws ClassNotFoundException if the underlying stream fails
+ * @throws IOException if the underlying stream fails
+ * @serialData the size of the list (int), followed by all the elements
+ * (Object) in proper order
+ */
+ private void readObject(ObjectInputStream s)
+ throws IOException, ClassNotFoundException
+ {
+ s.defaultReadObject();
+ int i = s.readInt();
+ while (--i >= 0)
+ addLastEntry(new Entry<T>((T) s.readObject()));
+ }
+
+ /**
+ * A ListIterator over the list. This class keeps track of its
+ * position in the list and the two list entries it is between.
+ *
+ * @author Original author unknown
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private final class LinkedListItr<I>
+ implements ListIterator<I>
+ {
+ /** Number of modifications we know about. */
+ private int knownMod = modCount;
+
+ /** Entry that will be returned by next(). */
+ private Entry<I> next;
+
+ /** Entry that will be returned by previous(). */
+ private Entry<I> previous;
+
+ /** Entry that will be affected by remove() or set(). */
+ private Entry<I> lastReturned;
+
+ /** Index of `next'. */
+ private int position;
+
+ /**
+ * Initialize the iterator.
+ *
+ * @param index the initial index
+ */
+ LinkedListItr(int index)
+ {
+ if (index == size)
+ {
+ next = null;
+ previous = (Entry<I>) last;
+ }
+ else
+ {
+ next = (Entry<I>) getEntry(index);
+ previous = next.previous;
+ }
+ position = index;
+ }
+
+ /**
+ * Checks for iterator consistency.
+ *
+ * @throws ConcurrentModificationException if the list was modified
+ */
+ private void checkMod()
+ {
+ if (knownMod != modCount)
+ throw new ConcurrentModificationException();
+ }
+
+ /**
+ * Returns the index of the next element.
+ *
+ * @return the next index
+ */
+ public int nextIndex()
+ {
+ return position;
+ }
+
+ /**
+ * Returns the index of the previous element.
+ *
+ * @return the previous index
+ */
+ public int previousIndex()
+ {
+ return position - 1;
+ }
+
+ /**
+ * Returns true if more elements exist via next.
+ *
+ * @return true if next will succeed
+ */
+ public boolean hasNext()
+ {
+ return (next != null);
+ }
+
+ /**
+ * Returns true if more elements exist via previous.
+ *
+ * @return true if previous will succeed
+ */
+ public boolean hasPrevious()
+ {
+ return (previous != null);
+ }
+
+ /**
+ * Returns the next element.
+ *
+ * @return the next element
+ * @throws ConcurrentModificationException if the list was modified
+ * @throws NoSuchElementException if there is no next
+ */
+ public I next()
+ {
+ checkMod();
+ if (next == null)
+ throw new NoSuchElementException();
+ position++;
+ lastReturned = previous = next;
+ next = lastReturned.next;
+ return lastReturned.data;
+ }
+
+ /**
+ * Returns the previous element.
+ *
+ * @return the previous element
+ * @throws ConcurrentModificationException if the list was modified
+ * @throws NoSuchElementException if there is no previous
+ */
+ public I previous()
+ {
+ checkMod();
+ if (previous == null)
+ throw new NoSuchElementException();
+ position--;
+ lastReturned = next = previous;
+ previous = lastReturned.previous;
+ return lastReturned.data;
+ }
+
+ /**
+ * Remove the most recently returned element from the list.
+ *
+ * @throws ConcurrentModificationException if the list was modified
+ * @throws IllegalStateException if there was no last element
+ */
+ public void remove()
+ {
+ checkMod();
+ if (lastReturned == null)
+ throw new IllegalStateException();
+
+ // Adjust the position to before the removed element, if the element
+ // being removed is behind the cursor.
+ if (lastReturned == previous)
+ position--;
+
+ next = lastReturned.next;
+ previous = lastReturned.previous;
+ removeEntry((Entry<T>) lastReturned);
+ knownMod++;
+
+ lastReturned = null;
+ }
+
+ /**
+ * Adds an element between the previous and next, and advance to the next.
+ *
+ * @param o the element to add
+ * @throws ConcurrentModificationException if the list was modified
+ */
+ public void add(I o)
+ {
+ checkMod();
+ modCount++;
+ knownMod++;
+ size++;
+ position++;
+ Entry<I> e = new Entry<I>(o);
+ e.previous = previous;
+ e.next = next;
+
+ if (previous != null)
+ previous.next = e;
+ else
+ first = (Entry<T>) e;
+
+ if (next != null)
+ next.previous = e;
+ else
+ last = (Entry<T>) e;
+
+ previous = e;
+ lastReturned = null;
+ }
+
+ /**
+ * Changes the contents of the element most recently returned.
+ *
+ * @param o the new element
+ * @throws ConcurrentModificationException if the list was modified
+ * @throws IllegalStateException if there was no last element
+ */
+ public void set(I o)
+ {
+ checkMod();
+ if (lastReturned == null)
+ throw new IllegalStateException();
+ lastReturned.data = o;
+ }
+ } // class LinkedListItr
+
+ /**
+ * Obtain an Iterator over this list in reverse sequential order.
+ *
+ * @return an Iterator over the elements of the list in
+ * reverse order.
+ * @since 1.6
+ */
+ public Iterator<T> descendingIterator()
+ {
+ return new Iterator<T>()
+ {
+ /** Number of modifications we know about. */
+ private int knownMod = modCount;
+
+ /** Entry that will be returned by next(). */
+ private Entry<T> next = last;
+
+ /** Entry that will be affected by remove() or set(). */
+ private Entry<T> lastReturned;
+
+ /** Index of `next'. */
+ private int position = size() - 1;
+
+ // This will get inlined, since it is private.
+ /**
+ * Checks for modifications made to the list from
+ * elsewhere while iteration is in progress.
+ *
+ * @throws ConcurrentModificationException if the
+ * list has been modified elsewhere.
+ */
+ private void checkMod()
+ {
+ if (knownMod != modCount)
+ throw new ConcurrentModificationException();
+ }
+
+ /**
+ * Tests to see if there are any more objects to
+ * return.
+ *
+ * @return true if the start of the list has not yet been
+ * reached.
+ */
+ public boolean hasNext()
+ {
+ return next != null;
+ }
+
+ /**
+ * Retrieves the next object from the list.
+ *
+ * @return The next object.
+ * @throws NoSuchElementException if there are
+ * no more objects to retrieve.
+ * @throws ConcurrentModificationException if the
+ * list has been modified elsewhere.
+ */
+ public T next()
+ {
+ checkMod();
+ if (next == null)
+ throw new NoSuchElementException();
+ --position;
+ lastReturned = next;
+ next = lastReturned.previous;
+ return lastReturned.data;
+ }
+
+ /**
+ * Removes the last object retrieved by <code>next()</code>
+ * from the list, if the list supports object removal.
+ *
+ * @throws ConcurrentModificationException if the list
+ * has been modified elsewhere.
+ * @throws IllegalStateException if the iterator is positioned
+ * before the start of the list or the last object has already
+ * been removed.
+ */
+ public void remove()
+ {
+ checkMod();
+ if (lastReturned == null)
+ throw new IllegalStateException();
+ removeEntry(lastReturned);
+ lastReturned = null;
+ ++knownMod;
+ }
+ };
+ }
+
+ /**
+ * Inserts the specified element at the front of the list.
+ *
+ * @param value the element to insert.
+ * @return true.
+ * @since 1.6
+ */
+ public boolean offerFirst(T value)
+ {
+ addFirst(value);
+ return true;
+ }
+
+ /**
+ * Inserts the specified element at the end of the list.
+ *
+ * @param value the element to insert.
+ * @return true.
+ * @since 1.6
+ */
+ public boolean offerLast(T value)
+ {
+ return add(value);
+ }
+
+ /**
+ * Returns the first element of the list without removing
+ * it.
+ *
+ * @return the first element of the list, or <code>null</code>
+ * if the list is empty.
+ * @since 1.6
+ */
+ public T peekFirst()
+ {
+ return peek();
+ }
+
+ /**
+ * Returns the last element of the list without removing
+ * it.
+ *
+ * @return the last element of the list, or <code>null</code>
+ * if the list is empty.
+ * @since 1.6
+ */
+ public T peekLast()
+ {
+ if (size == 0)
+ return null;
+ return getLast();
+ }
+
+ /**
+ * Removes and returns the first element of the list.
+ *
+ * @return the first element of the list, or <code>null</code>
+ * if the list is empty.
+ * @since 1.6
+ */
+ public T pollFirst()
+ {
+ return poll();
+ }
+
+ /**
+ * Removes and returns the last element of the list.
+ *
+ * @return the last element of the list, or <code>null</code>
+ * if the list is empty.
+ * @since 1.6
+ */
+ public T pollLast()
+ {
+ if (size == 0)
+ return null;
+ return removeLast();
+ }
+
+ /**
+ * Pops an element from the stack by removing and returning
+ * the first element in the list. This is equivalent to
+ * calling {@link #removeFirst()}.
+ *
+ * @return the top of the stack, which is the first element
+ * of the list.
+ * @throws NoSuchElementException if the list is empty.
+ * @since 1.6
+ * @see #removeFirst()
+ */
+ public T pop()
+ {
+ return removeFirst();
+ }
+
+ /**
+ * Pushes an element on to the stack by adding it to the
+ * front of the list. This is equivalent to calling
+ * {@link #addFirst(T)}.
+ *
+ * @param value the element to push on to the stack.
+ * @throws NoSuchElementException if the list is empty.
+ * @since 1.6
+ * @see #addFirst(T)
+ */
+ public void push(T value)
+ {
+ addFirst(value);
+ }
+
+ /**
+ * Removes the first occurrence of the specified element
+ * from the list, when traversing the list from head to
+ * tail. The list is unchanged if the element is not found.
+ * This is equivalent to calling {@link #remove(Object)}.
+ *
+ * @param o the element to remove.
+ * @return true if an instance of the object was removed.
+ * @since 1.6
+ */
+ public boolean removeFirstOccurrence(Object o)
+ {
+ return remove(o);
+ }
+
+ /**
+ * Removes the last occurrence of the specified element
+ * from the list, when traversing the list from head to
+ * tail. The list is unchanged if the element is not found.
+ *
+ * @param o the element to remove.
+ * @return true if an instance of the object was removed.
+ * @since 1.6
+ */
+ public boolean removeLastOccurrence(Object o)
+ {
+ Entry<T> e = last;
+ while (e != null)
+ {
+ if (equals(o, e.data))
+ {
+ removeEntry(e);
+ return true;
+ }
+ e = e.previous;
+ }
+ return false;
+ }
+
+}
diff --git a/libjava/classpath/java/util/List.java b/libjava/classpath/java/util/List.java
new file mode 100644
index 000000000..3a437a5a2
--- /dev/null
+++ b/libjava/classpath/java/util/List.java
@@ -0,0 +1,451 @@
+/* List.java -- An ordered collection which allows indexed access
+ Copyright (C) 1998, 2001, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * An ordered collection (also known as a list). This collection allows
+ * access to elements by position, as well as control on where elements
+ * are inserted. Unlike sets, duplicate elements are permitted by this
+ * general contract (if a subclass forbids duplicates, this should be
+ * documented).
+ * <p>
+ *
+ * List places additional requirements on <code>iterator</code>,
+ * <code>add</code>, <code>remove</code>, <code>equals</code>, and
+ * <code>hashCode</code>, in addition to requiring more methods. List
+ * indexing is 0-based (like arrays), although some implementations may
+ * require time proportional to the index to obtain an arbitrary element.
+ * The List interface is incompatible with Set; you cannot implement both
+ * simultaneously.
+ * <p>
+ *
+ * Lists also provide a <code>ListIterator</code> which allows bidirectional
+ * traversal and other features atop regular iterators. Lists can be
+ * searched for arbitrary elements, and allow easy insertion and removal
+ * of multiple elements in one method call.
+ * <p>
+ *
+ * Note: While lists may contain themselves as elements, this leads to
+ * undefined (usually infinite recursive) behavior for some methods like
+ * hashCode or equals.
+ *
+ * @author Original author unknown
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Collection
+ * @see Set
+ * @see ArrayList
+ * @see LinkedList
+ * @see Vector
+ * @see Arrays#asList(Object[])
+ * @see Collections#nCopies(int, Object)
+ * @see Collections#EMPTY_LIST
+ * @see AbstractList
+ * @see AbstractSequentialList
+ * @since 1.2
+ * @status updated to 1.4
+ */
+public interface List<E> extends Collection<E>
+{
+ /**
+ * Insert an element into the list at a given position (optional operation).
+ * This shifts all existing elements from that position to the end one
+ * index to the right. This version of add has no return, since it is
+ * assumed to always succeed if there is no exception.
+ *
+ * @param index the location to insert the item
+ * @param o the object to insert
+ * @throws UnsupportedOperationException if this list does not support the
+ * add operation
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ * @throws ClassCastException if o cannot be added to this list due to its
+ * type
+ * @throws IllegalArgumentException if o cannot be added to this list for
+ * some other reason
+ * @throws NullPointerException if o is null and this list doesn't support
+ * the addition of null values.
+ */
+ void add(int index, E o);
+
+ /**
+ * Add an element to the end of the list (optional operation). If the list
+ * imposes restraints on what can be inserted, such as no null elements,
+ * this should be documented.
+ *
+ * @param o the object to add
+ * @return true, as defined by Collection for a modified list
+ * @throws UnsupportedOperationException if this list does not support the
+ * add operation
+ * @throws ClassCastException if o cannot be added to this list due to its
+ * type
+ * @throws IllegalArgumentException if o cannot be added to this list for
+ * some other reason
+ * @throws NullPointerException if o is null and this list doesn't support
+ * the addition of null values.
+ */
+ boolean add(E o);
+
+ /**
+ * Insert the contents of a collection into the list at a given position
+ * (optional operation). Shift all elements at that position to the right
+ * by the number of elements inserted. This operation is undefined if
+ * this list is modified during the operation (for example, if you try
+ * to insert a list into itself).
+ *
+ * @param index the location to insert the collection
+ * @param c the collection to insert
+ * @return true if the list was modified by this action, that is, if c is
+ * non-empty
+ * @throws UnsupportedOperationException if this list does not support the
+ * addAll operation
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ * @throws ClassCastException if some element of c cannot be added to this
+ * list due to its type
+ * @throws IllegalArgumentException if some element of c cannot be added
+ * to this list for some other reason
+ * @throws NullPointerException if some element of c is null and this list
+ * doesn't support the addition of null values.
+ * @throws NullPointerException if the specified collection is null
+ * @see #add(int, Object)
+ */
+ boolean addAll(int index, Collection<? extends E> c);
+
+ /**
+ * Add the contents of a collection to the end of the list (optional
+ * operation). This operation is undefined if this list is modified
+ * during the operation (for example, if you try to insert a list into
+ * itself).
+ *
+ * @param c the collection to add
+ * @return true if the list was modified by this action, that is, if c is
+ * non-empty
+ * @throws UnsupportedOperationException if this list does not support the
+ * addAll operation
+ * @throws ClassCastException if some element of c cannot be added to this
+ * list due to its type
+ * @throws IllegalArgumentException if some element of c cannot be added
+ * to this list for some other reason
+ * @throws NullPointerException if the specified collection is null
+ * @throws NullPointerException if some element of c is null and this list
+ * doesn't support the addition of null values.
+ * @see #add(Object)
+ */
+ boolean addAll(Collection<? extends E> c);
+
+ /**
+ * Clear the list, such that a subsequent call to isEmpty() would return
+ * true (optional operation).
+ *
+ * @throws UnsupportedOperationException if this list does not support the
+ * clear operation
+ */
+ void clear();
+
+ /**
+ * Test whether this list contains a given object as one of its elements.
+ * This is defined as the existence of an element e such that
+ * <code>o == null ? e == null : o.equals(e)</code>.
+ *
+ * @param o the element to look for
+ * @return true if this list contains the element
+ * @throws ClassCastException if the type of o is not a valid type
+ * for this list.
+ * @throws NullPointerException if o is null and the list doesn't
+ * support null values.
+ */
+ boolean contains(Object o);
+
+ /**
+ * Test whether this list contains every element in a given collection.
+ *
+ * @param c the collection to test for
+ * @return true if for every element o in c, contains(o) would return true
+ * @throws NullPointerException if the collection is null
+ * @throws ClassCastException if the type of any element in c is not a valid
+ * type for this list.
+ * @throws NullPointerException if some element of c is null and this
+ * list does not support null values.
+ * @see #contains(Object)
+ */
+ boolean containsAll(Collection<?> c);
+
+ /**
+ * Test whether this list is equal to another object. A List is defined to be
+ * equal to an object if and only if that object is also a List, and the two
+ * lists have the same sequence. Two lists l1 and l2 are equal if and only
+ * if <code>l1.size() == l2.size()</code>, and for every integer n between 0
+ * and <code>l1.size() - 1</code> inclusive, <code>l1.get(n) == null ?
+ * l2.get(n) == null : l1.get(n).equals(l2.get(n))</code>.
+ *
+ * @param o the object to test for equality with this list
+ * @return true if o is equal to this list
+ * @see Object#equals(Object)
+ * @see #hashCode()
+ */
+ boolean equals(Object o);
+
+ /**
+ * Get the element at a given index in this list.
+ *
+ * @param index the index of the element to be returned
+ * @return the element at index index in this list
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ */
+ E get(int index);
+
+ /**
+ * 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()
+ * @see #equals(Object)
+ */
+ int hashCode();
+
+ /**
+ * Obtain the first index at which a given object is to be found in this
+ * list.
+ *
+ * @param o the object to search for
+ * @return the least integer n such that <code>o == null ? get(n) == null :
+ * o.equals(get(n))</code>, or -1 if there is no such index.
+ * @throws ClassCastException if the type of o is not a valid
+ * type for this list.
+ * @throws NullPointerException if o is null and this
+ * list does not support null values.
+ */
+ int indexOf(Object o);
+
+ /**
+ * Test whether this list is empty, that is, if size() == 0.
+ *
+ * @return true if this list contains no elements
+ */
+ boolean isEmpty();
+
+ /**
+ * Obtain an Iterator over this list, whose sequence is the list order.
+ *
+ * @return an Iterator over the elements of this list, in order
+ */
+ Iterator<E> iterator();
+
+ /**
+ * Obtain the last index at which a given object is to be found in this
+ * list.
+ *
+ * @return the greatest integer n such that <code>o == null ? get(n) == null
+ * : o.equals(get(n))</code>, or -1 if there is no such index.
+ * @throws ClassCastException if the type of o is not a valid
+ * type for this list.
+ * @throws NullPointerException if o is null and this
+ * list does not support null values.
+ */
+ int lastIndexOf(Object o);
+
+ /**
+ * Obtain a ListIterator over this list, starting at the beginning.
+ *
+ * @return a ListIterator over the elements of this list, in order, starting
+ * at the beginning
+ */
+ ListIterator<E> listIterator();
+
+ /**
+ * Obtain a ListIterator over this list, starting at a given position.
+ * A first call to next() would return the same as get(index), and a
+ * first call to previous() would return the same as get(index - 1).
+ *
+ * @param index the position, between 0 and size() inclusive, to begin the
+ * iteration from
+ * @return a ListIterator over the elements of this list, in order, starting
+ * at index
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ */
+ ListIterator<E> listIterator(int index);
+
+ /**
+ * Remove the element at a given position in this list (optional operation).
+ * Shifts all remaining elements to the left to fill the gap.
+ *
+ * @param index the position within the list of the object to remove
+ * @return the object that was removed
+ * @throws UnsupportedOperationException if this list does not support the
+ * remove operation
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ */
+ E remove(int index);
+
+ /**
+ * Remove the first occurence of an object from this list (optional
+ * operation). That is, remove the first element e such that
+ * <code>o == null ? e == null : o.equals(e)</code>.
+ *
+ * @param o the object to remove
+ * @return true if the list changed as a result of this call, that is, if
+ * the list contained at least one occurrence of o
+ * @throws UnsupportedOperationException if this list does not support the
+ * remove operation
+ * @throws ClassCastException if the type of o is not a valid
+ * type for this list.
+ * @throws NullPointerException if o is null and this
+ * list does not support removing null values.
+ */
+ boolean remove(Object o);
+
+ /**
+ * Remove all elements of a given collection from this list (optional
+ * operation). That is, remove every element e such that c.contains(e).
+ *
+ * @param c the collection to filter out
+ * @return true if this list was modified as a result of this call
+ * @throws UnsupportedOperationException if this list does not support the
+ * removeAll operation
+ * @throws NullPointerException if the collection is null
+ * @throws ClassCastException if the type of any element in c is not a valid
+ * type for this list.
+ * @throws NullPointerException if some element of c is null and this
+ * list does not support removing null values.
+ * @see #remove(Object)
+ * @see #contains(Object)
+ */
+ boolean removeAll(Collection<?> c);
+
+ /**
+ * Remove all elements of this list that are not contained in a given
+ * collection (optional operation). That is, remove every element e such
+ * that !c.contains(e).
+ *
+ * @param c the collection to retain
+ * @return true if this list was modified as a result of this call
+ * @throws UnsupportedOperationException if this list does not support the
+ * retainAll operation
+ * @throws NullPointerException if the collection is null
+ * @throws ClassCastException if the type of any element in c is not a valid
+ * type for this list.
+ * @throws NullPointerException if some element of c is null and this
+ * list does not support retaining null values.
+ * @see #remove(Object)
+ * @see #contains(Object)
+ */
+ boolean retainAll(Collection<?> c);
+
+ /**
+ * Replace an element of this list with another object (optional operation).
+ *
+ * @param index the position within this list of the element to be replaced
+ * @param o the object to replace it with
+ * @return the object that was replaced
+ * @throws UnsupportedOperationException if this list does not support the
+ * set operation
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ * @throws ClassCastException if o cannot be added to this list due to its
+ * type
+ * @throws IllegalArgumentException if o cannot be added to this list for
+ * some other reason
+ * @throws NullPointerException if o is null and this
+ * list does not support null values.
+ */
+ E set(int index, E o);
+
+ /**
+ * Get the number of elements in this list. If the list contains more
+ * than Integer.MAX_VALUE elements, return Integer.MAX_VALUE.
+ *
+ * @return the number of elements in the list
+ */
+ int size();
+
+ /**
+ * Obtain a List view of a subsection of this list, from fromIndex
+ * (inclusive) to toIndex (exclusive). If the two indices are equal, the
+ * sublist is empty. The returned list should be modifiable if and only
+ * if this list is modifiable. Changes to the returned list should be
+ * reflected in this list. If this list is structurally modified in
+ * any way other than through the returned list, the result of any subsequent
+ * operations on the returned list is undefined.
+ *
+ * @param fromIndex the index that the returned list should start from
+ * (inclusive)
+ * @param toIndex the index that the returned list should go to (exclusive)
+ * @return a List backed by a subsection of this list
+ * @throws IndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; size() || fromIndex &gt; toIndex
+ */
+ List<E> subList(int fromIndex, int toIndex);
+
+ /**
+ * Copy the current contents of this list into an array.
+ *
+ * @return an array of type Object[] and length equal to the length of this
+ * list, containing the elements currently in this list, in order
+ */
+ Object[] toArray();
+
+ /**
+ * Copy the current contents of this list into an array. If the array passed
+ * as an argument has length less than that of this list, an array of the
+ * same run-time type as a, and length equal to the length of this list, is
+ * allocated using Reflection. Otherwise, a itself is used. The elements of
+ * this list are copied into it, and if there is space in the array, the
+ * following element is set to null. The resultant array is returned.
+ * Note: The fact that the following element is set to null is only useful
+ * if it is known that this list does not contain any null elements.
+ *
+ * @param a the array to copy this list into
+ * @return an array containing the elements currently in this list, in
+ * order
+ * @throws ArrayStoreException if the type of any element of the
+ * collection is not a subtype of the element type of a
+ * @throws NullPointerException if the specified array is null
+ */
+ <T> T[] toArray(T[] a);
+}
diff --git a/libjava/classpath/java/util/ListIterator.java b/libjava/classpath/java/util/ListIterator.java
new file mode 100644
index 000000000..9b74528c5
--- /dev/null
+++ b/libjava/classpath/java/util/ListIterator.java
@@ -0,0 +1,170 @@
+/* ListIterator.java -- Extended Iterator for iterating over ordered lists
+ Copyright (C) 1998, 1999, 2001, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * An extended version of Iterator to support the extra features of Lists. The
+ * elements may be accessed in forward or reverse order, elements may be
+ * replaced as well as removed, and new elements may be inserted, during the
+ * traversal of the list.
+ * <p>
+ *
+ * A list with n elements provides n+1 iterator positions (the front, the end,
+ * or between two elements). Note that <code>remove</code> and <code>set</code>
+ * operate on the last element returned, whether it was by <code>next</code>
+ * or <code>previous</code>.
+ *
+ * @author Original author unknown
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Collection
+ * @see List
+ * @see Iterator
+ * @see Enumeration
+ * @since 1.2
+ * @status updated to 1.4
+ */
+public interface ListIterator<E> extends Iterator<E>
+{
+ /**
+ * Tests whether there are elements remaining in the list in the forward
+ * direction. In other words, next() will not fail with a
+ * NoSuchElementException.
+ *
+ * @return true if the list continues in the forward direction
+ */
+ boolean hasNext();
+
+ /**
+ * Tests whether there are elements remaining in the list in the reverse
+ * direction. In other words, previous() will not fail with a
+ * NoSuchElementException.
+ *
+ * @return true if the list continues in the reverse direction
+ */
+ boolean hasPrevious();
+
+ /**
+ * Obtain the next element in the list in the forward direction. Repeated
+ * calls to next may be used to iterate over the entire list, or calls to
+ * next and previous may be used together to go forwards and backwards.
+ * Alternating calls to next and previous will return the same element.
+ *
+ * @return the next element in the list in the forward direction
+ * @throws NoSuchElementException if there are no more elements
+ */
+ E next();
+
+ /**
+ * Obtain the next element in the list in the reverse direction. Repeated
+ * calls to previous may be used to iterate backwards over the entire list,
+ * or calls to next and previous may be used together to go forwards and
+ * backwards. Alternating calls to next and previous will return the same
+ * element.
+ *
+ * @return the next element in the list in the reverse direction
+ * @throws NoSuchElementException if there are no more elements
+ */
+ E previous();
+
+ /**
+ * Find the index of the element that would be returned by a call to next.
+ * If hasNext() returns false, this returns the list size.
+ *
+ * @return the index of the element that would be returned by next()
+ */
+ int nextIndex();
+
+ /**
+ * Find the index of the element that would be returned by a call to
+ * previous. If hasPrevious() returns false, this returns -1.
+ *
+ * @return the index of the element that would be returned by previous()
+ */
+ int previousIndex();
+
+ /**
+ * Insert an element into the list at the current position of the iterator
+ * (optional operation). The element is inserted in between the element that
+ * would be returned by previous and the element that would be returned by
+ * next. After the insertion, a subsequent call to next is unaffected, but
+ * a call to previous returns the item that was added. The values returned
+ * by nextIndex() and previousIndex() are incremented.
+ *
+ * @param o the object to insert into the list
+ * @throws ClassCastException if the object is of a type which cannot be added
+ * to this list.
+ * @throws IllegalArgumentException if some other aspect of the object stops
+ * it being added to this list.
+ * @throws UnsupportedOperationException if this ListIterator does not
+ * support the add operation.
+ */
+ void add(E o);
+
+ /**
+ * Remove from the list the element last returned by a call to next or
+ * previous (optional operation). This method may only be called if neither
+ * add nor remove have been called since the last call to next or previous.
+ *
+ * @throws IllegalStateException if neither next or previous have been
+ * called, or if add or remove has been called since the last call
+ * to next or previous
+ * @throws UnsupportedOperationException if this ListIterator does not
+ * support the remove operation
+ */
+ void remove();
+
+ /**
+ * Replace the element last returned by a call to next or previous with a
+ * given object (optional operation). This method may only be called if
+ * neither add nor remove have been called since the last call to next or
+ * previous.
+ *
+ * @param o the object to replace the element with
+ * @throws ClassCastException the object is of a type which cannot be added
+ * to this list
+ * @throws IllegalArgumentException some other aspect of the object stops
+ * it being added to this list
+ * @throws IllegalStateException if neither next or previous have been
+ * called, or if add or remove has been called since the last call
+ * to next or previous
+ * @throws UnsupportedOperationException if this ListIterator does not
+ * support the set operation
+ */
+ void set(E o);
+}
diff --git a/libjava/classpath/java/util/ListResourceBundle.java b/libjava/classpath/java/util/ListResourceBundle.java
new file mode 100644
index 000000000..2e48a22b5
--- /dev/null
+++ b/libjava/classpath/java/util/ListResourceBundle.java
@@ -0,0 +1,140 @@
+/* ListResourceBundle -- a resource bundle build around a list
+ Copyright (C) 1998, 1999, 2001, 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * 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
+ * @since 1.1
+ * @status updated to 1.4
+ */
+public abstract class ListResourceBundle extends ResourceBundle
+{
+ /**
+ * The constructor. It does nothing special.
+ */
+ public ListResourceBundle()
+ {
+ }
+
+ /**
+ * 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();
+ 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
+ */
+ public Enumeration<String> 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();
+ Set<String> s = new HashSet<String>();
+ int i = contents.length;
+ while (--i >= 0)
+ s.add((String) contents[i][0]);
+ ResourceBundle bundle = parent;
+ // Eliminate tail recursion.
+ while (bundle != null)
+ {
+ Enumeration<String> 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
diff --git a/libjava/classpath/java/util/Locale.java b/libjava/classpath/java/util/Locale.java
new file mode 100644
index 000000000..ef111737d
--- /dev/null
+++ b/libjava/classpath/java/util/Locale.java
@@ -0,0 +1,1029 @@
+/* Locale.java -- i18n locales
+ Copyright (C) 1998, 1999, 2001, 2002, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.classpath.SystemProperties;
+
+import gnu.java.lang.CPStringBuilder;
+
+import gnu.java.locale.LocaleHelper;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+import java.util.spi.LocaleNameProvider;
+
+/**
+ * Locales represent a specific country and culture. Classes which can be
+ * passed a Locale object tailor their information for a given locale. For
+ * instance, currency number formatting is handled differently for the USA
+ * and France.
+ *
+ * <p>Locales are made up of a language code, a country code, and an optional
+ * set of variant strings. Language codes are represented by
+ * <a href="http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt">
+ * ISO 639:1988</a> w/ additions from ISO 639/RA Newsletter No. 1/1989
+ * and a decision of the Advisory Committee of ISO/TC39 on August 8, 1997.
+ *
+ * <p>Country codes are represented by
+ * <a href="http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html">
+ * ISO 3166</a>. Variant strings are vendor and browser specific. Standard
+ * variant strings include "POSIX" for POSIX, "WIN" for MS-Windows, and
+ * "MAC" for Macintosh. When there is more than one variant string, they must
+ * be separated by an underscore (U+005F).
+ *
+ * <p>The default locale is determined by the values of the system properties
+ * user.language, user.country (or user.region), and user.variant, defaulting
+ * to "en_US". Note that the locale does NOT contain the conversion and
+ * formatting capabilities (for that, use ResourceBundle and java.text).
+ * Rather, it is an immutable tag object for identifying a given locale, which
+ * is referenced by these other classes when they must make locale-dependent
+ * decisions.
+ *
+ * @see ResourceBundle
+ * @see java.text.Format
+ * @see java.text.NumberFormat
+ * @see java.text.Collator
+ * @author Jochen Hoenicke
+ * @author Paul Fisher
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.1
+ * @status updated to 1.4
+ */
+public final class Locale implements Serializable, Cloneable
+{
+ /** Locale which represents the English language. */
+ public static final Locale ENGLISH = getLocale("en");
+
+ /** Locale which represents the French language. */
+ public static final Locale FRENCH = getLocale("fr");
+
+ /** Locale which represents the German language. */
+ public static final Locale GERMAN = getLocale("de");
+
+ /** Locale which represents the Italian language. */
+ public static final Locale ITALIAN = getLocale("it");
+
+ /** Locale which represents the Japanese language. */
+ public static final Locale JAPANESE = getLocale("ja");
+
+ /** Locale which represents the Korean language. */
+ public static final Locale KOREAN = getLocale("ko");
+
+ /** Locale which represents the Chinese language. */
+ public static final Locale CHINESE = getLocale("zh");
+
+ /** Locale which represents the Chinese language as used in China. */
+ public static final Locale SIMPLIFIED_CHINESE = getLocale("zh", "CN");
+
+ /**
+ * Locale which represents the Chinese language as used in Taiwan.
+ * Same as TAIWAN Locale.
+ */
+ public static final Locale TRADITIONAL_CHINESE = getLocale("zh", "TW");
+
+ /** Locale which represents France. */
+ public static final Locale FRANCE = getLocale("fr", "FR");
+
+ /** Locale which represents Germany. */
+ public static final Locale GERMANY = getLocale("de", "DE");
+
+ /** Locale which represents Italy. */
+ public static final Locale ITALY = getLocale("it", "IT");
+
+ /** Locale which represents Japan. */
+ public static final Locale JAPAN = getLocale("ja", "JP");
+
+ /** Locale which represents Korea. */
+ public static final Locale KOREA = getLocale("ko", "KR");
+
+ /**
+ * Locale which represents China.
+ * Same as SIMPLIFIED_CHINESE Locale.
+ */
+ public static final Locale CHINA = SIMPLIFIED_CHINESE;
+
+ /**
+ * Locale which represents the People's Republic of China.
+ * Same as CHINA Locale.
+ */
+ public static final Locale PRC = CHINA;
+
+ /**
+ * Locale which represents Taiwan.
+ * Same as TRADITIONAL_CHINESE Locale.
+ */
+ public static final Locale TAIWAN = TRADITIONAL_CHINESE;
+
+ /** Locale which represents the United Kingdom. */
+ public static final Locale UK = getLocale("en", "GB");
+
+ /** Locale which represents the United States. */
+ public static final Locale US = getLocale("en", "US");
+
+ /** Locale which represents the English speaking portion of Canada. */
+ public static final Locale CANADA = getLocale("en", "CA");
+
+ /** Locale which represents the French speaking portion of Canada. */
+ public static final Locale CANADA_FRENCH = getLocale("fr", "CA");
+
+ /** The root locale, used as the base case in lookups by
+ * locale-sensitive operations.
+ */
+ public static final Locale ROOT = new Locale("","","");
+
+ /**
+ * Compatible with JDK 1.1+.
+ */
+ private static final long serialVersionUID = 9149081749638150636L;
+
+ /**
+ * The language code, as returned by getLanguage().
+ *
+ * @serial the languange, possibly ""
+ */
+ private final String language;
+
+ /**
+ * The country code, as returned by getCountry().
+ *
+ * @serial the country, possibly ""
+ */
+ private final String country;
+
+ /**
+ * The variant code, as returned by getVariant().
+ *
+ * @serial the variant, possibly ""
+ */
+ private final String variant;
+
+ /**
+ * This is the cached hashcode. When writing to stream, we write -1.
+ *
+ * @serial should be -1 in serial streams
+ */
+ private int hashcode;
+
+ /**
+ * Array storing all available locales.
+ */
+ private static transient Locale[] availableLocales;
+
+ /**
+ * Locale cache. Only created locale objects are stored.
+ * Contains all supported locales when getAvailableLocales()
+ * got called.
+ */
+ private static transient HashMap localeMap;
+
+ /**
+ * The default locale. Except for during bootstrapping, this should never be
+ * null. Note the logic in the main constructor, to detect when
+ * bootstrapping has completed.
+ */
+ private static Locale defaultLocale;
+
+ static {
+ String language = SystemProperties.getProperty("user.language", "en");
+ String country = SystemProperties.getProperty("user.country", "US");
+ String region = SystemProperties.getProperty("user.region", null);
+ String variant = SystemProperties.getProperty("user.variant", "");
+
+ defaultLocale = getLocale(language,
+ (region != null) ? region : country,
+ variant);
+ }
+
+ /**
+ * Array storing all the available two-letter ISO639 languages.
+ */
+ private static transient String[] languageCache;
+
+ /**
+ * Array storing all the available two-letter ISO3166 country codes.
+ */
+ private static transient String[] countryCache;
+
+ /**
+ * Retrieves the locale with the specified language from the cache.
+ *
+ * @param language the language of the locale to retrieve.
+ * @return the locale.
+ */
+ private static Locale getLocale(String language)
+ {
+ return getLocale(language, "", "");
+ }
+
+ /**
+ * Retrieves the locale with the specified language and country
+ * from the cache.
+ *
+ * @param language the language of the locale to retrieve.
+ * @param country the country of the locale to retrieve.
+ * @return the locale.
+ */
+ private static Locale getLocale(String language, String country)
+ {
+ return getLocale(language, country, "");
+ }
+
+ /**
+ * Retrieves the locale with the specified language, country
+ * and variant from the cache.
+ *
+ * @param language the language of the locale to retrieve.
+ * @param country the country of the locale to retrieve.
+ * @param variant the variant of the locale to retrieve.
+ * @return the locale.
+ */
+ private static Locale getLocale(String language, String country, String variant)
+ {
+ if (localeMap == null)
+ localeMap = new HashMap(256);
+
+ String name = language + "_" + country + "_" + variant;
+ Locale locale = (Locale) localeMap.get(name);
+
+ if (locale == null)
+ {
+ locale = new Locale(language, country, variant);
+ localeMap.put(name, locale);
+ }
+
+ return locale;
+ }
+
+ /**
+ * Convert new iso639 codes to the old ones.
+ *
+ * @param language the language to check
+ * @return the appropriate code
+ */
+ private String convertLanguage(String language)
+ {
+ if (language.equals(""))
+ return language;
+ language = language.toLowerCase();
+ int index = "he,id,yi".indexOf(language);
+ if (index != -1)
+ return "iw,in,ji".substring(index, index + 2);
+ return language;
+ }
+
+ /**
+ * Creates a new locale for the given language and country.
+ *
+ * @param language lowercase two-letter ISO-639 A2 language code
+ * @param country uppercase two-letter ISO-3166 A2 contry code
+ * @param variant vendor and browser specific
+ * @throws NullPointerException if any argument is null
+ */
+ public Locale(String language, String country, String variant)
+ {
+ // During bootstrap, we already know the strings being passed in are
+ // the correct capitalization, and not null. We can't call
+ // String.toUpperCase during this time, since that depends on the
+ // default locale.
+ if (defaultLocale != null)
+ {
+ language = convertLanguage(language);
+ country = country.toUpperCase();
+ }
+ this.language = language.intern();
+ this.country = country.intern();
+ this.variant = variant.intern();
+ hashcode = language.hashCode() ^ country.hashCode() ^ variant.hashCode();
+ }
+
+ /**
+ * Creates a new locale for the given language and country.
+ *
+ * @param language lowercase two-letter ISO-639 A2 language code
+ * @param country uppercase two-letter ISO-3166 A2 country code
+ * @throws NullPointerException if either argument is null
+ */
+ public Locale(String language, String country)
+ {
+ this(language, country, "");
+ }
+
+ /**
+ * Creates a new locale for a language.
+ *
+ * @param language lowercase two-letter ISO-639 A2 language code
+ * @throws NullPointerException if either argument is null
+ * @since 1.4
+ */
+ public Locale(String language)
+ {
+ this(language, "", "");
+ }
+
+ /**
+ * Returns the default Locale. The default locale is generally once set
+ * on start up and then never changed. Normally you should use this locale
+ * for everywhere you need a locale. The initial setting matches the
+ * default locale, the user has chosen.
+ *
+ * @return the default locale for this virtual machine
+ */
+ public static Locale getDefault()
+ {
+ return defaultLocale;
+ }
+
+ /**
+ * Changes the default locale. Normally only called on program start up.
+ * Note that this doesn't change the locale for other programs. This has
+ * a security check,
+ * <code>PropertyPermission("user.language", "write")</code>, because of
+ * its potential impact to running code.
+ *
+ * @param newLocale the new default locale
+ * @throws NullPointerException if newLocale is null
+ * @throws SecurityException if permission is denied
+ */
+ public static void setDefault(Locale newLocale)
+ {
+ if (newLocale == null)
+ throw new NullPointerException();
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkPermission(new PropertyPermission("user.language", "write"));
+ defaultLocale = newLocale;
+ }
+
+ /**
+ * Returns the list of available locales.
+ *
+ * @return the installed locales
+ */
+ public static synchronized Locale[] getAvailableLocales()
+ {
+ if (availableLocales == null)
+ {
+ int len = LocaleHelper.getLocaleCount();
+ availableLocales = new Locale[len];
+
+ for (int i = 0; i < len; i++)
+ {
+ String language;
+ String country = "";
+ String variant = "";
+ String name = LocaleHelper.getLocaleName(i);
+
+ language = name.substring(0, 2);
+
+ if (name.length() > 2)
+ country = name.substring(3);
+
+ int index = country.indexOf("_");
+ if (index > 0)
+ {
+ variant = country.substring(index + 1);
+ country = country.substring(0, index - 1);
+ }
+
+ availableLocales[i] = getLocale(language, country, variant);
+ }
+ }
+
+ return (Locale[]) availableLocales.clone();
+ }
+
+ /**
+ * Returns a list of all 2-letter uppercase country codes as defined
+ * in ISO 3166.
+ *
+ * @return a list of acceptable country codes
+ */
+ public static String[] getISOCountries()
+ {
+ if (countryCache == null)
+ {
+ countryCache = getISOStrings("territories");
+ }
+
+ return (String[]) countryCache.clone();
+ }
+
+ /**
+ * Returns a list of all 2-letter lowercase language codes as defined
+ * in ISO 639 (both old and new variant).
+ *
+ * @return a list of acceptable language codes
+ */
+ public static String[] getISOLanguages()
+ {
+ if (languageCache == null)
+ {
+ languageCache = getISOStrings("languages");
+ }
+ return (String[]) languageCache.clone();
+ }
+
+ /**
+ * Returns the set of keys from the specified resource hashtable, filtered
+ * so that only two letter strings are returned.
+ *
+ * @param tableName the name of the table from which to retrieve the keys.
+ * @return an array of two-letter strings.
+ */
+ private static String[] getISOStrings(String tableName)
+ {
+ int count = 0;
+ ResourceBundle bundle =
+ ResourceBundle.getBundle("gnu.java.locale.LocaleInformation");
+ Enumeration e = bundle.getKeys();
+ ArrayList tempList = new ArrayList();
+
+ while (e.hasMoreElements())
+ {
+ String key = (String) e.nextElement();
+
+ if (key.startsWith(tableName + "."))
+ {
+ String str = key.substring(tableName.length() + 1);
+
+ if (str.length() == 2
+ && Character.isLetter(str.charAt(0))
+ && Character.isLetter(str.charAt(1)))
+ {
+ tempList.add(str);
+ ++count;
+ }
+ }
+ }
+
+ String[] strings = new String[count];
+
+ for (int a = 0; a < count; ++a)
+ strings[a] = (String) tempList.get(a);
+
+ return strings;
+ }
+
+ /**
+ * Returns the language code of this locale. Some language codes have changed
+ * as ISO 639 has evolved; this returns the old name, even if you built
+ * the locale with the new one.
+ *
+ * @return language code portion of this locale, or an empty String
+ */
+ public String getLanguage()
+ {
+ return language;
+ }
+
+ /**
+ * Returns the country code of this locale.
+ *
+ * @return country code portion of this locale, or an empty String
+ */
+ public String getCountry()
+ {
+ return country;
+ }
+
+ /**
+ * Returns the variant code of this locale.
+ *
+ * @return the variant code portion of this locale, or an empty String
+ */
+ public String getVariant()
+ {
+ return variant;
+ }
+
+ /**
+ * Gets the string representation of the current locale. This consists of
+ * the language, the country, and the variant, separated by an underscore.
+ * The variant is listed only if there is a language or country. Examples:
+ * "en", "de_DE", "_GB", "en_US_WIN", "de__POSIX", "fr__MAC".
+ *
+ * @return the string representation of this Locale
+ * @see #getDisplayName()
+ */
+ public String toString()
+ {
+ if (language.length() == 0 && country.length() == 0)
+ return "";
+ else if (country.length() == 0 && variant.length() == 0)
+ return language;
+ CPStringBuilder result = new CPStringBuilder(language);
+ result.append('_').append(country);
+ if (variant.length() != 0)
+ result.append('_').append(variant);
+ return result.toString();
+ }
+
+ /**
+ * Returns the three-letter ISO language abbrevation of this locale.
+ *
+ * @throws MissingResourceException if the three-letter code is not known
+ */
+ public String getISO3Language()
+ {
+ // We know all strings are interned so we can use '==' for better performance.
+ if (language == "")
+ return "";
+ int index
+ = ("aa,ab,af,am,ar,as,ay,az,ba,be,bg,bh,bi,bn,bo,br,ca,co,cs,cy,da,"
+ + "de,dz,el,en,eo,es,et,eu,fa,fi,fj,fo,fr,fy,ga,gd,gl,gn,gu,ha,iw,"
+ + "hi,hr,hu,hy,ia,in,ie,ik,in,is,it,iu,iw,ja,ji,jw,ka,kk,kl,km,kn,"
+ + "ko,ks,ku,ky,la,ln,lo,lt,lv,mg,mi,mk,ml,mn,mo,mr,ms,mt,my,na,ne,"
+ + "nl,no,oc,om,or,pa,pl,ps,pt,qu,rm,rn,ro,ru,rw,sa,sd,sg,sh,si,sk,"
+ + "sl,sm,sn,so,sq,sr,ss,st,su,sv,sw,ta,te,tg,th,ti,tk,tl,tn,to,tr,"
+ + "ts,tt,tw,ug,uk,ur,uz,vi,vo,wo,xh,ji,yo,za,zh,zu")
+ .indexOf(language);
+
+ if (index % 3 != 0 || language.length() != 2)
+ throw new MissingResourceException
+ ("Can't find ISO3 language for " + language,
+ "java.util.Locale", language);
+
+ // Don't read this aloud. These are the three letter language codes.
+ return
+ ("aarabkaframharaasmaymazebakbelbulbihbisbenbodbrecatcoscescymdandeu"
+ + "dzoellengepospaesteusfasfinfijfaofrafrygaigdhglggrngujhauhebhinhrv"
+ + "hunhyeinaindileipkindislitaikuhebjpnyidjawkatkazkalkhmkankorkaskur"
+ + "kirlatlinlaolitlavmlgmrimkdmalmonmolmarmsamltmyanaunepnldnorociorm"
+ + "oripanpolpusporquerohrunronruskinsansndsagsrpsinslkslvsmosnasomsqi"
+ + "srpsswsotsunsweswatamteltgkthatirtuktgltsntonturtsotattwiuigukrurd"
+ + "uzbvievolwolxhoyidyorzhazhozul")
+ .substring(index, index + 3);
+ }
+
+ /**
+ * Returns the three-letter ISO country abbrevation of the locale.
+ *
+ * @throws MissingResourceException if the three-letter code is not known
+ */
+ public String getISO3Country()
+ {
+ // We know all strings are interned so we can use '==' for better performance.
+ if (country == "")
+ return "";
+ int index
+ = ("AD,AE,AF,AG,AI,AL,AM,AN,AO,AQ,AR,AS,AT,AU,AW,AZ,BA,BB,BD,BE,BF,"
+ + "BG,BH,BI,BJ,BM,BN,BO,BR,BS,BT,BV,BW,BY,BZ,CA,CC,CF,CG,CH,CI,CK,"
+ + "CL,CM,CN,CO,CR,CU,CV,CX,CY,CZ,DE,DJ,DK,DM,DO,DZ,EC,EE,EG,EH,ER,"
+ + "ES,ET,FI,FJ,FK,FM,FO,FR,FX,GA,GB,GD,GE,GF,GH,GI,GL,GM,GN,GP,GQ,"
+ + "GR,GS,GT,GU,GW,GY,HK,HM,HN,HR,HT,HU,ID,IE,IL,IN,IO,IQ,IR,IS,IT,"
+ + "JM,JO,JP,KE,KG,KH,KI,KM,KN,KP,KR,KW,KY,KZ,LA,LB,LC,LI,LK,LR,LS,"
+ + "LT,LU,LV,LY,MA,MC,MD,MG,MH,MK,ML,MM,MN,MO,MP,MQ,MR,MS,MT,MU,MV,"
+ + "MW,MX,MY,MZ,NA,NC,NE,NF,NG,NI,NL,NO,NP,NR,NU,NZ,OM,PA,PE,PF,PG,"
+ + "PH,PK,PL,PM,PN,PR,PT,PW,PY,QA,RE,RO,RU,RW,SA,SB,SC,SD,SE,SG,SH,"
+ + "SI,SJ,SK,SL,SM,SN,SO,SR,ST,SV,SY,SZ,TC,TD,TF,TG,TH,TJ,TK,TM,TN,"
+ + "TO,TP,TR,TT,TV,TW,TZ,UA,UG,UM,US,UY,UZ,VA,VC,VE,VG,VI,VN,VU,WF,"
+ + "WS,YE,YT,YU,ZA,ZM,ZR,ZW")
+ .indexOf(country);
+
+ if (index % 3 != 0 || country.length() != 2)
+ throw new MissingResourceException
+ ("Can't find ISO3 country for " + country,
+ "java.util.Locale", country);
+
+ // Don't read this aloud. These are the three letter country codes.
+ return
+ ("ANDAREAFGATGAIAALBARMANTAGOATAARGASMAUTAUSABWAZEBIHBRBBGDBELBFABGR"
+ + "BHRBDIBENBMUBRNBOLBRABHSBTNBVTBWABLRBLZCANCCKCAFCOGCHECIVCOKCHLCMR"
+ + "CHNCOLCRICUBCPVCXRCYPCZEDEUDJIDNKDMADOMDZAECUESTEGYESHERIESPETHFIN"
+ + "FJIFLKFSMFROFRAFXXGABGBRGRDGEOGUFGHAGIBGRLGMBGINGLPGNQGRCSGSGTMGUM"
+ + "GNBGUYHKGHMDHNDHRVHTIHUNIDNIRLISRINDIOTIRQIRNISLITAJAMJORJPNKENKGZ"
+ + "KHMKIRCOMKNAPRKKORKWTCYMKAZLAOLBNLCALIELKALBRLSOLTULUXLVALBYMARMCO"
+ + "MDAMDGMHLMKDMLIMMRMNGMACMNPMTQMRTMSRMLTMUSMDVMWIMEXMYSMOZNAMNCLNER"
+ + "NFKNGANICNLDNORNPLNRUNIUNZLOMNPANPERPYFPNGPHLPAKPOLSPMPCNPRIPRTPLW"
+ + "PRYQATREUROMRUSRWASAUSLBSYCSDNSWESGPSHNSVNSJMSVKSLESMRSENSOMSURSTP"
+ + "SLVSYRSWZTCATCDATFTGOTHATJKTKLTKMTUNTONTMPTURTTOTUVTWNTZAUKRUGAUMI"
+ + "USAURYUZBVATVCTVENVGBVIRVNMVUTWLFWSMYEMMYTYUGZAFZMBZARZWE")
+ .substring(index, index + 3);
+ }
+
+ /**
+ * Gets the country name suitable for display to the user, formatted
+ * for the default locale. This has the same effect as
+ * <pre>
+ * getDisplayLanguage(Locale.getDefault());
+ * </pre>
+ *
+ * @return the language name of this locale localized to the default locale,
+ * with the ISO code as backup
+ */
+ public String getDisplayLanguage()
+ {
+ return getDisplayLanguage(defaultLocale);
+ }
+
+ /**
+ * <p>
+ * Gets the name of the language specified by this locale, in a form suitable
+ * for display to the user. If possible, the display name will be localized
+ * to the specified locale. For example, if the locale instance is
+ * <code>Locale.GERMANY</code>, and the specified locale is <code>Locale.UK</code>,
+ * the result would be 'German'. Using the German locale would instead give
+ * 'Deutsch'. If the display name can not be localized to the supplied
+ * locale, it will fall back on other output in the following order:
+ * </p>
+ * <ul>
+ * <li>the display name in the default locale</li>
+ * <li>the display name in English</li>
+ * <li>the ISO code</li>
+ * </ul>
+ * <p>
+ * If the language is unspecified by this locale, then the empty string is
+ * returned.
+ * </p>
+ *
+ * @param inLocale the locale to use for formatting the display string.
+ * @return the language name of this locale localized to the given locale,
+ * with the default locale, English and the ISO code as backups.
+ * @throws NullPointerException if the supplied locale is null.
+ */
+ public String getDisplayLanguage(Locale inLocale)
+ {
+ if (language.isEmpty())
+ return "";
+ try
+ {
+ ResourceBundle res =
+ ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
+ inLocale,
+ ClassLoader.getSystemClassLoader());
+
+ return res.getString("languages." + language);
+ }
+ catch (MissingResourceException e)
+ {
+ /* This means runtime support for the locale
+ * is not available, so we check providers. */
+ }
+ for (LocaleNameProvider p :
+ ServiceLoader.load(LocaleNameProvider.class))
+ {
+ for (Locale loc : p.getAvailableLocales())
+ {
+ if (loc.equals(inLocale))
+ {
+ String locLang = p.getDisplayLanguage(language,
+ inLocale);
+ if (locLang != null)
+ return locLang;
+ break;
+ }
+ }
+ }
+ if (inLocale.equals(Locale.ROOT)) // Base case
+ return language;
+ return getDisplayLanguage(LocaleHelper.getFallbackLocale(inLocale));
+ }
+
+ /**
+ * Returns the country name of this locale localized to the
+ * default locale. If the localized is not found, the ISO code
+ * is returned. This has the same effect as
+ * <pre>
+ * getDisplayCountry(Locale.getDefault());
+ * </pre>
+ *
+ * @return the country name of this locale localized to the given locale,
+ * with the ISO code as backup
+ */
+ public String getDisplayCountry()
+ {
+ return getDisplayCountry(defaultLocale);
+ }
+
+ /**
+ * <p>
+ * Gets the name of the country specified by this locale, in a form suitable
+ * for display to the user. If possible, the display name will be localized
+ * to the specified locale. For example, if the locale instance is
+ * <code>Locale.GERMANY</code>, and the specified locale is <code>Locale.UK</code>,
+ * the result would be 'Germany'. Using the German locale would instead give
+ * 'Deutschland'. If the display name can not be localized to the supplied
+ * locale, it will fall back on other output in the following order:
+ * </p>
+ * <ul>
+ * <li>the display name in the default locale</li>
+ * <li>the display name in English</li>
+ * <li>the ISO code</li>
+ * </ul>
+ * <p>
+ * If the country is unspecified by this locale, then the empty string is
+ * returned.
+ * </p>
+ *
+ * @param inLocale the locale to use for formatting the display string.
+ * @return the country name of this locale localized to the given locale,
+ * with the default locale, English and the ISO code as backups.
+ * @throws NullPointerException if the supplied locale is null.
+ */
+ public String getDisplayCountry(Locale inLocale)
+ {
+ if (country.isEmpty())
+ return "";
+ try
+ {
+ ResourceBundle res =
+ ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
+ inLocale,
+ ClassLoader.getSystemClassLoader());
+
+ return res.getString("territories." + country);
+ }
+ catch (MissingResourceException e)
+ {
+ /* This means runtime support for the locale
+ * is not available, so we check providers. */
+ }
+ for (LocaleNameProvider p :
+ ServiceLoader.load(LocaleNameProvider.class))
+ {
+ for (Locale loc : p.getAvailableLocales())
+ {
+ if (loc.equals(inLocale))
+ {
+ String locCountry = p.getDisplayCountry(country,
+ inLocale);
+ if (locCountry != null)
+ return locCountry;
+ break;
+ }
+ }
+ }
+ if (inLocale.equals(Locale.ROOT)) // Base case
+ return country;
+ return getDisplayCountry(LocaleHelper.getFallbackLocale(inLocale));
+ }
+
+ /**
+ * Returns the variant name of this locale localized to the
+ * default locale. If the localized is not found, the variant code
+ * itself is returned. This has the same effect as
+ * <pre>
+ * getDisplayVariant(Locale.getDefault());
+ * </pre>
+ *
+ * @return the variant code of this locale localized to the given locale,
+ * with the ISO code as backup
+ */
+ public String getDisplayVariant()
+ {
+ return getDisplayVariant(defaultLocale);
+ }
+
+
+ /**
+ * <p>
+ * Gets the name of the variant specified by this locale, in a form suitable
+ * for display to the user. If possible, the display name will be localized
+ * to the specified locale. For example, if the locale instance is a revised
+ * variant, and the specified locale is <code>Locale.UK</code>, the result
+ * would be 'REVISED'. Using the German locale would instead give
+ * 'Revidiert'. If the display name can not be localized to the supplied
+ * locale, it will fall back on other output in the following order:
+ * </p>
+ * <ul>
+ * <li>the display name in the default locale</li>
+ * <li>the display name in English</li>
+ * <li>the ISO code</li>
+ * </ul>
+ * <p>
+ * If the variant is unspecified by this locale, then the empty string is
+ * returned.
+ * </p>
+ *
+ * @param inLocale the locale to use for formatting the display string.
+ * @return the variant name of this locale localized to the given locale,
+ * with the default locale, English and the ISO code as backups.
+ * @throws NullPointerException if the supplied locale is null.
+ */
+ public String getDisplayVariant(Locale inLocale)
+ {
+ if (variant.isEmpty())
+ return "";
+ try
+ {
+ ResourceBundle res =
+ ResourceBundle.getBundle("gnu.java.locale.LocaleInformation",
+ inLocale,
+ ClassLoader.getSystemClassLoader());
+
+ return res.getString("variants." + variant);
+ }
+ catch (MissingResourceException e)
+ {
+ /* This means runtime support for the locale
+ * is not available, so we check providers. */
+ }
+ for (LocaleNameProvider p :
+ ServiceLoader.load(LocaleNameProvider.class))
+ {
+ for (Locale loc : p.getAvailableLocales())
+ {
+ if (loc.equals(inLocale))
+ {
+ String locVar = p.getDisplayVariant(variant,
+ inLocale);
+ if (locVar != null)
+ return locVar;
+ break;
+ }
+ }
+ }
+ if (inLocale.equals(Locale.ROOT)) // Base case
+ return country;
+ return getDisplayVariant(LocaleHelper.getFallbackLocale(inLocale));
+ }
+
+ /**
+ * Gets all local components suitable for display to the user, formatted
+ * for the default locale. For the language component, getDisplayLanguage
+ * is called. For the country component, getDisplayCountry is called.
+ * For the variant set component, getDisplayVariant is called.
+ *
+ * <p>The returned String will be one of the following forms:<br>
+ * <pre>
+ * language (country, variant)
+ * language (country)
+ * language (variant)
+ * country (variant)
+ * language
+ * country
+ * variant
+ * </pre>
+ *
+ * @return String version of this locale, suitable for display to the user
+ */
+ public String getDisplayName()
+ {
+ return getDisplayName(defaultLocale);
+ }
+
+ /**
+ * Gets all local components suitable for display to the user, formatted
+ * for a specified locale. For the language component,
+ * getDisplayLanguage(Locale) is called. For the country component,
+ * getDisplayCountry(Locale) is called. For the variant set component,
+ * getDisplayVariant(Locale) is called.
+ *
+ * <p>The returned String will be one of the following forms:<br>
+ * <pre>
+ * language (country, variant)
+ * language (country)
+ * language (variant)
+ * country (variant)
+ * language
+ * country
+ * variant
+ * </pre>
+ *
+ * @param locale locale to use for formatting
+ * @return String version of this locale, suitable for display to the user
+ */
+ public String getDisplayName(Locale locale)
+ {
+ CPStringBuilder result = new CPStringBuilder();
+ int count = 0;
+ String[] delimiters = {"", " (", ","};
+ if (language.length() != 0)
+ {
+ result.append(delimiters[count++]);
+ result.append(getDisplayLanguage(locale));
+ }
+ if (country.length() != 0)
+ {
+ result.append(delimiters[count++]);
+ result.append(getDisplayCountry(locale));
+ }
+ if (variant.length() != 0)
+ {
+ result.append(delimiters[count++]);
+ result.append(getDisplayVariant(locale));
+ }
+ if (count > 1)
+ result.append(")");
+ return result.toString();
+ }
+
+ /**
+ * Does the same as <code>Object.clone()</code> but does not throw
+ * a <code>CloneNotSupportedException</code>. Why anyone would
+ * use this method is a secret to me, since this class is immutable.
+ *
+ * @return the clone
+ */
+ public Object clone()
+ {
+ // This class is final, so no need to use native super.clone().
+ return new Locale(language, country, variant);
+ }
+
+ /**
+ * Return the hash code for this locale. The hashcode is the logical
+ * xor of the hash codes of the language, the country and the variant.
+ * The hash code is precomputed, since <code>Locale</code>s are often
+ * used in hash tables.
+ *
+ * @return the hashcode
+ */
+ public int hashCode()
+ {
+ return hashcode;
+ }
+
+ /**
+ * Compares two locales. To be equal, obj must be a Locale with the same
+ * language, country, and variant code.
+ *
+ * @param obj the other locale
+ * @return true if obj is equal to this
+ */
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ return true;
+ if (! (obj instanceof Locale))
+ return false;
+ Locale l = (Locale) obj;
+
+ return (language == l.language
+ && country == l.country
+ && variant == l.variant);
+ }
+
+ /**
+ * Write the locale to an object stream.
+ *
+ * @param s the stream to write to
+ * @throws IOException if the write fails
+ * @serialData The first three fields are Strings representing language,
+ * country, and variant. The fourth field is a placeholder for
+ * the cached hashcode, but this is always written as -1, and
+ * recomputed when reading it back.
+ */
+ private void writeObject(ObjectOutputStream s)
+ throws IOException
+ {
+ ObjectOutputStream.PutField fields = s.putFields();
+ fields.put("hashcode", -1);
+ s.defaultWriteObject();
+ }
+
+ /**
+ * Reads a locale from the input stream.
+ *
+ * @param s the stream to read from
+ * @throws IOException if reading fails
+ * @throws ClassNotFoundException if reading fails
+ * @serialData the hashCode is always invalid and must be recomputed
+ */
+ private void readObject(ObjectInputStream s)
+ throws IOException, ClassNotFoundException
+ {
+ s.defaultReadObject();
+ hashcode = language.hashCode() ^ country.hashCode() ^ variant.hashCode();
+ }
+} // class Locale
diff --git a/libjava/classpath/java/util/Map.java b/libjava/classpath/java/util/Map.java
new file mode 100644
index 000000000..1bcb1b2f0
--- /dev/null
+++ b/libjava/classpath/java/util/Map.java
@@ -0,0 +1,338 @@
+/* Map.java: interface Map -- An object that maps keys to values
+ interface Map.Entry -- an Entry in a Map
+ Copyright (C) 1998, 2001, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * An object that maps keys onto values. Keys cannot be duplicated. This
+ * interface replaces the obsolete {@link Dictionary} abstract class.
+ * <p>
+ *
+ * The map has three collection views, which are backed by the map
+ * (modifications on one show up on the other): a set of keys, a collection
+ * of values, and a set of key-value mappings. Some maps have a guaranteed
+ * order, but not all do.
+ * <p>
+ *
+ * Note: Be careful about using mutable keys. Behavior is unspecified if
+ * a key's comparison behavior is changed after the fact. As a corollary
+ * to this rule, don't use a Map as one of its own keys or values, as it makes
+ * hashCode and equals have undefined behavior.
+ * <p>
+ *
+ * All maps are recommended to provide a no argument constructor, which builds
+ * an empty map, and one that accepts a Map parameter and copies the mappings
+ * (usually by putAll), to create an equivalent map. Unfortunately, Java
+ * cannot enforce these suggestions.
+ * <p>
+ *
+ * The map may be unmodifiable, in which case unsupported operations will
+ * throw an UnsupportedOperationException. Note that some operations may be
+ * safe, such as putAll(m) where m is empty, even if the operation would
+ * normally fail with a non-empty argument.
+ *
+ * @author Original author unknown
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see HashMap
+ * @see TreeMap
+ * @see Hashtable
+ * @see SortedMap
+ * @see Collection
+ * @see Set
+ * @since 1.2
+ * @status updated to 1.4
+ */
+public interface Map<K, V>
+{
+ /**
+ * Remove all entries from this Map (optional operation).
+ *
+ * @throws UnsupportedOperationException if clear is not supported
+ */
+ void clear();
+
+ /**
+ * Returns true if this contains a mapping for the given key.
+ *
+ * @param key the key to search for
+ * @return true if the map contains the key
+ * @throws ClassCastException if the key is of an inappropriate type
+ * @throws NullPointerException if key is <code>null</code> but the map
+ * does not permit null keys
+ */
+ boolean containsKey(Object key);
+
+ /**
+ * Returns true if this contains at least one mapping with the given value.
+ * In other words, returns true if a value v exists where
+ * <code>(value == null ? v == null : value.equals(v))</code>. This usually
+ * requires linear time.
+ *
+ * @param value the value to search for
+ * @return true if the map contains the value
+ * @throws ClassCastException if the type of the value is not a valid type
+ * for this map.
+ * @throws NullPointerException if the value is null and the map doesn't
+ * support null values.
+ */
+ boolean containsValue(Object value);
+
+ /**
+ * Returns a set view of the mappings in this Map. Each element in the
+ * set is a Map.Entry. The set is backed by the map, so that changes in
+ * one show up in the other. Modifications made while an iterator is
+ * in progress cause undefined behavior. If the set supports removal,
+ * these methods remove the underlying mapping from the map:
+ * <code>Iterator.remove</code>, <code>Set.remove</code>,
+ * <code>removeAll</code>, <code>retainAll</code>, and <code>clear</code>.
+ * Element addition, via <code>add</code> or <code>addAll</code>, is
+ * not supported via this set.
+ *
+ * @return the set view of all mapping entries
+ * @see Map.Entry
+ */
+ Set<Map.Entry<K, V>> entrySet();
+
+ /**
+ * Compares the specified object with this map for equality. Returns
+ * <code>true</code> if the other object is a Map with the same mappings,
+ * that is,<br>
+ * <code>o instanceof Map && entrySet().equals(((Map) o).entrySet();</code>
+ * This allows comparison of maps, regardless of implementation.
+ *
+ * @param o the object to be compared
+ * @return true if the object equals this map
+ * @see Set#equals(Object)
+ */
+ boolean equals(Object o);
+
+ /**
+ * Returns the value mapped by the given key. Returns <code>null</code> if
+ * there is no mapping. However, in Maps that accept null values, you
+ * must rely on <code>containsKey</code> to determine if a mapping exists.
+ *
+ * @param key the key to look up
+ * @return the value associated with the key, or null if key not in map
+ * @throws ClassCastException if the key is an inappropriate type
+ * @throws NullPointerException if this map does not accept null keys
+ * @see #containsKey(Object)
+ */
+ V get(Object key);
+
+ /**
+ * Associates the given key to the given value (optional operation). If the
+ * map already contains the key, its value is replaced. Be aware that in
+ * a map that permits <code>null</code> values, a null return does not
+ * always imply that the mapping was created.
+ *
+ * @param key the key to map
+ * @param value the value to be mapped
+ * @return the previous value of the key, or null if there was no mapping
+ * @throws UnsupportedOperationException if the operation is not supported
+ * @throws ClassCastException if the key or value is of the wrong type
+ * @throws IllegalArgumentException if something about this key or value
+ * prevents it from existing in this map
+ * @throws NullPointerException if either the key or the value is null,
+ * and the map forbids null keys or values
+ * @see #containsKey(Object)
+ */
+ V put(K key, V value);
+
+ /**
+ * Returns the hash code for this map. This is the sum of all hashcodes
+ * for each Map.Entry object in entrySet. This allows comparison of maps,
+ * regardless of implementation, and satisfies the contract of
+ * Object.hashCode.
+ *
+ * @return the hash code
+ * @see Map.Entry#hashCode()
+ */
+ int hashCode();
+
+ /**
+ * Returns true if the map contains no mappings.
+ *
+ * @return true if the map is empty
+ */
+ boolean isEmpty();
+
+ /**
+ * Returns a set view of the keys in this Map. The set is backed by the
+ * map, so that changes in one show up in the other. Modifications made
+ * while an iterator is in progress cause undefined behavior. If the set
+ * supports removal, these methods remove the underlying mapping from
+ * the map: <code>Iterator.remove</code>, <code>Set.remove</code>,
+ * <code>removeAll</code>, <code>retainAll</code>, and <code>clear</code>.
+ * Element addition, via <code>add</code> or <code>addAll</code>, is
+ * not supported via this set.
+ *
+ * @return the set view of all keys
+ */
+ Set<K> keySet();
+
+ /**
+ * Copies all entries of the given map to this one (optional operation). If
+ * the map already contains a key, its value is replaced.
+ *
+ * @param m the mapping to load into this map
+ * @throws UnsupportedOperationException if the operation is not supported
+ * @throws ClassCastException if a key or value is of the wrong type
+ * @throws IllegalArgumentException if something about a key or value
+ * prevents it from existing in this map
+ * @throws NullPointerException if the map forbids null keys or values, or
+ * if <code>m</code> is null.
+ * @see #put(Object, Object)
+ */
+ void putAll(Map<? extends K, ? extends V> m);
+
+ /**
+ * Removes the mapping for this key if present (optional operation). If
+ * the key is not present, this returns null. Note that maps which permit
+ * null values may also return null if the key was removed.
+ *
+ * @param key the key to remove
+ * @return the value the key mapped to, or null if not present.
+ * @throws UnsupportedOperationException if deletion is unsupported
+ * @throws NullPointerException if the key is null and this map doesn't
+ * support null keys.
+ * @throws ClassCastException if the type of the key is not a valid type
+ * for this map.
+ */
+ V remove(Object o);
+
+ /**
+ * Returns the number of key-value mappings in the map. If there are more
+ * than Integer.MAX_VALUE mappings, return Integer.MAX_VALUE.
+ *
+ * @return the number of mappings
+ */
+ int size();
+
+ /**
+ * Returns a collection (or bag) view of the values in this Map. The
+ * collection is backed by the map, so that changes in one show up in
+ * the other. Modifications made while an iterator is in progress cause
+ * undefined behavior. If the collection supports removal, these methods
+ * remove the underlying mapping from the map: <code>Iterator.remove</code>,
+ * <code>Collection.remove</code>, <code>removeAll</code>,
+ * <code>retainAll</code>, and <code>clear</code>. Element addition, via
+ * <code>add</code> or <code>addAll</code>, is not supported via this
+ * collection.
+ *
+ * @return the collection view of all values
+ */
+ Collection<V> values();
+
+ /**
+ * A map entry (key-value pair). The Map.entrySet() method returns a set
+ * view of these objects; there is no other valid way to come across them.
+ * These objects are only valid for the duration of an iteration; in other
+ * words, if you mess with one after modifying the map, you are asking
+ * for undefined behavior.
+ *
+ * @author Original author unknown
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Map
+ * @see Map#entrySet()
+ * @since 1.2
+ * @status updated to 1.4
+ */
+ interface Entry<K, V>
+ {
+ /**
+ * Get the key corresponding to this entry.
+ *
+ * @return the key
+ */
+ K getKey();
+
+ /**
+ * Get the value corresponding to this entry. If you already called
+ * Iterator.remove(), this is undefined.
+ *
+ * @return the value
+ */
+ V getValue();
+
+ /**
+ * Replaces the value with the specified object (optional operation).
+ * This writes through to the map, and is undefined if you already
+ * called Iterator.remove().
+ *
+ * @param value the new value to store
+ * @return the old value
+ * @throws UnsupportedOperationException if the operation is not supported
+ * @throws ClassCastException if the value is of the wrong type
+ * @throws IllegalArgumentException if something about the value
+ * prevents it from existing in this map
+ * @throws NullPointerException if the map forbids null values
+ */
+ V setValue(V 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
+ * <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
+ */
+ int hashCode();
+
+ /**
+ * 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:
+ *
+<p><pre>(o instanceof Map.Entry)
+&& (getKey() == null ? ((Map.Entry) o).getKey() == null
+ : getKey().equals(((Map.Entry) o).getKey()))
+&& (getValue() == null ? ((Map.Entry) o).getValue() == null
+ : getValue().equals(((Map.Entry) o).getValue()))</pre>
+ *
+ * @param o the object to compare
+ *
+ * @return <code>true</code> if it is equal
+ */
+ boolean equals(Object o);
+ }
+}
diff --git a/libjava/classpath/java/util/MissingFormatArgumentException.java b/libjava/classpath/java/util/MissingFormatArgumentException.java
new file mode 100644
index 000000000..e53de0ae3
--- /dev/null
+++ b/libjava/classpath/java/util/MissingFormatArgumentException.java
@@ -0,0 +1,90 @@
+/* MissingFormatArgumentException.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Thrown when the a format specification for a {@link Formatter}
+ * refers to an argument that is non-existent, or an argument index
+ * references a non-existent argument.
+ *
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+public class MissingFormatArgumentException
+ extends IllegalFormatException
+{
+ private static final long serialVersionUID = 19190115L;
+
+ /**
+ * The format specification which contains the
+ * unmatched argument.
+ *
+ * @serial the format specification.
+ */
+ // Note: name fixed by serialization.
+ private String s;
+
+ /**
+ * Constructs a new <code>MissingFormatArgumentException</code>
+ * for a format specification, <code>s</code>, which refers
+ * to a non-existent argument.
+ *
+ * @param s the format specification.
+ * @throws NullPointerException if <code>s</code> is null.
+ */
+ public MissingFormatArgumentException(String s)
+ {
+ super("The specification, " + s +
+ ", refers to a non-existent argument.");
+ if (s == null)
+ throw new NullPointerException("The specification is null.");
+ this.s = s;
+ }
+
+ /**
+ * Returns the format specification.
+ *
+ * @return the format specification.
+ */
+ public String getFormatSpecifier()
+ {
+ return s;
+ }
+}
diff --git a/libjava/classpath/java/util/MissingFormatWidthException.java b/libjava/classpath/java/util/MissingFormatWidthException.java
new file mode 100644
index 000000000..e083172a9
--- /dev/null
+++ b/libjava/classpath/java/util/MissingFormatWidthException.java
@@ -0,0 +1,88 @@
+/* MissingFormatWidthException.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Thrown when the a format specification for a {@link Formatter}
+ * does not include a width for a value where one is required.
+ *
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+public class MissingFormatWidthException
+ extends IllegalFormatException
+{
+ private static final long serialVersionUID = 15560123L;
+
+ /**
+ * The format specification which contains the
+ * unmatched argument.
+ *
+ * @serial the format specification.
+ */
+ // Note: name fixed by serialization.
+ private String s;
+
+ /**
+ * Constructs a new <code>MissingFormatWidthException</code>
+ * for a format specification, <code>s</code>, which excludes
+ * a required width argument.
+ *
+ * @param s the format specification.
+ * @throws NullPointerException if <code>s</code> is null.
+ */
+ public MissingFormatWidthException(String s)
+ {
+ super("The specification, " + s + ", misses a required width.");
+ if (s == null)
+ throw new NullPointerException("The specification is null.");
+ this.s = s;
+ }
+
+ /**
+ * Returns the format specification.
+ *
+ * @return the format specification.
+ */
+ public String getFormatSpecifier()
+ {
+ return s;
+ }
+}
diff --git a/libjava/classpath/java/util/MissingResourceException.java b/libjava/classpath/java/util/MissingResourceException.java
new file mode 100644
index 000000000..26640de90
--- /dev/null
+++ b/libjava/classpath/java/util/MissingResourceException.java
@@ -0,0 +1,105 @@
+/* MissingResourceException.java -- thrown for a missing resource
+ Copyright (C) 1998, 1999, 2001, 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * This exception is thrown when a resource is missing.
+ *
+ * @author Jochen Hoenicke
+ * @author Warren Levy (warrenl@cygnus.com)
+ * @see ResourceBundle
+ * @since 1.1
+ * @status updated to 1.4
+ */
+public class MissingResourceException extends RuntimeException
+{
+ /**
+ * Compatible with JDK 1.1+.
+ */
+ private static final long serialVersionUID = -4876345176062000401L;
+
+ /**
+ * The name of the resource bundle requested by user.
+ *
+ * @serial the class name of the resource bundle
+ */
+ private final String className;
+
+ /**
+ * The key of the resource in the bundle requested by user.
+ *
+ * @serial the name of the resouce
+ */
+ private final String key;
+
+ /**
+ * Creates a new exception, with the specified parameters.
+ *
+ * @param s the detail message
+ * @param className the name of the resource bundle
+ * @param key the key of the missing resource
+ */
+ public MissingResourceException(String s, String className, String key)
+ {
+ super(s);
+ this.className = className;
+ this.key = key;
+ }
+
+ /**
+ * Gets the name of the resource bundle, for which a resource is missing.
+ *
+ * @return the name of the resource bundle
+ */
+ public String getClassName()
+ {
+ return className;
+ }
+
+ /**
+ * Gets the key of the resource that is missing bundle, this is an empty
+ * string if the whole resource bundle is missing.
+ *
+ * @return the name of the resource bundle
+ */
+ public String getKey()
+ {
+ return key;
+ }
+}
diff --git a/libjava/classpath/java/util/NoSuchElementException.java b/libjava/classpath/java/util/NoSuchElementException.java
new file mode 100644
index 000000000..5e1a2176d
--- /dev/null
+++ b/libjava/classpath/java/util/NoSuchElementException.java
@@ -0,0 +1,88 @@
+/* NoSuchElementException.java -- Attempt to access element that does not exist
+ Copyright (C) 1998, 1999, 2001, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/* 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.
+ */
+
+/**
+ * Exception thrown when an attempt is made to access an element that does not
+ * exist. This exception is thrown by the Enumeration, Iterator and
+ * ListIterator classes if the nextElement, next or previous method goes
+ * beyond the end of the list of elements that are being accessed. It is also
+ * thrown by Vector and Stack when attempting to access the first or last
+ * element of an empty collection.
+ *
+ * @author Warren Levy (warrenl@cygnus.com)
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Enumeration
+ * @see Iterator
+ * @see ListIterator
+ * @see Enumeration#nextElement()
+ * @see Iterator#next()
+ * @see ListIterator#previous()
+ * @since 1.0
+ * @status updated to 1.4
+ */
+public class NoSuchElementException extends RuntimeException
+{
+ /**
+ * Compatible with JDK 1.0.
+ */
+ private static final long serialVersionUID = 6769829250639411880L;
+
+ /**
+ * Constructs a NoSuchElementException with no detail message.
+ */
+ public NoSuchElementException()
+ {
+ }
+
+ /**
+ * Constructs a NoSuchElementException with a detail message.
+ *
+ * @param detail the detail message for the exception
+ */
+ public NoSuchElementException(String detail)
+ {
+ super(detail);
+ }
+}
diff --git a/libjava/classpath/java/util/Observable.java b/libjava/classpath/java/util/Observable.java
new file mode 100644
index 000000000..916abe4ac
--- /dev/null
+++ b/libjava/classpath/java/util/Observable.java
@@ -0,0 +1,182 @@
+/* Observable.java -- an object to be observed
+ Copyright (C) 1999, 2000, 2001, 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * 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)
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Observer
+ * @status updated to 1.4
+ */
+public class Observable
+{
+ /** Tracks whether this object has changed. */
+ private boolean changed;
+
+ /* List of the Observers registered as interested in this Observable. */
+ private LinkedHashSet observers;
+
+ /**
+ * Constructs an Observable with zero Observers.
+ */
+ public Observable()
+ {
+ observers = new LinkedHashSet();
+ }
+
+ /**
+ * Adds an Observer. If the observer was already added this method does
+ * nothing.
+ *
+ * @param observer Observer to add
+ * @throws NullPointerException if observer is null
+ */
+ public synchronized void addObserver(Observer observer)
+ {
+ if (observer == null)
+ throw new NullPointerException("can't add null observer");
+ observers.add(observer);
+ }
+
+ /**
+ * 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()
+ {
+ changed = false;
+ }
+
+ /**
+ * Returns the number of observers for this object.
+ *
+ * @return number of Observers for this
+ */
+ public synchronized int countObservers()
+ {
+ return observers.size();
+ }
+
+ /**
+ * Deletes an Observer of this Observable.
+ *
+ * @param victim Observer to delete
+ */
+ public synchronized void deleteObserver(Observer victim)
+ {
+ observers.remove(victim);
+ }
+
+ /**
+ * Deletes all Observers of this Observable.
+ */
+ public synchronized void deleteObservers()
+ {
+ observers.clear();
+ }
+
+ /**
+ * 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()
+ {
+ return changed;
+ }
+
+ /**
+ * If the Observable has actually changed then tell all Observers about it,
+ * then reset state to unchanged.
+ *
+ * @see #notifyObservers(Object)
+ * @see Observer#update(Observable, Object)
+ */
+ public void notifyObservers()
+ {
+ notifyObservers(null);
+ }
+
+ /**
+ * If the Observable has actually changed then tell all Observers about it,
+ * 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 argument to Observer's update method
+ * @see Observer#update(Observable, Object)
+ */
+ public void notifyObservers(Object obj)
+ {
+ if (! hasChanged())
+ return;
+ // 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();
+ }
+
+ /**
+ * Marks this Observable as having changed.
+ */
+ protected synchronized void setChanged()
+ {
+ changed = true;
+ }
+}
diff --git a/libjava/classpath/java/util/Observer.java b/libjava/classpath/java/util/Observer.java
new file mode 100644
index 000000000..c59a0ca6c
--- /dev/null
+++ b/libjava/classpath/java/util/Observer.java
@@ -0,0 +1,60 @@
+/* Observer.java -- an object that will be informed of changes in an Observable
+ Copyright (C) 1998, 1999, 2001, 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Interface that is implemented when a class wants to be informed of changes
+ * in Observable objects.
+ *
+ * @author Warren Levy (warrenl@cygnus.com)
+ * @see Observable
+ * @status updated to 1.4
+ */
+public interface Observer
+{
+ /**
+ * This method is called whenever the observable object changes, and has
+ * called <code>notifyObservers</code>. The Observable object can pass
+ * arbitrary information in the second parameter.
+ *
+ * @param observable the Observable object that changed
+ * @param arg arbitrary information, usually relating to the change
+ */
+ void update(Observable observable, Object arg);
+}
diff --git a/libjava/classpath/java/util/PriorityQueue.java b/libjava/classpath/java/util/PriorityQueue.java
new file mode 100644
index 000000000..5b6a36804
--- /dev/null
+++ b/libjava/classpath/java/util/PriorityQueue.java
@@ -0,0 +1,336 @@
+/* PriorityQueue.java -- Unbounded priority queue
+ Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.io.Serializable;
+
+/**
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+public class PriorityQueue<E> extends AbstractQueue<E> implements Serializable
+{
+ private static final int DEFAULT_CAPACITY = 11;
+
+ private static final long serialVersionUID = -7720805057305804111L;
+
+ /** Number of elements actually used in the storage array. */
+ int used;
+
+ /**
+ * This is the storage for the underlying binomial heap.
+ * The idea is, each node is less than or equal to its children.
+ * A node at index N (0-based) has two direct children, at
+ * nodes 2N+1 and 2N+2.
+ */
+ E[] storage;
+
+ /**
+ * The comparator we're using, or null for natural ordering.
+ */
+ Comparator<? super E> comparator;
+
+ public PriorityQueue()
+ {
+ this(DEFAULT_CAPACITY, null);
+ }
+
+ public PriorityQueue(Collection<? extends E> c)
+ {
+ this(Math.max(1, (int) (1.1 * c.size())), null);
+
+ // Special case where we can find the comparator to use.
+ if (c instanceof SortedSet)
+ {
+ SortedSet<? extends E> ss = (SortedSet<? extends E>) c;
+ this.comparator = (Comparator<? super E>) ss.comparator();
+ // We can insert the elements directly, since they are sorted.
+ int i = 0;
+ for (E val : ss)
+ {
+ if (val == null)
+ throw new NullPointerException();
+ storage[i++] = val;
+ }
+ }
+ else if (c instanceof PriorityQueue)
+ {
+ PriorityQueue<? extends E> pq = (PriorityQueue<? extends E>) c;
+ this.comparator = (Comparator<? super E>)pq.comparator();
+ // We can just copy the contents.
+ System.arraycopy(pq.storage, 0, storage, 0, pq.storage.length);
+ }
+
+ addAll(c);
+ }
+
+ public PriorityQueue(int cap)
+ {
+ this(cap, null);
+ }
+
+ public PriorityQueue(int cap, Comparator<? super E> comp)
+ {
+ if (cap < 1)
+ throw new IllegalArgumentException();
+ this.used = 0;
+ this.storage = (E[]) new Object[cap];
+ this.comparator = comp;
+ }
+
+ public PriorityQueue(PriorityQueue<? extends E> c)
+ {
+ this(Math.max(1, (int) (1.1 * c.size())),
+ (Comparator<? super E>)c.comparator());
+ // We can just copy the contents.
+ System.arraycopy(c.storage, 0, storage, 0, c.storage.length);
+ }
+
+ public PriorityQueue(SortedSet<? extends E> c)
+ {
+ this(Math.max(1, (int) (1.1 * c.size())),
+ (Comparator<? super E>)c.comparator());
+ // We can insert the elements directly, since they are sorted.
+ int i = 0;
+ for (E val : c)
+ {
+ if (val == null)
+ throw new NullPointerException();
+ storage[i++] = val;
+ }
+ }
+
+ public void clear()
+ {
+ Arrays.fill(storage, null);
+ used = 0;
+ }
+
+ public Comparator<? super E> comparator()
+ {
+ return comparator;
+ }
+
+ public Iterator<E> iterator()
+ {
+ return new Iterator<E>()
+ {
+ int index = -1;
+ int count = 0;
+
+ public boolean hasNext()
+ {
+ return count < used;
+ }
+
+ public E next()
+ {
+ while (storage[++index] == null)
+ ;
+
+ ++count;
+ return storage[index];
+ }
+
+ public void remove()
+ {
+ PriorityQueue.this.remove(index);
+ index--;
+ }
+ };
+ }
+
+ public boolean offer(E o)
+ {
+ if (o == null)
+ throw new NullPointerException();
+
+ int slot = findSlot(-1);
+
+ storage[slot] = o;
+ ++used;
+ bubbleUp(slot);
+
+ return true;
+ }
+
+ public E peek()
+ {
+ return used == 0 ? null : storage[0];
+ }
+
+ public E poll()
+ {
+ if (used == 0)
+ return null;
+ E result = storage[0];
+ remove(0);
+ return result;
+ }
+
+ public boolean remove(Object o)
+ {
+ if (o != null)
+ {
+ for (int i = 0; i < storage.length; ++i)
+ {
+ if (o.equals(storage[i]))
+ {
+ remove(i);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public int size()
+ {
+ return used;
+ }
+
+ // It is more efficient to implement this locally -- less searching
+ // for free slots.
+ public boolean addAll(Collection<? extends E> c)
+ {
+ if (c == this)
+ throw new IllegalArgumentException();
+
+ int newSlot = -1;
+ int save = used;
+ for (E val : c)
+ {
+ if (val == null)
+ throw new NullPointerException();
+ newSlot = findSlot(newSlot);
+ storage[newSlot] = val;
+ ++used;
+ bubbleUp(newSlot);
+ }
+
+ return save != used;
+ }
+
+ int findSlot(int start)
+ {
+ int slot;
+ if (used == storage.length)
+ {
+ resize();
+ slot = used;
+ }
+ else
+ {
+ for (slot = start + 1; slot < storage.length; ++slot)
+ {
+ if (storage[slot] == null)
+ break;
+ }
+ // We'll always find a slot.
+ }
+ return slot;
+ }
+
+ void remove(int index)
+ {
+ // Remove the element at INDEX. We do this by finding the least
+ // child and moving it into place, then iterating until we reach
+ // the bottom of the tree.
+ while (storage[index] != null)
+ {
+ int child = 2 * index + 1;
+
+ // See if we went off the end.
+ if (child >= storage.length)
+ {
+ storage[index] = null;
+ break;
+ }
+
+ // Find which child we want to promote. If one is not null,
+ // we pick it. If both are null, it doesn't matter, we're
+ // about to leave. If neither is null, pick the lesser.
+ if (child + 1 >= storage.length || storage[child + 1] == null)
+ {
+ // Nothing.
+ }
+ else if (storage[child] == null
+ || (Collections.compare(storage[child], storage[child + 1],
+ comparator) > 0))
+ ++child;
+ storage[index] = storage[child];
+ index = child;
+ }
+ --used;
+ }
+
+ void bubbleUp(int index)
+ {
+ // The element at INDEX was inserted into a blank spot. Now move
+ // it up the tree to its natural resting place.
+ while (index > 0)
+ {
+ // This works regardless of whether we're at 2N+1 or 2N+2.
+ int parent = (index - 1) / 2;
+ if (Collections.compare(storage[parent], storage[index], comparator)
+ <= 0)
+ {
+ // Parent is the same or smaller than this element, so the
+ // invariant is preserved. Note that if the new element
+ // is smaller than the parent, then it is necessarily
+ // smaller than the parent's other child.
+ break;
+ }
+
+ E temp = storage[index];
+ storage[index] = storage[parent];
+ storage[parent] = temp;
+
+ index = parent;
+ }
+ }
+
+ void resize()
+ {
+ E[] new_data = (E[]) new Object[2 * storage.length];
+ System.arraycopy(storage, 0, new_data, 0, storage.length);
+ storage = new_data;
+ }
+}
diff --git a/libjava/classpath/java/util/Properties.java b/libjava/classpath/java/util/Properties.java
new file mode 100644
index 000000000..b0f436f42
--- /dev/null
+++ b/libjava/classpath/java/util/Properties.java
@@ -0,0 +1,822 @@
+/* Properties.java -- a set of persistent properties
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.Reader;
+
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Element;
+import org.w3c.dom.bootstrap.DOMImplementationRegistry;
+import org.w3c.dom.ls.DOMImplementationLS;
+import org.w3c.dom.ls.LSOutput;
+import org.w3c.dom.ls.LSSerializer;
+
+/**
+ * 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 umlaut)
+ *
+ *
+<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>
+ *
+ * <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>.
+ *
+ * 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<Object, Object>
+{
+ // 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.
+ *
+ * @serial the default properties
+ */
+ protected Properties defaults;
+
+ /**
+ * Compatible with JDK 1.0+.
+ */
+ private static final long serialVersionUID = 4112578634029874840L;
+
+ /**
+ * Creates a new empty property list with no default values.
+ */
+ public Properties()
+ {
+ }
+
+ /**
+ * Create a new empty property list with the specified default values.
+ *
+ * @param defaults a Properties object containing the default values
+ */
+ public Properties(Properties defaults)
+ {
+ this.defaults = defaults;
+ }
+
+ /**
+ * 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 a character stream. The stream should
+ * have the following format: <br>
+ *
+ * An empty line or a line starting with <code>#</code> or
+ * <code>!</code> is ignored. An backslash (<code>\</code>) at the
+ * end of the line makes the line continueing on the next line
+ * (but make sure there is no whitespace after the backslash).
+ * Otherwise, each line describes a key/value pair. <br>
+ *
+ * The chars up to the first whitespace, = or : are the key. You
+ * can include this caracters in the key, if you precede them with
+ * a backslash (<code>\</code>). The key is followed by optional
+ * whitespaces, optionally one <code>=</code> or <code>:</code>,
+ * and optionally some more whitespaces. The rest of the line is
+ * the resource belonging to the key. <br>
+ *
+ * Escape sequences <code>\t, \n, \r, \\, \", \', \!, \#, \ </code>(a
+ * space), and unicode characters with the
+ * <code>\\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:\\u0020</pre>
+ *
+ * @param inReader the input {@link java.io.Reader}.
+ * @throws IOException if an error occurred when reading the input
+ * @throws NullPointerException if in is null
+ * @since 1.6
+ */
+ public void load(Reader inReader) throws IOException
+ {
+ BufferedReader reader = new BufferedReader(inReader);
+ String line;
+
+ while ((line = reader.readLine()) != null)
+ {
+ char c = 0;
+ int pos = 0;
+ // Leading whitespaces must be deleted first.
+ while (pos < line.length()
+ && Character.isWhitespace(c = line.charAt(pos)))
+ pos++;
+
+ // If empty line or begins with a comment character, skip this line.
+ if ((line.length() - pos) == 0
+ || line.charAt(pos) == '#' || line.charAt(pos) == '!')
+ continue;
+
+ // The characters up to the next Whitespace, ':', or '='
+ // describe the key. But look for escape sequences.
+ // Try to short-circuit when there is no escape char.
+ int start = pos;
+ boolean needsEscape = line.indexOf('\\', pos) != -1;
+ CPStringBuilder key = needsEscape ? new CPStringBuilder() : null;
+ while (pos < line.length()
+ && ! Character.isWhitespace(c = line.charAt(pos++))
+ && c != '=' && c != ':')
+ {
+ if (needsEscape && c == '\\')
+ {
+ if (pos == line.length())
+ {
+ // The line continues on the next line. If there
+ // is no next line, just treat it as a key with an
+ // empty value.
+ line = reader.readLine();
+ if (line == null)
+ line = "";
+ 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 if (needsEscape)
+ key.append(c);
+ }
+
+ boolean isDelim = (c == ':' || c == '=');
+
+ String keyString;
+ if (needsEscape)
+ keyString = key.toString();
+ else if (isDelim || Character.isWhitespace(c))
+ keyString = line.substring(start, pos - 1);
+ else
+ keyString = line.substring(start, 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++;
+ }
+
+ // Short-circuit if no escape chars found.
+ if (!needsEscape)
+ {
+ put(keyString, line.substring(pos));
+ continue;
+ }
+
+ // Escape char found so iterate through the rest of the line.
+ StringBuilder element = new StringBuilder(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();
+
+ // We might have seen a backslash at the end of
+ // the file. The JDK ignores the backslash in
+ // this case, so we follow for compatibility.
+ if (line == null)
+ break;
+
+ 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(keyString, element.toString());
+ }
+ }
+
+ /**
+ * Reads a property list from the supplied input stream.
+ * This method has the same functionality as {@link #load(Reader)}
+ * but the character encoding is assumed to be ISO-8859-1.
+ * Unicode characters not within the Latin1 set supplied by
+ * ISO-8859-1 should be escaped using '\\uXXXX' where XXXX
+ * is the UTF-16 code unit in hexadecimal.
+ *
+ * @param inStream the byte stream to read the property list from.
+ * @throws IOException if an I/O error occurs.
+ * @see #load(Reader)
+ * @since 1.2
+ */
+ public void load(InputStream inStream) throws IOException
+ {
+ load(new InputStreamReader(inStream, "ISO-8859-1"));
+ }
+
+ /**
+ * Calls <code>store(OutputStream out, String header)</code> and
+ * ignores the IOException that may be thrown.
+ *
+ * @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
+ */
+ @Deprecated
+ public void save(OutputStream out, String header)
+ {
+ try
+ {
+ store(out, header);
+ }
+ catch (IOException ex)
+ {
+ }
+ }
+
+ /**
+ * 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>
+ *
+ * Each line has the form <code>key = value</code>. Newlines,
+ * Returns and tabs are written as <code>\n,\t,\r</code> resp.
+ * The characters <code>\, !, #, =</code> and <code>:</code> are
+ * preceeded by a backslash. Spaces are preceded with a backslash,
+ * if and only if they are at the beginning of the key. Characters
+ * that are not in the ascii range 33 to 127 are written in the
+ * <code>\</code><code>u</code>xxxx Form.<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
+ * @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"));
+ if (header != null)
+ writer.println("#" + header);
+ writer.println ("#" + Calendar.getInstance ().getTime ());
+
+ Iterator iter = entrySet ().iterator ();
+ int i = size ();
+ CPStringBuilder s = new CPStringBuilder (); // Reuse the same buffer.
+ while (--i >= 0)
+ {
+ Map.Entry entry = (Map.Entry) iter.next ();
+ formatForOutput ((String) entry.getKey (), s, true);
+ s.append ('=');
+ formatForOutput ((String) entry.getValue (), s, false);
+ writer.println (s);
+ }
+
+ writer.flush ();
+ }
+
+ /**
+ * 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 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)
+ {
+ Properties prop = this;
+ // Eliminate tail recursion.
+ do
+ {
+ String value = (String) prop.get(key);
+ if (value != null)
+ return value;
+ prop = prop.defaults;
+ }
+ while (prop != null);
+ return null;
+ }
+
+ /**
+ * Gets the property with the specified key in this property list. If
+ * the key is not found, the default property list is searched. If the
+ * property is not found in 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)
+ {
+ String prop = getProperty(key);
+ if (prop == null)
+ prop = defaultValue;
+ return prop;
+ }
+
+ /**
+ * 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 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);
+ }
+
+ /**
+ * Prints the key/value pairs to the given print stream. This is
+ * mainly useful for debugging purposes.
+ *
+ * @param out the print stream, where the key/value pairs are written to
+ * @throws ClassCastException if this property contains a key or a
+ * value that isn't a string
+ * @see #list(PrintWriter)
+ */
+ public void list(PrintStream out)
+ {
+ PrintWriter writer = new PrintWriter (out);
+ list (writer);
+ }
+
+ /**
+ * Prints the key/value pairs to the given print writer. This is
+ * mainly useful for debugging purposes.
+ *
+ * @param out the print writer where the key/value pairs are written to
+ * @throws ClassCastException if this property contains a key or a
+ * value that isn't a string
+ * @see #list(PrintStream)
+ * @since 1.1
+ */
+ public void list(PrintWriter out)
+ {
+ out.println ("-- listing properties --");
+
+ Iterator iter = entrySet ().iterator ();
+ int i = size ();
+ while (--i >= 0)
+ {
+ Map.Entry entry = (Map.Entry) iter.next ();
+ out.print ((String) entry.getKey () + "=");
+
+ // JDK 1.3/1.4 restrict the printed value, but not the key,
+ // to 40 characters, including the truncating ellipsis.
+ String s = (String ) entry.getValue ();
+ if (s != null && s.length () > 40)
+ out.println (s.substring (0, 37) + "...");
+ else
+ out.println (s);
+ }
+ out.flush ();
+ }
+
+ /**
+ * 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)
+ */
+ private void formatForOutput(String str, CPStringBuilder buffer, boolean key)
+ {
+ 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++)
+ {
+ 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);
+ break;
+ 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;
+ }
+ }
+
+ /**
+ * <p>
+ * Encodes the properties as an XML file using the UTF-8 encoding.
+ * The format of the XML file matches the DTD
+ * <a href="http://java.sun.com/dtd/properties.dtd">
+ * http://java.sun.com/dtd/properties.dtd</a>.
+ * </p>
+ * <p>
+ * Invoking this method provides the same behaviour as invoking
+ * <code>storeToXML(os, comment, "UTF-8")</code>.
+ * </p>
+ *
+ * @param os the stream to output to.
+ * @param comment a comment to include at the top of the XML file, or
+ * <code>null</code> if one is not required.
+ * @throws IOException if the serialization fails.
+ * @throws NullPointerException if <code>os</code> is null.
+ * @since 1.5
+ */
+ public void storeToXML(OutputStream os, String comment)
+ throws IOException
+ {
+ storeToXML(os, comment, "UTF-8");
+ }
+
+ /**
+ * <p>
+ * Encodes the properties as an XML file using the supplied encoding.
+ * The format of the XML file matches the DTD
+ * <a href="http://java.sun.com/dtd/properties.dtd">
+ * http://java.sun.com/dtd/properties.dtd</a>.
+ * </p>
+ *
+ * @param os the stream to output to.
+ * @param comment a comment to include at the top of the XML file, or
+ * <code>null</code> if one is not required.
+ * @param encoding the encoding to use for the XML output.
+ * @throws IOException if the serialization fails.
+ * @throws NullPointerException if <code>os</code> or <code>encoding</code>
+ * is null.
+ * @since 1.5
+ */
+ public void storeToXML(OutputStream os, String comment, String encoding)
+ throws IOException
+ {
+ if (os == null)
+ throw new NullPointerException("Null output stream supplied.");
+ if (encoding == null)
+ throw new NullPointerException("Null encoding supplied.");
+ try
+ {
+ DOMImplementationRegistry registry =
+ DOMImplementationRegistry.newInstance();
+ DOMImplementation domImpl = registry.getDOMImplementation("LS 3.0");
+ DocumentType doctype =
+ domImpl.createDocumentType("properties", null,
+ "http://java.sun.com/dtd/properties.dtd");
+ Document doc = domImpl.createDocument(null, "properties", doctype);
+ Element root = doc.getDocumentElement();
+ if (comment != null)
+ {
+ Element commentElement = doc.createElement("comment");
+ commentElement.appendChild(doc.createTextNode(comment));
+ root.appendChild(commentElement);
+ }
+ Iterator iterator = entrySet().iterator();
+ while (iterator.hasNext())
+ {
+ Map.Entry entry = (Map.Entry) iterator.next();
+ Element entryElement = doc.createElement("entry");
+ entryElement.setAttribute("key", (String) entry.getKey());
+ entryElement.appendChild(doc.createTextNode((String)
+ entry.getValue()));
+ root.appendChild(entryElement);
+ }
+ DOMImplementationLS loadAndSave = (DOMImplementationLS) domImpl;
+ LSSerializer serializer = loadAndSave.createLSSerializer();
+ LSOutput output = loadAndSave.createLSOutput();
+ output.setByteStream(os);
+ output.setEncoding(encoding);
+ serializer.write(doc, output);
+ }
+ catch (ClassNotFoundException e)
+ {
+ throw (IOException)
+ new IOException("The XML classes could not be found.").initCause(e);
+ }
+ catch (InstantiationException e)
+ {
+ throw (IOException)
+ new IOException("The XML classes could not be instantiated.")
+ .initCause(e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw (IOException)
+ new IOException("The XML classes could not be accessed.")
+ .initCause(e);
+ }
+ }
+
+ /**
+ * <p>
+ * Decodes the contents of the supplied <code>InputStream</code> as
+ * an XML file, which represents a set of properties. The format of
+ * the XML file must match the DTD
+ * <a href="http://java.sun.com/dtd/properties.dtd">
+ * http://java.sun.com/dtd/properties.dtd</a>.
+ * </p>
+ *
+ * @param in the input stream from which to receive the XML data.
+ * @throws IOException if an I/O error occurs in reading the input data.
+ * @throws InvalidPropertiesFormatException if the input data does not
+ * constitute an XML properties
+ * file.
+ * @throws NullPointerException if <code>in</code> is null.
+ * @since 1.5
+ */
+ public void loadFromXML(InputStream in)
+ throws IOException, InvalidPropertiesFormatException
+ {
+ if (in == null)
+ throw new NullPointerException("Null input stream supplied.");
+ try
+ {
+ XMLInputFactory factory = XMLInputFactory.newInstance();
+ // Don't resolve external entity references
+ factory.setProperty("javax.xml.stream.isSupportingExternalEntities",
+ Boolean.FALSE);
+ XMLStreamReader reader = factory.createXMLStreamReader(in);
+ String name, key = null;
+ CPStringBuilder buf = null;
+ while (reader.hasNext())
+ {
+ switch (reader.next())
+ {
+ case XMLStreamConstants.START_ELEMENT:
+ name = reader.getLocalName();
+ if (buf == null && "entry".equals(name))
+ {
+ key = reader.getAttributeValue(null, "key");
+ if (key == null)
+ {
+ String msg = "missing 'key' attribute";
+ throw new InvalidPropertiesFormatException(msg);
+ }
+ buf = new CPStringBuilder();
+ }
+ else if (!"properties".equals(name) && !"comment".equals(name))
+ {
+ String msg = "unexpected element name '" + name + "'";
+ throw new InvalidPropertiesFormatException(msg);
+ }
+ break;
+ case XMLStreamConstants.END_ELEMENT:
+ name = reader.getLocalName();
+ if (buf != null && "entry".equals(name))
+ {
+ put(key, buf.toString());
+ buf = null;
+ }
+ else if (!"properties".equals(name) && !"comment".equals(name))
+ {
+ String msg = "unexpected element name '" + name + "'";
+ throw new InvalidPropertiesFormatException(msg);
+ }
+ break;
+ case XMLStreamConstants.CHARACTERS:
+ case XMLStreamConstants.SPACE:
+ case XMLStreamConstants.CDATA:
+ if (buf != null)
+ buf.append(reader.getText());
+ break;
+ }
+ }
+ reader.close();
+ }
+ catch (XMLStreamException e)
+ {
+ throw (InvalidPropertiesFormatException)
+ new InvalidPropertiesFormatException("Error in parsing XML.").
+ initCause(e);
+ }
+ }
+
+} // class Properties
diff --git a/libjava/classpath/java/util/PropertyPermission.java b/libjava/classpath/java/util/PropertyPermission.java
new file mode 100644
index 000000000..6ed4690d8
--- /dev/null
+++ b/libjava/classpath/java/util/PropertyPermission.java
@@ -0,0 +1,271 @@
+/* PropertyPermission.java -- permission to get and set System properties
+ Copyright (C) 1999, 2000, 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamField;
+import java.security.BasicPermission;
+import java.security.Permission;
+import java.security.PermissionCollection;
+
+/**
+ * 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>
+ *
+ * The action string is a comma-separated list of keywords. There are
+ * two possible actions:
+ * <dl>
+ * <dt>read</dt>
+ * <dd>Allows to read the property via <code>System.getProperty</code>.</dd>
+ * <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
+ * @see SecurityManager
+ * @author Jochen Hoenicke
+ * @since 1.2
+ * @status updated to 1.4
+ */
+public final class PropertyPermission extends BasicPermission
+{
+ /**
+ * 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;
+
+ /** 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 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);
+ }
+
+ /**
+ * Parse the action string and convert actions from external to internal
+ * form. This will set the internal actions field.
+ *
+ * @param str the action string
+ * @throws IllegalArgumentException if actions string contains an
+ * illegal action
+ */
+ private void setActions(String str)
+ {
+ // Initialising the class java.util.Locale ...
+ // tries to initialise the Locale.defaultLocale static
+ // which calls System.getProperty,
+ // which calls SecurityManager.checkPropertiesAccess,
+ // which creates a PropertyPermission with action "read,write",
+ // which calls setActions("read,write").
+ // If we now were to call toLowerCase on 'str',
+ // this would call Locale.getDefault() which returns null
+ // because Locale.defaultLocale hasn't been set yet
+ // then toLowerCase will fail with a null pointer exception.
+ //
+ // The solution is to take a punt on 'str' being lower case, and
+ // test accordingly. If that fails, we convert 'str' to lower case
+ // and try the tests again.
+ 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
+ {
+ String lstr = str.toLowerCase();
+ if ("read".equals(lstr))
+ actions = READ;
+ else if ("write".equals(lstr))
+ actions = WRITE;
+ else if ("read,write".equals(lstr) || "write,read".equals(lstr))
+ 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();
+ }
+
+ /**
+ * Check if this permission implies p. This returns true iff all of
+ * the following conditions are true:
+ * <ul>
+ * <li> p is a PropertyPermission </li>
+ * <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)
+ {
+ // 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; this is true if it has the same name and
+ * actions.
+ *
+ * @param obj the other object
+ * @return true if the two are equivalent
+ */
+ public boolean equals(Object obj)
+ {
+ return super.equals(obj) && actions == ((PropertyPermission) obj).actions;
+ }
+
+ /**
+ * Returns the hash code for this permission. It is equivalent to
+ * <code>getName().hashCode()</code>.
+ *
+ * @return the hash code
+ */
+ public int hashCode()
+ {
+ return super.hashCode();
+ }
+
+ /**
+ * 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"
+ */
+ public String getActions()
+ {
+ return actionStrings[actions];
+ }
+
+ /**
+ * Returns a permission collection suitable to take
+ * PropertyPermission objects.
+ *
+ * @return a new empty PermissionCollection
+ */
+ public PermissionCollection newPermissionCollection()
+ {
+ return new PropertyPermissionCollection();
+ }
+}
diff --git a/libjava/classpath/java/util/PropertyPermissionCollection.java b/libjava/classpath/java/util/PropertyPermissionCollection.java
new file mode 100644
index 000000000..768b11225
--- /dev/null
+++ b/libjava/classpath/java/util/PropertyPermissionCollection.java
@@ -0,0 +1,166 @@
+/* PropertyPermissionCollection.java -- a collection of PropertyPermissions
+ Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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:
+ *
+ * <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 - 1);
+ 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();
+ }
+}
diff --git a/libjava/classpath/java/util/PropertyResourceBundle.java b/libjava/classpath/java/util/PropertyResourceBundle.java
new file mode 100644
index 000000000..b528f08ca
--- /dev/null
+++ b/libjava/classpath/java/util/PropertyResourceBundle.java
@@ -0,0 +1,171 @@
+/* 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.
+
+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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+/**
+ * This class is a concrete <code>ResourceBundle</code> that gets it
+ * resources from a property file. This implies that the resources are
+ * strings. For more information about resource bundles see the class
+ * <code>ResourceBundle</code>.
+ *
+ * You should not use this class directly, or subclass it, but you get
+ * an object of this class automatically when you call
+ * <code>ResourceBundle.getBundle()</code> and there is a properties
+ * file.
+ *
+ * If there is also a class for this resource and the same locale, the
+ * 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>
+ *
+ * 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 char <code>\u00e4</code> is the
+ * german umlaut)
+ *
+ *
+<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(InputStream)
+ * @since 1.1
+ * @status updated to 1.4
+ */
+public class PropertyResourceBundle extends ResourceBundle
+{
+ /** The properties file this bundle is based on. */
+ private Properties properties;
+
+ /**
+ * Creates a new property resource bundle. The property file must
+ * be encoded using ISO-8859-1.
+ *
+ * @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(InputStream stream) throws IOException
+ {
+ properties = new Properties();
+ properties.load(stream);
+ }
+
+ /**
+ * Creates a new property resource bundle. The encoding of the property
+ * file is determined by the supplied {@link Reader} object.
+ *
+ * @param reader an input stream, where the resources are read from
+ * @throws NullPointerException if stream is null
+ * @throws IOException if reading the stream fails
+ * @since 1.6
+ */
+ public PropertyResourceBundle(Reader reader) throws IOException
+ {
+ properties = new Properties();
+ properties.load(reader);
+ }
+
+ /**
+ * 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 exist
+ */
+ public Object handleGetObject(String key)
+ {
+ return properties.getProperty(key);
+ }
+
+ /**
+ * This method should return all keys for which a resource exists.
+ *
+ * @return an enumeration of the keys
+ */
+ public Enumeration<String> getKeys()
+ {
+ if (parent == null)
+ // FIXME: bogus cast.
+ return (Enumeration<String>) 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<String> s = new HashSet<String>();
+ // FIXME: bogus cast.
+ Enumeration<String> e = (Enumeration<String>) properties.propertyNames();
+ while (e.hasMoreElements())
+ s.add(e.nextElement());
+ ResourceBundle bundle = parent;
+ // Eliminate tail recursion.
+ do
+ {
+ e = bundle.getKeys();
+ while (e.hasMoreElements())
+ s.add(e.nextElement());
+ bundle = bundle.parent;
+ }
+ while (bundle != null);
+ return Collections.enumeration(s);
+ }
+} // class PropertyResourceBundle
diff --git a/libjava/classpath/java/util/Random.java b/libjava/classpath/java/util/Random.java
new file mode 100644
index 000000000..999e89547
--- /dev/null
+++ b/libjava/classpath/java/util/Random.java
@@ -0,0 +1,429 @@
+/* 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.
+
+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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.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. 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
+ * same way. This should be even true for foreign implementations
+ * (like this), so every port must use the same algorithm as described
+ * here.
+ *
+ * If you want to implement your own pseudorandom algorithm, you
+ * should extend this class and overload the <code>next()</code> and
+ * <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
+ * generating passwords or encryption keys. See <code>SecureRandom</code>
+ * in package <code>java.security</code> for this purpose.
+ *
+ * For simple random doubles between 0.0 and 1.0, you may consider using
+ * Math.random instead.
+ *
+ * @see java.security.SecureRandom
+ * @see Math#random()
+ * @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.
+ *
+ * @serial whether nextNextGaussian is available
+ * @see #nextGaussian()
+ * @see #nextNextGaussian
+ */
+ private boolean haveNextNextGaussian;
+
+ /**
+ * 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.
+ *
+ * @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.
+ *
+ * @serial the internal state of this generator
+ * @see #next(int)
+ */
+ 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 if by
+ * <code>setSeed(System.currentTimeMillis());</code>.
+ *
+ * @see System#currentTimeMillis()
+ */
+ public Random()
+ {
+ this(System.currentTimeMillis());
+ }
+
+ /**
+ * Creates a new pseudorandom number generator, starting with the
+ * specified seed, using <code>setSeed(seed);</code>.
+ *
+ * @param seed the initial seed
+ */
+ public Random(long seed)
+ {
+ setSeed(seed);
+ }
+
+ /**
+ * Sets the seed for this pseudorandom number generator. As described
+ * 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 &lt;&lt; 48) - 1);
+ haveNextNextGaussian = false;
+}</pre>
+ *
+ * @param seed the new seed
+ */
+ public synchronized void setSeed(long seed)
+ {
+ this.seed = (seed ^ 0x5DEECE66DL) & ((1L << 48) - 1);
+ haveNextNextGaussian = false;
+ }
+
+ /**
+ * Generates the next pseudorandom number. This returns
+ * 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 &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)
+ {
+ seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
+ return (int) (seed >>> (48 - bits));
+ }
+
+ /**
+ * 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 &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)
+ {
+ int random;
+ // 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);
+ }
+ if (max < bytes.length)
+ {
+ random = next(32);
+ for (int j = max; j < bytes.length; j++)
+ {
+ bytes[j] = (byte) random;
+ random >>= 8;
+ }
+ }
+ }
+
+ /**
+ * Generates the next pseudorandom number. This returns
+ * 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>
+ *
+ * @return the next pseudorandom value
+ */
+ public int nextInt()
+ {
+ return next(32);
+ }
+
+ /**
+ * Generates the next pseudorandom number. This returns
+ * a value between 0(inclusive) and <code>n</code>(exclusive), and
+ * 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 &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(31);
+ 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 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
+ * ``less random'' than the high order bits.
+ *
+ * @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)
+ {
+ 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(31);
+ val = bits % n;
+ }
+ while (bits - val + (n - 1) < 0);
+ return val;
+ }
+
+ /**
+ * 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) &lt;&lt; 32) + next(32);
+}</pre>
+ *
+ * @return the next pseudorandom value
+ */
+ public long nextLong()
+ {
+ return ((long) next(32) << 32) + next(32);
+ }
+
+ /**
+ * 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
+ * @since 1.2
+ */
+ public boolean nextBoolean()
+ {
+ return next(1) != 0;
+ }
+
+ /**
+ * Generates the next pseudorandom float uniformly distributed
+ * between 0.0f (inclusive) and 1.0f (exclusive). The
+ * implementation is as follows.
+ *
+<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);
+ }
+
+ /**
+ * Generates the next pseudorandom double uniformly distributed
+ * between 0.0 (inclusive) and 1.0 (exclusive). The
+ * implementation is as follows.
+ *
+<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
+ * 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>
+ *
+ * <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
+ */
+ public synchronized double nextGaussian()
+ {
+ if (haveNextNextGaussian)
+ {
+ haveNextNextGaussian = false;
+ return nextNextGaussian;
+ }
+ 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;
+ }
+}
diff --git a/libjava/classpath/java/util/RandomAccess.java b/libjava/classpath/java/util/RandomAccess.java
new file mode 100644
index 000000000..054266a1c
--- /dev/null
+++ b/libjava/classpath/java/util/RandomAccess.java
@@ -0,0 +1,64 @@
+/* RandomAccess.java -- A tagging interface that lists can use to tailor
+ operations to the correct algorithm
+ Copyright (C) 2001, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Marker interface used to inform <code>List</code> implementations that
+ * they support fast (usually constant time) random access. This allows
+ * generic list algorithms to tailor their behavior based on the list
+ * type.
+ * <p>
+ *
+ * For example, some sorts are n*log(n) on an array, but decay to quadratic
+ * time on a linked list. As a rule of thumb, this interface should be
+ * used is this loop:<br>
+ * <code>for (int i = 0, n = list.size(); i &lt; n; i++) list.get(i);</code>
+ * <br>runs faster than this loop:<br>
+ * <code>for (Iterator i = list.iterator(); i.hasNext(); ) i.next();</code>
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see List
+ * @since 1.4
+ * @status updated to 1.4
+ */
+public interface RandomAccess
+{
+ // Tagging interface only.
+}
diff --git a/libjava/classpath/java/util/ResourceBundle.java b/libjava/classpath/java/util/ResourceBundle.java
new file mode 100644
index 000000000..966eb026c
--- /dev/null
+++ b/libjava/classpath/java/util/ResourceBundle.java
@@ -0,0 +1,625 @@
+/* ResourceBundle -- aids in loading resource bundles
+ Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004, 2005, 2006
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.classpath.VMStackWalker;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A resource bundle contains locale-specific data. If you need localized
+ * data, you can load a resource bundle that matches the locale with
+ * <code>getBundle</code>. Now you can get your object by calling
+ * <code>getObject</code> or <code>getString</code> on that bundle.
+ *
+ * <p>When a bundle is demanded for a specific locale, the ResourceBundle
+ * is searched in following order (<i>def. language</i> stands for the
+ * two letter ISO language code of the default locale (see
+ * <code>Locale.getDefault()</code>).
+ *
+<pre>baseName_<i>language code</i>_<i>country code</i>_<i>variant</i>
+baseName_<i>language code</i>_<i>country code</i>
+baseName_<i>language code</i>
+baseName_<i>def. language</i>_<i>def. country</i>_<i>def. variant</i>
+baseName_<i>def. language</i>_<i>def. country</i>
+baseName_<i>def. language</i>
+baseName</pre>
+ *
+ * <p>A bundle is backed up by less specific bundles (omitting variant, country
+ * or language). But it is not backed up by the default language locale.
+ *
+ * <p>If you provide a bundle for a given locale, say
+ * <code>Bundle_en_UK_POSIX</code>, you must also provide a bundle for
+ * all sub locales, ie. <code>Bundle_en_UK</code>, <code>Bundle_en</code>, and
+ * <code>Bundle</code>.
+ *
+ * <p>When a bundle is searched, we look first for a class with the given
+ * name, then for a file with <code>.properties</code> extension in the
+ * classpath. The name must be a fully qualified classname (with dots as
+ * path separators).
+ *
+ * <p>(Note: This implementation always backs up the class with a properties
+ * file if that is existing, but you shouldn't rely on this, if you want to
+ * be compatible to the standard JDK.)
+ *
+ * @author Jochen Hoenicke
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Locale
+ * @see ListResourceBundle
+ * @see PropertyResourceBundle
+ * @since 1.1
+ * @status updated to 1.4
+ */
+public abstract class ResourceBundle
+{
+ /**
+ * Maximum size of our cache of <code>ResourceBundle</code>s keyed by
+ * {@link BundleKey} instances.
+ *
+ * @see BundleKey
+ */
+ private static final int CACHE_SIZE = 100;
+
+ /**
+ * The parent bundle. This is consulted when you call getObject and there
+ * is no such resource in the current bundle. This field may be null.
+ */
+ protected ResourceBundle parent;
+
+ /**
+ * The locale of this resource bundle. You can read this with
+ * <code>getLocale</code> and it is automatically set in
+ * <code>getBundle</code>.
+ */
+ private Locale locale;
+
+ /**
+ * A VM-wide cache of resource bundles already fetched.
+ * <p>
+ * This {@link Map} is a Least Recently Used (LRU) cache, of the last
+ * {@link #CACHE_SIZE} accessed <code>ResourceBundle</code>s keyed by the
+ * tuple: default locale, resource-bundle name, resource-bundle locale, and
+ * classloader.
+ *
+ * @see BundleKey
+ */
+ private static Map<BundleKey,Object> bundleCache =
+ new LinkedHashMap<BundleKey,Object>(CACHE_SIZE + 1, 0.75F, true)
+ {
+ public boolean removeEldestEntry(Map.Entry<BundleKey,Object> entry)
+ {
+ return size() > CACHE_SIZE;
+ }
+ };
+
+ /**
+ * The constructor. It does nothing special.
+ */
+ public ResourceBundle()
+ {
+ }
+
+ /**
+ * Get a String from this resource bundle. Since most localized Objects
+ * are Strings, this method provides a convenient way to get them without
+ * casting.
+ *
+ * @param key the name of the resource
+ * @throws MissingResourceException if the resource can't be found
+ * @throws NullPointerException if key is null
+ * @throws ClassCastException if resource is not a string
+ */
+ public final String getString(String key)
+ {
+ return (String) getObject(key);
+ }
+
+ /**
+ * Get an array of Strings from this resource bundle. This method
+ * provides a convenient way to get it without casting.
+ *
+ * @param key the name of the resource
+ * @throws MissingResourceException if the resource can't be found
+ * @throws NullPointerException if key is null
+ * @throws ClassCastException if resource is not a string
+ */
+ public final String[] getStringArray(String key)
+ {
+ return (String[]) getObject(key);
+ }
+
+ /**
+ * Get an object from this resource bundle. This will call
+ * <code>handleGetObject</code> for this resource and all of its parents,
+ * until it finds a non-null resource.
+ *
+ * @param key the name of the resource
+ * @throws MissingResourceException if the resource can't be found
+ * @throws NullPointerException if key is null
+ */
+ public final Object getObject(String key)
+ {
+ for (ResourceBundle bundle = this; bundle != null; bundle = bundle.parent)
+ {
+ Object o = bundle.handleGetObject(key);
+ if (o != null)
+ return o;
+ }
+
+ String className = getClass().getName();
+ throw new MissingResourceException("Key '" + key
+ + "'not found in Bundle: "
+ + className, className, key);
+ }
+
+ /**
+ * Return the actual locale of this bundle. You can use it after calling
+ * getBundle, to know if the bundle for the desired locale was loaded or
+ * if the fall back was used.
+ *
+ * @return the bundle's locale
+ */
+ public Locale getLocale()
+ {
+ return locale;
+ }
+
+ /**
+ * Set the parent of this bundle. The parent is consulted when you call
+ * getObject and there is no such resource in the current bundle.
+ *
+ * @param parent the parent of this bundle
+ */
+ protected void setParent(ResourceBundle parent)
+ {
+ this.parent = parent;
+ }
+
+ /**
+ * Get the appropriate ResourceBundle for the default locale. This is like
+ * calling <code>getBundle(baseName, Locale.getDefault(),
+ * getClass().getClassLoader()</code>, except that any security check of
+ * getClassLoader won't fail.
+ *
+ * @param baseName the name of the ResourceBundle
+ * @return the desired resource bundle
+ * @throws MissingResourceException if the resource bundle can't be found
+ * @throws NullPointerException if baseName is null
+ */
+ public static ResourceBundle getBundle(String baseName)
+ {
+ ClassLoader cl = VMStackWalker.getCallingClassLoader();
+ if (cl == null)
+ cl = ClassLoader.getSystemClassLoader();
+ return getBundle(baseName, Locale.getDefault(), cl);
+ }
+
+ /**
+ * Get the appropriate ResourceBundle for the given locale. This is like
+ * calling <code>getBundle(baseName, locale,
+ * getClass().getClassLoader()</code>, except that any security check of
+ * getClassLoader won't fail.
+ *
+ * @param baseName the name of the ResourceBundle
+ * @param locale A locale
+ * @return the desired resource bundle
+ * @throws MissingResourceException if the resource bundle can't be found
+ * @throws NullPointerException if baseName or locale is null
+ */
+ public static ResourceBundle getBundle(String baseName, Locale locale)
+ {
+ ClassLoader cl = VMStackWalker.getCallingClassLoader();
+ if (cl == null)
+ cl = ClassLoader.getSystemClassLoader();
+ return getBundle(baseName, locale, cl);
+ }
+
+ /** Cache key for the ResourceBundle cache. Resource bundles are keyed
+ by the combination of bundle name, locale, and class loader. */
+ private static class BundleKey
+ {
+ Locale defaultLocale;
+ String baseName;
+ Locale locale;
+ ClassLoader classLoader;
+ int hashcode;
+
+ BundleKey() {}
+
+ BundleKey(Locale dl, String s, Locale l, ClassLoader cl)
+ {
+ set(dl, s, l, cl);
+ }
+
+ void set(Locale dl, String s, Locale l, ClassLoader cl)
+ {
+ defaultLocale = dl;
+ baseName = s;
+ locale = l;
+ classLoader = cl;
+ hashcode = defaultLocale.hashCode() ^ baseName.hashCode()
+ ^ locale.hashCode() ^ classLoader.hashCode();
+ }
+
+ public int hashCode()
+ {
+ return hashcode;
+ }
+
+ public boolean equals(Object o)
+ {
+ if (! (o instanceof BundleKey))
+ return false;
+ BundleKey key = (BundleKey) o;
+ return hashcode == key.hashcode
+ && defaultLocale.equals(key.defaultLocale)
+ && baseName.equals(key.baseName)
+ && locale.equals(key.locale)
+ && classLoader.equals(key.classLoader);
+ }
+
+ public String toString()
+ {
+ CPStringBuilder builder = new CPStringBuilder(getClass().getName());
+ builder.append("[defaultLocale=");
+ builder.append(defaultLocale);
+ builder.append(",baseName=");
+ builder.append(baseName);
+ builder.append(",locale=");
+ builder.append(locale);
+ builder.append(",classLoader=");
+ builder.append(classLoader);
+ builder.append("]");
+ return builder.toString();
+ }
+ }
+
+ /** A cache lookup key. This avoids having to a new one for every
+ * getBundle() call. */
+ private static final BundleKey lookupKey = new BundleKey();
+
+ /** Singleton cache entry to represent previous failed lookups. */
+ private static final Object nullEntry = new Object();
+
+ /**
+ * Get the appropriate ResourceBundle for the given locale. The following
+ * strategy is used:
+ *
+ * <p>A sequence of candidate bundle names are generated, and tested in
+ * this order, where the suffix 1 means the string from the specified
+ * locale, and the suffix 2 means the string from the default locale:</p>
+ *
+ * <ul>
+ * <li>baseName + "_" + language1 + "_" + country1 + "_" + variant1</li>
+ * <li>baseName + "_" + language1 + "_" + country1</li>
+ * <li>baseName + "_" + language1</li>
+ * <li>baseName + "_" + language2 + "_" + country2 + "_" + variant2</li>
+ * <li>baseName + "_" + language2 + "_" + country2</li>
+ * <li>baseName + "_" + language2</li>
+ * <li>baseName</li>
+ * </ul>
+ *
+ * <p>In the sequence, entries with an empty string are ignored. Next,
+ * <code>getBundle</code> tries to instantiate the resource bundle:</p>
+ *
+ * <ul>
+ * <li>First, an attempt is made to load a class in the specified classloader
+ * which is a subclass of ResourceBundle, and which has a public constructor
+ * with no arguments, via reflection.</li>
+ * <li>Next, a search is made for a property resource file, by replacing
+ * '.' with '/' and appending ".properties", and using
+ * ClassLoader.getResource(). If a file is found, then a
+ * PropertyResourceBundle is created from the file's contents.</li>
+ * </ul>
+ * If no resource bundle was found, a MissingResourceException is thrown.
+ *
+ * <p>Next, the parent chain is implemented. The remaining candidate names
+ * in the above sequence are tested in a similar manner, and if any results
+ * in a resource bundle, it is assigned as the parent of the first bundle
+ * using the <code>setParent</code> method (unless the first bundle already
+ * has a parent).</p>
+ *
+ * <p>For example, suppose the following class and property files are
+ * provided: MyResources.class, MyResources_fr_CH.properties,
+ * MyResources_fr_CH.class, MyResources_fr.properties,
+ * MyResources_en.properties, and MyResources_es_ES.class. The contents of
+ * all files are valid (that is, public non-abstract subclasses of
+ * ResourceBundle with public nullary constructors for the ".class" files,
+ * syntactically correct ".properties" files). The default locale is
+ * Locale("en", "UK").</p>
+ *
+ * <p>Calling getBundle with the shown locale argument values instantiates
+ * resource bundles from the following sources:</p>
+ *
+ * <ul>
+ * <li>Locale("fr", "CH"): result MyResources_fr_CH.class, parent
+ * MyResources_fr.properties, parent MyResources.class</li>
+ * <li>Locale("fr", "FR"): result MyResources_fr.properties, parent
+ * MyResources.class</li>
+ * <li>Locale("de", "DE"): result MyResources_en.properties, parent
+ * MyResources.class</li>
+ * <li>Locale("en", "US"): result MyResources_en.properties, parent
+ * MyResources.class</li>
+ * <li>Locale("es", "ES"): result MyResources_es_ES.class, parent
+ * MyResources.class</li>
+ * </ul>
+ *
+ * <p>The file MyResources_fr_CH.properties is never used because it is hidden
+ * by MyResources_fr_CH.class.</p>
+ *
+ * @param baseName the name of the ResourceBundle
+ * @param locale A locale
+ * @param classLoader a ClassLoader
+ * @return the desired resource bundle
+ * @throws MissingResourceException if the resource bundle can't be found
+ * @throws NullPointerException if any argument is null
+ * @since 1.2
+ */
+ // This method is synchronized so that the cache is properly
+ // handled.
+ public static synchronized ResourceBundle getBundle
+ (String baseName, Locale locale, ClassLoader classLoader)
+ {
+ Locale defaultLocale = Locale.getDefault();
+ // This will throw NullPointerException if any arguments are null.
+ lookupKey.set(defaultLocale, baseName, locale, classLoader);
+ Object obj = bundleCache.get(lookupKey);
+ if (obj instanceof ResourceBundle)
+ return (ResourceBundle) obj;
+
+ if (obj == nullEntry)
+ throw new MissingResourceException("Bundle " + baseName
+ + " not found for locale " + locale
+ + " by classloader " + classLoader,
+ baseName, "");
+ // First, look for a bundle for the specified locale. We don't want
+ // the base bundle this time.
+ boolean wantBase = locale.equals(defaultLocale);
+ ResourceBundle bundle = tryBundle(baseName, locale, classLoader, wantBase);
+ // Try the default locale if neccessary.
+ if (bundle == null && ! wantBase)
+ bundle = tryBundle(baseName, defaultLocale, classLoader, true);
+
+ BundleKey key = new BundleKey(defaultLocale, baseName, locale, classLoader);
+ if (bundle == null)
+ {
+ // Cache the fact that this lookup has previously failed.
+ bundleCache.put(key, nullEntry);
+ throw new MissingResourceException("Bundle " + baseName
+ + " not found for locale " + locale
+ + " by classloader " + classLoader,
+ baseName, "");
+ }
+ // Cache the result and return it.
+ bundleCache.put(key, bundle);
+ return bundle;
+ }
+
+ /**
+ * Override this method to provide the resource for a keys. This gets
+ * called by <code>getObject</code>. If you don't have a resource
+ * for the given key, you should return null instead throwing a
+ * MissingResourceException. You don't have to ask the parent, getObject()
+ * already does this; nor should you throw a MissingResourceException.
+ *
+ * @param key the key of the resource
+ * @return the resource for the key, or null if not in bundle
+ * @throws NullPointerException if key is null
+ */
+ protected abstract Object handleGetObject(String key);
+
+ /**
+ * This method should return all keys for which a resource exists; you
+ * should include the enumeration of any parent's keys, after filtering out
+ * duplicates.
+ *
+ * @return an enumeration of the keys
+ */
+ public abstract Enumeration<String> getKeys();
+
+ /**
+ * Tries to load a class or a property file with the specified name.
+ *
+ * @param localizedName the name
+ * @param classloader the classloader
+ * @return the resource bundle if it was loaded, otherwise the backup
+ */
+ private static ResourceBundle tryBundle(String localizedName,
+ ClassLoader classloader)
+ {
+ ResourceBundle bundle = null;
+ try
+ {
+ Class<?> rbClass;
+ if (classloader == null)
+ rbClass = Class.forName(localizedName);
+ else
+ rbClass = classloader.loadClass(localizedName);
+ // Note that we do the check up front instead of catching
+ // ClassCastException. The reason for this is that some crazy
+ // programs (Eclipse) have classes that do not extend
+ // ResourceBundle but that have the same name as a property
+ // bundle; in fact Eclipse relies on ResourceBundle not
+ // instantiating these classes.
+ if (ResourceBundle.class.isAssignableFrom(rbClass))
+ bundle = (ResourceBundle) rbClass.newInstance();
+ }
+ catch (Exception ex) {}
+
+ if (bundle == null)
+ {
+ try
+ {
+ InputStream is;
+ String resourceName
+ = localizedName.replace('.', '/') + ".properties";
+ if (classloader == null)
+ is = ClassLoader.getSystemResourceAsStream(resourceName);
+ else
+ is = classloader.getResourceAsStream(resourceName);
+ if (is != null)
+ bundle = new PropertyResourceBundle(is);
+ }
+ catch (IOException ex)
+ {
+ MissingResourceException mre = new MissingResourceException
+ ("Failed to load bundle: " + localizedName, localizedName, "");
+ mre.initCause(ex);
+ throw mre;
+ }
+ }
+
+ return bundle;
+ }
+
+ /**
+ * Tries to load the bundle for a given locale, also loads the backup
+ * locales with the same language.
+ *
+ * @param baseName the raw bundle name, without locale qualifiers
+ * @param locale the locale
+ * @param classLoader the classloader
+ * @param wantBase whether a resource bundle made only from the base name
+ * (with no locale information attached) should be returned.
+ * @return the resource bundle if it was loaded, otherwise the backup
+ */
+ private static ResourceBundle tryBundle(String baseName, Locale locale,
+ ClassLoader classLoader,
+ boolean wantBase)
+ {
+ String language = locale.getLanguage();
+ String country = locale.getCountry();
+ String variant = locale.getVariant();
+
+ int baseLen = baseName.length();
+
+ // Build up a CPStringBuilder containing the complete bundle name, fully
+ // qualified by locale.
+ CPStringBuilder sb = new CPStringBuilder(baseLen + variant.length() + 7);
+
+ sb.append(baseName);
+
+ if (language.length() > 0)
+ {
+ sb.append('_');
+ sb.append(language);
+
+ if (country.length() > 0)
+ {
+ sb.append('_');
+ sb.append(country);
+
+ if (variant.length() > 0)
+ {
+ sb.append('_');
+ sb.append(variant);
+ }
+ }
+ }
+
+ // Now try to load bundles, starting with the most specialized name.
+ // Build up the parent chain as we go.
+ String bundleName = sb.toString();
+ ResourceBundle first = null; // The most specialized bundle.
+ ResourceBundle last = null; // The least specialized bundle.
+
+ while (true)
+ {
+ ResourceBundle foundBundle = tryBundle(bundleName, classLoader);
+ if (foundBundle != null)
+ {
+ if (first == null)
+ first = foundBundle;
+ if (last != null)
+ last.parent = foundBundle;
+ foundBundle.locale = locale;
+ last = foundBundle;
+ }
+ int idx = bundleName.lastIndexOf('_');
+ // Try the non-localized base name only if we already have a
+ // localized child bundle, or wantBase is true.
+ if (idx > baseLen || (idx == baseLen && (first != null || wantBase)))
+ bundleName = bundleName.substring(0, idx);
+ else
+ break;
+ }
+
+ return first;
+ }
+
+ /**
+ * Remove all resources from the cache that were loaded
+ * using the class loader of the calling class.
+ *
+ * @since 1.6
+ */
+ public static final void clearCache()
+ {
+ clearCache(VMStackWalker.getCallingClassLoader());
+ }
+
+ /**
+ * Remove all resources from the cache that were loaded
+ * using the specified class loader.
+ *
+ * @param loader the loader used for the bundles that will be removed.
+ * @throws NullPointerException if {@code loader} is {@code null}.
+ * @since 1.6
+ */
+ public static final void clearCache(ClassLoader loader)
+ {
+ if (loader == null)
+ throw new NullPointerException("The loader can not be null.");
+ synchronized (ResourceBundle.class)
+ {
+ Iterator<BundleKey> iter = bundleCache.keySet().iterator();
+ while (iter.hasNext())
+ {
+ BundleKey key = iter.next();
+ if (key.classLoader == loader)
+ iter.remove();
+ }
+ }
+ }
+
+}
diff --git a/libjava/classpath/java/util/Scanner.java b/libjava/classpath/java/util/Scanner.java
new file mode 100644
index 000000000..59c4cc0ff
--- /dev/null
+++ b/libjava/classpath/java/util/Scanner.java
@@ -0,0 +1,2223 @@
+/* java.util.Scanner -- Parses primitive types and strings using regexps
+ Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.channels.ReadableByteChannel;
+
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.NumberFormat;
+import java.text.ParseException;
+
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.regex.MatchResult;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author E0327023 Hernadi Laszlo
+*/
+public class Scanner
+ implements Iterator <String>
+{
+ private static final String NOT_LONG = "\" is not a long"; //$NON-NLS-1$
+
+ private static final String ERR_PREFIX = "\""; //$NON-NLS-1$
+
+ private static final String NOT_INT = "\" is not an integer"; //$NON-NLS-1$
+
+ private static final String NOT_DOUBLE = "\" is not a double"; //$NON-NLS-1$
+
+ private static final String NOT_BYTE = "\" is not a byte"; //$NON-NLS-1$
+
+ private static final String NOT_BOOLEAN = "\" is not a boolean"; //$NON-NLS-1$
+
+ private static final String IS_NOT = "\" is not "; //$NON-NLS-1$
+
+ private static final String DEFAULT_PATTERN_S = "\\p{javaWhitespace}+"; //$NON-NLS-1$
+
+ private static final Pattern DEFAULT_PATTERN =
+ Pattern.compile (DEFAULT_PATTERN_S);
+
+ private static final String BIG_INTEGER = "BigInteger"; //$NON-NLS-1$
+
+ private final static String NEW_LINE =
+ System.getProperty ("line.separator");
+
+ private IOException lastIOException = null;
+
+ /**
+ * An InputStream source if a Constructor with an InputStream source is called, otherwise it
+ * stays <source> null </source>.
+ */
+ private InputStream bIS = null;
+
+ /**
+ * Length of the input Buffer, which is the maximum bytes to be read at once.
+ */
+ private final int MaxBufferLen = 1000000;
+
+ /**
+ * Minimum buffer length. If there are less chars in the Buffer than this value reading from
+ * source is tried.
+ */
+ private final int MIN_BUF_LEN = 100;
+
+ /**
+ * Maximum number of processed chars in the Buffer. If exeeded, all processed chars from the
+ * beginning of the Buffer will be discarded to save space. The bytes left are copyed into a new
+ * Buffer.
+ */
+ private final int MAX_PREFIX = 10000;
+
+ /**
+ * The Buffer which is used by the Matcher to find given patterns. It is filled up when matcher
+ * hits end or <code> MIN_BUF_LEN </code> is reached.
+ */
+ private String actBuffer = new String ();
+
+ /**
+ * The current radix to use by the methods getNextXXX and hasNextXXX.
+ */
+ private int currentRadix = 10;
+
+ /**
+ * The current locale.
+ *
+ * @see #useLocale(Locale)
+ * @see #locale()
+ */
+ private Locale actLocale = Locale.getDefault ();
+
+ /**
+ * The current pattern for the matcher.
+ */
+ private Pattern p = DEFAULT_PATTERN;
+
+ /**
+ * The current position in the Buffer, at which the next match should start.
+ */
+ private int actPos = 0;
+
+ /**
+ * A global buffer to save new allocations by reading from source.
+ */
+ private final byte[] tmpBuffer = new byte[this.MaxBufferLen];
+
+ /**
+ * The charsetName to use with the source.
+ */
+ private String charsetName = null;
+
+ /**
+ * The Matcher which is used.
+ */
+ private Matcher myMatcher = this.p.matcher (this.actBuffer);
+
+ /**
+ * The MatchResult is generated at each match, even if match() isn't called.
+ */
+ private MatchResult actResult = null;
+
+ /**
+ * A Readable source if a Constructor with a Readable source is called, otherwise it stays
+ * <source> null </source>.
+ */
+ private Readable readableSource = null;
+
+ /**
+ * A ReadableByteChannel source if a Constructor with a ReadableByteChannel source is called,
+ * otherwise it stays <source> null </source>.
+ */
+ private ReadableByteChannel rbcSource = null;
+
+ /**
+ * Indicates if the close() method was called.
+ */
+ private boolean isClosed = false;
+
+ /**
+ * For performance reasons the last Found is saved, if a hasNextXXX method was called.
+ */
+ private String lastFound = null;
+
+ private boolean lastFoundPresent = false;
+
+ private int lastNextPos = 0;
+
+ private int lastPatternHash = 0;
+
+ private int last_RegionStart = 0;
+
+ private int last_RegionEnd = 0;
+
+ private boolean last_anchor = false;
+
+ private boolean last_transparent = false;
+
+ private MatchResult lastResult = null;
+
+ /**
+ * To keep track of the current position in the stream for the toString method, each time
+ * processed chars are removed the amount is added to processedChars.
+ */
+ private int procesedChars = 0;
+
+ /**
+ * needInput is set <code> true </code> before a read method, and if there is no input it blocks
+ * and stays <code>true</code>. Right after a read it is set to <code>false</code>.
+ */
+ private boolean needInput = false;
+
+ private boolean skipped = false;
+
+ /**
+ * <code> {@link #doSkipp} </code> indicates that the found pattern belongs to the result. If
+ * <code> {@link #doSkipp} </code> is false the match result ends at the beginning of the match.
+ * In both cases the current position is set after the pattern, if the found pattern has to be
+ * removed, a nextXXX method is called.
+ */
+ private boolean doSkipp = false;
+
+ /**
+ * Indicates if the last match was valid or not.
+ */
+ private boolean matchValid = false;
+
+ private NumberFormat actFormat = NumberFormat.getInstance (this.actLocale);
+
+ private DecimalFormat df = (DecimalFormat) this.actFormat;
+
+ /**
+ * Indicates if current Locale should be used at the input.
+ */
+ private boolean useLocale = true;
+
+ private DecimalFormatSymbols dfs =
+ new DecimalFormatSymbols (this.actLocale);
+
+ /**
+ * Constructs a new Scanner with the given File as source.
+ * {@link #Scanner(InputStream, String)} is called with <code> null </code> as charsetName.
+ *
+ * @param source
+ * The File to use as source.
+ * @throws FileNotFoundException
+ * If the file is not found an Exception is thrown.
+ */
+ public Scanner (final File source) throws FileNotFoundException // TESTED
+ {
+ this (source, null);
+ }
+
+ /**
+ * Constructs a new Scanner with the given File as source. <br>
+ * {@link #Scanner(InputStream, String)} is called with the given charsetName.
+ *
+ * @param source
+ * The File to use as source.
+ * @param charsetName
+ * Current charset name of the file. If charsetName is null it behaves if it was not
+ * set.
+ * @throws FileNotFoundException
+ * If the file is not found an Exception is thrown.
+ */
+ public Scanner (final File source,
+ final String charsetName) throws FileNotFoundException
+ {
+ this (new FileInputStream (source), charsetName);
+ }
+
+ /**
+ * Constructs a new Scanner with the given inputStream. <br>
+ * {@link #Scanner(InputStream, String)} is called with <code> null </code> as charsetName.
+ *
+ * @param source
+ * The InputStream to use as source.
+ */
+ public Scanner (final InputStream source) // TESTED
+ {
+ this (source, null);
+ }
+
+ /**
+ * Constructs a new Scanner with the InputSream and a charsetName. Afterwards the Buffer is
+ * filled.
+ *
+ * @param source
+ * The InputStream to use as source.
+ * @param charsetName
+ * The charsetName to apply on the source's data.
+ */
+ public Scanner (final InputStream source, final String charsetName)
+ {
+ this.bIS = (new BufferedInputStream (source));
+ this.charsetName = charsetName;
+ myFillBuffer ();
+ }
+
+ /**
+ * Constructs a new Scanner with a Readable input as source.
+ *
+ * @param source
+ * The Readable to use as source.
+ */
+ public Scanner (final Readable source)
+ {
+ this.readableSource = source;
+ myFillBuffer ();
+ }
+
+ /**
+ * Constructs a new Scanner with a ReadableByteChannel as
+ * source. Therfore the {@link #Scanner(ReadableByteChannel,
+ * String)} is called with <code> null </code> as charsetName.
+ *
+ * @param source
+ * The ReadableByteChannel to use as source.
+ */
+ public Scanner (final ReadableByteChannel source)
+ {
+ this (source, null);
+ }
+
+ /**
+ * Constructs a new Scanner with a ReadableByteChannel as source and
+ * a given charsetName, which is to be applied on it. <br> It also
+ * initiates the main Buffer.
+ *
+ * @param source
+ * The ReadableByteChannel to use as source.
+ * @param charsetName
+ * The charsetName to be applied on the source.
+ */
+ public Scanner (final ReadableByteChannel source, final String charsetName)
+ {
+ this.charsetName = charsetName;
+ this.rbcSource = source;
+ myFillBuffer ();
+ }
+
+ /**
+ * Constructs a new Scanner using the given String as input only.
+ *
+ * @param source
+ * The whole String to be used as source.
+ */
+ public Scanner (final String source) // TESTED
+ {
+ this.actBuffer = new String (source);
+ this.myMatcher.reset (this.actBuffer);
+ }
+
+ /**
+ * Closes this Scanner. If an {@link IOException} occurs it is
+ * catched and is available under {@link #ioException()}.<br> After
+ * the Scanner is closed, all searches will lead to a {@link
+ * IllegalStateException}.
+ */
+ public void close ()
+ {
+ try
+ {
+ if (this.bIS != null)
+ this.bIS.close ();
+ if (this.rbcSource != null)
+ this.rbcSource.close ();
+ this.isClosed = true;
+ }
+ catch (IOException ioe)
+ {
+ this.lastIOException = ioe;
+ }
+ }
+
+ /**
+ * Returns the current delimiter.
+ *
+ * @return the current delimiter.
+ */
+ public Pattern delimiter () // TESTED
+ {
+ return this.p;
+ }
+
+ /**
+ * Tries to find the pattern in the current line.
+ *
+ * @param pattern The pattern which should be searched in the
+ * current line of the input.
+ * @throws NoSuchElementException
+ * If the pattern was not found.
+ * @return If the search was successful, the result or otherwise a
+ * {@link NoSuchElementException} is thrown.
+ */
+ public String findInLine (final Pattern pattern) throws NoSuchElementException // TESTED
+ {
+ String tmpStr = myNextLine (false);
+ return myFindPInStr (pattern, tmpStr, 0);
+ }
+
+ /**
+ * Compiles the given pattern into a {@link Pattern} and calls
+ * {@link #findInLine(Pattern)} with the compiled pattern and
+ * returns whatever it returns.
+ *
+ * @param pattern
+ * The pattern which should be matched in the input.
+ * @throws NoSuchElementException
+ * If the pattern was not found.
+ * @return The match in the current line.
+ */
+ public String findInLine (final String pattern) // TESTED
+ {
+ return findInLine (Pattern.compile (pattern));
+ }
+
+ /**
+ * Trys to match the pattern within the given horizon.
+ *
+ * @param pattern
+ * Pattern to search.
+ * @param horizon
+ * @return The result of the match.
+ * @throws IllegalArgumentException
+ * if the horizon is negative.
+ * @throws IllegalStateException
+ * if the Scanner is closed.
+ */
+ public String findWithinHorizon (final Pattern pattern, final int horizon)
+ throws IllegalArgumentException, IllegalStateException
+ {
+ if (horizon < 0)
+ {
+ throw new IllegalArgumentException (horizon + " is negative");
+ }
+
+ if (this.isClosed)
+ {
+ throw new IllegalStateException ("Scanner is closed");
+ }
+
+ // doSkipp is set true to get the matching patern together with the found String
+ this.doSkipp = true;
+ String rc = myFindPInStr (pattern, this.actBuffer, horizon);
+
+ if (rc != null)
+ {
+ this.actPos += rc.length ();
+ }
+
+ return rc;
+ }
+
+ /**
+ * Compile the pattern and call {@link #findWithinHorizon(Pattern,
+ * int)}.
+ *
+ * @param pattern
+ * Pattern to search.
+ * @param horizon
+ * @return The result of the match.
+ * @throws IllegalArgumentException
+ * if the horizon is negative.
+ * @throws IllegalStateException
+ * if the Scanner is closed.
+ */
+ public String findWithinHorizon (final String pattern, final int horizon)
+ throws IllegalArgumentException, IllegalStateException
+ {
+ return findWithinHorizon (Pattern.compile (pattern), horizon);
+ }
+
+ /**
+ * Checks if there is any next String using the current
+ * delimiter. Therefore the string must not be <code> null </code>
+ * and the length must be greater then 0. If a {@link
+ * NoSuchElementException} is thrown by the search method, it is
+ * catched and false is returned.
+ *
+ * @return <code> true </code> if there is any result using the current delimiter. This wouldn't
+ * lead to a {@link NoSuchElementException}.
+ * @throws IllegalStateException
+ * if the Scanner is closed.
+ */
+ public boolean hasNext () throws IllegalStateException // TESTED
+ {
+ String tmpStr = null;
+
+ try
+ {
+ tmpStr = myCoreNext (false, this.p);
+ }
+ catch (NoSuchElementException nf)
+ {
+ }
+
+ if (tmpStr == null || tmpStr.length () <= 0)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Searches the pattern in the next subString before the next
+ * current delimiter.
+ *
+ * @param pattern
+ * The pattern to search for.
+ * @return <code> true </code> if the pattern is found before the current delimiter.
+ * @throws IllegalStateException
+ * if the Scanner is closed.
+ */
+ public boolean hasNext (final Pattern pattern) throws IllegalStateException // TESTED
+ {
+ String tmpStr;
+
+ tmpStr = myNext (pattern, false);
+
+ if (tmpStr == null || tmpStr.length () <= 0)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Compiles the pattern to a {@link Pattern} and calls {@link
+ * #hasNext(Pattern)}.
+ *
+ * @see #hasNext(Pattern)
+ * @param pattern
+ * The pattern as string to search for.
+ * @return <code> true </code> if the pattern is found before the current delimiter.
+ * @throws IllegalStateException
+ * if the Scanner is closed.
+ */
+ public boolean hasNext (final String pattern) throws IllegalStateException // TESTED
+ {
+ return hasNext (Pattern.compile (pattern));
+ }
+
+ /**
+ * Checks if the string to the next delimiter can be interpreted as
+ * a BigDecimal number. <br> BigDecimal numbers are always tryed
+ * with radix 10.
+ *
+ * @see #nextBigDecimal()
+ * @return <code> true </code> if the next string is a BigDecimal number.
+ * @throws IllegalStateException
+ * if the Scanner is closed.
+ */
+ public boolean hasNextBigDecimal () throws IllegalStateException // TESTED
+ {
+ try
+ {
+ myBigDecimal (false);
+ return true;
+ }
+ catch (InputMismatchException nfe)
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the string to the next delimiter can be interpreted as
+ * a BigInteger number. <br> Call {@link #hasNextBigInteger(int)}
+ * with the current radix.
+ *
+ * @see #nextBigInteger()
+ * @return <code> true </code> if the next string is a BigInteger number.
+ * @throws IllegalStateException
+ * if the Scanner is closed.
+ */
+ public boolean hasNextBigInteger () throws IllegalStateException // TESTED
+ {
+ return hasNextBigInteger (this.currentRadix);
+ }
+
+ /**
+ * Checks if the string to the next delimiter can be interpreted as
+ * a BigInteger number. <br>
+ *
+ * @param radix
+ * The radix to use for this check. The global radix of the Scanner will not be
+ * changed.
+ * @return <code> true </code> if the next string is a BigInteger number.
+ * @throws IllegalStateException
+ * if the Scanner is closed.
+ */
+ public boolean hasNextBigInteger (final int radix) throws
+ IllegalStateException
+ {
+ try
+ {
+ myNextBigInteger (radix, false, BIG_INTEGER);
+ return true;
+ }
+ catch (InputMismatchException ime)
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the next string could be a boolean. The method handles
+ * the input not case sensitiv, so "true" and "TRUE" and even "tRuE"
+ * are <code> true </code>.
+ *
+ * @see #nextBoolean()
+ * @return Return <code> true </code> if the next string is a boolean.
+ * @throws IllegalStateException
+ * if the Scanner is closed.
+ */
+ public boolean hasNextBoolean () throws IllegalStateException // TESTED
+ {
+ try
+ {
+ myNextBoolean (false);
+ return true;
+ }
+ catch (InputMismatchException ime)
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the string to the next delimiter can be interpreted as
+ * a byte number. <br> Calls {@link #hasNextByte(int)} with the
+ * current radix.
+ *
+ * @see #nextByte()
+ * @return <code> true </code> if the next string is a byte number.
+ * @throws IllegalStateException
+ * if the Scanner is closed.
+ */
+ public boolean hasNextByte () throws IllegalStateException // TESTED
+ {
+ return hasNextByte (this.currentRadix);
+ }
+
+ /**
+ * Checks if the string to the next delimiter can be interpreted as
+ * a byte number with the given radix. <br> To check, the private
+ * method {@link #myNextByte(int, boolean)} is called, and if no
+ * error occurs the next string could be a byte.
+ *
+ * @see #nextByte(int)
+ * @param radix The radix to use for this check. The global radix of
+ * the Scanner will not be changed.
+ * @return <code> true </code> if the next string is a byte number.
+ * @throws IllegalStateException
+ * if the Scanner is closed.
+ */
+ public boolean hasNextByte (final int radix) throws IllegalStateException
+ {
+ try
+ {
+ myNextByte (radix, false);
+ return true;
+ }
+ catch (InputMismatchException ime)
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the string to the next delimiter can be interpreted as
+ * a double number. <br> To check, the private method {@link
+ * #myNextDouble(boolean)} is called, and if no error occurs the
+ * next string could be a double.
+ *
+ * @see #nextDouble()
+ * @return <code> true </code> if the next string is a double number.
+ * @throws IllegalStateException
+ * if the Scanner is closed.
+ */
+ public boolean hasNextDouble () throws IllegalStateException // TESTED
+ {
+ try
+ {
+ myNextDouble (false);
+ return true;
+ }
+ catch (InputMismatchException ime)
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the string to the next delimiter can be interpreted as
+ * a double number. Because every float is a double this is
+ * checked.<br> To check, the private method {@link
+ * #myNextDouble(boolean)} is called, and if no error occurs the
+ * next string could be a double.
+ *
+ * @see #nextFloat()
+ * @return <code> true </code> if the next string is a double number.
+ * @throws IllegalStateException
+ * if the Scanner is closed.
+ */
+ public boolean hasNextFloat () throws IllegalStateException // TESTED
+ {
+ try
+ {
+ myNextDouble (false);
+ // myNextFloat(false);
+ return true;
+ }
+ catch (InputMismatchException ime)
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the string to the next delimiter can be interpreted as
+ * an int number. <br> To check, the private method {@link
+ * #myNextInt(int, boolean)} is called, and if no error occurs the
+ * next string could be an int.
+ *
+ * @see #nextInt(int)
+ * @return <code> true </code> if the next string is an int number.
+ * @throws IllegalStateException
+ * if the Scanner is closed.
+ */
+ public boolean hasNextInt () throws IllegalStateException // TESTED
+ {
+ return hasNextInt (this.currentRadix);
+ }
+
+ /**
+ * Checks if the string to the next delimiter can be interpreted as
+ * an int number with the given radix. <br> To check, the private
+ * method {@link #myNextInt(int, boolean)} is called, and if no
+ * error occurs the next string could be an int.
+ *
+ * @see #nextInt(int)
+ * @param radix
+ * The radix to use for this check. The global radix of the Scanner will not be
+ * changed.
+ * @return <code> true </code> if the next string is an int number.
+ * @throws IllegalStateException
+ * if the Scanner is closed.
+ */
+ public boolean hasNextInt (final int radix) throws IllegalStateException
+ {
+ try
+ {
+ myNextInt (radix, false);
+ return true;
+ }
+ catch (InputMismatchException ime)
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if there is a current line, which ends at the next line
+ * break or the end of the input.
+ *
+ * @return <code> true </code> if there is a current line.
+ * @throws IllegalStateException
+ * if the Scanner is closed.
+ */
+ public boolean hasNextLine () throws IllegalStateException // TESTED
+ {
+ return (myNextLine (false) != null);
+ }
+
+ /**
+ * Checks if the string to the next delimiter can be interpreted as
+ * a long number. <br> To check, the private method {@link
+ * #myNextLong(int, boolean)} is called, and if no error occurs the
+ * next string could be a long.
+ *
+ * @see #nextLong()
+ * @return <code> true </code> if the next string is a long number.
+ * @throws IllegalStateException
+ * if the Scanner is closed.
+ */
+ public boolean hasNextLong () throws IllegalStateException // TESTED
+ {
+ return hasNextLong (this.currentRadix);
+ }
+
+ /**
+ * Checks if the string to the next delimiter can be interpreted as
+ * a long number with the given radix. <br> To check, the private
+ * method {@link #myNextLong(int, boolean)} is called, and if no
+ * error occurs the next string could be a long.
+ *
+ * @see #nextLong(int)
+ * @param radix
+ * The radix to use for this check. The global radix of the Scanner will not be
+ * changed.
+ * @return <code> true </code> if the next string is a long number.
+ * @throws IllegalStateException
+ * if the Scanner is closed.
+ */
+ public boolean hasNextLong (final int radix) throws IllegalStateException
+ {
+ try
+ {
+ myNextLong (radix, false);
+ return true;
+ }
+ catch (InputMismatchException ime)
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the string to the next delimiter can be interpreted as
+ * a short number with the given radix. <br> To check, the private
+ * method {@link #myNextShort(int, boolean)} is called, and if no
+ * error occurs the next string could be a short.
+ *
+ * @see #nextShort(int)
+ * @return <code> true </code> if the next string is a short number.
+ * @throws IllegalStateException
+ * if the Scanner is closed.
+ */
+ public boolean hasNextShort () throws IllegalStateException // TESTED
+ {
+ return hasNextShort (this.currentRadix);
+ }
+
+ /**
+ * Checks if the string to the next delimiter can be interpreted as
+ * a short number. <br> To check, the private method {@link
+ * #myNextShort(int, boolean)} is called, and if no error occurs the
+ * next string could be a short.
+ *
+ * @see #nextShort(int)
+ * @param radix
+ * The radix to use for this check. The global radix of the Scanner will not be
+ * changed.
+ * @return <code> true </code> if the next string is a short number.
+ * @throws IllegalStateException
+ * if the Scanner is closed.
+ */
+ public boolean hasNextShort (final int radix) throws IllegalStateException
+ {
+ try
+ {
+ myNextShort (radix, false);
+ return true;
+ }
+ catch (InputMismatchException ime)
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the last {@link IOException} occured.
+ *
+ * @return Returns the last {@link IOException}.
+ */
+ public IOException ioException ()
+ {
+ return this.lastIOException;
+ }
+
+ /**
+ * Returns the current value of {@link #useLocale}. This is used to
+ * tell the Scanner if it should use the Locale format or just
+ * handle numbers of the default format.
+ *
+ * @see #setUseLocale(boolean)
+ * @return the useLoclae.
+ */
+ public boolean isUseLocale () // TESTED
+ {
+ return this.useLocale;
+ }
+
+ /**
+ * Returns the current Locale. It is initialized with {@link
+ * Locale#getDefault()}.
+ *
+ * @see #useLocale(Locale)
+ * @return Returns the current Locale.
+ */
+ public Locale locale () // TESTED
+ {
+ return this.actLocale;
+ }
+
+ /**
+ * Returns the last MatchResult found. This is updated after every
+ * successfully search.
+ *
+ * @return Returns the last {@link MatchResult} found.
+ */
+ public MatchResult match () // TESTED
+ {
+ return this.actResult;
+ }
+
+ /**
+ * Uses the current delimiter to find the next string in the
+ * buffer. If a string is found the current position is set after
+ * the delimiter, otherwise a {@link NoSuchElementException} is
+ * thrown. A successful match sets the matchResult.
+ *
+ * @see #match()
+ * @return Returns the next string of the buffer.
+ * @throws NoSuchElementException
+ * If no element was found an exception is thrown.
+ * @throws IllegalStateException
+ * If the Scanner is closed.
+ */
+ public String next () throws NoSuchElementException, IllegalStateException // TESTED
+ {
+ return myCoreNext (true, this.p);
+ }
+
+ /**
+ * Tries to match the buffer with the given pattern. The current
+ * delimiter will not be changed.
+ *
+ * @param pattern
+ * The pattern to match.
+ * @return Returns the next string matching the pattern.
+ * @throws NoSuchElementException
+ * If no element was found an exception is thrown.
+ * @throws IllegalStateException
+ * If the Scanner is closed.
+ */
+ public String next (final Pattern pattern) throws NoSuchElementException, IllegalStateException // TESTED
+ {
+ return myNext (pattern, true);
+ }
+
+ /**
+ * Tries to match the buffer with the given pattern. The current
+ * delimiter will not be changed. Calls the {@link #next(Pattern)}
+ * with the compiled pattern.
+ *
+ * @see #next(Pattern)
+ * @param pattern
+ * The pattern to match.
+ * @return Returns the next string matching the pattern.
+ * @throws NoSuchElementException
+ * If no element was found an exception is thrown.
+ * @throws IllegalStateException
+ * If the Scanner is closed.
+ */
+ public String next (final String pattern) throws NoSuchElementException, IllegalStateException // TESTED
+ {
+ return next (Pattern.compile (pattern));
+ }
+
+ /**
+ * Tries to interpret the next string as a BigDecimal value.
+ *
+ * @return Returns the BigDecimal value of the next string.
+ * @throws NoSuchElementException
+ * If no string is found or the string is not a BigDecimal.
+ * @throws IllegalStateException
+ * If the Scanner is closed.
+ */
+ public BigDecimal nextBigDecimal () throws NoSuchElementException, IllegalStateException // TESTED
+ {
+ return myBigDecimal (true);
+ }
+
+ /**
+ * Tries to interpret the next string as a BigInteger value. Call
+ * {@link #nextBigInteger(int)} with the current radix as parameter,
+ * and return the value.
+ *
+ * @see #nextBigInteger(int)
+ * @return Returns the BigInteger value of the next string.
+ * @throws NoSuchElementException
+ * If no string is found or the string is not a BigInteger.
+ * @throws IllegalStateException
+ * If the Scanner is closed.
+ */
+ public BigInteger nextBigInteger () throws NoSuchElementException, IllegalStateException // TESTED
+ {
+ return nextBigInteger (this.currentRadix);
+ }
+
+ /**
+ * Tries to interpret the next string as a BigInteger value with the
+ * given radix.
+ *
+ * @param radix
+ * The radix to be used for this BigInteger. The current radix of the Scanner is not
+ * changed.
+ * @return Returns the BigInteger value of the next string.
+ * @throws NoSuchElementException
+ * If no string is found or the string is not a BigInteger.
+ * @throws IllegalStateException
+ * If the Scanner is closed.
+ */
+ public BigInteger nextBigInteger (final int radix) throws
+ NoSuchElementException, IllegalStateException
+ {
+ return myNextBigInteger (radix, true, BIG_INTEGER);
+ }
+
+ /**
+ * Tries to interpret the next string to the delimiter as a boolean
+ * value, ignoring case.
+ *
+ * @return Returns the boolean value of the next matching string or throws an exception.
+ * @throws NoSuchElementException
+ * If no string is found or the string is not a boolean.
+ * @throws IllegalStateException
+ * If the Scanner is closed.
+ */
+ public boolean nextBoolean () throws NoSuchElementException, IllegalStateException // TESTED
+ {
+ return myNextBoolean (true);
+ }
+
+ /**
+ * Tries to interpret the next string as a byte value. Call {@link
+ * #nextByte(int)} with the current radix as parameter, and return
+ * the value.
+ *
+ * @see #nextByte(int)
+ * @return Returns the byte value of the next string.
+ * @throws NoSuchElementException
+ * If no string is found or the string is not a byte
+ * @throws IllegalStateException
+ * If the Scanner is closed.
+ */
+ public byte nextByte () throws NoSuchElementException, IllegalStateException // TESTED
+ {
+ return nextByte (this.currentRadix);
+ }
+
+ /**
+ * Tries to interpret the next string as a byte value with the given
+ * radix.
+ *
+ * @param radix
+ * The radix to be used for this byte. The current radix of the Scanner is not
+ * changed.
+ * @return Returns the byte value of the next string.
+ * @throws NoSuchElementException
+ * If no string is found or the string is not a byte.
+ * @throws IllegalStateException
+ * If the Scanner is closed.
+ */
+ public byte nextByte (final int radix) throws NoSuchElementException,
+ IllegalStateException
+ {
+ return myNextByte (radix, true);
+ }
+
+ /**
+ * Tries to interpret the next string as a double value.
+ *
+ * @return Returns the int value of the next string.
+ * @throws NoSuchElementException
+ * If no string is found or the string is not a double.
+ * @throws IllegalStateException
+ * If the Scanner is closed.
+ */
+ public double nextDouble () throws NoSuchElementException, IllegalStateException // TESTED
+ {
+ return myNextDouble (true);
+ }
+
+ /**
+ * Tries to interpret the next string as a double value, and then
+ * casts down to float.
+ *
+ * @return Returns the int value of the next string.
+ * @throws NoSuchElementException
+ * If no string is found or the string is not a double.
+ * @throws IllegalStateException
+ * If the Scanner is closed.
+ */
+ public float nextFloat () throws NoSuchElementException, IllegalStateException // TESTED
+ {
+ return (float) myNextDouble (true);
+ // return myNextFloat(true);
+ }
+
+ /**
+ * Tries to interpret the next string as an int value. Calls {@link
+ * #nextInt(int)} with the current radix as parameter, and return
+ * the value.
+ *
+ * @see #nextInt(int)
+ * @return Returns the int value of the next string.
+ * @throws NoSuchElementException
+ * If no string is found or the string is not an int.
+ * @throws IllegalStateException
+ * If the Scanner is closed.
+ */
+ public int nextInt () throws NoSuchElementException, IllegalStateException // TESTED
+ {
+ return nextInt (this.currentRadix);
+ }
+
+ /**
+ * Tries to interpret the next string as an int value with the given
+ * radix.
+ *
+ * @param radix
+ * The radix to be used for this int. The current radix of the Scanner is not changed
+ * @return Returns the int value of the next string.
+ * @throws NoSuchElementException
+ * If no string is found or the string is not an int.
+ * @throws IllegalStateException
+ * If the Scanner is closed.
+ */
+ public int nextInt (final int radix) throws NoSuchElementException,
+ IllegalStateException
+ {
+ return myNextInt (radix, true);
+ }
+
+ /**
+ * Tries to match the system line seperator, and returns the current
+ * line.
+ *
+ * @return Returns the current line.
+ * @throws NoSuchElementException
+ * If the current delimiter is not found.
+ * @throws IllegalStateException
+ * If the Scanner is closed.
+ */
+ public String nextLine () throws NoSuchElementException, IllegalStateException // TESTED
+ {
+ return myNextLine (true);
+ }
+
+ /**
+ * Tries to interpret the next string as a long value. Calls {@link
+ * #nextLong(int)} with the current radix as parameter, and return
+ * the value.
+ *
+ * @see #nextLong(int)
+ * @return Returns the long value of the next string.
+ * @throws NoSuchElementException
+ * If no string is found or the string is not a long.
+ * @throws IllegalStateException
+ * If the Scanner is closed.
+ */
+ public long nextLong () throws NoSuchElementException, IllegalStateException // TESTED
+ {
+ return nextLong (this.currentRadix);
+ }
+
+ /**
+ * Tries to interpret the next string as a long value with the given
+ * radix.
+ *
+ * @param radix
+ * The radix to be used for this long. The current radix of the Scanner is not
+ * changed
+ * @return Returns the long value of the next string.
+ * @throws NoSuchElementException
+ * If no string is found or the string is not a long.
+ * @throws IllegalStateException
+ * If the Scanner is closed.
+ */
+ public long nextLong (final int radix) throws NoSuchElementException,
+ IllegalStateException
+ {
+ return myNextLong (radix, true);
+ }
+
+ /**
+ * Tries to interpret the next string as a short value. Calls {@link
+ * #nextShort(int)} with the current radix as parameter, and return
+ * the value.
+ *
+ * @see #nextShort(int)
+ * @return Returns the short value of the next string.
+ * @throws NoSuchElementException
+ * If no string is found or the string is not a short.
+ */
+ public short nextShort () throws NoSuchElementException // TESTED
+ {
+ return nextShort (this.currentRadix);
+ }
+
+ /**
+ * Tries to interpret the next string as a short value with the
+ * given radix.
+ *
+ * @param radix
+ * The radix to be used for this short. The current radix of the Scanner is not
+ * changed.
+ * @return Returns the short value of the next string.
+ * @throws NoSuchElementException
+ * If no string is found or the string is not a short.
+ */
+ public short nextShort (final int radix) throws NoSuchElementException
+ {
+ return myNextShort (radix, true);
+ }
+
+ /**
+ * @return Returns the current radix.
+ */
+ public int radix ()
+ {
+ return this.currentRadix;
+ }
+
+ /**
+ * The remove operation is not supported by this implementation of
+ * Iterator.
+ */
+ public void remove ()
+ {
+ }
+
+ /**
+ * @param useLocale the useLocale to set.
+ */
+ public void setUseLocale (final boolean useLocale) // TESTED
+ {
+ this.useLocale = useLocale;
+ }
+
+ /**
+ * Skips the given pattern. Sets skipped <code>true</code>.
+ *
+ * @param pattern
+ * Pattern which should be skipped.
+ * @return <code>this</code> with the skipped buffer.
+ * @throws NoSuchElementException
+ * If the Pattern is not found.
+ */
+ public Scanner skip (final Pattern pattern) throws NoSuchElementException
+ {
+ this.doSkipp = true;
+ int end;
+ boolean found;
+ Matcher matcher = pattern.matcher (this.actBuffer);
+ matcher.region (this.actPos - 1, this.actBuffer.length ());
+
+ found = matcher.find ();
+ found = myFillBuffer_loop (matcher, this.actPos - 1, found);
+ end = matcher.end ();
+
+ this.actPos = end + 1;
+
+ this.doSkipp = false;
+ this.skipped = true;
+
+ actResult = null;
+
+ if (!found)
+ {
+ throw new NoSuchElementException ();
+ }
+ return this;
+ }
+
+ /**
+ * Skips a given pattern. Calls {@link #skip(Pattern)} with the
+ * compiled pattern.
+ *
+ * @see #skip(Pattern)
+ * @param pattern
+ * Pattern which should be skipped.
+ * @return <code>this</code> with the skipped buffer.
+ */
+ public Scanner skip (final String pattern)
+ {
+ return skip (Pattern.compile (pattern));
+ }
+
+ /**
+ * Returns the string representation of this Scanner.
+ */
+ @Override
+ public String toString ()
+ {
+ String tmpStr2;
+ String rc = this.getClass ().getName ();
+ tmpStr2 = rc;
+ tmpStr2 = "[delimiters=" + this.p.pattern () + "]";
+ rc += tmpStr2;
+ tmpStr2 = "[position=" + (this.procesedChars + this.actPos) + "]";
+ rc += tmpStr2;
+ tmpStr2 = "[match valid=" + this.matchValid + "]";
+ rc += tmpStr2;
+ tmpStr2 = "[need input=" + this.needInput + "]";
+ rc += tmpStr2;
+ tmpStr2 = "[source closed=" + this.isClosed + "]";
+ rc += tmpStr2;
+ tmpStr2 = "[skipped=" + this.skipped + "]";
+ rc += tmpStr2;
+ tmpStr2 = "[group separator=\\" + this.dfs.getGroupingSeparator () + "]";
+ rc += tmpStr2;
+ tmpStr2 = "[decimal separator=\\" + this.dfs.getDecimalSeparator () + "]";
+ rc += tmpStr2;
+ tmpStr2 =
+ "[positive prefix=" + myConvert (this.df.getPositivePrefix ()) + "]";
+ rc += tmpStr2;
+ tmpStr2 =
+ "[negative prefix=" + myConvert (this.df.getNegativePrefix ()) + "]";
+ rc += tmpStr2;
+ tmpStr2 =
+ "[positive suffix=" + myConvert (this.df.getPositiveSuffix ()) + "]";
+ rc += tmpStr2;
+ tmpStr2 =
+ "[negative suffix=" + myConvert (this.df.getNegativeSuffix ()) + "]";
+ rc += tmpStr2;
+ tmpStr2 = "[NaN string=" + myConvert (this.dfs.getNaN ()) + "]";
+ rc += tmpStr2;
+ tmpStr2 = "[infinity string=" + myConvert (this.dfs.getInfinity ()) + "]";
+ rc += tmpStr2;
+ return rc;
+ }
+
+ /**
+ * Sets the current pattern to the given parameter, and updates the
+ * {@link Matcher} with the new pattern.
+ *
+ * @param pattern
+ * The new pattern to use.
+ * @return Returns the Scanner (<code>this</code>) with the new pattern.
+ */
+ public Scanner useDelimiter (final Pattern pattern) // TESTED
+ {
+ if (pattern != null)
+ {
+ this.p = pattern;
+ this.myMatcher = this.p.matcher (this.actBuffer);
+ }
+ return this;
+ }
+
+ /**
+ * Sets the current pattern to the given parameter. Compiles the
+ * pattern and calls {@link #useDelimiter(Pattern)}
+ *
+ * @see #useDelimiter(Pattern)
+ * @param pattern
+ * The new pattern to use.
+ * @return Returns the Scanner (<code>this</code>) with the new pattern.
+ */
+ public Scanner useDelimiter (final String pattern) // TESTED
+ {
+ return useDelimiter (Pattern.compile (pattern));
+ }
+
+ /**
+ * Sets the current Locale to the given parameter. Formats and
+ * Symbols are also set using the new Locale.
+ *
+ * @param locale The new Locale to use. If it is <code>null</code>
+ * nothing happens.
+ * @return Returns the Scanner (<code>this</code>) with the new Locale.
+ */
+ public Scanner useLocale (final Locale locale) // TESTED
+ {
+ if (locale != null)
+ {
+ this.actLocale = locale;
+ this.actFormat = NumberFormat.getInstance (this.actLocale);
+ this.dfs = new DecimalFormatSymbols (this.actLocale);
+ this.df = (DecimalFormat) this.actFormat;
+ }
+ return this;
+ }
+
+ /**
+ * Sets the current radix to the current value if the given radix is
+ * >= 2 and <= 36 otherwise an {@link IllegalArgumentException} is
+ * thrown.
+ *
+ * @param radix
+ * the new radix to use as default.
+ * @return <code> this </code> with the new radix value.
+ * @throws IllegalArgumentException
+ * When the given radix is out of bounds.
+ */
+ public Scanner useRadix (final int radix) throws IllegalArgumentException
+ {
+ if (radix < 2 || radix > 36)
+ {
+ throw new IllegalArgumentException ();
+ }
+ this.currentRadix = radix;
+ return this;
+ }
+
+ /**
+ * Checks if it is necessary to apply the current Locale on the
+ * String. If so the String is converted using the {@link
+ * NumberFormat#parse(String)} into a Number and then back to a
+ * default stringrepresentation of that Number.
+ *
+ * @see #setUseLocale(boolean)
+ * @param str
+ * String to convert into another string.
+ * @param radix Radix of the Number in the original string. It has
+ * to be 10 for anything to happen.
+ * @return Eighter the Stringrepresention of the number without the
+ * Locale or an unchanged string.
+ * @throws ParseException
+ * if {@link NumberFormat#parse(String)} fails to parse.
+ */
+ private String myApplyLocale (final String str,
+ final int radix) throws ParseException
+ {
+ String rc;
+
+ if (this.useLocale && radix == 10)
+ {
+ rc = this.actFormat.parse (str).toString ();
+ return rc;
+ }
+
+ return str;
+ }
+
+ /**
+ * If {@link #useLocale} is set and radix is 10 the string is tryed
+ * to be converted to string without Locale settings, because the
+ * "normal" convert from Local has only double precision and it is
+ * not enough for the about 50 digits of precision of the
+ * BigDecimal. So in the first step the string is seperated into the
+ * integer part which is converted to a long, and the fraction part
+ * is appended afterwards. Between the integer and the fraction part
+ * comes a ".". Finally the resulting string is returned.
+ *
+ * @see #setUseLocale(boolean)
+ * @param str String representation of a BigDecimal number.
+ * @return The default String representation (without Locale) of the
+ * BigInteger.
+ * @throws ParseException
+ * If the String has more than one decimal seperators a parse exception is thrown.
+ */
+ private String myApplyLocaleBD (final String str) throws ParseException
+ {
+ if (!this.useLocale || this.currentRadix != 10)
+ {
+ return str;
+ }
+
+ String negPrefix = this.df.getNegativePrefix ();
+ String negSuffix = this.df.getNegativeSuffix ();
+ String posPrefix = this.df.getPositivePrefix ();
+ String posSuffix = this.df.getPositiveSuffix ();
+
+ char d = this.dfs.getDecimalSeparator ();
+ int begin1, begin2;
+ boolean isNegativ = false;
+ String parts = null;
+
+ String tmpStr1 = "";
+
+ begin1 = str.indexOf (d);
+ begin2 = str.indexOf (d, begin1 + 1);
+
+ if (begin2 > 0)
+ {
+ throw new ParseException ("more than one Decimal seperators", begin2);
+ }
+
+ parts = str.substring (0, begin1);
+
+ if ((negPrefix.length () > 0
+ && str.substring (0, negPrefix.length ()).equals (negPrefix))
+ || (negSuffix.length () > 0
+ && str.substring (str.length () -
+ negSuffix.length ()).equals (negSuffix)))
+ {
+ parts += negSuffix;
+ isNegativ = true;
+ }
+ else
+ if ((posPrefix.length () > 0
+ && str.substring (0, posPrefix.length ()).equals (posPrefix))
+ || (posSuffix.length () > 0
+ && str.substring (str.length () -
+ posSuffix.length ()).equals (posSuffix)))
+ {
+ parts += posSuffix;
+ }
+
+ tmpStr1 = this.actFormat.parse (parts).toString ();
+
+ if (isNegativ)
+ {
+ tmpStr1 +=
+ "." + str.substring (str.indexOf (d) + 1,
+ str.length () - negSuffix.length ());
+ }
+ else
+ {
+ tmpStr1 +=
+ "." + str.substring (str.indexOf (d) + 1,
+ str.length () - posSuffix.length ());
+ }
+
+ return tmpStr1;
+ }
+
+ /**
+ * Tries to interpret the next String as a BigDecimal. Therfore the
+ * next String is get with {@link #myCoreNext(boolean, Pattern)} and
+ * then {@link #myApplyLocaleBD(String)} is called to convert the
+ * String into a BigDecimal.
+ *
+ * @param delete
+ * Should the found string be deleted or not.
+ * @return Returns the BigDecimal value of the next string.
+ * @throws InputMismatchException
+ * If the string is not a BigDecimal
+ */
+ private BigDecimal myBigDecimal (final boolean delete) throws
+ InputMismatchException
+ {
+ BigDecimal rc;
+ String tmp = myCoreNext (delete, this.p);
+ try
+ {
+ tmp = myApplyLocaleBD (tmp);
+ }
+ catch (ParseException e)
+ {
+ throw new InputMismatchException (ERR_PREFIX + tmp + IS_NOT +
+ "BigDecimal!!");
+ }
+ rc = new BigDecimal (tmp);
+
+ return rc;
+ }
+
+ /**
+ * Applies suffix ("\E") and prefix ("\Q") if str.length != 0 Used
+ * by the toString method.
+ *
+ * @param str
+ * the string on which the suffix and prefix should be applied.
+ * @return The new new string with the suffix and prefix.
+ */
+ private String myConvert (final String str)
+ {
+ if (str != null && str.length () > 0)
+ {
+ return "\\Q" + str + "\\E";
+ }
+ return str;
+ }
+
+ /**
+ * Searches the current Matcher for the current Pattern. If the end
+ * is reached during the search it tried to read again from the
+ * source. The search results are always saved in {@link #actResult}
+ * which is returned when match() is called. If doSkip is true the
+ * pattern is also taken.
+ *
+ * @param delete
+ * if true the aktPos is set.
+ * @param pattern
+ * pattern to search for.
+ * @return Returns the String which matches the pattern.
+ * @throws NoSuchElementException
+ * If the search has no result.
+ */
+ private String myCoreNext (final boolean delete, final Pattern pattern)
+ throws NoSuchElementException
+ {
+ if (this.isClosed)
+ {
+ throw new IllegalStateException ("Scanner closed");
+ }
+ if (shallUseLastFound (pattern != null ? pattern : this.p))
+ {
+ if (this.last_RegionEnd != this.myMatcher.regionEnd ())
+ {
+ System.out.println (this.last_RegionEnd + " != " +
+ this.myMatcher.regionEnd () + " (" +
+ (this.last_RegionEnd -
+ this.myMatcher.regionEnd ()) + ")");
+ }
+ if (delete)
+ {
+ this.actPos = this.lastNextPos;
+ this.lastFoundPresent = false;
+ this.actResult = this.lastResult;
+ }
+ return this.lastFound;
+ }
+
+ boolean found = false;
+ int left;
+ int endIndex;
+
+ String tmp2 = null;
+
+ if (this.actPos > this.MAX_PREFIX)
+ {
+ // skipp the processed chars so that the size of the buffer don't grow to much even with
+ // huge files
+ this.procesedChars += this.actPos;
+ this.actBuffer = this.actBuffer.substring (this.actPos);
+ this.actPos = 0;
+ this.myMatcher = pattern.matcher (this.actBuffer);
+ }
+
+ left = this.actBuffer.length () - this.actPos;
+ if (left < this.MIN_BUF_LEN)
+ {
+ myFillBuffer ();
+ }
+ found = this.myMatcher.find (this.actPos);
+
+ found = myFillBuffer_loop (this.myMatcher, this.actPos, found);
+
+ this.needInput = false;
+
+ if (found)
+ {
+ if (this.doSkipp)
+ {
+ endIndex = this.myMatcher.end ();
+ }
+ else
+ {
+ endIndex = this.myMatcher.start ();
+ }
+ tmp2 = this.actBuffer.substring (this.actPos, endIndex);
+ this.lastNextPos = this.myMatcher.end ();
+ /*
+ * if the delete flag is set, just set the current position after the end of the matched
+ * pattern.
+ */
+ if (delete)
+ {
+ this.actPos = this.lastNextPos;
+ }
+ else
+ {
+ this.lastFound = tmp2;
+ this.lastFoundPresent = true;
+ this.lastPatternHash = pattern.hashCode ();
+ }
+ this.last_RegionStart = this.myMatcher.regionStart ();
+ this.last_RegionEnd = this.myMatcher.regionEnd ();
+ this.last_anchor = this.myMatcher.hasAnchoringBounds ();
+ this.last_transparent = this.myMatcher.hasTransparentBounds ();
+ }
+ else if (this.myMatcher.hitEnd ())
+ // the end of input is matched
+ {
+ tmp2 = this.actBuffer.substring (this.actPos);
+ if (tmp2.length() == 0)
+ tmp2 = null;
+ this.lastNextPos = this.actBuffer.length ();
+ if (delete)
+ {
+ this.actPos = this.lastNextPos;
+ }
+ else
+ {
+ this.lastFound = tmp2;
+ this.lastFoundPresent = true;
+ this.lastPatternHash = pattern.hashCode ();
+ }
+ this.last_RegionStart = this.myMatcher.regionStart ();
+ this.last_RegionEnd = this.myMatcher.regionEnd ();
+ this.last_anchor = this.myMatcher.hasAnchoringBounds ();
+ this.last_transparent = this.myMatcher.hasTransparentBounds ();
+ }
+ else
+ {
+ /*
+ * if no match found an Exception is throwed
+ */
+ throw new NoSuchElementException ();
+ }
+ /*
+ * change the Result only when a nextXXX() method was called, not if a hasNextXXX() method
+ * is called
+ */
+ if (delete)
+ {
+ this.actResult = this.myMatcher.toMatchResult ();
+
+ this.matchValid = this.actResult != null;
+ }
+ else
+ {
+ this.lastResult = this.myMatcher.toMatchResult ();
+ }
+
+ this.skipped = this.doSkipp;
+ this.doSkipp = false;
+
+ return tmp2;
+ }
+
+ /**
+ * Used to fill the String buffer from a source. Therfore the 3
+ * possible sources are checked if they are not <code>null</code>
+ * and this not used, otherwise the read method is called on the
+ * source. If a charsetName is set and not <code>null</code> it is
+ * applied to convert to String.
+ */
+ private void myFillBuffer ()
+ {
+ int len;
+ String tmpStr;
+ CharBuffer cb = null;
+ ByteBuffer bb = null;
+
+ if (this.bIS != null)
+ {
+ try
+ {
+ len = this.bIS.read (this.tmpBuffer);
+ if (len < 0)
+ {
+ return;
+ }
+ if (this.charsetName != null)
+ {
+ tmpStr = new String (this.tmpBuffer, 0, len, this.charsetName);
+ }
+ else
+ {
+ tmpStr = new String (this.tmpBuffer, 0, len);
+ }
+ this.actBuffer += tmpStr;
+ }
+ catch (IOException e)
+ {
+ this.lastIOException = e;
+ }
+ }
+ else if (this.readableSource != null)
+ {
+ try
+ {
+ cb = CharBuffer.allocate (1000);
+ this.needInput = true;
+ len = this.readableSource.read (cb);
+ if (len < 0)
+ {
+ return;
+ }
+ this.needInput = false;
+ tmpStr = new String (cb.array ());
+ this.actBuffer += tmpStr;
+ }
+ catch (IOException e)
+ {
+ this.lastIOException = e;
+ }
+ }
+ else if (this.rbcSource != null)
+ {
+ try
+ {
+ bb = ByteBuffer.allocate (1000);
+ this.needInput = true;
+ len = this.rbcSource.read (bb);
+ this.needInput = false;
+ if (len < 0)
+ {
+ return;
+ }
+ if (this.charsetName != null)
+ {
+ tmpStr = new String (bb.array (), 0, len, this.charsetName);
+ }
+ else
+ {
+ tmpStr = new String (bb.array (), 0, len);
+ }
+ this.actBuffer += tmpStr;
+ }
+ catch (IOException e)
+ {
+ this.lastIOException = e;
+ }
+ }
+
+ this.myMatcher.reset (this.actBuffer);
+ }
+
+ /**
+ * A loop in which the {@link #myFillBuffer()} is called and checked
+ * if the pattern is found in the matcher and if the buffersize
+ * changes after the read.
+ *
+ * @param aktM
+ * The current Matcher.
+ * @param pos
+ * Position from which the matcher should start matching.
+ * @param found
+ * if already found.
+ * @return <code> true </code> if the matcher has found a match.
+ */
+ private boolean myFillBuffer_loop (final Matcher aktM, final int pos,
+ boolean found)
+ {
+ int tmp;
+
+ tmp = this.actBuffer.length ();
+ while (aktM.hitEnd ()
+ && ((this.bIS != null) || (this.readableSource != null)
+ || (this.rbcSource != null)))
+ {
+ myFillBuffer ();
+ if (tmp == this.actBuffer.length ())
+ {
+ break;
+ }
+ found = aktM.find (pos);
+ this.needInput = true;
+ }
+ return found;
+ }
+
+ /**
+ * Used to find the given pattern in the given string before the
+ * given horizon. Therfore the current matcher is copied, and
+ * overwritten using the given pattern and the given Sting. <br>
+ * After the search the original values are restored, and skipped is
+ * set <code> true </code>.
+ *
+ * @param pattern
+ * Pattern which should be matched.
+ * @param str
+ * The String in which the pattern should be matched.
+ * @param horizon
+ * the horizon whithin the match should be, if 0 then it is ignored.
+ * @return Returns the String in the given String that matches the pattern.
+ */
+ private String myFindPInStr (final Pattern pattern, final String str,
+ final int horizon)
+ {
+ String rc = null;
+ int curPos = this.actPos;
+ Matcher aktMatcher = this.myMatcher;
+
+ this.myMatcher = pattern.matcher (str);
+ if (horizon > 0)
+ {
+ this.myMatcher.useAnchoringBounds (true);
+ this.myMatcher.useTransparentBounds (true);
+ this.myMatcher.region (this.actPos, this.actPos + horizon);
+ }
+ rc = myCoreNext (true, pattern);
+ this.myMatcher = aktMatcher;
+
+ this.actPos = curPos;
+ this.skipped = true;
+
+ return rc;
+ }
+
+ /**
+ * Used by the {@link #hasNext(Pattern)} and {@link #next(Pattern)}
+ * methods. Therfore a substring is taken first to the current
+ * delimiter, afterwards the given pattern is searched in this
+ * subsring.<br> Finally the current Buffer and matcher (which have
+ * been temporarily changed) are set back.<br> <br> The {@link
+ * #skipped} is set <code> true </code>.
+ *
+ * @param pattern
+ * Pattern to find until the current delimiter.
+ * @param delete
+ * Is <code> true </code> if a next method is called.<br>
+ * Is <code> false </code> if a hasNext method is called.
+ * @return Returns the String which is returned by the public methods.
+ */
+ private String myNext (final Pattern pattern, final boolean delete)
+ {
+ String tmpStr;
+ Matcher aktMatcher = this.myMatcher;
+ String result;
+ String currBuffer = this.actBuffer;
+ int currAktPos;
+
+ tmpStr = myCoreNext (delete, this.p);
+ this.myMatcher = pattern.matcher (tmpStr);
+ this.actBuffer = tmpStr;
+ currAktPos = this.actPos;
+ this.actPos = 0;
+ result = myCoreNext (delete, pattern);
+ this.actPos = currAktPos;
+
+ this.actBuffer = currBuffer;
+ this.myMatcher = aktMatcher;
+ this.skipped = true;
+
+ return result;
+ }
+
+ /**
+ * Calls the next() method internally to get the next String, and
+ * trys to apply a locale which is only applied if the radix is 10
+ * and useLocale is <code> true </code>. Afterwards it is tried to
+ * call the Constructor of a {@link BigInteger} with the given
+ * radix.
+ *
+ * @param radix The radix to use.
+ * @param delete If the found String should be removed from input or
+ * not.
+ * @param name name of "BigInteger" in case of an Error.
+ * @return Returns the new BigInteger created if there is no Error.
+ * @throws InputMismatchException
+ * If there is a {@link ParseException} or a {@link NumberFormatException}.
+ */
+ private BigInteger myNextBigInteger (final int radix, final boolean delete,
+ final String name)
+ {
+ BigInteger rc;
+ String tmp = myPrepareForNext (this.p, delete);
+
+ try
+ {
+ tmp = myApplyLocale (tmp, radix);
+ rc = new BigInteger (tmp, radix);
+ return rc;
+ }
+ catch (NumberFormatException nfe)
+ {
+ }
+ catch (ParseException e)
+ {
+ }
+ throw new InputMismatchException (ERR_PREFIX + tmp + IS_NOT + name);
+ }
+
+ /**
+ * Checks if the next String is either "true" or "false", otherwise
+ * an {@link InputMismatchException} is thrown. It ignores the case
+ * of the string so that "true" and "TRUE" and even "TrUe" are
+ * accepted.
+ *
+ * @param delete Should the found value be removed from the input or
+ * not.
+ * @return Returns the boolean value (if it is a boolean).
+ * @throws InputMismatchException
+ * If the next String is not a boolean.
+ */
+ private boolean myNextBoolean (final boolean delete) throws
+ InputMismatchException
+ {
+ String tmp = myPrepareForNext (this.p, delete);
+ if (tmp.equalsIgnoreCase ("true"))
+ {
+ return true;
+ }
+ else if (tmp.equalsIgnoreCase ("false"))
+ {
+ return false;
+ }
+ else
+ {
+ throw new InputMismatchException (ERR_PREFIX + tmp + NOT_BOOLEAN);
+ }
+ }
+
+ /**
+ * Calls the {@link #myPrepareForNext(Pattern, boolean)} which calls
+ * the {@link #myCoreNext(boolean, Pattern)} to return the next
+ * String matching the current delimier. Afterwards it is tryed to
+ * convert the String into a byte. Any Error will lead into a {@link
+ * InputMismatchException}.
+ *
+ * @param radix The radix to use.
+ * @param delete Should the found String be removed from the input.
+ * @return Returns the byte value of the String.
+ * @throws InputMismatchException if the next String is not a byte.
+ */
+ private byte myNextByte (final int radix,
+ final boolean delete) throws InputMismatchException
+ {
+ byte rc;
+ String tmp = myPrepareForNext (this.p, delete);
+
+ try
+ {
+ tmp = myApplyLocale (tmp, radix);
+ rc = Byte.parseByte (tmp, radix);
+ return rc;
+ }
+ catch (NumberFormatException nfe)
+ {
+ }
+ catch (ParseException e)
+ {
+ }
+ throw new InputMismatchException (ERR_PREFIX + tmp + NOT_BYTE);
+ }
+
+ /**
+ * Tries to interpret the next String as a double value. To verify
+ * if the double value is correct, it is converted back to a String
+ * using the default Locale and this String is compared with the
+ * String from which the double was converted. If the two Strings
+ * don't match, an {@link InputMismatchException} is thrown.<br>
+ * <br> The radix used is always 10 even if the global radix is
+ * changed.
+ *
+ * @param delete Should the String be removed, if true it will be
+ * also removed if the String is not a double value.
+ * @return Returns the double value of the next String.
+ * @throws InputMismatchException if the next String is not a
+ * double.
+ */
+ private double myNextDouble (final boolean delete) throws
+ InputMismatchException
+ {
+ double rc;
+ String tmp = myPrepareForNext (this.p, delete);
+
+ try
+ {
+ tmp = myApplyLocale (tmp, 10);
+ rc = Double.parseDouble (tmp);
+ if (("" + rc).equals (tmp))
+ {
+ return rc;
+ }
+ }
+ catch (ParseException e)
+ {
+ }
+ throw new InputMismatchException (ERR_PREFIX + tmp + NOT_DOUBLE);
+ }
+
+ /**
+ * Tries to interpret the next String as an int value. Therfore
+ * {@link #myApplyLocale(String, int)} decides if the current Locale
+ * should be applied or not and then the result is parsed using
+ * {@link Integer#parseInt(String, int)}. Any Error will lead to an
+ * {@link InputMismatchException}.
+ *
+ * @param radix The radix to use.
+ * @param delete <code> true </code> if the String should be deleted
+ * from the input.
+ * @return Returns the int value of the String.
+ * @throws InputMismatchException if the next String is not an int.
+ */
+ private int myNextInt (final int radix,
+ final boolean delete) throws InputMismatchException
+ {
+ int rc;
+ String tmp = myPrepareForNext (this.p, delete);
+ try
+ {
+ tmp = myApplyLocale (tmp, radix);
+ rc = Integer.parseInt (tmp, radix);
+ return rc;
+ }
+ catch (NumberFormatException nfe)
+ {
+ }
+ catch (ParseException e)
+ {
+ }
+ throw new InputMismatchException (ERR_PREFIX + tmp + NOT_INT);
+ }
+
+ /**
+ * Finds the next line using the {@link #NEW_LINE} constant which is
+ * set to the system specific line seperator.
+ *
+ * @param delete should the found line be deleted from the input.
+ * @return the current line.
+ */
+ private String myNextLine (final boolean delete)
+ {
+ return myPrepareForNext (Pattern.compile (NEW_LINE), delete);
+ }
+
+ /**
+ * Tries to interpret the next String as a long value with the given
+ * radix. Therfore the {@link Long#parseLong(String, int)} is called
+ * and every Error will lead into a {@link InputMismatchException}.
+ *
+ * @param radix The radix to be used.
+ * @param delete Should the found String be deleted from the input.
+ * @return the long value of the next String.
+ * @throws InputMismatchException if the next String is not a long.
+ */
+ private long myNextLong (final int radix,
+ final boolean delete) throws InputMismatchException
+ {
+ long rc;
+ String tmp = myPrepareForNext (this.p, delete);
+
+ try
+ {
+ tmp = myApplyLocale (tmp, radix);
+ rc = Long.parseLong (tmp, radix);
+ return rc;
+ }
+ catch (NumberFormatException nfe)
+ {
+ }
+ catch (ParseException e)
+ {
+ }
+ throw new InputMismatchException (ERR_PREFIX + tmp + NOT_LONG);
+ }
+
+ /**
+ * Tries to interpret the next String as a short value with the
+ * given radix. Therfore the {@link Short#parseShort(String, int)}
+ * is called and every Error will lead into a {@link
+ * InputMismatchException} .
+ *
+ * @param radix
+ * The radix to be used.
+ * @param delete
+ * Should the found String be deleted from the input.
+ * @return the long value of the next String.
+ * @throws InputMismatchException
+ * if the next String is not a short.
+ */
+ private short myNextShort (final int radix,
+ final boolean delete) throws
+ InputMismatchException
+ {
+ short rc;
+ String tmp = myPrepareForNext (this.p, delete);
+
+ try
+ {
+ tmp = myApplyLocale (tmp, radix);
+ rc = Short.parseShort (tmp, radix);
+ return rc;
+ }
+ catch (NumberFormatException nfe)
+ {
+ }
+ catch (ParseException e)
+ {
+ }
+ throw new InputMismatchException (ERR_PREFIX + tmp +
+ "\" is not a short");
+ }
+
+ /**
+ * Sets the current pattern to the given pattern and calls the
+ * {@link #myCoreNext(boolean, Pattern)}. Finally sets the pattern
+ * back to its old value.
+ *
+ * @param aktPattern Pattern to be used for the next match.
+ * @param delete Should the found String be deleted or not.
+ * @return Return the String returned from {@link
+ * #myCoreNext(boolean, Pattern)}.
+ */
+ private String myPrepareForNext (final Pattern aktPattern,
+ final boolean delete)
+ {
+
+ String rc;
+ Pattern oldPattern = this.p;
+ useDelimiter (aktPattern);
+
+ rc = myCoreNext (delete, aktPattern);
+
+ useDelimiter (oldPattern);
+
+ return rc;
+ }
+
+ /**
+ * Determinates if the last found can be used, so that after a
+ * hasNextXXX the nextXXX has not to search if nothing has
+ * changed.<br /> Used in {@link #myCoreNext(boolean, Pattern)}.
+ *
+ * @param aktP The pattern which should be checked.
+ * @return <code> true </code> if the searchresult is already ready.
+ */
+ private boolean shallUseLastFound (final Pattern aktP)
+ {
+ if (this.lastFoundPresent &&
+ this.lastPatternHash == aktP.hashCode () &&
+ this.last_RegionStart == this.myMatcher.regionStart () &&
+ this.last_anchor == this.myMatcher.hasAnchoringBounds () &&
+ this.last_transparent == this.myMatcher.hasTransparentBounds ())
+ {
+ if (this.last_RegionEnd != this.myMatcher.regionEnd ())
+ {
+ int tmpVal =
+ this.myMatcher.regionEnd () -
+ this.last_RegionEnd - this.MAX_PREFIX;
+ if (tmpVal > 0 && tmpVal < 20)
+ {
+ this.last_RegionEnd =
+ this.myMatcher.regionEnd ();
+ return true;
+ }
+ }
+ else
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/libjava/classpath/java/util/ServiceConfigurationError.java b/libjava/classpath/java/util/ServiceConfigurationError.java
new file mode 100644
index 000000000..40d744c23
--- /dev/null
+++ b/libjava/classpath/java/util/ServiceConfigurationError.java
@@ -0,0 +1,94 @@
+/* ServiceConfigurationError.java -- An error on service loading.
+ Copyright (C) 2007 Free Software Foundation
+
+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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * <p>
+ * An error thrown when a problem occurs during the loading
+ * of a service provider by a {@link ServiceLoader}. Such
+ * an error can occur for a number of reasons:
+ * </p>
+ * <ul>
+ * <li>An I/O error occurs</li>
+ * <li>The configuration file doesn't meet the specifications</li>
+ * <li>A listed class can not be found</li>
+ * <li>A listed class does not implement the service</li>
+ * <li>A listed class can not be instantiated</li>
+ * </ul>
+ *
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.6
+ */
+public class ServiceConfigurationError
+ extends Error
+{
+
+ /**
+ * Compatible with JDK 1.6
+ */
+ private static final long serialVersionUID = 74132770414881L;
+
+ /**
+ * Constructs a new {@link ServiceConfigurationError}
+ * with the specified message.
+ *
+ * @param message a message describing the error, or
+ * <code>null</code> if none is required.
+ */
+ public ServiceConfigurationError(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * Constructs a new {@link ServiceConfigurationError}
+ * with the specified message and cause.
+ *
+ * @param message a message describing the error, or
+ * <code>null</code> if none is required.
+ * @param cause the cause of the error, or
+ * <code>null</code> if this is unknown
+ * or inappropriate.
+ */
+ public ServiceConfigurationError(String message,
+ Throwable cause)
+ {
+ super(message,cause);
+ }
+
+}
diff --git a/libjava/classpath/java/util/ServiceLoader.java b/libjava/classpath/java/util/ServiceLoader.java
new file mode 100644
index 000000000..bcac0704a
--- /dev/null
+++ b/libjava/classpath/java/util/ServiceLoader.java
@@ -0,0 +1,274 @@
+/* ServiceLoader.java -- Allows loading of plug-in services.
+ Copyright (C) 2006, 2007 Free Software Foundation
+
+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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.classpath.ServiceFactory;
+
+/**
+ * <p>
+ * Facilities for loading service providers. A service is
+ * defined by a set of interfaces or abstract classes, and
+ * a service provider gives a concrete implementation of this.
+ * Service providers may be installed as part of the runtime
+ * environment using JAR files in the extension directories,
+ * or may be simply supplied on the classpath.
+ * </p>
+ * <p>
+ * In terms of loading a service, the service is defined by
+ * a single interface or abstract class which the provider
+ * implements. This may not constitute the entire service,
+ * but is simply a mechanism by which a provider of the
+ * service can be loaded and its capabilities determined.
+ * The variety of possible services means that no more
+ * requirements are made of the service provider other than
+ * that it must have an accessible zero argument constructor
+ * in order to allow an instance to be created.
+ * </p>
+ * <p>
+ * Service providers are listed in a file named after the
+ * service type in the directory <code>META-INF/services</code>.
+ * The file contains a list of classes, and must be encoded
+ * using UTF-8. Whitespace is ignored. Comments can be
+ * included by using a <code>'#'</code> prefix; anything occurring
+ * on the same line after this symbol is ignored. Duplicate classes
+ * are ignored.
+ * </p>
+ * <p>
+ * The classes are loaded using the same classloader that was
+ * queried in order to locate the configuration file. As a result,
+ * the providers do not need to reside in the same JAR file as the
+ * resource; they merely have to be accessible to this classloader,
+ * which may differ from the one that loaded the file itself.
+ * </p>
+ * <p>
+ * Providers are located and instantiated lazily, as calls to the
+ * {@link #iterator()} are made. Providers are cached, and those in
+ * the cache are returned first. The cache may be cleared by calling
+ * {@link #reload()}. Service loaders always execute in the security
+ * context of the caller, so ideally calls should be made from a trusted
+ * source.
+ * </p>
+ * <p>
+ * Note that this class is not thread-safe, and that strange errors may
+ * occur as the result of the use of remote URLs occurring on the classpath,
+ * which lead to erroneous web pages.
+ * </p>
+ *
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.6
+ */
+public final class ServiceLoader<S>
+ implements Iterable<S>
+{
+
+ /**
+ * The class of the service provider.
+ */
+ private Class<S> spi;
+
+ /**
+ * The class loader for the service provider.
+ */
+ private ClassLoader loader;
+
+ /**
+ * The cache of service providers.
+ */
+ private List<S> cache;
+
+ /**
+ * The {@link gnu.classpath.ServiceFactory} iterator
+ * from which providers are obtained.
+ */
+ private Iterator<S> serviceIt;
+
+ /**
+ * Constructs a new {@link ServiceLoader} with
+ * the specified provider and class loader.
+ *
+ * @param spi the service to load.
+ * @param loader the class loader to use.
+ */
+ private ServiceLoader(Class<S> spi, ClassLoader loader)
+ {
+ this.spi = spi;
+ this.loader = loader;
+ cache = new ArrayList<S>();
+ }
+
+ /**
+ * Lazily loads the available providers. The iterator first returns
+ * providers from the cache, in instantiation order, followed by any
+ * remaining providers, which are added to the cache after loading.
+ * The actual loading and parsing of the configuration file takes
+ * place in the {@link Iterator#hasNext()} and {@link Iterator#next()}
+ * methods, which means that they may result in a
+ * {@link ServiceConfigurationError} being thrown. If such an error
+ * does occur, subsequent invocations will attempt to recover.
+ * The {@link remove()} method is not supported and instead throws
+ * an {@link UnsupportedOperationException}.
+ *
+ * @return an iterator that lazily loads service providers.
+ */
+ public Iterator<S> iterator()
+ {
+ return new Iterator<S>()
+ {
+ /**
+ * The cache iterator.
+ */
+ private Iterator<S> cacheIt = cache.iterator();
+
+ public boolean hasNext()
+ {
+ if (cacheIt.hasNext())
+ return true;
+ if (serviceIt == null)
+ serviceIt =
+ ServiceFactory.lookupProviders(spi, loader, true);
+ return serviceIt.hasNext();
+ }
+
+ public S next()
+ {
+ if (cacheIt.hasNext())
+ return cacheIt.next();
+ if (serviceIt == null)
+ serviceIt =
+ ServiceFactory.lookupProviders(spi, loader, true);
+ S nextService = serviceIt.next();
+ cache.add(nextService);
+ return nextService;
+ }
+
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ /**
+ * Creates a new service loader for the given service,
+ * using the context class loader of the current thread.
+ * This is equivalent to calling <code>ServiceLoader.load(service,
+ * Thread.currentThread().getContextClassLoader())</code>.
+ *
+ * @param service the interface or abstract class that represents
+ * the service.
+ * @return a new {@link ServiceLoader} instance.
+ */
+ public static <S> ServiceLoader<S> load(Class<S> service)
+ {
+ return load(service,
+ Thread.currentThread().getContextClassLoader());
+ }
+
+ /**
+ * Creates a new service loader for the given service,
+ * using the specified class loader. The class loader is
+ * used to access the configuration file and the service
+ * provider instances themselves. If the loader is
+ * <code>null</code>, the system class loader (or, if
+ * this is also <code>null</code>, the bootstrap class
+ * loader).
+ *
+ * @param service the interface or abstract class that represents
+ * the service.
+ * @param loader the class loader used to load the configuration
+ * file and service providers.
+ * @return a new {@link ServiceLoader} instance.
+ */
+ public static <S> ServiceLoader<S> load(Class<S> service,
+ ClassLoader loader)
+ {
+ if (loader == null)
+ loader = ClassLoader.getSystemClassLoader();
+ return new ServiceLoader(service, loader);
+ }
+
+ /**
+ * Creates a new service loader for the given service,
+ * using the extension class loader. If the extension
+ * class loader can not be found, the system class loader
+ * is used (or, if this is <code>null</code>, the
+ * bootstrap class loader). The primary use of this method
+ * is to only obtain installed services, ignoring any which
+ * may appear on the classpath. This is equivalent to calling
+ * <code>load(service, extClassLoader)</code> where
+ * <code>extClassLoader</code> is the extension class loader
+ * (or <code>null</code> if this is unavailable).
+ *
+ * @param service the interface or abstract class that represents
+ * the service.
+ * @return a new {@link ServiceLoader} instance.
+ */
+ public static <S> ServiceLoader<S> loadInstalled(Class<S> service)
+ {
+ /* We expect the extension class loader to be the parent
+ * of the system class loader, as in
+ * ClassLoader.getDefaultSystemClassLoader() */
+ return load(service,
+ ClassLoader.getSystemClassLoader().getParent());
+ }
+
+ /**
+ * Clears the cache of the provider, so that all providers
+ * are again read from the configuration file and instantiated.
+ */
+ public void reload()
+ {
+ cache.clear();
+ }
+
+ /**
+ * Returns a textual representation of this
+ * {@link ServiceLoader}.
+ *
+ * @return a textual representation of the
+ * service loader.
+ */
+ public String toString()
+ {
+ return getClass().getName() +
+ "[spi=" + spi +
+ ",loader=" + loader +
+ "]";
+ }
+
+}
diff --git a/libjava/classpath/java/util/Set.java b/libjava/classpath/java/util/Set.java
new file mode 100644
index 000000000..35f75b5e7
--- /dev/null
+++ b/libjava/classpath/java/util/Set.java
@@ -0,0 +1,265 @@
+/* Set.java -- A collection that prohibits duplicates
+ Copyright (C) 1998, 2001, 2004, 2005
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * A collection that contains no duplicates. In other words, for two set
+ * elements e1 and e2, <code>e1.equals(e2)</code> returns false. There
+ * are additional stipulations on <code>add</code>, <code>equals</code>
+ * and <code>hashCode</code>, as well as the requirements that constructors
+ * do not permit duplicate elements. The Set interface is incompatible with
+ * List; you cannot implement both simultaneously.
+ * <p>
+ *
+ * Note: Be careful about using mutable objects in sets. In particular,
+ * if a mutable object changes to become equal to another set element, you
+ * have violated the contract. As a special case of this, a Set is not
+ * allowed to be an element of itself, without risking undefined behavior.
+ *
+ * @author Original author unknown
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Collection
+ * @see List
+ * @see SortedSet
+ * @see HashSet
+ * @see TreeSet
+ * @see LinkedHashSet
+ * @see AbstractSet
+ * @see Collections#singleton(Object)
+ * @see Collections#EMPTY_SET
+ * @since 1.2
+ * @status updated to 1.4
+ */
+public interface Set<E> extends Collection<E>
+{
+ /**
+ * Adds the specified element to the set if it is not already present
+ * (optional operation). In particular, the comparison algorithm is
+ * <code>o == null ? e == null : o.equals(e)</code>. Sets need not permit
+ * all values, and may document what exceptions will be thrown if
+ * a value is not permitted.
+ *
+ * @param o the object to add
+ * @return true if the object was not previously in the set
+ * @throws UnsupportedOperationException if this operation is not allowed
+ * @throws ClassCastException if the class of o prevents it from being added
+ * @throws IllegalArgumentException if some aspect of o prevents it from
+ * being added
+ * @throws NullPointerException if null is not permitted in this set
+ */
+ boolean add(E o);
+
+ /**
+ * Adds all of the elements of the given collection to this set (optional
+ * operation). If the argument is also a Set, this returns the mathematical
+ * <i>union</i> of the two. The behavior is unspecified if the set is
+ * modified while this is taking place.
+ *
+ * @param c the collection to add
+ * @return true if the set changed as a result
+ * @throws UnsupportedOperationException if this operation is not allowed
+ * @throws ClassCastException if the class of an element prevents it from
+ * being added
+ * @throws IllegalArgumentException if something about an element prevents
+ * it from being added
+ * @throws NullPointerException if null is not permitted in this set, or
+ * if the argument c is null
+ * @see #add(Object)
+ */
+ boolean addAll(Collection<? extends E> c);
+
+ /**
+ * Removes all elements from this set (optional operation). This set will
+ * be empty afterwords, unless an exception occurs.
+ *
+ * @throws UnsupportedOperationException if this operation is not allowed
+ */
+ void clear();
+
+ /**
+ * Returns true if the set contains the specified element. In other words,
+ * this looks for <code>o == null ? e == null : o.equals(e)</code>.
+ *
+ * @param o the object to look for
+ * @return true if it is found in the set
+ * @throws ClassCastException if the type of o is not a valid type
+ * for this set.
+ * @throws NullPointerException if o is null and this set doesn't
+ * support null values.
+ */
+ boolean contains(Object o);
+
+ /**
+ * Returns true if this set contains all elements in the specified
+ * collection. If the argument is also a set, this is the <i>subset</i>
+ * relationship.
+ *
+ * @param c the collection to check membership in
+ * @return true if all elements in this set are in c
+ * @throws NullPointerException if c is null
+ * @throws ClassCastException if the type of any element in c is not
+ * a valid type for this set.
+ * @throws NullPointerException if some element of c is null and this
+ * set doesn't support null values.
+ * @see #contains(Object)
+ */
+ boolean containsAll(Collection<?> c);
+
+ /**
+ * Compares the specified object to this for equality. For sets, the object
+ * must be a set, the two must have the same size, and every element in
+ * one must be in the other.
+ *
+ * @param o the object to compare to
+ * @return true if it is an equal set
+ */
+ boolean equals(Object o);
+
+ /**
+ * Returns the hash code for this set. In order to satisfy the contract of
+ * equals, this is the sum of the hashcode of all elements in the set.
+ *
+ * @return the sum of the hashcodes of all set elements
+ * @see #equals(Object)
+ */
+ int hashCode();
+
+ /**
+ * Returns true if the set contains no elements.
+ *
+ * @return true if the set is empty
+ */
+ boolean isEmpty();
+
+ /**
+ * Returns an iterator over the set. The iterator has no specific order,
+ * unless further specified.
+ *
+ * @return a set iterator
+ */
+ Iterator<E> iterator();
+
+ /**
+ * Removes the specified element from this set (optional operation). If
+ * an element e exists, <code>o == null ? e == null : o.equals(e)</code>,
+ * it is removed from the set.
+ *
+ * @param o the object to remove
+ * @return true if the set changed (an object was removed)
+ * @throws UnsupportedOperationException if this operation is not allowed
+ * @throws ClassCastException if the type of o is not a valid type
+ * for this set.
+ * @throws NullPointerException if o is null and this set doesn't allow
+ * the removal of a null value.
+ */
+ boolean remove(Object o);
+
+ /**
+ * Removes from this set all elements contained in the specified collection
+ * (optional operation). If the argument is a set, this returns the
+ * <i>asymmetric set difference</i> of the two sets.
+ *
+ * @param c the collection to remove from this set
+ * @return true if this set changed as a result
+ * @throws UnsupportedOperationException if this operation is not allowed
+ * @throws NullPointerException if c is null
+ * @throws ClassCastException if the type of any element in c is not
+ * a valid type for this set.
+ * @throws NullPointerException if some element of c is null and this
+ * set doesn't support removing null values.
+ * @see #remove(Object)
+ */
+ boolean removeAll(Collection<?> c);
+
+ /**
+ * Retains only the elements in this set that are also in the specified
+ * collection (optional operation). If the argument is also a set, this
+ * performs the <i>intersection</i> of the two sets.
+ *
+ * @param c the collection to keep
+ * @return true if this set was modified
+ * @throws UnsupportedOperationException if this operation is not allowed
+ * @throws NullPointerException if c is null
+ * @throws ClassCastException if the type of any element in c is not
+ * a valid type for this set.
+ * @throws NullPointerException if some element of c is null and this
+ * set doesn't support retaining null values.
+ * @see #remove(Object)
+ */
+ boolean retainAll(Collection<?> c);
+
+ /**
+ * Returns the number of elements in the set. If there are more
+ * than Integer.MAX_VALUE mappings, return Integer.MAX_VALUE. This is
+ * the <i>cardinality</i> of the set.
+ *
+ * @return the number of elements
+ */
+ int size();
+
+ /**
+ * Returns an array containing the elements of this set. If the set
+ * makes a guarantee about iteration order, the array has the same
+ * order. The array is distinct from the set; modifying one does not
+ * affect the other.
+ *
+ * @return an array of this set's elements
+ * @see #toArray(Object[])
+ */
+ Object[] toArray();
+
+ /**
+ * Returns an array containing the elements of this set, of the same runtime
+ * type of the argument. If the given set is large enough, it is reused,
+ * and null is inserted in the first unused slot. Otherwise, reflection
+ * is used to build a new array. If the set makes a guarantee about iteration
+ * order, the array has the same order. The array is distinct from the set;
+ * modifying one does not affect the other.
+ *
+ * @param a the array to determine the return type; if it is big enough
+ * it is used and returned
+ * @return an array holding the elements of the set
+ * @throws ArrayStoreException if the runtime type of a is not a supertype
+ * of all elements in the set
+ * @throws NullPointerException if a is null
+ * @see #toArray()
+ */
+ <T> T[] toArray(T[] a);
+}
diff --git a/libjava/classpath/java/util/SimpleTimeZone.java b/libjava/classpath/java/util/SimpleTimeZone.java
new file mode 100644
index 000000000..6b3b55f17
--- /dev/null
+++ b/libjava/classpath/java/util/SimpleTimeZone.java
@@ -0,0 +1,1052 @@
+/* java.util.SimpleTimeZone
+ Copyright (C) 1998, 1999, 2000, 2003, 2004, 2005, 2007
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+
+/**
+ * This class represents a simple time zone offset and handles
+ * daylight savings. It can only handle one daylight savings rule, so
+ * it can't represent historical changes.
+ *
+ * This object is tightly bound to the Gregorian calendar. It assumes
+ * a regular seven days week, and the month lengths are that of the
+ * Gregorian Calendar. It can only handle daylight savings for years
+ * lying in the AD era.
+ *
+ * @see Calendar
+ * @see GregorianCalendar
+ * @author Jochen Hoenicke
+ */
+public class SimpleTimeZone extends TimeZone
+{
+ /**
+ * The raw time zone offset in milliseconds to GMT, ignoring
+ * daylight savings.
+ * @serial
+ */
+ private int rawOffset;
+
+ /**
+ * True, if this timezone uses daylight savings, false otherwise.
+ * @serial
+ */
+ private boolean useDaylight;
+
+ /**
+ * The daylight savings offset. This is a positive offset in
+ * milliseconds with respect to standard time. Typically this
+ * is one hour, but for some time zones this may be half an hour.
+ * @serial
+ * @since JDK1.1.4
+ */
+ private int dstSavings = 60 * 60 * 1000;
+
+ /**
+ * The first year, in which daylight savings rules applies.
+ * @serial
+ */
+ private int startYear;
+ private static final int DOM_MODE = 1;
+ private static final int DOW_IN_MONTH_MODE = 2;
+ private static final int DOW_GE_DOM_MODE = 3;
+ private static final int DOW_LE_DOM_MODE = 4;
+
+ /**
+ * The mode of the start rule. This takes one of the following values:
+ * <dl>
+ * <dt>DOM_MODE (1)</dt>
+ * <dd> startDay contains the day in month of the start date,
+ * startDayOfWeek is unused. </dd>
+ * <dt>DOW_IN_MONTH_MODE (2)</dt>
+ * <dd> The startDay gives the day of week in month, and
+ * startDayOfWeek the day of week. For example startDay=2 and
+ * startDayOfWeek=Calender.SUNDAY specifies that the change is on
+ * the second sunday in that month. You must make sure, that this
+ * day always exists (ie. don't specify the 5th sunday).
+ * </dd>
+ * <dt>DOW_GE_DOM_MODE (3)</dt>
+ * <dd> The start is on the first startDayOfWeek on or after
+ * startDay. For example startDay=13 and
+ * startDayOfWeek=Calendar.FRIDAY specifies that the daylight
+ * savings start on the first FRIDAY on or after the 13th of that
+ * Month. Make sure that the change is always in the given month, or
+ * the result is undefined.
+ * </dd>
+ * <dt>DOW_LE_DOM_MONTH (4)</dt>
+ * <dd> The start is on the first startDayOfWeek on or before the
+ * startDay. Make sure that the change is always in the given
+ * month, or the result is undefined.
+ </dd>
+ * </dl>
+ * @serial */
+ private int startMode;
+
+ /**
+ * The month in which daylight savings start. This is one of the
+ * constants Calendar.JANUARY, ..., Calendar.DECEMBER.
+ * @serial
+ */
+ private int startMonth;
+
+ /**
+ * This variable can have different meanings. See startMode for details
+ * @see #startMode
+ * @serial
+ */
+ private int startDay;
+
+ /**
+ * This variable specifies the day of week the change takes place. If
+ * startMode == DOM_MODE, this is undefined.
+ * @serial
+ * @see #startMode
+ */
+ private int startDayOfWeek;
+
+ /**
+ * This variable specifies the time of change to daylight savings.
+ * This time is given in milliseconds after midnight in startTimeMode
+ * chosen time mode.
+ * @serial
+ */
+ private int startTime;
+
+ /**
+ * This variable specifies the mode that startTime is specified in. By
+ * default it is WALL_TIME, but can also be STANDARD_TIME or UTC_TIME. For
+ * startTime, STANDARD_TIME and WALL_TIME are equivalent.
+ * @serial
+ */
+ private int startTimeMode = WALL_TIME;
+
+ /**
+ * The month in which daylight savings ends. This is one of the
+ * constants Calendar.JANUARY, ..., Calendar.DECEMBER.
+ * @serial
+ */
+ private int endMonth;
+
+ /**
+ * This variable gives the mode for the end of daylight savings rule.
+ * It can take the same values as startMode.
+ * @serial
+ * @see #startMode
+ */
+ private int endMode;
+
+ /**
+ * This variable can have different meanings. See startMode for details
+ * @serial
+ * @see #startMode
+ */
+ private int endDay;
+
+ /**
+ * This variable specifies the day of week the change takes place. If
+ * endMode == DOM_MODE, this is undefined.
+ * @serial
+ * @see #startMode
+ */
+ private int endDayOfWeek;
+
+ /**
+ * This variable specifies the time of change back to standard time.
+ * This time is given in milliseconds after midnight in endTimeMode
+ * chosen time mode.
+ * @serial
+ */
+ private int endTime;
+
+ /**
+ * This variable specifies the mode that endTime is specified in. By
+ * default it is WALL_TIME, but can also be STANDARD_TIME or UTC_TIME.
+ * @serial
+ */
+ private int endTimeMode = WALL_TIME;
+
+ /**
+ * This variable points to a deprecated array from JDK 1.1. It is
+ * ignored in JDK 1.2 but streamed out for compatibility with JDK 1.1.
+ * The array contains the lengths of the months in the year and is
+ * assigned from a private static final field to avoid allocating
+ * the array for every instance of the object.
+ * Note that static final fields are not serialized.
+ * @serial
+ */
+ private byte[] monthLength = monthArr;
+ private static final byte[] monthArr =
+ {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30,
+ 31, 30, 31
+ };
+
+ /**
+ * The version of the serialized data on the stream.
+ * <dl>
+ * <dt>0 or not present on stream</dt>
+ * <dd> JDK 1.1.3 or earlier, only provides this fields:
+ * rawOffset, startDay, startDayOfWeek, startMonth, startTime,
+ * startYear, endDay, endDayOfWeek, endMonth, endTime
+ * </dd>
+ * <dd> JDK 1.1.4 or later. This includes three new fields, namely
+ * startMode, endMode and dstSavings. And there is a optional section
+ * as described in writeObject.
+ * </dd>
+ * </dl>
+ *
+ * XXX - JDK 1.2 Beta 4 docu states 1.1.4, but my 1.1.5 has the old
+ * version.
+ *
+ * When streaming out this class it is always written in the latest
+ * version.
+ * @serial
+ * @since JDK1.1.4
+ */
+ private int serialVersionOnStream = 2;
+ private static final long serialVersionUID = -403250971215465050L;
+
+ /**
+ * Constant to indicate that start and end times are specified in standard
+ * time, without adjusting for daylight savings.
+ */
+ public static final int STANDARD_TIME = 1;
+
+ /**
+ * Constant to indicate that start and end times are specified in wall
+ * time, adjusting for daylight savings. This is the default.
+ */
+ public static final int WALL_TIME = 0;
+
+ /**
+ * Constant to indicate that start and end times are specified in UTC.
+ */
+ public static final int UTC_TIME = 2;
+
+ /**
+ * Create a <code>SimpleTimeZone</code> with the given time offset
+ * from GMT and without daylight savings.
+ * @param rawOffset the time offset from GMT in milliseconds.
+ * @param id The identifier of this time zone.
+ */
+ public SimpleTimeZone(int rawOffset, String id)
+ {
+ this.rawOffset = rawOffset;
+ setID(id);
+ useDaylight = false;
+ startYear = 0;
+ }
+
+ /**
+ * Create a <code>SimpleTimeZone</code> with the given time offset
+ * from GMT and with daylight savings. The start/end parameters
+ * can have different meaning (replace WEEKDAY with a real day of
+ * week). Only the first two meanings were supported by earlier
+ * versions of jdk.
+ *
+ * <dl>
+ * <dt><code>day &gt; 0, dayOfWeek = Calendar.WEEKDAY</code></dt>
+ * <dd>The start/end of daylight savings is on the <code>day</code>-th
+ * <code>WEEKDAY</code> in the given month. </dd>
+ * <dt><code>day &lt; 0, dayOfWeek = Calendar.WEEKDAY</code></dt>
+ * <dd>The start/end of daylight savings is on the <code>-day</code>-th
+ * <code>WEEKDAY</code> counted from the <i>end</i> of the month. </dd>
+ * <dt><code>day &gt; 0, dayOfWeek = 0</code></dt>
+ * <dd>The start/end of daylight is on the <code>day</code>-th day of
+ * the month. </dd>
+ * <dt><code>day &gt; 0, dayOfWeek = -Calendar.WEEKDAY</code></dt>
+ * <dd>The start/end of daylight is on the first WEEKDAY on or after
+ * the <code>day</code>-th day of the month. You must make sure that
+ * this day lies in the same month. </dd>
+ * <dt><code>day &lt; 0, dayOfWeek = -Calendar.WEEKDAY</code></dt>
+ * <dd>The start/end of daylight is on the first WEEKDAY on or
+ * <i>before</i> the <code>-day</code>-th day of the month. You
+ * must make sure that this day lies in the same month. </dd>
+ * </dl>
+ *
+ * If you give a non existing month, a day that is zero, or too big,
+ * or a dayOfWeek that is too big, the result is undefined.
+ *
+ * The start rule must have a different month than the end rule.
+ * This restriction shouldn't hurt for all possible time zones.
+ *
+ * @param rawOffset The time offset from GMT in milliseconds.
+ * @param id The identifier of this time zone.
+ * @param startMonth The start month of daylight savings; use the
+ * constants in Calendar.
+ * @param startDayOfWeekInMonth A day in month or a day of week number, as
+ * described above.
+ * @param startDayOfWeek The start rule day of week; see above.
+ * @param startTime A time in millis in standard time.
+ * @param endMonth The end month of daylight savings; use the
+ * constants in Calendar.
+ * @param endDayOfWeekInMonth A day in month or a day of week number, as
+ * described above.
+ * @param endDayOfWeek The end rule day of week; see above.
+ * @param endTime A time in millis in standard time.
+ * @throws IllegalArgumentException if parameters are invalid or out of
+ * range.
+ */
+ public SimpleTimeZone(int rawOffset, String id, int startMonth,
+ int startDayOfWeekInMonth, int startDayOfWeek,
+ int startTime, int endMonth, int endDayOfWeekInMonth,
+ int endDayOfWeek, int endTime)
+ {
+ this.rawOffset = rawOffset;
+ setID(id);
+ useDaylight = true;
+
+ setStartRule(startMonth, startDayOfWeekInMonth, startDayOfWeek, startTime);
+ setEndRule(endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime);
+ if (startMonth == endMonth)
+ throw new IllegalArgumentException("startMonth and endMonth must be different");
+ this.startYear = 0;
+ }
+
+ /**
+ * This constructs a new SimpleTimeZone that supports a daylight savings
+ * rule. The parameter are the same as for the constructor above, except
+ * there is the additional dstSavaings parameter.
+ *
+ * @param dstSavings the amount of savings for daylight savings
+ * time in milliseconds. This must be positive.
+ * @since 1.2
+ */
+ public SimpleTimeZone(int rawOffset, String id, int startMonth,
+ int startDayOfWeekInMonth, int startDayOfWeek,
+ int startTime, int endMonth, int endDayOfWeekInMonth,
+ int endDayOfWeek, int endTime, int dstSavings)
+ {
+ this(rawOffset, id, startMonth, startDayOfWeekInMonth, startDayOfWeek,
+ startTime, endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime);
+
+ this.dstSavings = dstSavings;
+ }
+
+ /**
+ * This constructs a new SimpleTimeZone that supports a daylight savings
+ * rule. The parameter are the same as for the constructor above, except
+ * there are the additional startTimeMode, endTimeMode, and dstSavings
+ * parameters.
+ *
+ * @param startTimeMode the mode that start times are specified in. One of
+ * WALL_TIME, STANDARD_TIME, or UTC_TIME.
+ * @param endTimeMode the mode that end times are specified in. One of
+ * WALL_TIME, STANDARD_TIME, or UTC_TIME.
+ * @param dstSavings the amount of savings for daylight savings
+ * time in milliseconds. This must be positive.
+ * @throws IllegalArgumentException if parameters are invalid or out of
+ * range.
+ * @since 1.4
+ */
+ public SimpleTimeZone(int rawOffset, String id, int startMonth,
+ int startDayOfWeekInMonth, int startDayOfWeek,
+ int startTime, int startTimeMode, int endMonth,
+ int endDayOfWeekInMonth, int endDayOfWeek,
+ int endTime, int endTimeMode, int dstSavings)
+ {
+ this(rawOffset, id, startMonth, startDayOfWeekInMonth, startDayOfWeek,
+ startTime, endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime);
+
+ if (startTimeMode < WALL_TIME || startTimeMode > UTC_TIME)
+ throw new IllegalArgumentException("startTimeMode must be one of WALL_TIME, STANDARD_TIME, or UTC_TIME");
+ if (endTimeMode < WALL_TIME || endTimeMode > UTC_TIME)
+ throw new IllegalArgumentException("endTimeMode must be one of WALL_TIME, STANDARD_TIME, or UTC_TIME");
+
+ this.dstSavings = dstSavings;
+ this.startTimeMode = startTimeMode;
+ this.endTimeMode = endTimeMode;
+ }
+
+ /**
+ * Sets the first year, where daylight savings applies. The daylight
+ * savings rule never apply for years in the BC era. Note that this
+ * is gregorian calendar specific.
+ * @param year the start year.
+ */
+ public void setStartYear(int year)
+ {
+ startYear = year;
+ useDaylight = true;
+ }
+
+ /**
+ * Checks if the month, day, dayOfWeek arguments are in range and
+ * returns the mode of the rule.
+ * @param month the month parameter as in the constructor
+ * @param day the day parameter as in the constructor
+ * @param dayOfWeek the day of week parameter as in the constructor
+ * @return the mode of this rule see startMode.
+ * @exception IllegalArgumentException if parameters are out of range.
+ * @see #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)
+ * @see #startMode
+ */
+ private int checkRule(int month, int day, int dayOfWeek)
+ {
+ if (month < 0 || month > 11)
+ throw new IllegalArgumentException("month out of range");
+
+ int daysInMonth = getDaysInMonth(month, 1);
+ if (dayOfWeek == 0)
+ {
+ if (day <= 0 || day > daysInMonth)
+ throw new IllegalArgumentException("day out of range");
+ return DOM_MODE;
+ }
+ else if (dayOfWeek > 0)
+ {
+ if (Math.abs(day) > (daysInMonth + 6) / 7)
+ throw new IllegalArgumentException("dayOfWeekInMonth out of range");
+ if (dayOfWeek > Calendar.SATURDAY)
+ throw new IllegalArgumentException("dayOfWeek out of range");
+ return DOW_IN_MONTH_MODE;
+ }
+ else
+ {
+ if (day == 0 || Math.abs(day) > daysInMonth)
+ throw new IllegalArgumentException("day out of range");
+ if (dayOfWeek < -Calendar.SATURDAY)
+ throw new IllegalArgumentException("dayOfWeek out of range");
+ if (day < 0)
+ return DOW_LE_DOM_MODE;
+ else
+ return DOW_GE_DOM_MODE;
+ }
+ }
+
+ /**
+ * Sets the daylight savings start rule. You must also set the
+ * end rule with <code>setEndRule</code> or the result of
+ * getOffset is undefined. For the parameters see the ten-argument
+ * constructor above.
+ *
+ * @param month The month where daylight savings start, zero
+ * based. You should use the constants in Calendar.
+ * @param day A day of month or day of week in month.
+ * @param dayOfWeek The day of week where daylight savings start.
+ * @param time The time in milliseconds standard time where daylight
+ * savings start.
+ * @exception IllegalArgumentException if parameters are out of range.
+ * @see SimpleTimeZone
+ */
+ public void setStartRule(int month, int day, int dayOfWeek, int time)
+ {
+ this.startMode = checkRule(month, day, dayOfWeek);
+ this.startMonth = month;
+ this.startDay = day;
+ this.startDayOfWeek = Math.abs(dayOfWeek);
+ this.startTime = time;
+ this.startTimeMode = WALL_TIME;
+ }
+
+ /**
+ * Sets the daylight savings start rule. You must also set the
+ * end rule with <code>setEndRule</code> or the result of
+ * getOffset is undefined. For the parameters see the ten-argument
+ * constructor above.
+ *
+ * Note that this API isn't incredibly well specified. It appears that the
+ * after flag must override the parameters, since normally, the day and
+ * dayofweek can select this. I.e., if day < 0 and dayOfWeek < 0, on or
+ * before mode is chosen. But if after == true, this implementation
+ * overrides the signs of the other arguments. And if dayOfWeek == 0, it
+ * falls back to the behavior in the other APIs. I guess this should be
+ * checked against Sun's implementation.
+ *
+ * @param month The month where daylight savings start, zero
+ * based. You should use the constants in Calendar.
+ * @param day A day of month or day of week in month.
+ * @param dayOfWeek The day of week where daylight savings start.
+ * @param time The time in milliseconds standard time where daylight
+ * savings start.
+ * @param after If true, day and dayOfWeek specify first day of week on or
+ * after day, else first day of week on or before.
+ * @since 1.2
+ * @see SimpleTimeZone
+ */
+ public void setStartRule(int month, int day, int dayOfWeek, int time,
+ boolean after)
+ {
+ if (after)
+ setStartRule(month, day, -dayOfWeek, time);
+ else
+ setStartRule(month, -day, -dayOfWeek, time);
+ }
+
+ /**
+ * Sets the daylight savings start rule. You must also set the
+ * end rule with <code>setEndRule</code> or the result of
+ * getOffset is undefined. For the parameters see the ten-argument
+ * constructor above.
+ *
+ * @param month The month where daylight savings start, zero
+ * based. You should use the constants in Calendar.
+ * @param day A day of month or day of week in month.
+ * @param time The time in milliseconds standard time where daylight
+ * savings start.
+ * @see SimpleTimeZone
+ * @since 1.2
+ */
+ public void setStartRule(int month, int day, int time)
+ {
+ setStartRule(month, day, 0, time);
+ }
+
+ /**
+ * Sets the daylight savings end rule. You must also set the
+ * start rule with <code>setStartRule</code> or the result of
+ * getOffset is undefined. For the parameters see the ten-argument
+ * constructor above.
+ *
+ * @param month The end month of daylight savings.
+ * @param day A day in month, or a day of week in month.
+ * @param dayOfWeek A day of week, when daylight savings ends.
+ * @param time A time in millis in standard time.
+ * @see #setStartRule(int, int, int, int)
+ */
+ public void setEndRule(int month, int day, int dayOfWeek, int time)
+ {
+ this.endMode = checkRule(month, day, dayOfWeek);
+ this.endMonth = month;
+ this.endDay = day;
+ this.endDayOfWeek = Math.abs(dayOfWeek);
+ this.endTime = time;
+ this.endTimeMode = WALL_TIME;
+ useDaylight = true;
+ }
+
+ /**
+ * Sets the daylight savings end rule. You must also set the
+ * start rule with <code>setStartRule</code> or the result of
+ * getOffset is undefined. For the parameters see the ten-argument
+ * constructor above.
+ *
+ * Note that this API isn't incredibly well specified. It appears that the
+ * after flag must override the parameters, since normally, the day and
+ * dayofweek can select this. I.e., if day < 0 and dayOfWeek < 0, on or
+ * before mode is chosen. But if after == true, this implementation
+ * overrides the signs of the other arguments. And if dayOfWeek == 0, it
+ * falls back to the behavior in the other APIs. I guess this should be
+ * checked against Sun's implementation.
+ *
+ * @param month The end month of daylight savings.
+ * @param day A day in month, or a day of week in month.
+ * @param dayOfWeek A day of week, when daylight savings ends.
+ * @param time A time in millis in standard time.
+ * @param after If true, day and dayOfWeek specify first day of week on or
+ * after day, else first day of week on or before.
+ * @since 1.2
+ * @see #setStartRule(int, int, int, int, boolean)
+ */
+ public void setEndRule(int month, int day, int dayOfWeek, int time,
+ boolean after)
+ {
+ if (after)
+ setEndRule(month, day, -dayOfWeek, time);
+ else
+ setEndRule(month, -day, -dayOfWeek, time);
+ }
+
+ /**
+ * Sets the daylight savings end rule. You must also set the
+ * start rule with <code>setStartRule</code> or the result of
+ * getOffset is undefined. For the parameters see the ten-argument
+ * constructor above.
+ *
+ * @param month The end month of daylight savings.
+ * @param day A day in month, or a day of week in month.
+ * @param time A time in millis in standard time.
+ * @see #setStartRule(int, int, int)
+ */
+ public void setEndRule(int month, int day, int time)
+ {
+ setEndRule(month, day, 0, time);
+ }
+
+ /**
+ * Gets the time zone offset, for current date, modified in case of
+ * daylight savings. This is the offset to add to UTC to get the local
+ * time.
+ *
+ * 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:
+ * <code>offset = cal.get(Calendar.ZONE_OFFSET)
+ * + cal.get(Calendar.DST_OFFSET);</code>
+ *
+ * This version doesn't suffer this inaccuracy.
+ *
+ * The arguments don't follow the approach for setting start and end rules.
+ * The day must be a positive number and dayOfWeek must be a positive value
+ * from Calendar. dayOfWeek is redundant, but must match the other values
+ * or an inaccurate result may be returned.
+ *
+ * @param era the era of the given date
+ * @param year the year of the given date
+ * @param month the month of the given date, 0 for January.
+ * @param day the day of month
+ * @param dayOfWeek the day of week; this must match the other fields.
+ * @param millis the millis in the day (in local standard time)
+ * @return the time zone offset in milliseconds.
+ * @throws IllegalArgumentException if arguments are incorrect.
+ */
+ public int getOffset(int era, int year, int month, int day, int dayOfWeek,
+ int millis)
+ {
+ int daysInMonth = getDaysInMonth(month, year);
+ if (day < 1 || day > daysInMonth)
+ throw new IllegalArgumentException("day out of range");
+ if (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY)
+ throw new IllegalArgumentException("dayOfWeek out of range");
+ if (month < Calendar.JANUARY || month > Calendar.DECEMBER)
+ throw new IllegalArgumentException("month out of range:" + month);
+
+ // This method is called by Calendar, so we mustn't use that class.
+ int daylightSavings = 0;
+ if (useDaylight && era == GregorianCalendar.AD && year >= startYear)
+ {
+ int orig_year = year;
+ int time = startTime + (startTimeMode == UTC_TIME ? rawOffset : 0);
+ // This does only work for Gregorian calendars :-(
+ // This is mainly because setStartYear doesn't take an era.
+ boolean afterStart = ! isBefore(year, month, day, dayOfWeek, millis,
+ startMode, startMonth, startDay,
+ startDayOfWeek, time);
+ millis += dstSavings;
+ if (millis >= 24 * 60 * 60 * 1000)
+ {
+ millis -= 24 * 60 * 60 * 1000;
+ dayOfWeek = (dayOfWeek % 7) + 1;
+ if (++day > daysInMonth)
+ {
+ day = 1;
+ if (month++ == Calendar.DECEMBER)
+ {
+ month = Calendar.JANUARY;
+ year++;
+ }
+ }
+ }
+ time = endTime + (endTimeMode == UTC_TIME ? rawOffset : 0);
+ if (endTimeMode != WALL_TIME)
+ time += dstSavings;
+ boolean beforeEnd = isBefore(year, month, day, dayOfWeek, millis,
+ endMode, endMonth, endDay, endDayOfWeek,
+ time);
+
+ if (year != orig_year)
+ afterStart = false;
+ if (startMonth < endMonth)
+ // use daylight savings, if the date is after the start of
+ // savings, and before the end of savings.
+ daylightSavings = afterStart && beforeEnd ? dstSavings : 0;
+ else
+ // use daylight savings, if the date is before the end of
+ // savings, or after the start of savings.
+ daylightSavings = beforeEnd || afterStart ? dstSavings : 0;
+ }
+ return rawOffset + daylightSavings;
+ }
+
+ /**
+ * Returns the time zone offset to GMT in milliseconds, ignoring
+ * day light savings.
+ * @return the time zone offset.
+ */
+ public int getRawOffset()
+ {
+ return rawOffset;
+ }
+
+ /**
+ * Sets the standard time zone offset to GMT.
+ * @param rawOffset The time offset from GMT in milliseconds.
+ */
+ public void setRawOffset(int rawOffset)
+ {
+ this.rawOffset = rawOffset;
+ }
+
+ /**
+ * Gets the daylight savings offset. This is a positive offset in
+ * milliseconds with respect to standard time. Typically this
+ * is one hour, but for some time zones this may be half an our.
+ * @return the daylight savings offset in milliseconds.
+ *
+ * @since 1.2
+ */
+ public int getDSTSavings()
+ {
+ return dstSavings;
+ }
+
+ /**
+ * Sets the daylight savings offset. This is a positive offset in
+ * milliseconds with respect to standard time.
+ *
+ * @param dstSavings the daylight savings offset in milliseconds.
+ *
+ * @since 1.2
+ */
+ public void setDSTSavings(int dstSavings)
+ {
+ if (dstSavings <= 0)
+ throw new IllegalArgumentException("illegal value for dstSavings");
+
+ this.dstSavings = dstSavings;
+ }
+
+ /**
+ * Returns if this time zone uses daylight savings time.
+ * @return true, if we use daylight savings time, false otherwise.
+ */
+ public boolean useDaylightTime()
+ {
+ return useDaylight;
+ }
+
+ /**
+ * Returns the number of days in the given month.
+ * Uses gregorian rules prior to 1582 (The default and earliest cutover)
+ * @param month The month, zero based; use one of the Calendar constants.
+ * @param year The year.
+ */
+ private int getDaysInMonth(int month, int year)
+ {
+ if (month == Calendar.FEBRUARY)
+ {
+ if ((year & 3) != 0)
+ return 28;
+
+ // Assume default Gregorian cutover,
+ // all years prior to this must be Julian
+ if (year < 1582)
+ return 29;
+
+ // Gregorian rules
+ return ((year % 100) != 0 || (year % 400) == 0) ? 29 : 28;
+ }
+ else
+ return monthArr[month];
+ }
+
+ /**
+ * Checks if the date given in calXXXX, is before the change between
+ * dst and standard time.
+ * @param calYear the year of the date to check (for leap day checking).
+ * @param calMonth the month of the date to check.
+ * @param calDayOfMonth the day of month of the date to check.
+ * @param calDayOfWeek the day of week of the date to check.
+ * @param calMillis the millis of day of the date to check (standard time).
+ * @param mode the change mode; same semantic as startMode.
+ * @param month the change month; same semantic as startMonth.
+ * @param day the change day; same semantic as startDay.
+ * @param dayOfWeek the change day of week;
+ * @param millis the change time in millis since midnight standard time.
+ * same semantic as startDayOfWeek.
+ * @return true, if cal is before the change, false if cal is on
+ * or after the change.
+ */
+ private boolean isBefore(int calYear, int calMonth, int calDayOfMonth,
+ int calDayOfWeek, int calMillis, int mode,
+ int month, int day, int dayOfWeek, int millis)
+ {
+ // This method is called by Calendar, so we mustn't use that class.
+ // We have to do all calculations by hand.
+ // check the months:
+ // XXX - this is not correct:
+ // for the DOW_GE_DOM and DOW_LE_DOM modes the change date may
+ // be in a different month.
+ if (calMonth != month)
+ return calMonth < month;
+
+ // check the day:
+ switch (mode)
+ {
+ case DOM_MODE:
+ if (calDayOfMonth != day)
+ return calDayOfMonth < day;
+ break;
+ case DOW_IN_MONTH_MODE:
+ {
+ // This computes the day of month of the day of type
+ // "dayOfWeek" that lies in the same (sunday based) week as cal.
+ calDayOfMonth += (dayOfWeek - calDayOfWeek);
+
+ // Now we convert it to 7 based number (to get a one based offset
+ // after dividing by 7). If we count from the end of the
+ // month, we get want a -7 based number counting the days from
+ // the end:
+ if (day < 0)
+ calDayOfMonth -= getDaysInMonth(calMonth, calYear) + 7;
+ else
+ calDayOfMonth += 6;
+
+ // day > 0 day < 0
+ // S M T W T F S S M T W T F S
+ // 7 8 9 10 11 12 -36-35-34-33-32-31
+ // 13 14 15 16 17 18 19 -30-29-28-27-26-25-24
+ // 20 21 22 23 24 25 26 -23-22-21-20-19-18-17
+ // 27 28 29 30 31 32 33 -16-15-14-13-12-11-10
+ // 34 35 36 -9 -8 -7
+ // Now we calculate the day of week in month:
+ int week = calDayOfMonth / 7;
+
+ // day > 0 day < 0
+ // S M T W T F S S M T W T F S
+ // 1 1 1 1 1 1 -5 -5 -4 -4 -4 -4
+ // 1 2 2 2 2 2 2 -4 -4 -4 -3 -3 -3 -3
+ // 2 3 3 3 3 3 3 -3 -3 -3 -2 -2 -2 -2
+ // 3 4 4 4 4 4 4 -2 -2 -2 -1 -1 -1 -1
+ // 4 5 5 -1 -1 -1
+ if (week != day)
+ return week < day;
+
+ if (calDayOfWeek != dayOfWeek)
+ return calDayOfWeek < dayOfWeek;
+
+ // daylight savings starts/ends on the given day.
+ break;
+ }
+ case DOW_LE_DOM_MODE:
+ // The greatest sunday before or equal December, 12
+ // is the same as smallest sunday after or equal December, 6.
+ day = Math.abs(day) - 6;
+ case DOW_GE_DOM_MODE:
+ // Calculate the day of month of the day of type
+ // "dayOfWeek" that lies before (or on) the given date.
+ calDayOfMonth -= (calDayOfWeek < dayOfWeek ? 7 : 0) + calDayOfWeek
+ - dayOfWeek;
+ if (calDayOfMonth < day)
+ return true;
+ if (calDayOfWeek != dayOfWeek || calDayOfMonth >= day + 7)
+ return false;
+
+ // now we have the same day
+ break;
+ }
+
+ // the millis decides:
+ return (calMillis < millis);
+ }
+
+ /**
+ * Determines if the given date is in daylight savings time.
+ * @return true, if it is in daylight savings time, false otherwise.
+ */
+ public boolean inDaylightTime(Date date)
+ {
+ Calendar cal = Calendar.getInstance(this);
+ cal.setTime(date);
+ return (cal.get(Calendar.DST_OFFSET) != 0);
+ }
+
+ /**
+ * Generates the hashCode for the SimpleDateFormat object. It is
+ * the rawOffset, possibly, if useDaylightSavings is true, xored
+ * with startYear, startMonth, startDayOfWeekInMonth, ..., endTime.
+ */
+ public synchronized int hashCode()
+ {
+ return rawOffset
+ ^ (useDaylight
+ ? startMonth ^ startDay ^ startDayOfWeek ^ startTime ^ endMonth
+ ^ endDay ^ endDayOfWeek ^ endTime : 0);
+ }
+
+ public synchronized boolean equals(Object o)
+ {
+ if (this == o)
+ return true;
+ if (! (o instanceof SimpleTimeZone))
+ return false;
+ SimpleTimeZone zone = (SimpleTimeZone) o;
+ if (zone.hashCode() != hashCode() || ! getID().equals(zone.getID())
+ || rawOffset != zone.rawOffset || useDaylight != zone.useDaylight)
+ return false;
+ if (! useDaylight)
+ return true;
+ return (startYear == zone.startYear && startMonth == zone.startMonth
+ && startDay == zone.startDay
+ && startDayOfWeek == zone.startDayOfWeek
+ && startTime == zone.startTime
+ && startTimeMode == zone.startTimeMode && endMonth == zone.endMonth
+ && endDay == zone.endDay && endDayOfWeek == zone.endDayOfWeek
+ && endTime == zone.endTime && endTimeMode == zone.endTimeMode);
+ }
+
+ /**
+ * Test if the other time zone uses the same rule and only
+ * possibly differs in ID. This implementation for this particular
+ * class will return true if the other object is a SimpleTimeZone,
+ * the raw offsets and useDaylight are identical and if useDaylight
+ * is true, also the start and end datas are identical.
+ * @return true if this zone uses the same rule.
+ */
+ public boolean hasSameRules(TimeZone other)
+ {
+ if (this == other)
+ return true;
+ if (! (other instanceof SimpleTimeZone))
+ return false;
+ SimpleTimeZone zone = (SimpleTimeZone) other;
+ if (zone.hashCode() != hashCode() || rawOffset != zone.rawOffset
+ || useDaylight != zone.useDaylight)
+ return false;
+ if (! useDaylight)
+ return true;
+ return (startYear == zone.startYear && startMonth == zone.startMonth
+ && startDay == zone.startDay
+ && startDayOfWeek == zone.startDayOfWeek
+ && startTime == zone.startTime
+ && startTimeMode == zone.startTimeMode && endMonth == zone.endMonth
+ && endDay == zone.endDay && endDayOfWeek == zone.endDayOfWeek
+ && endTime == zone.endTime && endTimeMode == zone.endTimeMode);
+ }
+
+ /**
+ * Returns a string representation of this SimpleTimeZone object.
+ * @return a string representation of this SimpleTimeZone object.
+ */
+ public String toString()
+ {
+ // the test for useDaylight is an incompatibility to jdk1.2, but
+ // I think this shouldn't hurt.
+ return getClass().getName() + "[" + "id=" + getID() + ",offset="
+ + rawOffset + ",dstSavings=" + dstSavings + ",useDaylight="
+ + useDaylight
+ + (useDaylight
+ ? ",startYear=" + startYear + ",startMode=" + startMode
+ + ",startMonth=" + startMonth + ",startDay=" + startDay
+ + ",startDayOfWeek=" + startDayOfWeek + ",startTime="
+ + startTime + ",startTimeMode=" + startTimeMode + ",endMode="
+ + endMode + ",endMonth=" + endMonth + ",endDay=" + endDay
+ + ",endDayOfWeek=" + endDayOfWeek + ",endTime=" + endTime
+ + ",endTimeMode=" + endTimeMode : "") + "]";
+ }
+
+ /**
+ * Reads a serialized simple time zone from stream.
+ * @see #writeObject
+ */
+ private void readObject(java.io.ObjectInputStream input)
+ throws java.io.IOException, ClassNotFoundException
+ {
+ input.defaultReadObject();
+ if (serialVersionOnStream == 0)
+ {
+ // initialize the new fields to default values.
+ dstSavings = 60 * 60 * 1000;
+ endMode = DOW_IN_MONTH_MODE;
+ startMode = DOW_IN_MONTH_MODE;
+ startTimeMode = WALL_TIME;
+ endTimeMode = WALL_TIME;
+ serialVersionOnStream = 2;
+ }
+ else
+ {
+ int length = input.readInt();
+ byte[] byteArray = new byte[length];
+ input.read(byteArray, 0, length);
+ if (length >= 4)
+ {
+ // Lets hope that Sun does extensions to the serialized
+ // form in a sane manner.
+ startDay = byteArray[0];
+ startDayOfWeek = byteArray[1];
+ endDay = byteArray[2];
+ endDayOfWeek = byteArray[3];
+ }
+ }
+ }
+
+ /**
+ * Serializes this object to a stream. @serialdata The object is
+ * first written in the old JDK 1.1 format, so that it can be read
+ * by the old classes. This means, that the
+ * <code>start/endDay(OfWeek)</code>-Fields are written in the
+ * DOW_IN_MONTH_MODE rule, since this was the only supported rule
+ * in 1.1.
+ *
+ * In the optional section, we write first the length of an byte
+ * array as int and afterwards the byte array itself. The byte
+ * array contains in this release four elements, namely the real
+ * startDay, startDayOfWeek endDay, endDayOfWeek in that Order.
+ * These fields are needed, because for compatibility reasons only
+ * approximative values are written to the required section, as
+ * described above.
+ */
+ private void writeObject(java.io.ObjectOutputStream output)
+ throws java.io.IOException
+ {
+ byte[] byteArray = new byte[]
+ {
+ (byte) startDay, (byte) startDayOfWeek, (byte) endDay,
+ (byte) endDayOfWeek
+ };
+
+ /* calculate the approximation for JDK 1.1 */
+ switch (startMode)
+ {
+ case DOM_MODE:
+ startDayOfWeek = Calendar.SUNDAY; // random day of week
+
+ // fall through
+ case DOW_GE_DOM_MODE:
+ case DOW_LE_DOM_MODE:
+ startDay = (startDay + 6) / 7;
+ }
+ switch (endMode)
+ {
+ case DOM_MODE:
+ endDayOfWeek = Calendar.SUNDAY;
+
+ // fall through
+ case DOW_GE_DOM_MODE:
+ case DOW_LE_DOM_MODE:
+ endDay = (endDay + 6) / 7;
+ }
+
+ // the required part:
+ output.defaultWriteObject();
+ // the optional part:
+ output.writeInt(byteArray.length);
+ output.write(byteArray, 0, byteArray.length);
+ }
+}
diff --git a/libjava/classpath/java/util/SortedMap.java b/libjava/classpath/java/util/SortedMap.java
new file mode 100644
index 000000000..2b98848f4
--- /dev/null
+++ b/libjava/classpath/java/util/SortedMap.java
@@ -0,0 +1,173 @@
+/* SortedMap.java -- A map that makes guarantees about the order of its keys
+ Copyright (C) 1998, 2001, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * A map which guarantees its key's iteration order. The entries in the
+ * map are related by the <i>natural ordering</i> of the keys if they
+ * are Comparable, or by the provided Comparator. Additional operations
+ * take advantage of the sorted nature of the map.
+ * <p>
+ *
+ * All keys entered in the map must be mutually comparable; in other words,
+ * <code>k1.compareTo(k2)</code> or <code>comparator.compare(k1, k2)</code>
+ * must not throw a ClassCastException. The ordering must be <i>consistent
+ * with equals</i> (see {@link Comparator} for this definition), if the
+ * map is to obey the general contract of the Map interface. If not,
+ * the results are well-defined, but probably not what you wanted.
+ * <p>
+ *
+ * It is recommended that all implementing classes provide four constructors:
+ * 1) one that takes no arguments and builds an empty map sorted by natural
+ * order of the keys; 2) one that takes a Comparator for the sorting order;
+ * 3) one that takes a Map and sorts according to the natural order of its
+ * keys; and 4) one that takes a SortedMap and sorts by the same comparator.
+ * Unfortunately, the Java language does not provide a way to enforce this.
+ *
+ * @author Original author unknown
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Map
+ * @see TreeMap
+ * @see SortedSet
+ * @see Comparable
+ * @see Comparator
+ * @see Collection
+ * @see ClassCastException
+ * @since 1.2
+ * @status updated to 1.4
+ */
+public interface SortedMap<K, V> extends Map<K, V>
+{
+ /**
+ * Returns the comparator used in sorting this map, or null if it is
+ * the keys' natural ordering.
+ *
+ * @return the sorting comparator
+ */
+ Comparator<? super K> comparator();
+
+ /**
+ * Returns the first (lowest sorted) key in the map.
+ *
+ * @return the first key
+ * @throws NoSuchElementException if this map is empty.
+ */
+ K firstKey();
+
+ /**
+ * Returns a view of the portion of the map strictly less than toKey. The
+ * view is backed by this map, so changes in one show up in the other.
+ * The submap supports all optional operations of the original.
+ * <p>
+ *
+ * The returned map throws an IllegalArgumentException any time a key is
+ * used which is out of the range of toKey. Note that the endpoint, toKey,
+ * is not included; if you want this value to be included, pass its successor
+ * object in to toKey. For example, for Integers, you could request
+ * <code>headMap(new Integer(limit.intValue() + 1))</code>.
+ *
+ * @param toKey the exclusive upper range of the submap
+ * @return the submap
+ * @throws ClassCastException if toKey is not comparable to the map contents
+ * @throws IllegalArgumentException if this is a subMap, and toKey is out
+ * of range
+ * @throws NullPointerException if toKey is null but the map does not allow
+ * null keys
+ */
+ SortedMap<K, V> headMap(K toKey);
+
+ /**
+ * Returns the last (highest sorted) key in the map.
+ *
+ * @return the last key
+ * @throws NoSuchElementException if this map is empty.
+ */
+ K lastKey();
+
+ /**
+ * Returns a view of the portion of the map greater than or equal to
+ * fromKey, and strictly less than toKey. The view is backed by this map,
+ * so changes in one show up in the other. The submap supports all
+ * optional operations of the original.
+ * <p>
+ *
+ * The returned map throws an IllegalArgumentException any time a key is
+ * used which is out of the range of fromKey and toKey. Note that the
+ * lower endpoint is included, but the upper is not; if you want to
+ * change the inclusion or exclusion of an endpoint, pass its successor
+ * object in instead. For example, for Integers, you could request
+ * <code>subMap(new Integer(lowlimit.intValue() + 1),
+ * new Integer(highlimit.intValue() + 1))</code> to reverse
+ * the inclusiveness of both endpoints.
+ *
+ * @param fromKey the inclusive lower range of the submap
+ * @param toKey the exclusive upper range of the submap
+ * @return the submap
+ * @throws ClassCastException if fromKey or toKey is not comparable to
+ * the map contents
+ * @throws IllegalArgumentException if this is a subMap, and fromKey or
+ * toKey is out of range
+ * @throws NullPointerException if fromKey or toKey is null but the map
+ * does not allow null keys
+ */
+ SortedMap<K, V> subMap(K fromKey, K toKey);
+
+ /**
+ * Returns a view of the portion of the map greater than or equal to
+ * fromKey. The view is backed by this map, so changes in one show up
+ * in the other. The submap supports all optional operations of the original.
+ * <p>
+ *
+ * The returned map throws an IllegalArgumentException any time a key is
+ * used which is out of the range of fromKey. Note that the endpoint, fromKey, is
+ * included; if you do not want this value to be included, pass its successor object in
+ * to fromKey. For example, for Integers, you could request
+ * <code>tailMap(new Integer(limit.intValue() + 1))</code>.
+ *
+ * @param fromKey the inclusive lower range of the submap
+ * @return the submap
+ * @throws ClassCastException if fromKey is not comparable to the map
+ * contents
+ * @throws IllegalArgumentException if this is a subMap, and fromKey is out
+ * of range
+ * @throws NullPointerException if fromKey is null but the map does not allow
+ * null keys
+ */
+ SortedMap<K, V> tailMap(K fromKey);
+}
diff --git a/libjava/classpath/java/util/SortedSet.java b/libjava/classpath/java/util/SortedSet.java
new file mode 100644
index 000000000..89f155a0d
--- /dev/null
+++ b/libjava/classpath/java/util/SortedSet.java
@@ -0,0 +1,176 @@
+/* SortedSet.java -- A set that makes guarantees about the order of its
+ elements
+ Copyright (C) 1998, 2001, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * A set which guarantees its iteration order. The elements in the set
+ * are related by the <i>natural ordering</i> if they are Comparable, or
+ * by the provided Comparator. Additional operations take advantage of
+ * the sorted nature of the set.
+ * <p>
+ *
+ * All elements entered in the set must be mutually comparable; in other words,
+ * <code>k1.compareTo(k2)</code> or <code>comparator.compare(k1, k2)</code>
+ * must not throw a ClassCastException. The ordering must be <i>consistent
+ * with equals</i> (see {@link Comparator} for this definition), if the
+ * set is to obey the general contract of the Set interface. If not,
+ * the results are well-defined, but probably not what you wanted.
+ * <p>
+ *
+ * It is recommended that all implementing classes provide four constructors:
+ * 1) one that takes no arguments and builds an empty set sorted by natural
+ * order of the elements; 2) one that takes a Comparator for the sorting order;
+ * 3) one that takes a Set and sorts according to the natural order of its
+ * elements; and 4) one that takes a SortedSet and sorts by the same
+ * comparator. Unfortunately, the Java language does not provide a way to
+ * enforce this.
+ *
+ * @author Original author unknown
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Set
+ * @see TreeSet
+ * @see SortedMap
+ * @see Collection
+ * @see Comparable
+ * @see Comparator
+ * @see ClassCastException
+ * @since 1.2
+ * @status updated to 1.4
+ */
+public interface SortedSet<E> extends Set<E>
+{
+ /**
+ * Returns the comparator used in sorting this set, or null if it is
+ * the elements' natural ordering.
+ *
+ * @return the sorting comparator
+ */
+ Comparator<? super E> comparator();
+
+ /**
+ * Returns the first (lowest sorted) element in the set.
+ *
+ * @return the first element
+ * @throws NoSuchElementException if the set is empty.
+ */
+ E first();
+
+ /**
+ * Returns a view of the portion of the set strictly less than toElement. The
+ * view is backed by this set, so changes in one show up in the other.
+ * The subset supports all optional operations of the original.
+ * <p>
+ *
+ * The returned set throws an IllegalArgumentException any time an element is
+ * used which is out of the range of toElement. Note that the endpoint, toElement,
+ * is not included; if you want this value included, pass its successor object in to
+ * toElement. For example, for Integers, you could request
+ * <code>headSet(new Integer(limit.intValue() + 1))</code>.
+ *
+ * @param toElement the exclusive upper range of the subset
+ * @return the subset
+ * @throws ClassCastException if toElement is not comparable to the set
+ * contents
+ * @throws IllegalArgumentException if this is a subSet, and toElement is out
+ * of range
+ * @throws NullPointerException if toElement is null but the set does not
+ * allow null elements
+ */
+ SortedSet<E> headSet(E toElement);
+
+ /**
+ * Returns the last (highest sorted) element in the set.
+ *
+ * @return the last element
+ * @throws NoSuchElementException if the set is empty.
+ */
+ E last();
+
+ /**
+ * Returns a view of the portion of the set greater than or equal to
+ * fromElement, and strictly less than toElement. The view is backed by
+ * this set, so changes in one show up in the other. The subset supports all
+ * optional operations of the original.
+ * <p>
+ *
+ * The returned set throws an IllegalArgumentException any time an element is
+ * used which is out of the range of fromElement and toElement. Note that the
+ * lower endpoint is included, but the upper is not; if you want to
+ * change the inclusion or exclusion of an endpoint, pass its successor
+ * object in instead. For example, for Integers, you can request
+ * <code>subSet(new Integer(lowlimit.intValue() + 1),
+ * new Integer(highlimit.intValue() + 1))</code> to reverse
+ * the inclusiveness of both endpoints.
+ *
+ * @param fromElement the inclusive lower range of the subset
+ * @param toElement the exclusive upper range of the subset
+ * @return the subset
+ * @throws ClassCastException if fromElement or toElement is not comparable
+ * to the set contents
+ * @throws IllegalArgumentException if this is a subSet, and fromElement or
+ * toElement is out of range
+ * @throws NullPointerException if fromElement or toElement is null but the
+ * set does not allow null elements
+ */
+ SortedSet<E> subSet(E fromElement, E toElement);
+
+ /**
+ * Returns a view of the portion of the set greater than or equal to
+ * fromElement. The view is backed by this set, so changes in one show up
+ * in the other. The subset supports all optional operations of the original.
+ * <p>
+ *
+ * The returned set throws an IllegalArgumentException any time an element is
+ * used which is out of the range of fromElement. Note that the endpoint,
+ * fromElement, is included; if you do not want this value to be included, pass its
+ * successor object in to fromElement. For example, for Integers, you could request
+ * <code>tailSet(new Integer(limit.intValue() + 1))</code>.
+ *
+ * @param fromElement the inclusive lower range of the subset
+ * @return the subset
+ * @throws ClassCastException if fromElement is not comparable to the set
+ * contents
+ * @throws IllegalArgumentException if this is a subSet, and fromElement is
+ * out of range
+ * @throws NullPointerException if fromElement is null but the set does not
+ * allow null elements
+ */
+ SortedSet<E> tailSet(E fromElement);
+}
diff --git a/libjava/classpath/java/util/Stack.java b/libjava/classpath/java/util/Stack.java
new file mode 100644
index 000000000..1d8792882
--- /dev/null
+++ b/libjava/classpath/java/util/Stack.java
@@ -0,0 +1,161 @@
+/* Stack.java - Class that provides a Last In First Out (LIFO)
+ datatype, known more commonly as a Stack
+ Copyright (C) 1998, 1999, 2001, 2004, 2005
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/* 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
+
+/**
+ * Stack provides a Last In First Out (LIFO) data type, commonly known
+ * as a Stack. Stack itself extends Vector and provides the additional
+ * methods for stack manipulation (push, pop, peek). You can also seek for
+ * the 1-based position of an element on the stack.
+ *
+ * @author Warren Levy (warrenl@cygnus.com)
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see List
+ * @see AbstractList
+ * @see LinkedList
+ * @since 1.0
+ * @status updated to 1.4
+ */
+public class Stack<T> extends Vector<T>
+{
+ // We could use Vector methods internally for the following methods,
+ // but have used Vector fields directly for efficiency (i.e. this
+ // often reduces out duplicate bounds checking).
+
+ /**
+ * Compatible with JDK 1.0+.
+ */
+ private static final long serialVersionUID = 1224463164541339165L;
+
+ /**
+ * This constructor creates a new Stack, initially empty
+ */
+ public Stack()
+ {
+ }
+
+ /**
+ * Pushes an Object onto the top of the stack. This method is effectively
+ * the same as addElement(item).
+ *
+ * @param item the Object to push onto the stack
+ * @return the Object pushed onto the stack
+ * @see Vector#addElement(Object)
+ */
+ public T push(T item)
+ {
+ // When growing the Stack, use the Vector routines in case more
+ // memory is needed.
+ // Note: spec indicates that this method *always* returns obj passed in!
+
+ addElement(item);
+ return item;
+ }
+
+ /**
+ * Pops an item from the stack and returns it. The item popped is
+ * removed from the Stack.
+ *
+ * @return the Object popped from the stack
+ * @throws EmptyStackException if the stack is empty
+ */
+ @SuppressWarnings("unchecked")
+ public synchronized T pop()
+ {
+ if (elementCount == 0)
+ throw new EmptyStackException();
+
+ modCount++;
+ T obj = (T) elementData[--elementCount];
+
+ // Set topmost element to null to assist the gc in cleanup.
+ elementData[elementCount] = null;
+ return obj;
+ }
+
+ /**
+ * Returns the top Object on the stack without removing it.
+ *
+ * @return the top Object on the stack
+ * @throws EmptyStackException if the stack is empty
+ */
+ @SuppressWarnings("unchecked")
+ public synchronized T peek()
+ {
+ if (elementCount == 0)
+ throw new EmptyStackException();
+
+ return (T) elementData[elementCount - 1];
+ }
+
+ /**
+ * Tests if the stack is empty.
+ *
+ * @return true if the stack contains no items, false otherwise
+ */
+ public synchronized boolean empty()
+ {
+ return elementCount == 0;
+ }
+
+ /**
+ * Returns the position of an Object on the stack, with the top
+ * most Object being at position 1, and each Object deeper in the
+ * stack at depth + 1.
+ *
+ * @param o The object to search for
+ * @return The 1 based depth of the Object, or -1 if the Object
+ * is not on the stack
+ */
+ public synchronized int search(Object o)
+ {
+ int i = elementCount;
+ while (--i >= 0)
+ if (equals(o, elementData[i]))
+ return elementCount - i;
+ return -1;
+ }
+}
diff --git a/libjava/classpath/java/util/StringTokenizer.java b/libjava/classpath/java/util/StringTokenizer.java
new file mode 100644
index 000000000..d16ec9bc4
--- /dev/null
+++ b/libjava/classpath/java/util/StringTokenizer.java
@@ -0,0 +1,269 @@
+/* StringTokenizer -- breaks a String into tokens
+ Copyright (C) 1998, 1999, 2001, 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * 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. This is much simpler than {@link java.io.StreamTokenizer}.
+ *
+ * <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 candidates for being returned.
+ *
+ * <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.
+ *
+ * @author Jochen Hoenicke
+ * @author Warren Levy (warrenl@cygnus.com)
+ * @see java.io.StreamTokenizer
+ * @status updated to 1.4
+ */
+public class StringTokenizer implements Enumeration<Object>
+{
+ // 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 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 final boolean retDelims;
+
+ /**
+ * Creates a new StringTokenizer for the string <code>str</code>,
+ * 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
+ * @throws NullPointerException if str is null
+ */
+ public StringTokenizer(String str)
+ {
+ this(str, " \t\n\r\f", false);
+ }
+
+ /**
+ * 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
+ * @throws NullPointerException if either argument is null
+ */
+ public StringTokenizer(String str, String delim)
+ {
+ this(str, delim, false);
+ }
+
+ /**
+ * Create a new StringTokenizer, that splits the given string on
+ * the given delimiter characters. If you set
+ * <code>returnDelims</code> to <code>true</code>, the delimiter
+ * 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
+ * @throws NullPointerException if str or delim is null
+ */
+ public StringTokenizer(String str, String delim, boolean returnDelims)
+ {
+ len = str.length();
+ this.str = str;
+ this.delim = delim;
+ this.retDelims = returnDelims;
+ this.pos = 0;
+ }
+
+ /**
+ * Tells if there are more tokens.
+ *
+ * @return true if the next call of nextToken() will succeed
+ */
+ public boolean hasMoreTokens()
+ {
+ if (! retDelims)
+ {
+ while (pos < len && delim.indexOf(str.charAt(pos)) >= 0)
+ pos++;
+ }
+ return pos < len;
+ }
+
+ /**
+ * Returns the nextToken, changing the delimiter set to the given
+ * <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
+ * @throws NoSuchElementException if there are no more tokens
+ * @throws NullPointerException if delim is null
+ */
+ public String nextToken(String delim) throws NoSuchElementException
+ {
+ this.delim = delim;
+ return nextToken();
+ }
+
+ /**
+ * Returns the nextToken of the string.
+ *
+ * @return the next token with respect to the current delimiter characters
+ * @throws NoSuchElementException if there are no more tokens
+ */
+ public String nextToken() throws NoSuchElementException
+ {
+ if (pos < len && delim.indexOf(str.charAt(pos)) >= 0)
+ {
+ if (retDelims)
+ return str.substring(pos, ++pos);
+ while (++pos < len && delim.indexOf(str.charAt(pos)) >= 0)
+ ;
+ }
+ if (pos < len)
+ {
+ int start = pos;
+ while (++pos < len && delim.indexOf(str.charAt(pos)) < 0)
+ ;
+
+ return str.substring(start, pos);
+ }
+ throw new NoSuchElementException();
+ }
+
+ /**
+ * This does the same as hasMoreTokens. This is the
+ * <code>Enumeration</code> interface method.
+ *
+ * @return true, if the next call of nextElement() will succeed
+ * @see #hasMoreTokens()
+ */
+ public boolean hasMoreElements()
+ {
+ return hasMoreTokens();
+ }
+
+ /**
+ * This does the same as nextTokens. This is the
+ * <code>Enumeration</code> interface method.
+ *
+ * @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
+ {
+ return nextToken();
+ }
+
+ /**
+ * 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()
+ */
+ public int countTokens()
+ {
+ int count = 0;
+ int delimiterCount = 0;
+ 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 < len)
+ {
+ 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
+ if (tokenFound)
+ count++;
+
+ // if counting delmiters add them into the token count
+ return retDelims ? count + delimiterCount : count;
+ }
+} // class StringTokenizer
diff --git a/libjava/classpath/java/util/TimeZone.java b/libjava/classpath/java/util/TimeZone.java
new file mode 100644
index 000000000..86a62918e
--- /dev/null
+++ b/libjava/classpath/java/util/TimeZone.java
@@ -0,0 +1,1781 @@
+/* java.util.TimeZone
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.classpath.SystemProperties;
+import gnu.java.lang.CPStringBuilder;
+import gnu.java.util.ZoneInfo;
+
+import java.io.File;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.text.DateFormatSymbols;
+
+/**
+ * This class represents a time zone offset and handles daylight savings.
+ *
+ * You can get the default time zone with <code>getDefault</code>.
+ * This represents the time zone where program is running.
+ *
+ * Another way to create a time zone is <code>getTimeZone</code>, where
+ * you can give an identifier as parameter. For instance, the identifier
+ * of the Central European Time zone is "CET".
+ *
+ * With the <code>getAvailableIDs</code> method, you can get all the
+ * supported time zone identifiers.
+ *
+ * @see Calendar
+ * @see SimpleTimeZone
+ * @author Jochen Hoenicke
+ */
+public abstract class TimeZone implements java.io.Serializable, Cloneable
+{
+
+ /**
+ * Constant used to indicate that a short timezone abbreviation should
+ * be returned, such as "EST"
+ */
+ public static final int SHORT = 0;
+
+ /**
+ * Constant used to indicate that a long timezone name should be
+ * returned, such as "Eastern Standard Time".
+ */
+ public static final int LONG = 1;
+
+ /**
+ * The time zone identifier, e.g. PST.
+ */
+ private String ID;
+
+ /**
+ * The default time zone, as returned by getDefault.
+ */
+ private static TimeZone defaultZone0;
+
+ /**
+ * Tries to get the default TimeZone for this system if not already
+ * set. It will call <code>getDefaultTimeZone(String)</code> with
+ * the result of <code>System.getProperty("user.timezone")</code>.
+ * If that fails it calls <code>VMTimeZone.getDefaultTimeZoneId()</code>.
+ * If that also fails GMT is returned.
+ */
+ private static synchronized TimeZone defaultZone()
+ {
+ /* Look up default timezone */
+ if (defaultZone0 == null)
+ {
+ defaultZone0 = (TimeZone) AccessController.doPrivileged
+ (new PrivilegedAction()
+ {
+ public Object run()
+ {
+ TimeZone zone = null;
+
+ // Prefer System property user.timezone.
+ String tzid = System.getProperty("user.timezone");
+ if (tzid != null && !tzid.equals(""))
+ zone = getDefaultTimeZone(tzid);
+
+ // Try platfom specific way.
+ if (zone == null)
+ zone = VMTimeZone.getDefaultTimeZoneId();
+
+ // Fall back on GMT.
+ if (zone == null)
+ zone = getTimeZone ("GMT");
+
+ return zone;
+ }
+ });
+ }
+
+ return defaultZone0;
+ }
+
+ private static final long serialVersionUID = 3581463369166924961L;
+
+ /**
+ * Flag whether zoneinfo data should be used,
+ * otherwise builtin timezone data will be provided.
+ */
+ private static String zoneinfo_dir;
+
+ /**
+ * Cached copy of getAvailableIDs().
+ */
+ private static String[] availableIDs = null;
+
+ /**
+ * JDK 1.1.x compatibility aliases.
+ */
+ private static HashMap aliases0;
+
+ /**
+ * HashMap for timezones by ID.
+ */
+ private static HashMap timezones0;
+ /* initialize this static field lazily to overhead if
+ * it is not needed:
+ */
+ // Package-private to avoid a trampoline.
+ static HashMap timezones()
+ {
+ if (timezones0 == null)
+ {
+ HashMap timezones = new HashMap();
+ timezones0 = timezones;
+
+ zoneinfo_dir = SystemProperties.getProperty("gnu.java.util.zoneinfo.dir");
+ if (zoneinfo_dir != null && !new File(zoneinfo_dir).isDirectory())
+ zoneinfo_dir = null;
+
+ if (zoneinfo_dir != null)
+ {
+ aliases0 = new HashMap();
+
+ // These deprecated aliases for JDK 1.1.x compatibility
+ // should take precedence over data files read from
+ // /usr/share/zoneinfo.
+ aliases0.put("ACT", "Australia/Darwin");
+ aliases0.put("AET", "Australia/Sydney");
+ aliases0.put("AGT", "America/Argentina/Buenos_Aires");
+ aliases0.put("ART", "Africa/Cairo");
+ aliases0.put("AST", "America/Juneau");
+ aliases0.put("BST", "Asia/Colombo");
+ aliases0.put("CAT", "Africa/Gaborone");
+ aliases0.put("CNT", "America/St_Johns");
+ aliases0.put("CST", "CST6CDT");
+ aliases0.put("CTT", "Asia/Brunei");
+ aliases0.put("EAT", "Indian/Comoro");
+ aliases0.put("ECT", "CET");
+ aliases0.put("EST", "EST5EDT");
+ aliases0.put("EST5", "EST5EDT");
+ aliases0.put("IET", "EST5EDT");
+ aliases0.put("IST", "Asia/Calcutta");
+ aliases0.put("JST", "Asia/Seoul");
+ aliases0.put("MIT", "Pacific/Niue");
+ aliases0.put("MST", "MST7MDT");
+ aliases0.put("MST7", "MST7MDT");
+ aliases0.put("NET", "Indian/Mauritius");
+ aliases0.put("NST", "Pacific/Auckland");
+ aliases0.put("PLT", "Indian/Kerguelen");
+ aliases0.put("PNT", "MST7MDT");
+ aliases0.put("PRT", "America/Anguilla");
+ aliases0.put("PST", "PST8PDT");
+ aliases0.put("SST", "Pacific/Ponape");
+ aliases0.put("VST", "Asia/Bangkok");
+ return timezones;
+ }
+
+ TimeZone tz;
+ // Automatically generated by scripts/timezones.pl
+ // XXX - Should we read this data from a file?
+ tz = new SimpleTimeZone(-11000 * 3600, "MIT");
+ timezones0.put("MIT", tz);
+ timezones0.put("Pacific/Apia", tz);
+ timezones0.put("Pacific/Midway", tz);
+ timezones0.put("Pacific/Niue", tz);
+ timezones0.put("Pacific/Pago_Pago", tz);
+ tz = new SimpleTimeZone
+ (-10000 * 3600, "America/Adak",
+ Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600);
+ timezones0.put("America/Adak", tz);
+ tz = new SimpleTimeZone(-10000 * 3600, "HST");
+ timezones0.put("HST", tz);
+ timezones0.put("Pacific/Fakaofo", tz);
+ timezones0.put("Pacific/Honolulu", tz);
+ timezones0.put("Pacific/Johnston", tz);
+ timezones0.put("Pacific/Rarotonga", tz);
+ timezones0.put("Pacific/Tahiti", tz);
+ tz = new SimpleTimeZone(-9500 * 3600, "Pacific/Marquesas");
+ timezones0.put("Pacific/Marquesas", tz);
+ tz = new SimpleTimeZone
+ (-9000 * 3600, "AST",
+ Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600);
+ timezones0.put("AST", tz);
+ timezones0.put("America/Anchorage", tz);
+ timezones0.put("America/Juneau", tz);
+ timezones0.put("America/Nome", tz);
+ timezones0.put("America/Yakutat", tz);
+ tz = new SimpleTimeZone(-9000 * 3600, "Pacific/Gambier");
+ timezones0.put("Pacific/Gambier", tz);
+ tz = new SimpleTimeZone
+ (-8000 * 3600, "America/Tijuana",
+ Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones0.put("America/Tijuana", tz);
+ tz = new SimpleTimeZone
+ (-8000 * 3600, "PST",
+ Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600);
+ timezones0.put("PST", tz);
+ timezones0.put("PST8PDT", tz);
+ timezones0.put("America/Dawson", tz);
+ timezones0.put("America/Los_Angeles", tz);
+ timezones0.put("America/Vancouver", tz);
+ timezones0.put("America/Whitehorse", tz);
+ timezones0.put("US/Pacific-New", tz);
+ tz = new SimpleTimeZone(-8000 * 3600, "Pacific/Pitcairn");
+ timezones0.put("Pacific/Pitcairn", tz);
+ tz = new SimpleTimeZone
+ (-7000 * 3600, "America/Chihuahua",
+ Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones0.put("America/Chihuahua", tz);
+ timezones0.put("America/Mazatlan", tz);
+ tz = new SimpleTimeZone(-7000 * 3600, "MST7");
+ timezones0.put("MST7", tz);
+ timezones0.put("PNT", tz);
+ timezones0.put("America/Dawson_Creek", tz);
+ timezones0.put("America/Hermosillo", tz);
+ timezones0.put("America/Phoenix", tz);
+ tz = new SimpleTimeZone
+ (-7000 * 3600, "MST",
+ Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600);
+ timezones0.put("MST", tz);
+ timezones0.put("MST7MDT", tz);
+ timezones0.put("America/Boise", tz);
+ timezones0.put("America/Cambridge_Bay", tz);
+ timezones0.put("America/Denver", tz);
+ timezones0.put("America/Edmonton", tz);
+ timezones0.put("America/Inuvik", tz);
+ timezones0.put("America/Shiprock", tz);
+ timezones0.put("America/Yellowknife", tz);
+ tz = new SimpleTimeZone
+ (-6000 * 3600, "America/Cancun",
+ Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones0.put("America/Cancun", tz);
+ timezones0.put("America/Merida", tz);
+ timezones0.put("America/Mexico_City", tz);
+ timezones0.put("America/Monterrey", tz);
+ tz = new SimpleTimeZone(-6000 * 3600, "America/Belize");
+ timezones0.put("America/Belize", tz);
+ timezones0.put("America/Costa_Rica", tz);
+ timezones0.put("America/El_Salvador", tz);
+ timezones0.put("America/Guatemala", tz);
+ timezones0.put("America/Managua", tz);
+ timezones0.put("America/Regina", tz);
+ timezones0.put("America/Swift_Current", tz);
+ timezones0.put("America/Tegucigalpa", tz);
+ timezones0.put("Pacific/Galapagos", tz);
+ tz = new SimpleTimeZone
+ (-6000 * 3600, "CST",
+ Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600);
+ timezones0.put("CST", tz);
+ timezones0.put("CST6CDT", tz);
+ timezones0.put("America/Chicago", tz);
+ timezones0.put("America/Indiana/Knox", tz);
+ timezones0.put("America/Indiana/Petersburg", tz);
+ timezones0.put("America/Indiana/Vincennes", tz);
+ timezones0.put("America/Menominee", tz);
+ timezones0.put("America/North_Dakota/Center", tz);
+ timezones0.put("America/North_Dakota/New_Salem", tz);
+ timezones0.put("America/Rainy_River", tz);
+ timezones0.put("America/Rankin_Inlet", tz);
+ timezones0.put("America/Winnipeg", tz);
+ tz = new SimpleTimeZone
+ (-6000 * 3600, "Pacific/Easter",
+ Calendar.OCTOBER, 2, Calendar.SATURDAY, 22000 * 3600,
+ Calendar.MARCH, 2, Calendar.SATURDAY, 22000 * 3600);
+ timezones0.put("Pacific/Easter", tz);
+ tz = new SimpleTimeZone(-5000 * 3600, "EST5");
+ timezones0.put("EST5", tz);
+ timezones0.put("IET", tz);
+ timezones0.put("America/Atikokan", tz);
+ timezones0.put("America/Bogota", tz);
+ timezones0.put("America/Cayman", tz);
+ timezones0.put("America/Eirunepe", tz);
+ timezones0.put("America/Guayaquil", tz);
+ timezones0.put("America/Jamaica", tz);
+ timezones0.put("America/Lima", tz);
+ timezones0.put("America/Panama", tz);
+ timezones0.put("America/Rio_Branco", tz);
+ tz = new SimpleTimeZone
+ (-5000 * 3600, "America/Havana",
+ Calendar.APRIL, 1, Calendar.SUNDAY, 0 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 1000 * 3600);
+ timezones0.put("America/Havana", tz);
+ tz = new SimpleTimeZone
+ (-5000 * 3600, "America/Grand_Turk",
+ Calendar.APRIL, 1, Calendar.SUNDAY, 0 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600);
+ timezones0.put("America/Grand_Turk", tz);
+ timezones0.put("America/Port-au-Prince", tz);
+ tz = new SimpleTimeZone
+ (-5000 * 3600, "EST",
+ Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600);
+ timezones0.put("EST", tz);
+ timezones0.put("EST5EDT", tz);
+ timezones0.put("America/Detroit", tz);
+ timezones0.put("America/Indiana/Indianapolis", tz);
+ timezones0.put("America/Indiana/Marengo", tz);
+ timezones0.put("America/Indiana/Vevay", tz);
+ timezones0.put("America/Iqaluit", tz);
+ timezones0.put("America/Kentucky/Louisville", tz);
+ timezones0.put("America/Kentucky/Monticello", tz);
+ timezones0.put("America/Montreal", tz);
+ timezones0.put("America/Nassau", tz);
+ timezones0.put("America/New_York", tz);
+ timezones0.put("America/Nipigon", tz);
+ timezones0.put("America/Pangnirtung", tz);
+ timezones0.put("America/Thunder_Bay", tz);
+ timezones0.put("America/Toronto", tz);
+ tz = new SimpleTimeZone
+ (-4000 * 3600, "America/Asuncion",
+ Calendar.OCTOBER, 3, Calendar.SUNDAY, 0 * 3600,
+ Calendar.MARCH, 2, Calendar.SUNDAY, 0 * 3600);
+ timezones0.put("America/Asuncion", tz);
+ tz = new SimpleTimeZone(-4000 * 3600, "PRT");
+ timezones0.put("PRT", tz);
+ timezones0.put("America/Anguilla", tz);
+ timezones0.put("America/Antigua", tz);
+ timezones0.put("America/Aruba", tz);
+ timezones0.put("America/Barbados", tz);
+ timezones0.put("America/Blanc-Sablon", tz);
+ timezones0.put("America/Boa_Vista", tz);
+ timezones0.put("America/Caracas", tz);
+ timezones0.put("America/Curacao", tz);
+ timezones0.put("America/Dominica", tz);
+ timezones0.put("America/Grenada", tz);
+ timezones0.put("America/Guadeloupe", tz);
+ timezones0.put("America/Guyana", tz);
+ timezones0.put("America/La_Paz", tz);
+ timezones0.put("America/Manaus", tz);
+ timezones0.put("America/Martinique", tz);
+ timezones0.put("America/Montserrat", tz);
+ timezones0.put("America/Port_of_Spain", tz);
+ timezones0.put("America/Porto_Velho", tz);
+ timezones0.put("America/Puerto_Rico", tz);
+ timezones0.put("America/Santo_Domingo", tz);
+ timezones0.put("America/St_Kitts", tz);
+ timezones0.put("America/St_Lucia", tz);
+ timezones0.put("America/St_Thomas", tz);
+ timezones0.put("America/St_Vincent", tz);
+ timezones0.put("America/Tortola", tz);
+ tz = new SimpleTimeZone
+ (-4000 * 3600, "America/Campo_Grande",
+ Calendar.NOVEMBER, 1, Calendar.SUNDAY, 0 * 3600,
+ Calendar.FEBRUARY, -1, Calendar.SUNDAY, 0 * 3600);
+ timezones0.put("America/Campo_Grande", tz);
+ timezones0.put("America/Cuiaba", tz);
+ tz = new SimpleTimeZone
+ (-4000 * 3600, "America/Goose_Bay",
+ Calendar.MARCH, 2, Calendar.SUNDAY, 60000,
+ Calendar.NOVEMBER, 1, Calendar.SUNDAY, 60000);
+ timezones0.put("America/Goose_Bay", tz);
+ tz = new SimpleTimeZone
+ (-4000 * 3600, "America/Glace_Bay",
+ Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600);
+ timezones0.put("America/Glace_Bay", tz);
+ timezones0.put("America/Halifax", tz);
+ timezones0.put("America/Moncton", tz);
+ timezones0.put("America/Thule", tz);
+ timezones0.put("Atlantic/Bermuda", tz);
+ tz = new SimpleTimeZone
+ (-4000 * 3600, "America/Santiago",
+ Calendar.OCTOBER, 9, -Calendar.SUNDAY, 0 * 3600,
+ Calendar.MARCH, 9, -Calendar.SUNDAY, 0 * 3600);
+ timezones0.put("America/Santiago", tz);
+ timezones0.put("Antarctica/Palmer", tz);
+ tz = new SimpleTimeZone
+ (-4000 * 3600, "Atlantic/Stanley",
+ Calendar.SEPTEMBER, 1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.APRIL, 3, Calendar.SUNDAY, 2000 * 3600);
+ timezones0.put("Atlantic/Stanley", tz);
+ tz = new SimpleTimeZone
+ (-3500 * 3600, "CNT",
+ Calendar.MARCH, 2, Calendar.SUNDAY, 60000,
+ Calendar.NOVEMBER, 1, Calendar.SUNDAY, 60000);
+ timezones0.put("CNT", tz);
+ timezones0.put("America/St_Johns", tz);
+ tz = new SimpleTimeZone
+ (-3000 * 3600, "America/Godthab",
+ Calendar.MARCH, 30, -Calendar.SATURDAY, 22000 * 3600,
+ Calendar.OCTOBER, 30, -Calendar.SATURDAY, 23000 * 3600);
+ timezones0.put("America/Godthab", tz);
+ tz = new SimpleTimeZone
+ (-3000 * 3600, "America/Miquelon",
+ Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600);
+ timezones0.put("America/Miquelon", tz);
+ tz = new SimpleTimeZone
+ (-3000 * 3600, "America/Montevideo",
+ Calendar.OCTOBER, 1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600);
+ timezones0.put("America/Montevideo", tz);
+ tz = new SimpleTimeZone
+ (-3000 * 3600, "America/Sao_Paulo",
+ Calendar.NOVEMBER, 1, Calendar.SUNDAY, 0 * 3600,
+ Calendar.FEBRUARY, -1, Calendar.SUNDAY, 0 * 3600);
+ timezones0.put("America/Sao_Paulo", tz);
+ tz = new SimpleTimeZone(-3000 * 3600, "AGT");
+ timezones0.put("AGT", tz);
+ timezones0.put("America/Araguaina", tz);
+ timezones0.put("America/Argentina/Buenos_Aires", tz);
+ timezones0.put("America/Argentina/Catamarca", tz);
+ timezones0.put("America/Argentina/Cordoba", tz);
+ timezones0.put("America/Argentina/Jujuy", tz);
+ timezones0.put("America/Argentina/La_Rioja", tz);
+ timezones0.put("America/Argentina/Mendoza", tz);
+ timezones0.put("America/Argentina/Rio_Gallegos", tz);
+ timezones0.put("America/Argentina/San_Juan", tz);
+ timezones0.put("America/Argentina/Tucuman", tz);
+ timezones0.put("America/Argentina/Ushuaia", tz);
+ timezones0.put("America/Bahia", tz);
+ timezones0.put("America/Belem", tz);
+ timezones0.put("America/Cayenne", tz);
+ timezones0.put("America/Fortaleza", tz);
+ timezones0.put("America/Maceio", tz);
+ timezones0.put("America/Paramaribo", tz);
+ timezones0.put("America/Recife", tz);
+ timezones0.put("Antarctica/Rothera", tz);
+ tz = new SimpleTimeZone(-2000 * 3600, "America/Noronha");
+ timezones0.put("America/Noronha", tz);
+ timezones0.put("Atlantic/South_Georgia", tz);
+ tz = new SimpleTimeZone
+ (-1000 * 3600, "America/Scoresbysund",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 0 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 1000 * 3600);
+ timezones0.put("America/Scoresbysund", tz);
+ timezones0.put("Atlantic/Azores", tz);
+ tz = new SimpleTimeZone(-1000 * 3600, "Atlantic/Cape_Verde");
+ timezones0.put("Atlantic/Cape_Verde", tz);
+ tz = new SimpleTimeZone(0 * 3600, "GMT");
+ timezones0.put("GMT", tz);
+ timezones0.put("UTC", tz);
+ timezones0.put("Africa/Abidjan", tz);
+ timezones0.put("Africa/Accra", tz);
+ timezones0.put("Africa/Bamako", tz);
+ timezones0.put("Africa/Banjul", tz);
+ timezones0.put("Africa/Bissau", tz);
+ timezones0.put("Africa/Casablanca", tz);
+ timezones0.put("Africa/Conakry", tz);
+ timezones0.put("Africa/Dakar", tz);
+ timezones0.put("Africa/El_Aaiun", tz);
+ timezones0.put("Africa/Freetown", tz);
+ timezones0.put("Africa/Lome", tz);
+ timezones0.put("Africa/Monrovia", tz);
+ timezones0.put("Africa/Nouakchott", tz);
+ timezones0.put("Africa/Ouagadougou", tz);
+ timezones0.put("Africa/Sao_Tome", tz);
+ timezones0.put("America/Danmarkshavn", tz);
+ timezones0.put("Atlantic/Reykjavik", tz);
+ timezones0.put("Atlantic/St_Helena", tz);
+ tz = new SimpleTimeZone
+ (0 * 3600, "WET",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 1000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600);
+ timezones0.put("WET", tz);
+ timezones0.put("Atlantic/Canary", tz);
+ timezones0.put("Atlantic/Faroe", tz);
+ timezones0.put("Atlantic/Madeira", tz);
+ timezones0.put("Europe/Dublin", tz);
+ timezones0.put("Europe/Guernsey", tz);
+ timezones0.put("Europe/Isle_of_Man", tz);
+ timezones0.put("Europe/Jersey", tz);
+ timezones0.put("Europe/Lisbon", tz);
+ timezones0.put("Europe/London", tz);
+ tz = new SimpleTimeZone(1000 * 3600, "Africa/Algiers");
+ timezones0.put("Africa/Algiers", tz);
+ timezones0.put("Africa/Bangui", tz);
+ timezones0.put("Africa/Brazzaville", tz);
+ timezones0.put("Africa/Douala", tz);
+ timezones0.put("Africa/Kinshasa", tz);
+ timezones0.put("Africa/Lagos", tz);
+ timezones0.put("Africa/Libreville", tz);
+ timezones0.put("Africa/Luanda", tz);
+ timezones0.put("Africa/Malabo", tz);
+ timezones0.put("Africa/Ndjamena", tz);
+ timezones0.put("Africa/Niamey", tz);
+ timezones0.put("Africa/Porto-Novo", tz);
+ tz = new SimpleTimeZone
+ (1000 * 3600, "Africa/Windhoek",
+ Calendar.SEPTEMBER, 1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600);
+ timezones0.put("Africa/Windhoek", tz);
+ tz = new SimpleTimeZone
+ (1000 * 3600, "CET",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+ timezones0.put("CET", tz);
+ timezones0.put("ECT", tz);
+ timezones0.put("MET", tz);
+ timezones0.put("Africa/Ceuta", tz);
+ timezones0.put("Africa/Tunis", tz);
+ timezones0.put("Arctic/Longyearbyen", tz);
+ timezones0.put("Atlantic/Jan_Mayen", tz);
+ timezones0.put("Europe/Amsterdam", tz);
+ timezones0.put("Europe/Andorra", tz);
+ timezones0.put("Europe/Belgrade", tz);
+ timezones0.put("Europe/Berlin", tz);
+ timezones0.put("Europe/Bratislava", tz);
+ timezones0.put("Europe/Brussels", tz);
+ timezones0.put("Europe/Budapest", tz);
+ timezones0.put("Europe/Copenhagen", tz);
+ timezones0.put("Europe/Gibraltar", tz);
+ timezones0.put("Europe/Ljubljana", tz);
+ timezones0.put("Europe/Luxembourg", tz);
+ timezones0.put("Europe/Madrid", tz);
+ timezones0.put("Europe/Malta", tz);
+ timezones0.put("Europe/Monaco", tz);
+ timezones0.put("Europe/Oslo", tz);
+ timezones0.put("Europe/Paris", tz);
+ timezones0.put("Europe/Podgorica", tz);
+ timezones0.put("Europe/Prague", tz);
+ timezones0.put("Europe/Rome", tz);
+ timezones0.put("Europe/San_Marino", tz);
+ timezones0.put("Europe/Sarajevo", tz);
+ timezones0.put("Europe/Skopje", tz);
+ timezones0.put("Europe/Stockholm", tz);
+ timezones0.put("Europe/Tirane", tz);
+ timezones0.put("Europe/Vaduz", tz);
+ timezones0.put("Europe/Vatican", tz);
+ timezones0.put("Europe/Vienna", tz);
+ timezones0.put("Europe/Warsaw", tz);
+ timezones0.put("Europe/Zagreb", tz);
+ timezones0.put("Europe/Zurich", tz);
+ tz = new SimpleTimeZone
+ (2000 * 3600, "ART",
+ Calendar.APRIL, -1, Calendar.FRIDAY, 0 * 3600,
+ Calendar.SEPTEMBER, -1, Calendar.THURSDAY, 24000 * 3600);
+ timezones0.put("ART", tz);
+ timezones0.put("Africa/Cairo", tz);
+ tz = new SimpleTimeZone(2000 * 3600, "CAT");
+ timezones0.put("CAT", tz);
+ timezones0.put("Africa/Blantyre", tz);
+ timezones0.put("Africa/Bujumbura", tz);
+ timezones0.put("Africa/Gaborone", tz);
+ timezones0.put("Africa/Harare", tz);
+ timezones0.put("Africa/Johannesburg", tz);
+ timezones0.put("Africa/Kigali", tz);
+ timezones0.put("Africa/Lubumbashi", tz);
+ timezones0.put("Africa/Lusaka", tz);
+ timezones0.put("Africa/Maputo", tz);
+ timezones0.put("Africa/Maseru", tz);
+ timezones0.put("Africa/Mbabane", tz);
+ timezones0.put("Africa/Tripoli", tz);
+ timezones0.put("Asia/Jerusalem", tz);
+ tz = new SimpleTimeZone
+ (2000 * 3600, "Asia/Amman",
+ Calendar.MARCH, -1, Calendar.THURSDAY, 0 * 3600,
+ Calendar.OCTOBER, -1, Calendar.FRIDAY, 1000 * 3600);
+ timezones0.put("Asia/Amman", tz);
+ tz = new SimpleTimeZone
+ (2000 * 3600, "Asia/Beirut",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 0 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600);
+ timezones0.put("Asia/Beirut", tz);
+ tz = new SimpleTimeZone
+ (2000 * 3600, "Asia/Damascus",
+ Calendar.APRIL, 1, 0, 0 * 3600,
+ Calendar.OCTOBER, 1, 0, 0 * 3600);
+ timezones0.put("Asia/Damascus", tz);
+ tz = new SimpleTimeZone
+ (2000 * 3600, "Asia/Gaza",
+ Calendar.APRIL, 1, 0, 0 * 3600,
+ Calendar.OCTOBER, 3, Calendar.FRIDAY, 0 * 3600);
+ timezones0.put("Asia/Gaza", tz);
+ tz = new SimpleTimeZone
+ (2000 * 3600, "EET",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 3000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 4000 * 3600);
+ timezones0.put("EET", tz);
+ timezones0.put("Asia/Istanbul", tz);
+ timezones0.put("Asia/Nicosia", tz);
+ timezones0.put("Europe/Athens", tz);
+ timezones0.put("Europe/Bucharest", tz);
+ timezones0.put("Europe/Chisinau", tz);
+ timezones0.put("Europe/Helsinki", tz);
+ timezones0.put("Europe/Istanbul", tz);
+ timezones0.put("Europe/Kiev", tz);
+ timezones0.put("Europe/Mariehamn", tz);
+ timezones0.put("Europe/Nicosia", tz);
+ timezones0.put("Europe/Riga", tz);
+ timezones0.put("Europe/Simferopol", tz);
+ timezones0.put("Europe/Sofia", tz);
+ timezones0.put("Europe/Tallinn", tz);
+ timezones0.put("Europe/Uzhgorod", tz);
+ timezones0.put("Europe/Vilnius", tz);
+ timezones0.put("Europe/Zaporozhye", tz);
+ tz = new SimpleTimeZone
+ (2000 * 3600, "Europe/Kaliningrad",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+ timezones0.put("Europe/Kaliningrad", tz);
+ timezones0.put("Europe/Minsk", tz);
+ tz = new SimpleTimeZone
+ (3000 * 3600, "Asia/Baghdad",
+ Calendar.APRIL, 1, 0, 3000 * 3600,
+ Calendar.OCTOBER, 1, 0, 4000 * 3600);
+ timezones0.put("Asia/Baghdad", tz);
+ tz = new SimpleTimeZone
+ (3000 * 3600, "Europe/Moscow",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+ timezones0.put("Europe/Moscow", tz);
+ timezones0.put("Europe/Volgograd", tz);
+ tz = new SimpleTimeZone(3000 * 3600, "EAT");
+ timezones0.put("EAT", tz);
+ timezones0.put("Africa/Addis_Ababa", tz);
+ timezones0.put("Africa/Asmara", tz);
+ timezones0.put("Africa/Dar_es_Salaam", tz);
+ timezones0.put("Africa/Djibouti", tz);
+ timezones0.put("Africa/Kampala", tz);
+ timezones0.put("Africa/Khartoum", tz);
+ timezones0.put("Africa/Mogadishu", tz);
+ timezones0.put("Africa/Nairobi", tz);
+ timezones0.put("Antarctica/Syowa", tz);
+ timezones0.put("Asia/Aden", tz);
+ timezones0.put("Asia/Bahrain", tz);
+ timezones0.put("Asia/Kuwait", tz);
+ timezones0.put("Asia/Qatar", tz);
+ timezones0.put("Asia/Riyadh", tz);
+ timezones0.put("Indian/Antananarivo", tz);
+ timezones0.put("Indian/Comoro", tz);
+ timezones0.put("Indian/Mayotte", tz);
+ tz = new SimpleTimeZone(3500 * 3600, "Asia/Tehran");
+ timezones0.put("Asia/Tehran", tz);
+ tz = new SimpleTimeZone
+ (4000 * 3600, "Asia/Baku",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 4000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 5000 * 3600);
+ timezones0.put("Asia/Baku", tz);
+ tz = new SimpleTimeZone
+ (4000 * 3600, "Asia/Yerevan",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+ timezones0.put("Asia/Yerevan", tz);
+ timezones0.put("Europe/Samara", tz);
+ tz = new SimpleTimeZone(4000 * 3600, "NET");
+ timezones0.put("NET", tz);
+ timezones0.put("Asia/Dubai", tz);
+ timezones0.put("Asia/Muscat", tz);
+ timezones0.put("Asia/Tbilisi", tz);
+ timezones0.put("Indian/Mahe", tz);
+ timezones0.put("Indian/Mauritius", tz);
+ timezones0.put("Indian/Reunion", tz);
+ tz = new SimpleTimeZone(4500 * 3600, "Asia/Kabul");
+ timezones0.put("Asia/Kabul", tz);
+ tz = new SimpleTimeZone
+ (5000 * 3600, "Asia/Yekaterinburg",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+ timezones0.put("Asia/Yekaterinburg", tz);
+ tz = new SimpleTimeZone(5000 * 3600, "PLT");
+ timezones0.put("PLT", tz);
+ timezones0.put("Asia/Aqtau", tz);
+ timezones0.put("Asia/Aqtobe", tz);
+ timezones0.put("Asia/Ashgabat", tz);
+ timezones0.put("Asia/Dushanbe", tz);
+ timezones0.put("Asia/Karachi", tz);
+ timezones0.put("Asia/Oral", tz);
+ timezones0.put("Asia/Samarkand", tz);
+ timezones0.put("Asia/Tashkent", tz);
+ timezones0.put("Indian/Kerguelen", tz);
+ timezones0.put("Indian/Maldives", tz);
+ tz = new SimpleTimeZone(5500 * 3600, "BST");
+ timezones0.put("BST", tz);
+ timezones0.put("IST", tz);
+ timezones0.put("Asia/Calcutta", tz);
+ timezones0.put("Asia/Colombo", tz);
+ tz = new SimpleTimeZone(5750 * 3600, "Asia/Katmandu");
+ timezones0.put("Asia/Katmandu", tz);
+ tz = new SimpleTimeZone(6000 * 3600, "Antarctica/Mawson");
+ timezones0.put("Antarctica/Mawson", tz);
+ timezones0.put("Antarctica/Vostok", tz);
+ timezones0.put("Asia/Almaty", tz);
+ timezones0.put("Asia/Bishkek", tz);
+ timezones0.put("Asia/Dhaka", tz);
+ timezones0.put("Asia/Qyzylorda", tz);
+ timezones0.put("Asia/Thimphu", tz);
+ timezones0.put("Indian/Chagos", tz);
+ tz = new SimpleTimeZone
+ (6000 * 3600, "Asia/Novosibirsk",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+ timezones0.put("Asia/Novosibirsk", tz);
+ timezones0.put("Asia/Omsk", tz);
+ tz = new SimpleTimeZone(6500 * 3600, "Asia/Rangoon");
+ timezones0.put("Asia/Rangoon", tz);
+ timezones0.put("Indian/Cocos", tz);
+ tz = new SimpleTimeZone(7000 * 3600, "VST");
+ timezones0.put("VST", tz);
+ timezones0.put("Antarctica/Davis", tz);
+ timezones0.put("Asia/Bangkok", tz);
+ timezones0.put("Asia/Jakarta", tz);
+ timezones0.put("Asia/Phnom_Penh", tz);
+ timezones0.put("Asia/Pontianak", tz);
+ timezones0.put("Asia/Saigon", tz);
+ timezones0.put("Asia/Vientiane", tz);
+ timezones0.put("Indian/Christmas", tz);
+ tz = new SimpleTimeZone
+ (7000 * 3600, "Asia/Hovd",
+ Calendar.MARCH, -1, Calendar.SATURDAY, 2000 * 3600,
+ Calendar.SEPTEMBER, -1, Calendar.SATURDAY, 2000 * 3600);
+ timezones0.put("Asia/Hovd", tz);
+ tz = new SimpleTimeZone
+ (7000 * 3600, "Asia/Krasnoyarsk",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+ timezones0.put("Asia/Krasnoyarsk", tz);
+ tz = new SimpleTimeZone(8000 * 3600, "CTT");
+ timezones0.put("CTT", tz);
+ timezones0.put("Antarctica/Casey", tz);
+ timezones0.put("Asia/Brunei", tz);
+ timezones0.put("Asia/Chongqing", tz);
+ timezones0.put("Asia/Harbin", tz);
+ timezones0.put("Asia/Hong_Kong", tz);
+ timezones0.put("Asia/Kashgar", tz);
+ timezones0.put("Asia/Kuala_Lumpur", tz);
+ timezones0.put("Asia/Kuching", tz);
+ timezones0.put("Asia/Macau", tz);
+ timezones0.put("Asia/Makassar", tz);
+ timezones0.put("Asia/Manila", tz);
+ timezones0.put("Asia/Shanghai", tz);
+ timezones0.put("Asia/Singapore", tz);
+ timezones0.put("Asia/Taipei", tz);
+ timezones0.put("Asia/Urumqi", tz);
+ timezones0.put("Australia/Perth", tz);
+ tz = new SimpleTimeZone
+ (8000 * 3600, "Asia/Irkutsk",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+ timezones0.put("Asia/Irkutsk", tz);
+ tz = new SimpleTimeZone
+ (8000 * 3600, "Asia/Ulaanbaatar",
+ Calendar.MARCH, -1, Calendar.SATURDAY, 2000 * 3600,
+ Calendar.SEPTEMBER, -1, Calendar.SATURDAY, 2000 * 3600);
+ timezones0.put("Asia/Ulaanbaatar", tz);
+ tz = new SimpleTimeZone(8750 * 3600, "Australia/Eucla");
+ timezones0.put("Australia/Eucla", tz);
+ tz = new SimpleTimeZone
+ (9000 * 3600, "Asia/Choibalsan",
+ Calendar.MARCH, -1, Calendar.SATURDAY, 2000 * 3600,
+ Calendar.SEPTEMBER, -1, Calendar.SATURDAY, 2000 * 3600);
+ timezones0.put("Asia/Choibalsan", tz);
+ tz = new SimpleTimeZone(9000 * 3600, "JST");
+ timezones0.put("JST", tz);
+ timezones0.put("Asia/Dili", tz);
+ timezones0.put("Asia/Jayapura", tz);
+ timezones0.put("Asia/Pyongyang", tz);
+ timezones0.put("Asia/Seoul", tz);
+ timezones0.put("Asia/Tokyo", tz);
+ timezones0.put("Pacific/Palau", tz);
+ tz = new SimpleTimeZone
+ (9000 * 3600, "Asia/Yakutsk",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+ timezones0.put("Asia/Yakutsk", tz);
+ tz = new SimpleTimeZone
+ (9500 * 3600, "Australia/Adelaide",
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.MARCH, -1, Calendar.SUNDAY, 3000 * 3600);
+ timezones0.put("Australia/Adelaide", tz);
+ timezones0.put("Australia/Broken_Hill", tz);
+ tz = new SimpleTimeZone(9500 * 3600, "ACT");
+ timezones0.put("ACT", tz);
+ timezones0.put("Australia/Darwin", tz);
+ tz = new SimpleTimeZone(10000 * 3600, "Antarctica/DumontDUrville");
+ timezones0.put("Antarctica/DumontDUrville", tz);
+ timezones0.put("Australia/Brisbane", tz);
+ timezones0.put("Australia/Lindeman", tz);
+ timezones0.put("Pacific/Guam", tz);
+ timezones0.put("Pacific/Port_Moresby", tz);
+ timezones0.put("Pacific/Saipan", tz);
+ timezones0.put("Pacific/Truk", tz);
+ tz = new SimpleTimeZone
+ (10000 * 3600, "Asia/Sakhalin",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+ timezones0.put("Asia/Sakhalin", tz);
+ timezones0.put("Asia/Vladivostok", tz);
+ tz = new SimpleTimeZone
+ (10000 * 3600, "Australia/Currie",
+ Calendar.OCTOBER, 1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.MARCH, -1, Calendar.SUNDAY, 3000 * 3600);
+ timezones0.put("Australia/Currie", tz);
+ timezones0.put("Australia/Hobart", tz);
+ tz = new SimpleTimeZone
+ (10000 * 3600, "AET",
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.MARCH, -1, Calendar.SUNDAY, 3000 * 3600);
+ timezones0.put("AET", tz);
+ timezones0.put("Australia/Melbourne", tz);
+ timezones0.put("Australia/Sydney", tz);
+ tz = new SimpleTimeZone
+ (10500 * 3600, "Australia/Lord_Howe",
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, 500 * 3600);
+ timezones0.put("Australia/Lord_Howe", tz);
+ tz = new SimpleTimeZone
+ (11000 * 3600, "Asia/Magadan",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+ timezones0.put("Asia/Magadan", tz);
+ tz = new SimpleTimeZone(11000 * 3600, "SST");
+ timezones0.put("SST", tz);
+ timezones0.put("Pacific/Efate", tz);
+ timezones0.put("Pacific/Guadalcanal", tz);
+ timezones0.put("Pacific/Kosrae", tz);
+ timezones0.put("Pacific/Noumea", tz);
+ timezones0.put("Pacific/Ponape", tz);
+ tz = new SimpleTimeZone(11500 * 3600, "Pacific/Norfolk");
+ timezones0.put("Pacific/Norfolk", tz);
+ tz = new SimpleTimeZone
+ (12000 * 3600, "NST",
+ Calendar.OCTOBER, 1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.MARCH, 3, Calendar.SUNDAY, 3000 * 3600);
+ timezones0.put("NST", tz);
+ timezones0.put("Antarctica/McMurdo", tz);
+ timezones0.put("Antarctica/South_Pole", tz);
+ timezones0.put("Pacific/Auckland", tz);
+ tz = new SimpleTimeZone
+ (12000 * 3600, "Asia/Anadyr",
+ Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600,
+ Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600);
+ timezones0.put("Asia/Anadyr", tz);
+ timezones0.put("Asia/Kamchatka", tz);
+ tz = new SimpleTimeZone(12000 * 3600, "Pacific/Fiji");
+ timezones0.put("Pacific/Fiji", tz);
+ timezones0.put("Pacific/Funafuti", tz);
+ timezones0.put("Pacific/Kwajalein", tz);
+ timezones0.put("Pacific/Majuro", tz);
+ timezones0.put("Pacific/Nauru", tz);
+ timezones0.put("Pacific/Tarawa", tz);
+ timezones0.put("Pacific/Wake", tz);
+ timezones0.put("Pacific/Wallis", tz);
+ tz = new SimpleTimeZone
+ (12750 * 3600, "Pacific/Chatham",
+ Calendar.OCTOBER, 1, Calendar.SUNDAY, 2750 * 3600,
+ Calendar.MARCH, 3, Calendar.SUNDAY, 3750 * 3600);
+ timezones0.put("Pacific/Chatham", tz);
+ tz = new SimpleTimeZone(13000 * 3600, "Pacific/Enderbury");
+ timezones0.put("Pacific/Enderbury", tz);
+ timezones0.put("Pacific/Tongatapu", tz);
+ tz = new SimpleTimeZone(14000 * 3600, "Pacific/Kiritimati");
+ timezones0.put("Pacific/Kiritimati", tz);
+ }
+ return timezones0;
+ }
+
+ /**
+ * Maps a time zone name (with optional GMT offset and daylight time
+ * zone name) to one of the known time zones. This method called
+ * with the result of <code>System.getProperty("user.timezone")</code>
+ * or <code>getDefaultTimeZoneId()</code>. Note that giving one of
+ * the standard tz data names from ftp://elsie.nci.nih.gov/pub/ is
+ * preferred.
+ * The time zone name can be given as follows:
+ * <code>(standard zone name)[(GMT offset)[(DST zone name)[DST offset]]]
+ * </code>
+ * <p>
+ * If only a (standard zone name) is given (no numbers in the
+ * String) then it gets mapped directly to the TimeZone with that
+ * name, if that fails null is returned.
+ * <p>
+ * Alternately, a POSIX-style TZ string can be given, defining the time zone:
+ * <code>std offset dst offset,date/time,date/time</code>
+ * See the glibc manual, or the man page for <code>tzset</code> for details
+ * of this format.
+ * <p>
+ * A GMT offset is the offset to add to the local time to get GMT.
+ * If a (GMT offset) is included (either in seconds or hours) then
+ * an attempt is made to find a TimeZone name matching both the name
+ * and the offset (that doesn't observe daylight time, if the
+ * timezone observes daylight time then you must include a daylight
+ * time zone name after the offset), if that fails then a TimeZone
+ * with the given GMT offset is returned (whether or not the
+ * TimeZone observes daylight time is ignored), if that also fails
+ * the GMT TimeZone is returned.
+ * <p>
+ * If the String ends with (GMT offset)(daylight time zone name)
+ * then an attempt is made to find a TimeZone with the given name and
+ * GMT offset that also observes (the daylight time zone name is not
+ * currently used in any other way), if that fails a TimeZone with
+ * the given GMT offset that observes daylight time is returned, if
+ * that also fails the GMT TimeZone is returned.
+ * <p>
+ * Examples: In Chicago, the time zone id could be "CST6CDT", but
+ * the preferred name would be "America/Chicago". In Indianapolis
+ * (which does not have Daylight Savings Time) the string could be
+ * "EST5", but the preferred name would be "America/Indianapolis".
+ * The standard time zone name for The Netherlands is "Europe/Amsterdam",
+ * but can also be given as "CET-1CEST".
+ */
+ static TimeZone getDefaultTimeZone(String sysTimeZoneId)
+ {
+ String stdName = null;
+ int stdOffs;
+ int dstOffs;
+ try
+ {
+ int idLength = sysTimeZoneId.length();
+
+ int index = 0;
+ int prevIndex;
+ char c;
+
+ // get std
+ do
+ c = sysTimeZoneId.charAt(index);
+ while (c != '+' && c != '-' && c != ',' && c != ':'
+ && ! Character.isDigit(c) && c != '\0' && ++index < idLength);
+
+ if (index >= idLength)
+ return getTimeZoneInternal(sysTimeZoneId);
+
+ stdName = sysTimeZoneId.substring(0, index);
+ prevIndex = index;
+
+ // get the std offset
+ do
+ c = sysTimeZoneId.charAt(index++);
+ while ((c == '-' || c == '+' || c == ':' || Character.isDigit(c))
+ && index < idLength);
+ if (index < idLength)
+ index--;
+
+ { // convert the dst string to a millis number
+ String offset = sysTimeZoneId.substring(prevIndex, index);
+ prevIndex = index;
+
+ if (offset.charAt(0) == '+' || offset.charAt(0) == '-')
+ stdOffs = parseTime(offset.substring(1));
+ else
+ stdOffs = parseTime(offset);
+
+ if (offset.charAt(0) == '-')
+ stdOffs = -stdOffs;
+
+ // TZ timezone offsets are positive when WEST of the meridian.
+ stdOffs = -stdOffs;
+ }
+
+ // Done yet? (Format: std offset)
+ if (index >= idLength)
+ {
+ // Do we have an existing timezone with that name and offset?
+ TimeZone tz = getTimeZoneInternal(stdName);
+ if (tz != null)
+ if (tz.getRawOffset() == stdOffs)
+ return tz;
+
+ // Custom then.
+ return new SimpleTimeZone(stdOffs, stdName);
+ }
+
+ // get dst
+ do
+ c = sysTimeZoneId.charAt(index);
+ while (c != '+' && c != '-' && c != ',' && c != ':'
+ && ! Character.isDigit(c) && c != '\0' && ++index < idLength);
+
+ // Done yet? (Format: std offset dst)
+ if (index >= idLength)
+ {
+ // Do we have an existing timezone with that name and offset
+ // which has DST?
+ TimeZone tz = getTimeZoneInternal(stdName);
+ if (tz != null)
+ if (tz.getRawOffset() == stdOffs && tz.useDaylightTime())
+ return tz;
+
+ // Custom then.
+ return new SimpleTimeZone(stdOffs, stdName);
+ }
+
+ // get the dst offset
+ prevIndex = index;
+ do
+ c = sysTimeZoneId.charAt(index++);
+ while ((c == '-' || c == '+' || c == ':' || Character.isDigit(c))
+ && index < idLength);
+ if (index < idLength)
+ index--;
+
+ if (index == prevIndex && (c == ',' || c == ';'))
+ {
+ // Missing dst offset defaults to one hour ahead of standard
+ // time.
+ dstOffs = stdOffs + 60 * 60 * 1000;
+ }
+ else
+ { // convert the dst string to a millis number
+ String offset = sysTimeZoneId.substring(prevIndex, index);
+ prevIndex = index;
+
+ if (offset.charAt(0) == '+' || offset.charAt(0) == '-')
+ dstOffs = parseTime(offset.substring(1));
+ else
+ dstOffs = parseTime(offset);
+
+ if (offset.charAt(0) == '-')
+ dstOffs = -dstOffs;
+
+ // TZ timezone offsets are positive when WEST of the meridian.
+ dstOffs = -dstOffs;
+ }
+
+ // Done yet? (Format: std offset dst offset)
+ // FIXME: We don't support DST without a rule given. Should we?
+ if (index >= idLength)
+ {
+ // Time Zone existing with same name, dst and offsets?
+ TimeZone tz = getTimeZoneInternal(stdName);
+ if (tz != null)
+ if (tz.getRawOffset() == stdOffs && tz.useDaylightTime()
+ && tz.getDSTSavings() == (dstOffs - stdOffs))
+ return tz;
+
+ return new SimpleTimeZone(stdOffs, stdName);
+ }
+
+ // get the DST rule
+ if (sysTimeZoneId.charAt(index) == ','
+ || sysTimeZoneId.charAt(index) == ';')
+ {
+ index++;
+ int offs = index;
+ while (sysTimeZoneId.charAt(index) != ','
+ && sysTimeZoneId.charAt(index) != ';')
+ index++;
+ String startTime = sysTimeZoneId.substring(offs, index);
+ index++;
+ String endTime = sysTimeZoneId.substring(index);
+
+ index = startTime.indexOf('/');
+ int startMillis;
+ int endMillis;
+ String startDate;
+ String endDate;
+ if (index != -1)
+ {
+ startDate = startTime.substring(0, index);
+ startMillis = parseTime(startTime.substring(index + 1));
+ }
+ else
+ {
+ startDate = startTime;
+ // if time isn't given, default to 2:00:00 AM.
+ startMillis = 2 * 60 * 60 * 1000;
+ }
+ index = endTime.indexOf('/');
+ if (index != -1)
+ {
+ endDate = endTime.substring(0, index);
+ endMillis = parseTime(endTime.substring(index + 1));
+ }
+ else
+ {
+ endDate = endTime;
+ // if time isn't given, default to 2:00:00 AM.
+ endMillis = 2 * 60 * 60 * 1000;
+ }
+
+ int[] start = getDateParams(startDate);
+ int[] end = getDateParams(endDate);
+ return new SimpleTimeZone(stdOffs, stdName, start[0], start[1],
+ start[2], startMillis, end[0], end[1],
+ end[2], endMillis, (dstOffs - stdOffs));
+ }
+ }
+
+ // FIXME: Produce a warning here?
+ catch (IndexOutOfBoundsException _)
+ {
+ }
+ catch (NumberFormatException _)
+ {
+ }
+
+ return null;
+ }
+
+ /**
+ * Parses and returns the params for a POSIX TZ date field,
+ * in the format int[]{ month, day, dayOfWeek }, following the
+ * SimpleTimeZone constructor rules.
+ */
+ private static int[] getDateParams(String date)
+ {
+ int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+ int month;
+
+ if (date.charAt(0) == 'M' || date.charAt(0) == 'm')
+ {
+ int day;
+
+ // Month, week of month, day of week
+
+ // "Mm.w.d". d is between 0 (Sunday) and 6. Week w is
+ // between 1 and 5; Week 1 is the first week in which day d
+ // occurs and Week 5 specifies the last d day in the month.
+ // Month m is between 1 and 12.
+
+ month = Integer.parseInt(date.substring(1, date.indexOf('.')));
+ int week = Integer.parseInt(date.substring(date.indexOf('.') + 1,
+ date.lastIndexOf('.')));
+ int dayOfWeek = Integer.parseInt(date.substring(date.lastIndexOf('.')
+ + 1));
+ dayOfWeek++; // Java day of week is one-based, Sunday is first day.
+
+ if (week == 5)
+ day = -1; // last day of month is -1 in java, 5 in TZ
+ else
+ {
+ // First day of week starting on or after. For example,
+ // to specify the second Sunday of April, set month to
+ // APRIL, day-of-month to 8, and day-of-week to -SUNDAY.
+ day = (week - 1) * 7 + 1;
+ dayOfWeek = -dayOfWeek;
+ }
+
+ month--; // Java month is zero-based.
+ return new int[] { month, day, dayOfWeek };
+ }
+
+ // julian day, either zero-based 0<=n<=365 (incl feb 29)
+ // or one-based 1<=n<=365 (no feb 29)
+ int julianDay; // Julian day,
+
+ if (date.charAt(0) != 'J' || date.charAt(0) != 'j')
+ {
+ julianDay = Integer.parseInt(date.substring(1));
+ julianDay++; // make 1-based
+ // Adjust day count to include feb 29.
+ dayCount = new int[]
+ {
+ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335
+ };
+ }
+ else
+ // 1-based julian day
+ julianDay = Integer.parseInt(date);
+
+ int i = 11;
+ while (i > 0)
+ if (dayCount[i] < julianDay)
+ break;
+ else
+ i--;
+ julianDay -= dayCount[i];
+ month = i;
+ return new int[] { month, julianDay, 0 };
+ }
+
+ /**
+ * Parses a time field hh[:mm[:ss]], returning the result
+ * in milliseconds. No leading sign.
+ */
+ private static int parseTime(String time)
+ {
+ int millis = 0;
+ int i = 0;
+
+ while (i < time.length())
+ if (time.charAt(i) == ':')
+ break;
+ else
+ i++;
+ millis = 60 * 60 * 1000 * Integer.parseInt(time.substring(0, i));
+ if (i >= time.length())
+ return millis;
+
+ int iprev = ++i;
+ while (i < time.length())
+ if (time.charAt(i) == ':')
+ break;
+ else
+ i++;
+ millis += 60 * 1000 * Integer.parseInt(time.substring(iprev, i));
+ if (i >= time.length())
+ return millis;
+
+ millis += 1000 * Integer.parseInt(time.substring(++i));
+ return millis;
+ }
+
+ /**
+ * Gets the time zone offset, for current date, modified in case of
+ * daylight savings. This is the offset to add to UTC to get the local
+ * time.
+ * @param era the era of the given date
+ * @param year the year of the given date
+ * @param month the month of the given date, 0 for January.
+ * @param day the day of month
+ * @param dayOfWeek the day of week
+ * @param milliseconds the millis in the day (in local standard time)
+ * @return the time zone offset in milliseconds.
+ */
+ public abstract int getOffset(int era, int year, int month,
+ int day, int dayOfWeek, int milliseconds);
+
+ /**
+ * Get the time zone offset for the specified date, modified in case of
+ * daylight savings. This is the offset to add to UTC to get the local
+ * time.
+ * @param date the date represented in millisecends
+ * since January 1, 1970 00:00:00 GMT.
+ * @since 1.4
+ */
+ public int getOffset(long date)
+ {
+ return (inDaylightTime(new Date(date))
+ ? getRawOffset() + getDSTSavings()
+ : getRawOffset());
+ }
+
+ /**
+ * Gets the time zone offset, ignoring daylight savings. This is
+ * the offset to add to UTC to get the local time.
+ * @return the time zone offset in milliseconds.
+ */
+ public abstract int getRawOffset();
+
+ /**
+ * Sets the time zone offset, ignoring daylight savings. This is
+ * the offset to add to UTC to get the local time.
+ * @param offsetMillis the time zone offset to GMT.
+ */
+ public abstract void setRawOffset(int offsetMillis);
+
+ /**
+ * Gets the identifier of this time zone. For instance, PST for
+ * Pacific Standard Time.
+ * @returns the ID of this time zone.
+ */
+ public String getID()
+ {
+ return ID;
+ }
+
+ /**
+ * Sets the identifier of this time zone. For instance, PST for
+ * Pacific Standard Time.
+ * @param id the new time zone ID.
+ * @throws NullPointerException if <code>id</code> is <code>null</code>
+ */
+ public void setID(String id)
+ {
+ if (id == null)
+ throw new NullPointerException();
+
+ this.ID = id;
+ }
+
+ /**
+ * This method returns a string name of the time zone suitable
+ * for displaying to the user. The string returned will be the long
+ * description of the timezone in the current locale. The name
+ * displayed will assume daylight savings time is not in effect.
+ *
+ * @return The name of the time zone.
+ */
+ public final String getDisplayName()
+ {
+ return (getDisplayName(false, LONG, Locale.getDefault()));
+ }
+
+ /**
+ * This method returns a string name of the time zone suitable
+ * for displaying to the user. The string returned will be the long
+ * description of the timezone in the specified locale. The name
+ * displayed will assume daylight savings time is not in effect.
+ *
+ * @param locale The locale for this timezone name.
+ *
+ * @return The name of the time zone.
+ */
+ public final String getDisplayName(Locale locale)
+ {
+ return (getDisplayName(false, LONG, locale));
+ }
+
+ /**
+ * This method returns a string name of the time zone suitable
+ * for displaying to the user. The string returned will be of the
+ * specified type in the current locale.
+ *
+ * @param dst Whether or not daylight savings time is in effect.
+ * @param style <code>LONG</code> for a long name, <code>SHORT</code> for
+ * a short abbreviation.
+ *
+ * @return The name of the time zone.
+ */
+ public final String getDisplayName(boolean dst, int style)
+ {
+ return (getDisplayName(dst, style, Locale.getDefault()));
+ }
+
+
+ /**
+ * This method returns a string name of the time zone suitable
+ * for displaying to the user. The string returned will be of the
+ * specified type in the specified locale.
+ *
+ * @param dst Whether or not daylight savings time is in effect.
+ * @param style <code>LONG</code> for a long name, <code>SHORT</code> for
+ * a short abbreviation.
+ * @param locale The locale for this timezone name.
+ *
+ * @return The name of the time zone.
+ */
+ public String getDisplayName(boolean dst, int style, Locale locale)
+ {
+ DateFormatSymbols dfs;
+ try
+ {
+ dfs = new DateFormatSymbols(locale);
+
+ // The format of the value returned is defined by us.
+ String[][]zoneinfo = dfs.getZoneStrings();
+ for (int i = 0; i < zoneinfo.length; i++)
+ {
+ if (zoneinfo[i][0].equals(getID()))
+ {
+ if (!dst)
+ {
+ if (style == SHORT)
+ return (zoneinfo[i][2]);
+ else
+ return (zoneinfo[i][1]);
+ }
+ else
+ {
+ if (style == SHORT)
+ return (zoneinfo[i][4]);
+ else
+ return (zoneinfo[i][3]);
+ }
+ }
+ }
+ }
+ catch (MissingResourceException e)
+ {
+ }
+
+ return getDefaultDisplayName(dst);
+ }
+
+ private String getDefaultDisplayName(boolean dst)
+ {
+ int offset = getRawOffset() + (dst ? getDSTSavings() : 0);
+
+ CPStringBuilder sb = new CPStringBuilder(9);
+ sb.append("GMT");
+
+ offset = offset / (1000 * 60);
+ int hours = Math.abs(offset) / 60;
+ int minutes = Math.abs(offset) % 60;
+
+ if (minutes != 0 || hours != 0)
+ {
+ sb.append(offset >= 0 ? '+' : '-');
+ sb.append((char) ('0' + hours / 10));
+ sb.append((char) ('0' + hours % 10));
+ sb.append(':');
+ sb.append((char) ('0' + minutes / 10));
+ sb.append((char) ('0' + minutes % 10));
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Returns true, if this time zone uses Daylight Savings Time.
+ */
+ public abstract boolean useDaylightTime();
+
+ /**
+ * Returns true, if the given date is in Daylight Savings Time in this
+ * time zone.
+ * @param date the given Date.
+ */
+ public abstract boolean inDaylightTime(Date date);
+
+ /**
+ * Gets the daylight savings offset. This is a positive offset in
+ * milliseconds with respect to standard time. Typically this
+ * is one hour, but for some time zones this may be half an our.
+ * <p>The default implementation returns 3600000 milliseconds
+ * (one hour) if the time zone uses daylight savings time
+ * (as specified by {@link #useDaylightTime()}), otherwise
+ * it returns 0.
+ * @return the daylight savings offset in milliseconds.
+ * @since 1.4
+ */
+ public int getDSTSavings ()
+ {
+ return useDaylightTime () ? 3600000 : 0;
+ }
+
+ /**
+ * Gets the TimeZone for the given ID.
+ * @param ID the time zone identifier.
+ * @return The time zone for the identifier or GMT, if no such time
+ * zone exists.
+ */
+ private static TimeZone getTimeZoneInternal(String ID)
+ {
+ // First check timezones hash
+ TimeZone tz = null;
+ TimeZone tznew = null;
+ for (int pass = 0; pass < 2; pass++)
+ {
+ synchronized (TimeZone.class)
+ {
+ tz = (TimeZone) timezones().get(ID);
+ if (tz != null)
+ {
+ if (!tz.getID().equals(ID))
+ {
+ // We always return a timezone with the requested ID.
+ // This is the same behaviour as with JDK1.2.
+ tz = (TimeZone) tz.clone();
+ tz.setID(ID);
+ // We also save the alias, so that we return the same
+ // object again if getTimeZone is called with the same
+ // alias.
+ timezones().put(ID, tz);
+ }
+ return tz;
+ }
+ else if (tznew != null)
+ {
+ timezones().put(ID, tznew);
+ return tznew;
+ }
+ }
+
+ if (pass == 1 || zoneinfo_dir == null)
+ return null;
+
+ // aliases0 is never changing after first timezones(), so should
+ // be safe without synchronization.
+ String zonename = (String) aliases0.get(ID);
+ if (zonename == null)
+ zonename = ID;
+
+ // Read the file outside of the critical section, it is expensive.
+ tznew = ZoneInfo.readTZFile (ID, zoneinfo_dir
+ + File.separatorChar + zonename);
+ if (tznew == null)
+ return null;
+ }
+
+ return null;
+ }
+
+ /**
+ * Gets the TimeZone for the given ID.
+ * @param ID the time zone identifier.
+ * @return The time zone for the identifier or GMT, if no such time
+ * zone exists.
+ */
+ public static TimeZone getTimeZone(String ID)
+ {
+ // Check for custom IDs first
+ if (ID.startsWith("GMT") && ID.length() > 3)
+ {
+ int pos = 3;
+ int offset_direction = 1;
+
+ if (ID.charAt(pos) == '-')
+ {
+ offset_direction = -1;
+ pos++;
+ }
+ else if (ID.charAt(pos) == '+')
+ {
+ pos++;
+ }
+
+ try
+ {
+ int hour, minute;
+
+ String offset_str = ID.substring(pos);
+ int idx = offset_str.indexOf(":");
+ if (idx != -1)
+ {
+ hour = Integer.parseInt(offset_str.substring(0, idx));
+ minute = Integer.parseInt(offset_str.substring(idx + 1));
+ }
+ else
+ {
+ int offset_length = offset_str.length();
+ if (offset_length <= 2)
+ {
+ // Only hour
+ hour = Integer.parseInt(offset_str);
+ minute = 0;
+ }
+ else
+ {
+ // hour and minute, not separated by colon
+ hour = Integer.parseInt
+ (offset_str.substring(0, offset_length - 2));
+ minute = Integer.parseInt
+ (offset_str.substring(offset_length - 2));
+ }
+ }
+
+ // Custom IDs have to be normalized
+ CPStringBuilder sb = new CPStringBuilder(9);
+ sb.append("GMT");
+
+ sb.append(offset_direction >= 0 ? '+' : '-');
+ sb.append((char) ('0' + hour / 10));
+ sb.append((char) ('0' + hour % 10));
+ sb.append(':');
+ sb.append((char) ('0' + minute / 10));
+ sb.append((char) ('0' + minute % 10));
+ ID = sb.toString();
+
+ return new SimpleTimeZone((hour * (60 * 60 * 1000)
+ + minute * (60 * 1000))
+ * offset_direction, ID);
+ }
+ catch (NumberFormatException e)
+ {
+ }
+ }
+
+ TimeZone tz = getTimeZoneInternal(ID);
+ if (tz != null)
+ return tz;
+
+ return new SimpleTimeZone(0, "GMT");
+ }
+
+ /**
+ * Gets the available IDs according to the given time zone
+ * offset.
+ * @param rawOffset the given time zone GMT offset.
+ * @return An array of IDs, where the time zone has the specified GMT
+ * offset. For example <code>{"Phoenix", "Denver"}</code>, since both have
+ * GMT-07:00, but differ in daylight savings behaviour.
+ */
+ public static String[] getAvailableIDs(int rawOffset)
+ {
+ synchronized (TimeZone.class)
+ {
+ HashMap h = timezones();
+ int count = 0;
+ if (zoneinfo_dir == null)
+ {
+ Iterator iter = h.entrySet().iterator();
+ while (iter.hasNext())
+ {
+ // Don't iterate the values, since we want to count
+ // doubled values (aliases)
+ Map.Entry entry = (Map.Entry) iter.next();
+ if (((TimeZone) entry.getValue()).getRawOffset() == rawOffset)
+ count++;
+ }
+
+ String[] ids = new String[count];
+ count = 0;
+ iter = h.entrySet().iterator();
+ while (iter.hasNext())
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ if (((TimeZone) entry.getValue()).getRawOffset() == rawOffset)
+ ids[count++] = (String) entry.getKey();
+ }
+ return ids;
+ }
+ }
+
+ String[] s = getAvailableIDs();
+ int count = 0;
+ for (int i = 0; i < s.length; i++)
+ {
+ TimeZone t = getTimeZoneInternal(s[i]);
+ if (t == null || t.getRawOffset() != rawOffset)
+ s[i] = null;
+ else
+ count++;
+ }
+ String[] ids = new String[count];
+ count = 0;
+ for (int i = 0; i < s.length; i++)
+ if (s[i] != null)
+ ids[count++] = s[i];
+
+ return ids;
+ }
+
+ private static int getAvailableIDs(File d, String prefix, ArrayList list)
+ {
+ String[] files = d.list();
+ int count = files.length;
+ boolean top = prefix.length() == 0;
+ list.add (files);
+ for (int i = 0; i < files.length; i++)
+ {
+ if (top
+ && (files[i].equals("posix")
+ || files[i].equals("right")
+ || files[i].endsWith(".tab")
+ || aliases0.get(files[i]) != null))
+ {
+ files[i] = null;
+ count--;
+ continue;
+ }
+
+ File f = new File(d, files[i]);
+ if (f.isDirectory())
+ {
+ count += getAvailableIDs(f, prefix + files[i]
+ + File.separatorChar, list) - 1;
+ files[i] = null;
+ }
+ else
+ files[i] = prefix + files[i];
+ }
+ return count;
+ }
+
+ /**
+ * Gets all available IDs.
+ * @return An array of all supported IDs.
+ */
+ public static String[] getAvailableIDs()
+ {
+ synchronized (TimeZone.class)
+ {
+ HashMap h = timezones();
+ if (zoneinfo_dir == null)
+ return (String[]) h.keySet().toArray(new String[h.size()]);
+
+ if (availableIDs != null)
+ {
+ String[] ids = new String[availableIDs.length];
+ for (int i = 0; i < availableIDs.length; i++)
+ ids[i] = availableIDs[i];
+ return ids;
+ }
+
+ File d = new File(zoneinfo_dir);
+ ArrayList list = new ArrayList(30);
+ int count = getAvailableIDs(d, "", list) + aliases0.size();
+ availableIDs = new String[count];
+ String[] ids = new String[count];
+
+ count = 0;
+ for (int i = 0; i < list.size(); i++)
+ {
+ String[] s = (String[]) list.get(i);
+ for (int j = 0; j < s.length; j++)
+ if (s[j] != null)
+ {
+ availableIDs[count] = s[j];
+ ids[count++] = s[j];
+ }
+ }
+
+ Iterator iter = aliases0.entrySet().iterator();
+ while (iter.hasNext())
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ availableIDs[count] = (String) entry.getKey();
+ ids[count++] = (String) entry.getKey();
+ }
+
+ return ids;
+ }
+ }
+
+ /**
+ * Returns the time zone under which the host is running. This
+ * can be changed with setDefault.
+ *
+ * @return A clone of the current default time zone for this host.
+ * @see #setDefault
+ */
+ public static TimeZone getDefault()
+ {
+ return (TimeZone) defaultZone().clone();
+ }
+
+ public static void setDefault(TimeZone zone)
+ {
+ // Hmmmm. No Security checks?
+ defaultZone0 = zone;
+ }
+
+ /**
+ * Test if the other time zone uses the same rule and only
+ * possibly differs in ID. This implementation for this particular
+ * class will return true if the raw offsets are identical. Subclasses
+ * should override this method if they use daylight savings.
+ * @return true if this zone has the same raw offset
+ */
+ public boolean hasSameRules(TimeZone other)
+ {
+ return other.getRawOffset() == getRawOffset();
+ }
+
+ /**
+ * Returns a clone of this object. I can't imagine, why this is
+ * useful for a time zone.
+ */
+ public Object clone()
+ {
+ try
+ {
+ return super.clone();
+ }
+ catch (CloneNotSupportedException ex)
+ {
+ return null;
+ }
+ }
+}
diff --git a/libjava/classpath/java/util/Timer.java b/libjava/classpath/java/util/Timer.java
new file mode 100644
index 000000000..99027554c
--- /dev/null
+++ b/libjava/classpath/java/util/Timer.java
@@ -0,0 +1,704 @@
+/* Timer.java -- Timer that runs TimerTasks at a later time.
+ Copyright (C) 2000, 2001, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Timer that can run TimerTasks at a later time.
+ * TimerTasks can be scheduled for one time execution at some time in the
+ * future. They can be scheduled to be rescheduled at a time period after the
+ * task was last executed. Or they can be scheduled to be executed repeatedly
+ * at a fixed rate.
+ * <p>
+ * The normal scheduling will result in a more or less even delay in time
+ * between successive executions, but the executions could drift in time if
+ * the task (or other tasks) takes a long time to execute. Fixed delay
+ * scheduling guarantees more or less that the task will be executed at a
+ * specific time, but if there is ever a delay in execution then the period
+ * between successive executions will be shorter. The first method of
+ * repeated scheduling is preferred for repeated tasks in response to user
+ * interaction, the second method of repeated scheduling is preferred for tasks
+ * that act like alarms.
+ * <p>
+ * The Timer keeps a binary heap as a task priority queue which means that
+ * scheduling and serving of a task in a queue of n tasks costs O(log n).
+ *
+ * @see TimerTask
+ * @since 1.3
+ * @author Mark Wielaard (mark@klomp.org)
+ */
+public class Timer
+{
+ /**
+ * Priority Task Queue.
+ * TimerTasks are kept in a binary heap.
+ * The scheduler calls sleep() on the queue when it has nothing to do or
+ * has to wait. A sleeping scheduler can be notified by calling interrupt()
+ * which is automatically called by the enqueue(), cancel() and
+ * timerFinalized() methods.
+ */
+ private static final class TaskQueue
+ {
+ /** Default size of this queue */
+ private static final int DEFAULT_SIZE = 32;
+
+ /** Whether to return null when there is nothing in the queue */
+ private boolean nullOnEmpty;
+
+ /**
+ * The heap containing all the scheduled TimerTasks
+ * sorted by the TimerTask.scheduled field.
+ * Null when the stop() method has been called.
+ */
+ private TimerTask heap[];
+
+ /**
+ * The actual number of elements in the heap
+ * Can be less then heap.length.
+ * Note that heap[0] is used as a sentinel.
+ */
+ private int elements;
+
+ /**
+ * Creates a TaskQueue of default size without any elements in it.
+ */
+ public TaskQueue()
+ {
+ heap = new TimerTask[DEFAULT_SIZE];
+ elements = 0;
+ nullOnEmpty = false;
+ }
+
+ /**
+ * Adds a TimerTask at the end of the heap.
+ * Grows the heap if necessary by doubling the heap in size.
+ */
+ private void add(TimerTask task)
+ {
+ elements++;
+ if (elements == heap.length)
+ {
+ TimerTask new_heap[] = new TimerTask[heap.length * 2];
+ System.arraycopy(heap, 0, new_heap, 0, heap.length);
+ heap = new_heap;
+ }
+ heap[elements] = task;
+ }
+
+ /**
+ * Removes the last element from the heap.
+ * Shrinks the heap in half if
+ * elements+DEFAULT_SIZE/2 <= heap.length/4.
+ */
+ private void remove()
+ {
+ // clear the entry first
+ heap[elements] = null;
+ elements--;
+ if (elements + DEFAULT_SIZE / 2 <= (heap.length / 4))
+ {
+ TimerTask new_heap[] = new TimerTask[heap.length / 2];
+ System.arraycopy(heap, 0, new_heap, 0, elements + 1);
+ heap = new_heap;
+ }
+ }
+
+ /**
+ * Adds a task to the queue and puts it at the correct place
+ * in the heap.
+ */
+ public synchronized void enqueue(TimerTask task)
+ {
+ // Check if it is legal to add another element
+ if (heap == null)
+ {
+ throw new IllegalStateException
+ ("cannot enqueue when stop() has been called on queue");
+ }
+
+ heap[0] = task; // sentinel
+ add(task); // put the new task at the end
+ // Now push the task up in the heap until it has reached its place
+ int child = elements;
+ int parent = child / 2;
+ while (heap[parent].scheduled > task.scheduled)
+ {
+ heap[child] = heap[parent];
+ child = parent;
+ parent = child / 2;
+ }
+ // This is the correct place for the new task
+ heap[child] = task;
+ heap[0] = null; // clear sentinel
+ // Maybe sched() is waiting for a new element
+ this.notify();
+ }
+
+ /**
+ * Returns the top element of the queue.
+ * Can return null when no task is in the queue.
+ */
+ private TimerTask top()
+ {
+ if (elements == 0)
+ {
+ return null;
+ }
+ else
+ {
+ return heap[1];
+ }
+ }
+
+ /**
+ * Returns the top task in the Queue.
+ * Removes the element from the heap and reorders the heap first.
+ * Can return null when there is nothing in the queue.
+ */
+ public synchronized TimerTask serve()
+ {
+ // The task to return
+ TimerTask task = null;
+
+ while (task == null)
+ {
+ // Get the next task
+ task = top();
+
+ // return null when asked to stop
+ // or if asked to return null when the queue is empty
+ if ((heap == null) || (task == null && nullOnEmpty))
+ {
+ return null;
+ }
+
+ // Do we have a task?
+ if (task != null)
+ {
+ // The time to wait until the task should be served
+ long time = task.scheduled - System.currentTimeMillis();
+ if (time > 0)
+ {
+ // This task should not yet be served
+ // So wait until this task is ready
+ // or something else happens to the queue
+ task = null; // set to null to make sure we call top()
+ try
+ {
+ this.wait(time);
+ }
+ catch (InterruptedException _)
+ {
+ }
+ }
+ }
+ else
+ {
+ // wait until a task is added
+ // or something else happens to the queue
+ try
+ {
+ this.wait();
+ }
+ catch (InterruptedException _)
+ {
+ }
+ }
+ }
+
+ // reconstruct the heap
+ TimerTask lastTask = heap[elements];
+ remove();
+
+ // drop lastTask at the beginning and move it down the heap
+ int parent = 1;
+ int child = 2;
+ heap[1] = lastTask;
+ while (child <= elements)
+ {
+ if (child < elements)
+ {
+ if (heap[child].scheduled > heap[child + 1].scheduled)
+ {
+ child++;
+ }
+ }
+
+ if (lastTask.scheduled <= heap[child].scheduled)
+ break; // found the correct place (the parent) - done
+
+ heap[parent] = heap[child];
+ parent = child;
+ child = parent * 2;
+ }
+
+ // this is the correct new place for the lastTask
+ heap[parent] = lastTask;
+
+ // return the task
+ return task;
+ }
+
+ /**
+ * When nullOnEmpty is true the serve() method will return null when
+ * there are no tasks in the queue, otherwise it will wait until
+ * a new element is added to the queue. It is used to indicate to
+ * the scheduler that no new tasks will ever be added to the queue.
+ */
+ public synchronized void setNullOnEmpty(boolean nullOnEmpty)
+ {
+ this.nullOnEmpty = nullOnEmpty;
+ this.notify();
+ }
+
+ /**
+ * When this method is called the current and all future calls to
+ * serve() will return null. It is used to indicate to the Scheduler
+ * that it should stop executing since no more tasks will come.
+ */
+ public synchronized void stop()
+ {
+ this.heap = null;
+ this.elements = 0;
+ this.notify();
+ }
+
+ /**
+ * Remove all canceled tasks from the queue.
+ */
+ public synchronized int purge()
+ {
+ int removed = 0;
+ // Null out any elements that are canceled. Skip element 0 as
+ // it is the sentinel.
+ for (int i = elements; i > 0; --i)
+ {
+ if (heap[i].scheduled < 0)
+ {
+ ++removed;
+
+ // Remove an element by pushing the appropriate child
+ // into place, and then iterating to the bottom of the
+ // tree.
+ int index = i;
+ while (heap[index] != null)
+ {
+ int child = 2 * index;
+ if (child >= heap.length)
+ {
+ // Off end; we're done.
+ heap[index] = null;
+ break;
+ }
+
+ if (child + 1 >= heap.length || heap[child + 1] == null)
+ {
+ // Nothing -- we're done.
+ }
+ else if (heap[child] == null
+ || (heap[child].scheduled
+ > heap[child + 1].scheduled))
+ ++child;
+ heap[index] = heap[child];
+ index = child;
+ }
+ }
+ }
+
+ // Make a new heap if we shrank enough.
+ int newLen = heap.length;
+ while (elements - removed + DEFAULT_SIZE / 2 <= newLen / 4)
+ newLen /= 2;
+ if (newLen != heap.length)
+ {
+ TimerTask[] newHeap = new TimerTask[newLen];
+ System.arraycopy(heap, 0, newHeap, 0, elements + 1);
+ heap = newHeap;
+ }
+
+ return removed;
+ }
+ } // TaskQueue
+
+ /**
+ * The scheduler that executes all the tasks on a particular TaskQueue,
+ * reschedules any repeating tasks and that waits when no task has to be
+ * executed immediately. Stops running when canceled or when the parent
+ * Timer has been finalized and no more tasks have to be executed.
+ */
+ private static final class Scheduler implements Runnable
+ {
+ // The priority queue containing all the TimerTasks.
+ private TaskQueue queue;
+
+ /**
+ * Creates a new Scheduler that will schedule the tasks on the
+ * given TaskQueue.
+ */
+ public Scheduler(TaskQueue queue)
+ {
+ this.queue = queue;
+ }
+
+ public void run()
+ {
+ TimerTask task;
+ while ((task = queue.serve()) != null)
+ {
+ // If this task has not been canceled
+ if (task.scheduled >= 0)
+ {
+
+ // Mark execution time
+ task.lastExecutionTime = task.scheduled;
+
+ // Repeatable task?
+ if (task.period < 0)
+ {
+ // Last time this task is executed
+ task.scheduled = -1;
+ }
+
+ // Run the task
+ try
+ {
+ task.run();
+ }
+ catch (ThreadDeath death)
+ {
+ // If an exception escapes, the Timer becomes invalid.
+ queue.stop();
+ throw death;
+ }
+ catch (Throwable t)
+ {
+ // If an exception escapes, the Timer becomes invalid.
+ queue.stop();
+ }
+ }
+
+ // Calculate next time and possibly re-enqueue.
+ if (task.scheduled >= 0)
+ {
+ if (task.fixed)
+ {
+ task.scheduled += task.period;
+ }
+ else
+ {
+ task.scheduled = task.period + System.currentTimeMillis();
+ }
+
+ try
+ {
+ queue.enqueue(task);
+ }
+ catch (IllegalStateException ise)
+ {
+ // Ignore. Apparently the Timer queue has been stopped.
+ }
+ }
+ }
+ }
+ } // Scheduler
+
+ // Number of Timers created.
+ // Used for creating nice Thread names.
+ private static int nr;
+
+ // The queue that all the tasks are put in.
+ // Given to the scheduler
+ private TaskQueue queue;
+
+ // The Scheduler that does all the real work
+ private Scheduler scheduler;
+
+ // Used to run the scheduler.
+ // Also used to checked if the Thread is still running by calling
+ // thread.isAlive(). Sometimes a Thread is suddenly killed by the system
+ // (if it belonged to an Applet).
+ private Thread thread;
+
+ // When cancelled we don't accept any more TimerTasks.
+ private boolean canceled;
+
+ /**
+ * Creates a new Timer with a non daemon Thread as Scheduler, with normal
+ * priority and a default name.
+ */
+ public Timer()
+ {
+ this(false);
+ }
+
+ /**
+ * Creates a new Timer with a daemon Thread as scheduler if daemon is true,
+ * with normal priority and a default name.
+ */
+ public Timer(boolean daemon)
+ {
+ this(daemon, Thread.NORM_PRIORITY);
+ }
+
+ /**
+ * Create a new Timer whose Thread has the indicated name. It will have
+ * normal priority and will not be a daemon thread.
+ * @param name the name of the Thread
+ * @since 1.5
+ */
+ public Timer(String name)
+ {
+ this(false, Thread.NORM_PRIORITY, name);
+ }
+
+ /**
+ * Create a new Timer whose Thread has the indicated name. It will have
+ * normal priority. The boolean argument controls whether or not it
+ * will be a daemon thread.
+ * @param name the name of the Thread
+ * @param daemon true if the Thread should be a daemon thread
+ * @since 1.5
+ */
+ public Timer(String name, boolean daemon)
+ {
+ this(daemon, Thread.NORM_PRIORITY, name);
+ }
+
+ /**
+ * Creates a new Timer with a daemon Thread as scheduler if daemon is true,
+ * with the priority given and a default name.
+ */
+ private Timer(boolean daemon, int priority)
+ {
+ this(daemon, priority, "Timer-" + (++nr));
+ }
+
+ /**
+ * Creates a new Timer with a daemon Thread as scheduler if daemon is true,
+ * with the priority and name given.E
+ */
+ private Timer(boolean daemon, int priority, String name)
+ {
+ canceled = false;
+ queue = new TaskQueue();
+ scheduler = new Scheduler(queue);
+ thread = new Thread(scheduler, name);
+ thread.setDaemon(daemon);
+ thread.setPriority(priority);
+ thread.start();
+ }
+
+ /**
+ * Cancels the execution of the scheduler. If a task is executing it will
+ * normally finish execution, but no other tasks will be executed and no
+ * more tasks can be scheduled.
+ */
+ public void cancel()
+ {
+ canceled = true;
+ queue.stop();
+ }
+
+ /**
+ * Schedules the task at Time time, repeating every period
+ * milliseconds if period is positive and at a fixed rate if fixed is true.
+ *
+ * @exception IllegalArgumentException if time is negative
+ * @exception IllegalStateException if the task was already scheduled or
+ * canceled or this Timer is canceled or the scheduler thread has died
+ */
+ private void schedule(TimerTask task, long time, long period, boolean fixed)
+ {
+ if (time < 0)
+ throw new IllegalArgumentException("negative time");
+
+ if (task.scheduled == 0 && task.lastExecutionTime == -1)
+ {
+ task.scheduled = time;
+ task.period = period;
+ task.fixed = fixed;
+ }
+ else
+ {
+ throw new IllegalStateException
+ ("task was already scheduled or canceled");
+ }
+
+ if (!this.canceled && this.thread != null)
+ {
+ queue.enqueue(task);
+ }
+ else
+ {
+ throw new IllegalStateException
+ ("timer was canceled or scheduler thread has died");
+ }
+ }
+
+ private static void positiveDelay(long delay)
+ {
+ if (delay < 0)
+ {
+ throw new IllegalArgumentException("delay is negative");
+ }
+ }
+
+ private static void positivePeriod(long period)
+ {
+ if (period < 0)
+ {
+ throw new IllegalArgumentException("period is negative");
+ }
+ }
+
+ /**
+ * Schedules the task at the specified data for one time execution.
+ *
+ * @exception IllegalArgumentException if date.getTime() is negative
+ * @exception IllegalStateException if the task was already scheduled or
+ * canceled or this Timer is canceled or the scheduler thread has died
+ */
+ public void schedule(TimerTask task, Date date)
+ {
+ long time = date.getTime();
+ schedule(task, time, -1, false);
+ }
+
+ /**
+ * Schedules the task at the specified date and reschedules the task every
+ * period milliseconds after the last execution of the task finishes until
+ * this timer or the task is canceled.
+ *
+ * @exception IllegalArgumentException if period or date.getTime() is
+ * negative
+ * @exception IllegalStateException if the task was already scheduled or
+ * canceled or this Timer is canceled or the scheduler thread has died
+ */
+ public void schedule(TimerTask task, Date date, long period)
+ {
+ positivePeriod(period);
+ long time = date.getTime();
+ schedule(task, time, period, false);
+ }
+
+ /**
+ * Schedules the task after the specified delay milliseconds for one time
+ * execution.
+ *
+ * @exception IllegalArgumentException if delay or
+ * System.currentTimeMillis + delay is negative
+ * @exception IllegalStateException if the task was already scheduled or
+ * canceled or this Timer is canceled or the scheduler thread has died
+ */
+ public void schedule(TimerTask task, long delay)
+ {
+ positiveDelay(delay);
+ long time = System.currentTimeMillis() + delay;
+ schedule(task, time, -1, false);
+ }
+
+ /**
+ * Schedules the task after the delay milliseconds and reschedules the
+ * task every period milliseconds after the last execution of the task
+ * finishes until this timer or the task is canceled.
+ *
+ * @exception IllegalArgumentException if delay or period is negative
+ * @exception IllegalStateException if the task was already scheduled or
+ * canceled or this Timer is canceled or the scheduler thread has died
+ */
+ public void schedule(TimerTask task, long delay, long period)
+ {
+ positiveDelay(delay);
+ positivePeriod(period);
+ long time = System.currentTimeMillis() + delay;
+ schedule(task, time, period, false);
+ }
+
+ /**
+ * Schedules the task at the specified date and reschedules the task at a
+ * fixed rate every period milliseconds until this timer or the task is
+ * canceled.
+ *
+ * @exception IllegalArgumentException if period or date.getTime() is
+ * negative
+ * @exception IllegalStateException if the task was already scheduled or
+ * canceled or this Timer is canceled or the scheduler thread has died
+ */
+ public void scheduleAtFixedRate(TimerTask task, Date date, long period)
+ {
+ positivePeriod(period);
+ long time = date.getTime();
+ schedule(task, time, period, true);
+ }
+
+ /**
+ * Schedules the task after the delay milliseconds and reschedules the task
+ * at a fixed rate every period milliseconds until this timer or the task
+ * is canceled.
+ *
+ * @exception IllegalArgumentException if delay or
+ * System.currentTimeMillis + delay is negative
+ * @exception IllegalStateException if the task was already scheduled or
+ * canceled or this Timer is canceled or the scheduler thread has died
+ */
+ public void scheduleAtFixedRate(TimerTask task, long delay, long period)
+ {
+ positiveDelay(delay);
+ positivePeriod(period);
+ long time = System.currentTimeMillis() + delay;
+ schedule(task, time, period, true);
+ }
+
+ /**
+ * Tells the scheduler that the Timer task died
+ * so there will be no more new tasks scheduled.
+ */
+ protected void finalize() throws Throwable
+ {
+ queue.setNullOnEmpty(true);
+ }
+
+ /**
+ * Removes all cancelled tasks from the queue.
+ * @return the number of tasks removed
+ * @since 1.5
+ */
+ public int purge()
+ {
+ return queue.purge();
+ }
+}
diff --git a/libjava/classpath/java/util/TimerTask.java b/libjava/classpath/java/util/TimerTask.java
new file mode 100644
index 000000000..b03118ad0
--- /dev/null
+++ b/libjava/classpath/java/util/TimerTask.java
@@ -0,0 +1,145 @@
+/* TimerTask.java -- Task that can be run at a later time if given to a Timer.
+ Copyright (C) 2000 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Task that can be run at a later time if given to a Timer.
+ * The TimerTask must implement a run method that will be called by the
+ * Timer when the task is scheduled for execution. The task can check when
+ * it should have been scheduled and cancel itself when no longer needed.
+ * <p>
+ * Example:
+ * <pre>
+ * Timer timer = new Timer();
+ * TimerTask task = new TimerTask() {
+ * public void run() {
+ * if (this.scheduledExecutionTime() &lt; System.currentTimeMillis() + 500)
+ * // Do something
+ * else
+ * // Complain: We are more then half a second late!
+ * if (someStopCondition)
+ * this.cancel(); // This was our last execution
+ * };
+ * timer.scheduleAtFixedRate(task, 1000, 1000); // schedule every second
+ * </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,
+ * in this implementation).
+ * <p>
+ * This class also implements <code>Runnable</code> to make it possible to
+ * give a TimerTask directly as a target to a <code>Thread</code>.
+ *
+ * @see Timer
+ * @since 1.3
+ * @author Mark Wielaard (mark@klomp.org)
+ */
+public abstract class TimerTask implements Runnable
+{
+ /**
+ * If positive the next time this task should be run.
+ * If negative this TimerTask is canceled or executed for the last time.
+ */
+ long scheduled;
+
+ /**
+ * If positive the last time this task was run.
+ * If negative this TimerTask has not yet been scheduled.
+ */
+ long lastExecutionTime;
+
+ /**
+ * If positive the number of milliseconds between runs of this task.
+ * If -1 this task doesn't have to be run more then once.
+ */
+ long period;
+
+ /**
+ * If true the next time this task should be run is relative to
+ * the last scheduled time, otherwise it can drift in time.
+ */
+ boolean fixed;
+
+ /**
+ * Creates a TimerTask and marks it as not yet scheduled.
+ */
+ protected TimerTask()
+ {
+ this.scheduled = 0;
+ this.lastExecutionTime = -1;
+ }
+
+ /**
+ * Marks the task as canceled and prevents any further execution.
+ * Returns true if the task was scheduled for any execution in the future
+ * and this cancel operation prevents that execution from happening.
+ * <p>
+ * A task that has been canceled can never be scheduled again.
+ * <p>
+ * In this implementation the TimerTask it is possible that the Timer does
+ * keep a reference to the TimerTask until the first time the TimerTask
+ * is actually scheduled. But the reference will disappear immediatly when
+ * cancel is called from within the TimerTask run method.
+ */
+ public boolean cancel()
+ {
+ boolean prevented_execution = (this.scheduled >= 0);
+ this.scheduled = -1;
+ return prevented_execution;
+ }
+
+ /**
+ * Method that is called when this task is scheduled for execution.
+ */
+ public abstract void run();
+
+ /**
+ * Returns the last time this task was scheduled or (when called by the
+ * task from the run method) the time the current execution of the task
+ * was scheduled. When the task has not yet run the return value is
+ * undefined.
+ * <p>
+ * Can be used (when the task is scheduled at fixed rate) to see the
+ * difference between the requested schedule time and the actual time
+ * that can be found with <code>System.currentTimeMillis()</code>.
+ */
+ public long scheduledExecutionTime()
+ {
+ return lastExecutionTime;
+ }
+}
diff --git a/libjava/classpath/java/util/TooManyListenersException.java b/libjava/classpath/java/util/TooManyListenersException.java
new file mode 100644
index 000000000..92ad772f2
--- /dev/null
+++ b/libjava/classpath/java/util/TooManyListenersException.java
@@ -0,0 +1,78 @@
+/* TooManyListenersException.java -- thrown when a unicast event can't accept
+ another Listener
+ Copyright (C) 1998, 1999, 2001, 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * This exception is part of the java event model. It is thrown if an
+ * event listener is added via the addXyzEventListener method, but the
+ * object doesn't support any more listeners, e.g. it only supports a
+ * single event listener.
+ *
+ * @author Jochen Hoenicke
+ * @author Warren Levy (warrenl@cygnus.com)
+ * @see EventListener
+ * @see EventObject
+ * @since 1.1
+ * @status updated to 1.4
+ */
+public class TooManyListenersException extends Exception
+{
+ /**
+ * Compatible with JDK 1.1+.
+ */
+ private static final long serialVersionUID = 5074640544770687831L;
+
+ /**
+ * Constructs a TooManyListenersException with no detail message.
+ */
+ public TooManyListenersException()
+ {
+ }
+
+ /**
+ * Constructs a TooManyListenersException with a detail message.
+ *
+ * @param detail the detail message
+ */
+ public TooManyListenersException(String detail)
+ {
+ super(detail);
+ }
+}
diff --git a/libjava/classpath/java/util/TreeMap.java b/libjava/classpath/java/util/TreeMap.java
new file mode 100644
index 000000000..87c532fc1
--- /dev/null
+++ b/libjava/classpath/java/util/TreeMap.java
@@ -0,0 +1,3322 @@
+/* TreeMap.java -- a class providing a basic Red-Black Tree data structure,
+ mapping Object --> Object
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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 gnu.java.lang.CPStringBuilder;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+/**
+ * This class provides a red-black tree implementation of the SortedMap
+ * interface. Elements in the Map will be sorted by either a user-provided
+ * Comparator object, or by the natural ordering of the keys.
+ *
+ * The algorithms are adopted from Corman, Leiserson, and Rivest's
+ * <i>Introduction to Algorithms.</i> TreeMap guarantees O(log n)
+ * insertion and deletion of elements. That being said, there is a large
+ * enough constant coefficient in front of that "log n" (overhead involved
+ * in keeping the tree balanced), that TreeMap may not be the best choice
+ * for small collections. If something is already sorted, you may want to
+ * just use a LinkedHashMap to maintain the order while providing O(1) access.
+ *
+ * TreeMap is a part of the JDK1.2 Collections API. Null keys are allowed
+ * only if a Comparator is used which can deal with them; natural ordering
+ * cannot cope with null. Null values are always allowed. Note that the
+ * ordering must be <i>consistent with equals</i> to correctly implement
+ * the Map interface. If this condition is violated, the map is still
+ * well-behaved, but you may have suprising results when comparing it to
+ * other maps.<p>
+ *
+ * This implementation is not synchronized. If you need to share this between
+ * multiple threads, do something like:<br>
+ * <code>SortedMap m
+ * = Collections.synchronizedSortedMap(new TreeMap(...));</code><p>
+ *
+ * The iterators are <i>fail-fast</i>, meaning that any structural
+ * modification, except for <code>remove()</code> called on the iterator
+ * itself, cause the iterator to throw a
+ * <code>ConcurrentModificationException</code> rather than exhibit
+ * non-deterministic behavior.
+ *
+ * @author Jon Zeppieri
+ * @author Bryce McKinlay
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @see Map
+ * @see HashMap
+ * @see Hashtable
+ * @see LinkedHashMap
+ * @see Comparable
+ * @see Comparator
+ * @see Collection
+ * @see Collections#synchronizedSortedMap(SortedMap)
+ * @since 1.2
+ * @status updated to 1.6
+ */
+public class TreeMap<K, V> extends AbstractMap<K, V>
+ implements NavigableMap<K, V>, Cloneable, Serializable
+{
+ // Implementation note:
+ // A red-black tree is a binary search tree with the additional properties
+ // that all paths to a leaf node visit the same number of black nodes,
+ // and no red node has red children. To avoid some null-pointer checks,
+ // we use the special node nil which is always black, has no relatives,
+ // and has key and value of null (but is not equal to a mapping of null).
+
+ /**
+ * Compatible with JDK 1.2.
+ */
+ private static final long serialVersionUID = 919286545866124006L;
+
+ /**
+ * Color status of a node. Package visible for use by nested classes.
+ */
+ static final int RED = -1,
+ BLACK = 1;
+
+ /**
+ * Sentinal node, used to avoid null checks for corner cases and make the
+ * delete rebalance code simpler. The rebalance code must never assign
+ * the parent, left, or right of nil, but may safely reassign the color
+ * to be black. This object must never be used as a key in a TreeMap, or
+ * it will break bounds checking of a SubMap.
+ */
+ static final Node nil = new Node(null, null, BLACK);
+ static
+ {
+ // Nil is self-referential, so we must initialize it after creation.
+ nil.parent = nil;
+ nil.left = nil;
+ nil.right = nil;
+ }
+
+ /**
+ * The root node of this TreeMap.
+ */
+ private transient Node root;
+
+ /**
+ * The size of this TreeMap. Package visible for use by nested classes.
+ */
+ transient int size;
+
+ /**
+ * The cache for {@link #entrySet()}.
+ */
+ private transient Set<Map.Entry<K,V>> entries;
+
+ /**
+ * The cache for {@link #descendingMap()}.
+ */
+ private transient NavigableMap<K,V> descendingMap;
+
+ /**
+ * The cache for {@link #navigableKeySet()}.
+ */
+ private transient NavigableSet<K> nKeys;
+
+ /**
+ * Counts the number of modifications this TreeMap has undergone, used
+ * by Iterators to know when to throw ConcurrentModificationExceptions.
+ * Package visible for use by nested classes.
+ */
+ transient int modCount;
+
+ /**
+ * This TreeMap's comparator, or null for natural ordering.
+ * Package visible for use by nested classes.
+ * @serial the comparator ordering this tree, or null
+ */
+ final Comparator<? super K> comparator;
+
+ /**
+ * Class to represent an entry in the tree. Holds a single key-value pair,
+ * plus pointers to parent and child nodes.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static final class Node<K, V> extends AbstractMap.SimpleEntry<K, V>
+ {
+ // All fields package visible for use by nested classes.
+ /** The color of this node. */
+ int color;
+
+ /** The left child node. */
+ Node<K, V> left = nil;
+ /** The right child node. */
+ Node<K, V> right = nil;
+ /** The parent node. */
+ Node<K, V> parent = nil;
+
+ /**
+ * Simple constructor.
+ * @param key the key
+ * @param value the value
+ */
+ Node(K key, V value, int color)
+ {
+ super(key, value);
+ this.color = color;
+ }
+ }
+
+ /**
+ * Instantiate a new TreeMap with no elements, using the keys' natural
+ * ordering to sort. All entries in the map must have a key which implements
+ * Comparable, and which are <i>mutually comparable</i>, otherwise map
+ * operations may throw a {@link ClassCastException}. Attempts to use
+ * a null key will throw a {@link NullPointerException}.
+ *
+ * @see Comparable
+ */
+ public TreeMap()
+ {
+ this((Comparator) null);
+ }
+
+ /**
+ * Instantiate a new TreeMap with no elements, using the provided comparator
+ * to sort. All entries in the map must have keys which are mutually
+ * comparable by the Comparator, otherwise map operations may throw a
+ * {@link ClassCastException}.
+ *
+ * @param c the sort order for the keys of this map, or null
+ * for the natural order
+ */
+ public TreeMap(Comparator<? super K> c)
+ {
+ comparator = c;
+ fabricateTree(0);
+ }
+
+ /**
+ * Instantiate a new TreeMap, initializing it with all of the elements in
+ * the provided Map. The elements will be sorted using the natural
+ * ordering of the keys. This algorithm runs in n*log(n) time. All entries
+ * in the map must have keys which implement Comparable and are mutually
+ * comparable, otherwise map operations may throw a
+ * {@link ClassCastException}.
+ *
+ * @param map a Map, whose entries will be put into this TreeMap
+ * @throws ClassCastException if the keys in the provided Map are not
+ * comparable
+ * @throws NullPointerException if map is null
+ * @see Comparable
+ */
+ public TreeMap(Map<? extends K, ? extends V> map)
+ {
+ this((Comparator) null);
+ putAll(map);
+ }
+
+ /**
+ * Instantiate a new TreeMap, initializing it with all of the elements in
+ * the provided SortedMap. The elements will be sorted using the same
+ * comparator as in the provided SortedMap. This runs in linear time.
+ *
+ * @param sm a SortedMap, whose entries will be put into this TreeMap
+ * @throws NullPointerException if sm is null
+ */
+ public TreeMap(SortedMap<K, ? extends V> sm)
+ {
+ this(sm.comparator());
+ int pos = sm.size();
+ Iterator itr = sm.entrySet().iterator();
+
+ fabricateTree(pos);
+ Node node = firstNode();
+
+ while (--pos >= 0)
+ {
+ Map.Entry me = (Map.Entry) itr.next();
+ node.key = me.getKey();
+ node.value = me.getValue();
+ node = successor(node);
+ }
+ }
+
+ /**
+ * Clears the Map so it has no keys. This is O(1).
+ */
+ public void clear()
+ {
+ if (size > 0)
+ {
+ modCount++;
+ root = nil;
+ size = 0;
+ }
+ }
+
+ /**
+ * Returns a shallow clone of this TreeMap. The Map itself is cloned,
+ * but its contents are not.
+ *
+ * @return the clone
+ */
+ public Object clone()
+ {
+ TreeMap copy = null;
+ try
+ {
+ copy = (TreeMap) super.clone();
+ }
+ catch (CloneNotSupportedException x)
+ {
+ }
+ copy.entries = null;
+ copy.fabricateTree(size);
+
+ Node node = firstNode();
+ Node cnode = copy.firstNode();
+
+ while (node != nil)
+ {
+ cnode.key = node.key;
+ cnode.value = node.value;
+ node = successor(node);
+ cnode = copy.successor(cnode);
+ }
+ return copy;
+ }
+
+ /**
+ * Return the comparator used to sort this map, or null if it is by
+ * natural order.
+ *
+ * @return the map's comparator
+ */
+ public Comparator<? super K> comparator()
+ {
+ return comparator;
+ }
+
+ /**
+ * Returns true if the map contains a mapping for the given key.
+ *
+ * @param key the key to look for
+ * @return true if the key has a mapping
+ * @throws ClassCastException if key is not comparable to map elements
+ * @throws NullPointerException if key is null and the comparator is not
+ * tolerant of nulls
+ */
+ public boolean containsKey(Object key)
+ {
+ return getNode((K) key) != nil;
+ }
+
+ /**
+ * Returns true if the map contains at least one mapping to the given value.
+ * This requires linear time.
+ *
+ * @param value the value to look for
+ * @return true if the value appears in a mapping
+ */
+ public boolean containsValue(Object value)
+ {
+ Node node = firstNode();
+ while (node != nil)
+ {
+ if (equals(value, node.value))
+ return true;
+ node = successor(node);
+ }
+ return false;
+ }
+
+ /**
+ * Returns a "set view" of this TreeMap's entries. The set is backed by
+ * the TreeMap, so changes in one show up in the other. The set supports
+ * element removal, but not element addition.<p>
+ *
+ * Note that the iterators for all three views, from keySet(), entrySet(),
+ * and values(), traverse the TreeMap in sorted sequence.
+ *
+ * @return a set view of the entries
+ * @see #keySet()
+ * @see #values()
+ * @see Map.Entry
+ */
+ public Set<Map.Entry<K,V>> entrySet()
+ {
+ if (entries == null)
+ // Create an AbstractSet with custom implementations of those methods
+ // that can be overriden easily and efficiently.
+ entries = new NavigableEntrySet();
+ return entries;
+ }
+
+ /**
+ * Returns the first (lowest) key in the map.
+ *
+ * @return the first key
+ * @throws NoSuchElementException if the map is empty
+ */
+ public K firstKey()
+ {
+ if (root == nil)
+ throw new NoSuchElementException();
+ return firstNode().key;
+ }
+
+ /**
+ * Return the value in this TreeMap associated with the supplied key,
+ * or <code>null</code> if the key maps to nothing. NOTE: Since the value
+ * could also be null, you must use containsKey to see if this key
+ * actually maps to something.
+ *
+ * @param key the key for which to fetch an associated value
+ * @return what the key maps to, if present
+ * @throws ClassCastException if key is not comparable to elements in the map
+ * @throws NullPointerException if key is null but the comparator does not
+ * tolerate nulls
+ * @see #put(Object, Object)
+ * @see #containsKey(Object)
+ */
+ public V get(Object key)
+ {
+ // Exploit fact that nil.value == null.
+ return getNode((K) key).value;
+ }
+
+ /**
+ * Returns a view of this Map including all entries with keys less than
+ * <code>toKey</code>. The returned map is backed by the original, so changes
+ * in one appear in the other. The submap will throw an
+ * {@link IllegalArgumentException} for any attempt to access or add an
+ * element beyond the specified cutoff. The returned map does not include
+ * the endpoint; if you want inclusion, pass the successor element
+ * or call <code>headMap(toKey, true)</code>. This is equivalent to
+ * calling <code>headMap(toKey, false)</code>.
+ *
+ * @param toKey the (exclusive) cutoff point
+ * @return a view of the map less than the cutoff
+ * @throws ClassCastException if <code>toKey</code> is not compatible with
+ * the comparator (or is not Comparable, for natural ordering)
+ * @throws NullPointerException if toKey is null, but the comparator does not
+ * tolerate null elements
+ */
+ public SortedMap<K, V> headMap(K toKey)
+ {
+ return headMap(toKey, false);
+ }
+
+ /**
+ * Returns a view of this Map including all entries with keys less than
+ * (or equal to, if <code>inclusive</code> is true) <code>toKey</code>.
+ * The returned map is backed by the original, so changes in one appear
+ * in the other. The submap will throw an {@link IllegalArgumentException}
+ * for any attempt to access or add an element beyond the specified cutoff.
+ *
+ * @param toKey the cutoff point
+ * @param inclusive true if the cutoff point should be included.
+ * @return a view of the map less than (or equal to, if <code>inclusive</code>
+ * is true) the cutoff.
+ * @throws ClassCastException if <code>toKey</code> is not compatible with
+ * the comparator (or is not Comparable, for natural ordering)
+ * @throws NullPointerException if toKey is null, but the comparator does not
+ * tolerate null elements
+ */
+ public NavigableMap<K, V> headMap(K toKey, boolean inclusive)
+ {
+ return new SubMap((K)(Object)nil, inclusive
+ ? successor(getNode(toKey)).key : toKey);
+ }
+
+ /**
+ * Returns a "set view" of this TreeMap's keys. The set is backed by the
+ * TreeMap, so changes in one show up in the other. The set supports
+ * element removal, but not element addition.
+ *
+ * @return a set view of the keys
+ * @see #values()
+ * @see #entrySet()
+ */
+ public Set<K> keySet()
+ {
+ if (keys == null)
+ // Create an AbstractSet with custom implementations of those methods
+ // that can be overriden easily and efficiently.
+ keys = new KeySet();
+ return keys;
+ }
+
+ /**
+ * Returns the last (highest) key in the map.
+ *
+ * @return the last key
+ * @throws NoSuchElementException if the map is empty
+ */
+ public K lastKey()
+ {
+ if (root == nil)
+ throw new NoSuchElementException("empty");
+ return lastNode().key;
+ }
+
+ /**
+ * Puts the supplied value into the Map, mapped by the supplied key.
+ * The value may be retrieved by any object which <code>equals()</code>
+ * this key. NOTE: Since the prior value could also be null, you must
+ * first use containsKey if you want to see if you are replacing the
+ * key's mapping.
+ *
+ * @param key the key used to locate the value
+ * @param value the value to be stored in the Map
+ * @return the prior mapping of the key, or null if there was none
+ * @throws ClassCastException if key is not comparable to current map keys
+ * @throws NullPointerException if key is null, but the comparator does
+ * not tolerate nulls
+ * @see #get(Object)
+ * @see Object#equals(Object)
+ */
+ public V put(K key, V value)
+ {
+ Node<K,V> current = root;
+ Node<K,V> parent = nil;
+ int comparison = 0;
+
+ // Find new node's parent.
+ while (current != nil)
+ {
+ parent = current;
+ comparison = compare(key, current.key);
+ if (comparison > 0)
+ current = current.right;
+ else if (comparison < 0)
+ current = current.left;
+ else // Key already in tree.
+ return current.setValue(value);
+ }
+
+ // Set up new node.
+ Node n = new Node(key, value, RED);
+ n.parent = parent;
+
+ // Insert node in tree.
+ modCount++;
+ size++;
+ if (parent == nil)
+ {
+ // Special case inserting into an empty tree.
+ root = n;
+ return null;
+ }
+ if (comparison > 0)
+ parent.right = n;
+ else
+ parent.left = n;
+
+ // Rebalance after insert.
+ insertFixup(n);
+ return null;
+ }
+
+ /**
+ * Copies all elements of the given map into this TreeMap. If this map
+ * already has a mapping for a key, the new mapping replaces the current
+ * one.
+ *
+ * @param m the map to be added
+ * @throws ClassCastException if a key in m is not comparable with keys
+ * in the map
+ * @throws NullPointerException if a key in m is null, and the comparator
+ * does not tolerate nulls
+ */
+ public void putAll(Map<? extends K, ? extends V> m)
+ {
+ Iterator itr = m.entrySet().iterator();
+ int pos = m.size();
+ while (--pos >= 0)
+ {
+ Map.Entry<K,V> e = (Map.Entry<K,V>) itr.next();
+ put(e.getKey(), e.getValue());
+ }
+ }
+
+ /**
+ * Removes from the TreeMap and returns the value which is mapped by the
+ * supplied key. If the key maps to nothing, then the TreeMap remains
+ * unchanged, and <code>null</code> is returned. NOTE: Since the value
+ * could also be null, you must use containsKey to see if you are
+ * actually removing a mapping.
+ *
+ * @param key the key used to locate the value to remove
+ * @return whatever the key mapped to, if present
+ * @throws ClassCastException if key is not comparable to current map keys
+ * @throws NullPointerException if key is null, but the comparator does
+ * not tolerate nulls
+ */
+ public V remove(Object key)
+ {
+ Node<K, V> n = getNode((K)key);
+ if (n == nil)
+ return null;
+ // Note: removeNode can alter the contents of n, so save value now.
+ V result = n.value;
+ removeNode(n);
+ return result;
+ }
+
+ /**
+ * Returns the number of key-value mappings currently in this Map.
+ *
+ * @return the size
+ */
+ public int size()
+ {
+ return size;
+ }
+
+ /**
+ * Returns a view of this Map including all entries with keys greater or
+ * equal to <code>fromKey</code> and less than <code>toKey</code> (a
+ * half-open interval). The returned map is backed by the original, so
+ * changes in one appear in the other. The submap will throw an
+ * {@link IllegalArgumentException} for any attempt to access or add an
+ * element beyond the specified cutoffs. The returned map includes the low
+ * endpoint but not the high; if you want to reverse this behavior on
+ * either end, pass in the successor element or call
+ * {@link #subMap(K,boolean,K,boolean)}. This call is equivalent to
+ * <code>subMap(fromKey, true, toKey, false)</code>.
+ *
+ * @param fromKey the (inclusive) low cutoff point
+ * @param toKey the (exclusive) high cutoff point
+ * @return a view of the map between the cutoffs
+ * @throws ClassCastException if either cutoff is not compatible with
+ * the comparator (or is not Comparable, for natural ordering)
+ * @throws NullPointerException if fromKey or toKey is null, but the
+ * comparator does not tolerate null elements
+ * @throws IllegalArgumentException if fromKey is greater than toKey
+ */
+ public SortedMap<K, V> subMap(K fromKey, K toKey)
+ {
+ return subMap(fromKey, true, toKey, false);
+ }
+
+ /**
+ * Returns a view of this Map including all entries with keys greater (or
+ * equal to, if <code>fromInclusive</code> is true) <code>fromKey</code> and
+ * less than (or equal to, if <code>toInclusive</code> is true)
+ * <code>toKey</code>. The returned map is backed by the original, so
+ * changes in one appear in the other. The submap will throw an
+ * {@link IllegalArgumentException} for any attempt to access or add an
+ * element beyond the specified cutoffs.
+ *
+ * @param fromKey the low cutoff point
+ * @param fromInclusive true if the low cutoff point should be included.
+ * @param toKey the high cutoff point
+ * @param toInclusive true if the high cutoff point should be included.
+ * @return a view of the map for the specified range.
+ * @throws ClassCastException if either cutoff is not compatible with
+ * the comparator (or is not Comparable, for natural ordering)
+ * @throws NullPointerException if fromKey or toKey is null, but the
+ * comparator does not tolerate null elements
+ * @throws IllegalArgumentException if fromKey is greater than toKey
+ */
+ public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive,
+ K toKey, boolean toInclusive)
+ {
+ return new SubMap(fromInclusive ? fromKey : successor(getNode(fromKey)).key,
+ toInclusive ? successor(getNode(toKey)).key : toKey);
+ }
+
+ /**
+ * Returns a view of this Map including all entries with keys greater or
+ * equal to <code>fromKey</code>. The returned map is backed by the
+ * original, so changes in one appear in the other. The submap will throw an
+ * {@link IllegalArgumentException} for any attempt to access or add an
+ * element beyond the specified cutoff. The returned map includes the
+ * endpoint; if you want to exclude it, pass in the successor element.
+ * This is equivalent to calling <code>tailMap(fromKey, true)</code>.
+ *
+ * @param fromKey the (inclusive) low cutoff point
+ * @return a view of the map above the cutoff
+ * @throws ClassCastException if <code>fromKey</code> is not compatible with
+ * the comparator (or is not Comparable, for natural ordering)
+ * @throws NullPointerException if fromKey is null, but the comparator
+ * does not tolerate null elements
+ */
+ public SortedMap<K, V> tailMap(K fromKey)
+ {
+ return tailMap(fromKey, true);
+ }
+
+ /**
+ * Returns a view of this Map including all entries with keys greater or
+ * equal to <code>fromKey</code>. The returned map is backed by the
+ * original, so changes in one appear in the other. The submap will throw an
+ * {@link IllegalArgumentException} for any attempt to access or add an
+ * element beyond the specified cutoff. The returned map includes the
+ * endpoint; if you want to exclude it, pass in the successor element.
+ *
+ * @param fromKey the low cutoff point
+ * @param inclusive true if the cutoff point should be included.
+ * @return a view of the map above the cutoff
+ * @throws ClassCastException if <code>fromKey</code> is not compatible with
+ * the comparator (or is not Comparable, for natural ordering)
+ * @throws NullPointerException if fromKey is null, but the comparator
+ * does not tolerate null elements
+ */
+ public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive)
+ {
+ return new SubMap(inclusive ? fromKey : successor(getNode(fromKey)).key,
+ (K)(Object)nil);
+ }
+
+ /**
+ * Returns a "collection view" (or "bag view") of this TreeMap's values.
+ * The collection is backed by the TreeMap, so changes in one show up
+ * in the other. The collection supports element removal, but not element
+ * addition.
+ *
+ * @return a bag view of the values
+ * @see #keySet()
+ * @see #entrySet()
+ */
+ public Collection<V> values()
+ {
+ if (values == null)
+ // We don't bother overriding many of the optional methods, as doing so
+ // wouldn't provide any significant performance advantage.
+ values = new AbstractCollection<V>()
+ {
+ public int size()
+ {
+ return size;
+ }
+
+ public Iterator<V> iterator()
+ {
+ return new TreeIterator(VALUES);
+ }
+
+ public void clear()
+ {
+ TreeMap.this.clear();
+ }
+ };
+ return values;
+ }
+
+ /**
+ * Compares two elements by the set comparator, or by natural ordering.
+ * Package visible for use by nested classes.
+ *
+ * @param o1 the first object
+ * @param o2 the second object
+ * @throws ClassCastException if o1 and o2 are not mutually comparable,
+ * or are not Comparable with natural ordering
+ * @throws NullPointerException if o1 or o2 is null with natural ordering
+ */
+ final int compare(K o1, K o2)
+ {
+ return (comparator == null
+ ? ((Comparable) o1).compareTo(o2)
+ : comparator.compare(o1, o2));
+ }
+
+ /**
+ * Maintain red-black balance after deleting a node.
+ *
+ * @param node the child of the node just deleted, possibly nil
+ * @param parent the parent of the node just deleted, never nil
+ */
+ private void deleteFixup(Node<K,V> node, Node<K,V> parent)
+ {
+ // if (parent == nil)
+ // throw new InternalError();
+ // If a black node has been removed, we need to rebalance to avoid
+ // violating the "same number of black nodes on any path" rule. If
+ // node is red, we can simply recolor it black and all is well.
+ while (node != root && node.color == BLACK)
+ {
+ if (node == parent.left)
+ {
+ // Rebalance left side.
+ Node<K,V> sibling = parent.right;
+ // if (sibling == nil)
+ // throw new InternalError();
+ if (sibling.color == RED)
+ {
+ // Case 1: Sibling is red.
+ // Recolor sibling and parent, and rotate parent left.
+ sibling.color = BLACK;
+ parent.color = RED;
+ rotateLeft(parent);
+ sibling = parent.right;
+ }
+
+ if (sibling.left.color == BLACK && sibling.right.color == BLACK)
+ {
+ // Case 2: Sibling has no red children.
+ // Recolor sibling, and move to parent.
+ sibling.color = RED;
+ node = parent;
+ parent = parent.parent;
+ }
+ else
+ {
+ if (sibling.right.color == BLACK)
+ {
+ // Case 3: Sibling has red left child.
+ // Recolor sibling and left child, rotate sibling right.
+ sibling.left.color = BLACK;
+ sibling.color = RED;
+ rotateRight(sibling);
+ sibling = parent.right;
+ }
+ // Case 4: Sibling has red right child. Recolor sibling,
+ // right child, and parent, and rotate parent left.
+ sibling.color = parent.color;
+ parent.color = BLACK;
+ sibling.right.color = BLACK;
+ rotateLeft(parent);
+ node = root; // Finished.
+ }
+ }
+ else
+ {
+ // Symmetric "mirror" of left-side case.
+ Node<K,V> sibling = parent.left;
+ // if (sibling == nil)
+ // throw new InternalError();
+ if (sibling.color == RED)
+ {
+ // Case 1: Sibling is red.
+ // Recolor sibling and parent, and rotate parent right.
+ sibling.color = BLACK;
+ parent.color = RED;
+ rotateRight(parent);
+ sibling = parent.left;
+ }
+
+ if (sibling.right.color == BLACK && sibling.left.color == BLACK)
+ {
+ // Case 2: Sibling has no red children.
+ // Recolor sibling, and move to parent.
+ sibling.color = RED;
+ node = parent;
+ parent = parent.parent;
+ }
+ else
+ {
+ if (sibling.left.color == BLACK)
+ {
+ // Case 3: Sibling has red right child.
+ // Recolor sibling and right child, rotate sibling left.
+ sibling.right.color = BLACK;
+ sibling.color = RED;
+ rotateLeft(sibling);
+ sibling = parent.left;
+ }
+ // Case 4: Sibling has red left child. Recolor sibling,
+ // left child, and parent, and rotate parent right.
+ sibling.color = parent.color;
+ parent.color = BLACK;
+ sibling.left.color = BLACK;
+ rotateRight(parent);
+ node = root; // Finished.
+ }
+ }
+ }
+ node.color = BLACK;
+ }
+
+ /**
+ * Construct a perfectly balanced tree consisting of n "blank" nodes. This
+ * permits a tree to be generated from pre-sorted input in linear time.
+ *
+ * @param count the number of blank nodes, non-negative
+ */
+ private void fabricateTree(final int count)
+ {
+ if (count == 0)
+ {
+ root = nil;
+ size = 0;
+ return;
+ }
+
+ // We color every row of nodes black, except for the overflow nodes.
+ // I believe that this is the optimal arrangement. We construct the tree
+ // in place by temporarily linking each node to the next node in the row,
+ // then updating those links to the children when working on the next row.
+
+ // Make the root node.
+ root = new Node(null, null, BLACK);
+ size = count;
+ Node row = root;
+ int rowsize;
+
+ // Fill each row that is completely full of nodes.
+ for (rowsize = 2; rowsize + rowsize <= count; rowsize <<= 1)
+ {
+ Node parent = row;
+ Node last = null;
+ for (int i = 0; i < rowsize; i += 2)
+ {
+ Node left = new Node(null, null, BLACK);
+ Node right = new Node(null, null, BLACK);
+ left.parent = parent;
+ left.right = right;
+ right.parent = parent;
+ parent.left = left;
+ Node next = parent.right;
+ parent.right = right;
+ parent = next;
+ if (last != null)
+ last.right = left;
+ last = right;
+ }
+ row = row.left;
+ }
+
+ // Now do the partial final row in red.
+ int overflow = count - rowsize;
+ Node parent = row;
+ int i;
+ for (i = 0; i < overflow; i += 2)
+ {
+ Node left = new Node(null, null, RED);
+ Node right = new Node(null, null, RED);
+ left.parent = parent;
+ right.parent = parent;
+ parent.left = left;
+ Node next = parent.right;
+ parent.right = right;
+ parent = next;
+ }
+ // Add a lone left node if necessary.
+ if (i - overflow == 0)
+ {
+ Node left = new Node(null, null, RED);
+ left.parent = parent;
+ parent.left = left;
+ parent = parent.right;
+ left.parent.right = nil;
+ }
+ // Unlink the remaining nodes of the previous row.
+ while (parent != nil)
+ {
+ Node next = parent.right;
+ parent.right = nil;
+ parent = next;
+ }
+ }
+
+ /**
+ * Returns the first sorted node in the map, or nil if empty. Package
+ * visible for use by nested classes.
+ *
+ * @return the first node
+ */
+ final Node<K, V> firstNode()
+ {
+ // Exploit fact that nil.left == nil.
+ Node node = root;
+ while (node.left != nil)
+ node = node.left;
+ return node;
+ }
+
+ /**
+ * Return the TreeMap.Node associated with key, or the nil node if no such
+ * node exists in the tree. Package visible for use by nested classes.
+ *
+ * @param key the key to search for
+ * @return the node where the key is found, or nil
+ */
+ final Node<K, V> getNode(K key)
+ {
+ Node<K,V> current = root;
+ while (current != nil)
+ {
+ int comparison = compare(key, current.key);
+ if (comparison > 0)
+ current = current.right;
+ else if (comparison < 0)
+ current = current.left;
+ else
+ return current;
+ }
+ return current;
+ }
+
+ /**
+ * Find the "highest" node which is &lt; key. If key is nil, return last
+ * node. Package visible for use by nested classes.
+ *
+ * @param key the upper bound, exclusive
+ * @return the previous node
+ */
+ final Node<K,V> highestLessThan(K key)
+ {
+ return highestLessThan(key, false);
+ }
+
+ /**
+ * Find the "highest" node which is &lt; (or equal to,
+ * if <code>equal</code> is true) key. If key is nil,
+ * return last node. Package visible for use by nested
+ * classes.
+ *
+ * @param key the upper bound, exclusive
+ * @param equal true if the key should be returned if found.
+ * @return the previous node
+ */
+ final Node<K,V> highestLessThan(K key, boolean equal)
+ {
+ if (key == nil)
+ return lastNode();
+
+ Node<K,V> last = nil;
+ Node<K,V> current = root;
+ int comparison = 0;
+
+ while (current != nil)
+ {
+ last = current;
+ comparison = compare(key, current.key);
+ if (comparison > 0)
+ current = current.right;
+ else if (comparison < 0)
+ current = current.left;
+ else // Exact match.
+ return (equal ? last : predecessor(last));
+ }
+ return comparison < 0 ? predecessor(last) : last;
+ }
+
+ /**
+ * Maintain red-black balance after inserting a new node.
+ *
+ * @param n the newly inserted node
+ */
+ private void insertFixup(Node<K,V> n)
+ {
+ // Only need to rebalance when parent is a RED node, and while at least
+ // 2 levels deep into the tree (ie: node has a grandparent). Remember
+ // that nil.color == BLACK.
+ while (n.parent.color == RED && n.parent.parent != nil)
+ {
+ if (n.parent == n.parent.parent.left)
+ {
+ Node uncle = n.parent.parent.right;
+ // Uncle may be nil, in which case it is BLACK.
+ if (uncle.color == RED)
+ {
+ // Case 1. Uncle is RED: Change colors of parent, uncle,
+ // and grandparent, and move n to grandparent.
+ n.parent.color = BLACK;
+ uncle.color = BLACK;
+ uncle.parent.color = RED;
+ n = uncle.parent;
+ }
+ else
+ {
+ if (n == n.parent.right)
+ {
+ // Case 2. Uncle is BLACK and x is right child.
+ // Move n to parent, and rotate n left.
+ n = n.parent;
+ rotateLeft(n);
+ }
+ // Case 3. Uncle is BLACK and x is left child.
+ // Recolor parent, grandparent, and rotate grandparent right.
+ n.parent.color = BLACK;
+ n.parent.parent.color = RED;
+ rotateRight(n.parent.parent);
+ }
+ }
+ else
+ {
+ // Mirror image of above code.
+ Node uncle = n.parent.parent.left;
+ // Uncle may be nil, in which case it is BLACK.
+ if (uncle.color == RED)
+ {
+ // Case 1. Uncle is RED: Change colors of parent, uncle,
+ // and grandparent, and move n to grandparent.
+ n.parent.color = BLACK;
+ uncle.color = BLACK;
+ uncle.parent.color = RED;
+ n = uncle.parent;
+ }
+ else
+ {
+ if (n == n.parent.left)
+ {
+ // Case 2. Uncle is BLACK and x is left child.
+ // Move n to parent, and rotate n right.
+ n = n.parent;
+ rotateRight(n);
+ }
+ // Case 3. Uncle is BLACK and x is right child.
+ // Recolor parent, grandparent, and rotate grandparent left.
+ n.parent.color = BLACK;
+ n.parent.parent.color = RED;
+ rotateLeft(n.parent.parent);
+ }
+ }
+ }
+ root.color = BLACK;
+ }
+
+ /**
+ * Returns the last sorted node in the map, or nil if empty.
+ *
+ * @return the last node
+ */
+ private Node<K,V> lastNode()
+ {
+ // Exploit fact that nil.right == nil.
+ Node node = root;
+ while (node.right != nil)
+ node = node.right;
+ return node;
+ }
+
+ /**
+ * Find the "lowest" node which is &gt;= key. If key is nil, return either
+ * nil or the first node, depending on the parameter first. Package visible
+ * for use by nested classes.
+ *
+ * @param key the lower bound, inclusive
+ * @param first true to return the first element instead of nil for nil key
+ * @return the next node
+ */
+ final Node<K,V> lowestGreaterThan(K key, boolean first)
+ {
+ return lowestGreaterThan(key, first, true);
+ }
+
+ /**
+ * Find the "lowest" node which is &gt; (or equal to, if <code>equal</code>
+ * is true) key. If key is nil, return either nil or the first node, depending
+ * on the parameter first. Package visible for use by nested classes.
+ *
+ * @param key the lower bound, inclusive
+ * @param first true to return the first element instead of nil for nil key
+ * @param equal true if the key should be returned if found.
+ * @return the next node
+ */
+ final Node<K,V> lowestGreaterThan(K key, boolean first, boolean equal)
+ {
+ if (key == nil)
+ return first ? firstNode() : nil;
+
+ Node<K,V> last = nil;
+ Node<K,V> current = root;
+ int comparison = 0;
+
+ while (current != nil)
+ {
+ last = current;
+ comparison = compare(key, current.key);
+ if (comparison > 0)
+ current = current.right;
+ else if (comparison < 0)
+ current = current.left;
+ else
+ return (equal ? current : successor(current));
+ }
+ return comparison > 0 ? successor(last) : last;
+ }
+
+ /**
+ * Return the node preceding the given one, or nil if there isn't one.
+ *
+ * @param node the current node, not nil
+ * @return the prior node in sorted order
+ */
+ private Node<K,V> predecessor(Node<K,V> node)
+ {
+ if (node.left != nil)
+ {
+ node = node.left;
+ while (node.right != nil)
+ node = node.right;
+ return node;
+ }
+
+ Node parent = node.parent;
+ // Exploit fact that nil.left == nil and node is non-nil.
+ while (node == parent.left)
+ {
+ node = parent;
+ parent = node.parent;
+ }
+ return parent;
+ }
+
+ /**
+ * Construct a tree from sorted keys in linear time. Package visible for
+ * use by TreeSet.
+ *
+ * @param s the stream to read from
+ * @param count the number of keys to read
+ * @param readValues true to read values, false to insert "" as the value
+ * @throws ClassNotFoundException if the underlying stream fails
+ * @throws IOException if the underlying stream fails
+ * @see #readObject(ObjectInputStream)
+ * @see TreeSet#readObject(ObjectInputStream)
+ */
+ final void putFromObjStream(ObjectInputStream s, int count,
+ boolean readValues)
+ throws IOException, ClassNotFoundException
+ {
+ fabricateTree(count);
+ Node node = firstNode();
+
+ while (--count >= 0)
+ {
+ node.key = s.readObject();
+ node.value = readValues ? s.readObject() : "";
+ node = successor(node);
+ }
+ }
+
+ /**
+ * Construct a tree from sorted keys in linear time, with values of "".
+ * Package visible for use by TreeSet, which uses a value type of String.
+ *
+ * @param keys the iterator over the sorted keys
+ * @param count the number of nodes to insert
+ * @see TreeSet#TreeSet(SortedSet)
+ */
+ final void putKeysLinear(Iterator<K> keys, int count)
+ {
+ fabricateTree(count);
+ Node<K,V> node = firstNode();
+
+ while (--count >= 0)
+ {
+ node.key = keys.next();
+ node.value = (V) "";
+ node = successor(node);
+ }
+ }
+
+ /**
+ * Deserializes this object from the given stream.
+ *
+ * @param s the stream to read from
+ * @throws ClassNotFoundException if the underlying stream fails
+ * @throws IOException if the underlying stream fails
+ * @serialData the <i>size</i> (int), followed by key (Object) and value
+ * (Object) pairs in sorted order
+ */
+ private void readObject(ObjectInputStream s)
+ throws IOException, ClassNotFoundException
+ {
+ s.defaultReadObject();
+ int size = s.readInt();
+ putFromObjStream(s, size, true);
+ }
+
+ /**
+ * Remove node from tree. This will increment modCount and decrement size.
+ * Node must exist in the tree. Package visible for use by nested classes.
+ *
+ * @param node the node to remove
+ */
+ final void removeNode(Node<K,V> node)
+ {
+ Node<K,V> splice;
+ Node<K,V> child;
+
+ modCount++;
+ size--;
+
+ // Find splice, the node at the position to actually remove from the tree.
+ if (node.left == nil)
+ {
+ // Node to be deleted has 0 or 1 children.
+ splice = node;
+ child = node.right;
+ }
+ else if (node.right == nil)
+ {
+ // Node to be deleted has 1 child.
+ splice = node;
+ child = node.left;
+ }
+ else
+ {
+ // Node has 2 children. Splice is node's predecessor, and we swap
+ // its contents into node.
+ splice = node.left;
+ while (splice.right != nil)
+ splice = splice.right;
+ child = splice.left;
+ node.key = splice.key;
+ node.value = splice.value;
+ }
+
+ // Unlink splice from the tree.
+ Node parent = splice.parent;
+ if (child != nil)
+ child.parent = parent;
+ if (parent == nil)
+ {
+ // Special case for 0 or 1 node remaining.
+ root = child;
+ return;
+ }
+ if (splice == parent.left)
+ parent.left = child;
+ else
+ parent.right = child;
+
+ if (splice.color == BLACK)
+ deleteFixup(child, parent);
+ }
+
+ /**
+ * Rotate node n to the left.
+ *
+ * @param node the node to rotate
+ */
+ private void rotateLeft(Node<K,V> node)
+ {
+ Node child = node.right;
+ // if (node == nil || child == nil)
+ // throw new InternalError();
+
+ // Establish node.right link.
+ node.right = child.left;
+ if (child.left != nil)
+ child.left.parent = node;
+
+ // Establish child->parent link.
+ child.parent = node.parent;
+ if (node.parent != nil)
+ {
+ if (node == node.parent.left)
+ node.parent.left = child;
+ else
+ node.parent.right = child;
+ }
+ else
+ root = child;
+
+ // Link n and child.
+ child.left = node;
+ node.parent = child;
+ }
+
+ /**
+ * Rotate node n to the right.
+ *
+ * @param node the node to rotate
+ */
+ private void rotateRight(Node<K,V> node)
+ {
+ Node child = node.left;
+ // if (node == nil || child == nil)
+ // throw new InternalError();
+
+ // Establish node.left link.
+ node.left = child.right;
+ if (child.right != nil)
+ child.right.parent = node;
+
+ // Establish child->parent link.
+ child.parent = node.parent;
+ if (node.parent != nil)
+ {
+ if (node == node.parent.right)
+ node.parent.right = child;
+ else
+ node.parent.left = child;
+ }
+ else
+ root = child;
+
+ // Link n and child.
+ child.right = node;
+ node.parent = child;
+ }
+
+ /**
+ * Return the node following the given one, or nil if there isn't one.
+ * Package visible for use by nested classes.
+ *
+ * @param node the current node, not nil
+ * @return the next node in sorted order
+ */
+ final Node<K,V> successor(Node<K,V> node)
+ {
+ if (node.right != nil)
+ {
+ node = node.right;
+ while (node.left != nil)
+ node = node.left;
+ return node;
+ }
+
+ Node<K,V> parent = node.parent;
+ // Exploit fact that nil.right == nil and node is non-nil.
+ while (node == parent.right)
+ {
+ node = parent;
+ parent = parent.parent;
+ }
+ return parent;
+ }
+
+ /**
+ * Serializes this object to the given stream.
+ *
+ * @param s the stream to write to
+ * @throws IOException if the underlying stream fails
+ * @serialData the <i>size</i> (int), followed by key (Object) and value
+ * (Object) pairs in sorted order
+ */
+ private void writeObject(ObjectOutputStream s) throws IOException
+ {
+ s.defaultWriteObject();
+
+ Node node = firstNode();
+ s.writeInt(size);
+ while (node != nil)
+ {
+ s.writeObject(node.key);
+ s.writeObject(node.value);
+ node = successor(node);
+ }
+ }
+
+ /**
+ * Iterate over TreeMap's entries. This implementation is parameterized
+ * to give a sequential view of keys, values, or entries.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private final class TreeIterator implements Iterator
+ {
+ /**
+ * The type of this Iterator: {@link #KEYS}, {@link #VALUES},
+ * or {@link #ENTRIES}.
+ */
+ private final int type;
+ /** The number of modifications to the backing Map that we know about. */
+ private int knownMod = modCount;
+ /** The last Entry returned by a next() call. */
+ private Node last;
+ /** The next entry that should be returned by next(). */
+ private Node next;
+ /**
+ * The last node visible to this iterator. This is used when iterating
+ * on a SubMap.
+ */
+ private final Node max;
+
+ /**
+ * Construct a new TreeIterator with the supplied type.
+ * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
+ */
+ TreeIterator(int type)
+ {
+ this(type, firstNode(), nil);
+ }
+
+ /**
+ * Construct a new TreeIterator with the supplied type. Iteration will
+ * be from "first" (inclusive) to "max" (exclusive).
+ *
+ * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES}
+ * @param first where to start iteration, nil for empty iterator
+ * @param max the cutoff for iteration, nil for all remaining nodes
+ */
+ TreeIterator(int type, Node first, Node max)
+ {
+ this.type = type;
+ this.next = first;
+ this.max = max;
+ }
+
+ /**
+ * Returns true if the Iterator has more elements.
+ * @return true if there are more elements
+ */
+ public boolean hasNext()
+ {
+ return next != max;
+ }
+
+ /**
+ * Returns the next element in the Iterator's sequential view.
+ * @return the next element
+ * @throws ConcurrentModificationException if the TreeMap was modified
+ * @throws NoSuchElementException if there is none
+ */
+ public Object next()
+ {
+ if (knownMod != modCount)
+ throw new ConcurrentModificationException();
+ if (next == max)
+ throw new NoSuchElementException();
+ last = next;
+ next = successor(last);
+
+ if (type == VALUES)
+ return last.value;
+ else if (type == KEYS)
+ return last.key;
+ return last;
+ }
+
+ /**
+ * Removes from the backing TreeMap the last element which was fetched
+ * with the <code>next()</code> method.
+ * @throws ConcurrentModificationException if the TreeMap was modified
+ * @throws IllegalStateException if called when there is no last element
+ */
+ public void remove()
+ {
+ if (last == null)
+ throw new IllegalStateException();
+ if (knownMod != modCount)
+ throw new ConcurrentModificationException();
+
+ removeNode(last);
+ last = null;
+ knownMod++;
+ }
+ } // class TreeIterator
+
+ /**
+ * Implementation of {@link #subMap(Object, Object)} and other map
+ * ranges. This class provides a view of a portion of the original backing
+ * map, and throws {@link IllegalArgumentException} for attempts to
+ * access beyond that range.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private final class SubMap
+ extends AbstractMap<K,V>
+ implements NavigableMap<K,V>
+ {
+ /**
+ * The lower range of this view, inclusive, or nil for unbounded.
+ * Package visible for use by nested classes.
+ */
+ final K minKey;
+
+ /**
+ * The upper range of this view, exclusive, or nil for unbounded.
+ * Package visible for use by nested classes.
+ */
+ final K maxKey;
+
+ /**
+ * The cache for {@link #entrySet()}.
+ */
+ private Set<Map.Entry<K,V>> entries;
+
+ /**
+ * The cache for {@link #descendingMap()}.
+ */
+ private NavigableMap<K,V> descendingMap;
+
+ /**
+ * The cache for {@link #navigableKeySet()}.
+ */
+ private NavigableSet<K> nKeys;
+
+ /**
+ * Create a SubMap representing the elements between minKey (inclusive)
+ * and maxKey (exclusive). If minKey is nil, SubMap has no lower bound
+ * (headMap). If maxKey is nil, the SubMap has no upper bound (tailMap).
+ *
+ * @param minKey the lower bound
+ * @param maxKey the upper bound
+ * @throws IllegalArgumentException if minKey &gt; maxKey
+ */
+ SubMap(K minKey, K maxKey)
+ {
+ if (minKey != nil && maxKey != nil && compare(minKey, maxKey) > 0)
+ throw new IllegalArgumentException("fromKey > toKey");
+ this.minKey = minKey;
+ this.maxKey = maxKey;
+ }
+
+ /**
+ * Check if "key" is in within the range bounds for this SubMap. The
+ * lower ("from") SubMap range is inclusive, and the upper ("to") bound
+ * is exclusive. Package visible for use by nested classes.
+ *
+ * @param key the key to check
+ * @return true if the key is in range
+ */
+ boolean keyInRange(K key)
+ {
+ return ((minKey == nil || compare(key, minKey) >= 0)
+ && (maxKey == nil || compare(key, maxKey) < 0));
+ }
+
+ public Entry<K,V> ceilingEntry(K key)
+ {
+ Entry<K,V> n = TreeMap.this.ceilingEntry(key);
+ if (n != null && keyInRange(n.getKey()))
+ return n;
+ return null;
+ }
+
+ public K ceilingKey(K key)
+ {
+ K found = TreeMap.this.ceilingKey(key);
+ if (keyInRange(found))
+ return found;
+ else
+ return null;
+ }
+
+ public NavigableSet<K> descendingKeySet()
+ {
+ return descendingMap().navigableKeySet();
+ }
+
+ public NavigableMap<K,V> descendingMap()
+ {
+ if (descendingMap == null)
+ descendingMap = new DescendingMap(this);
+ return descendingMap;
+ }
+
+ public void clear()
+ {
+ Node next = lowestGreaterThan(minKey, true);
+ Node max = lowestGreaterThan(maxKey, false);
+ while (next != max)
+ {
+ Node current = next;
+ next = successor(current);
+ removeNode(current);
+ }
+ }
+
+ public Comparator<? super K> comparator()
+ {
+ return comparator;
+ }
+
+ public boolean containsKey(Object key)
+ {
+ return keyInRange((K) key) && TreeMap.this.containsKey(key);
+ }
+
+ public boolean containsValue(Object value)
+ {
+ Node node = lowestGreaterThan(minKey, true);
+ Node max = lowestGreaterThan(maxKey, false);
+ while (node != max)
+ {
+ if (equals(value, node.getValue()))
+ return true;
+ node = successor(node);
+ }
+ return false;
+ }
+
+ public Set<Map.Entry<K,V>> entrySet()
+ {
+ if (entries == null)
+ // Create an AbstractSet with custom implementations of those methods
+ // that can be overriden easily and efficiently.
+ entries = new SubMap.NavigableEntrySet();
+ return entries;
+ }
+
+ public Entry<K,V> firstEntry()
+ {
+ Node<K,V> node = lowestGreaterThan(minKey, true);
+ if (node == nil || ! keyInRange(node.key))
+ return null;
+ return node;
+ }
+
+ public K firstKey()
+ {
+ Entry<K,V> e = firstEntry();
+ if (e == null)
+ throw new NoSuchElementException();
+ return e.getKey();
+ }
+
+ public Entry<K,V> floorEntry(K key)
+ {
+ Entry<K,V> n = TreeMap.this.floorEntry(key);
+ if (n != null && keyInRange(n.getKey()))
+ return n;
+ return null;
+ }
+
+ public K floorKey(K key)
+ {
+ K found = TreeMap.this.floorKey(key);
+ if (keyInRange(found))
+ return found;
+ else
+ return null;
+ }
+
+ public V get(Object key)
+ {
+ if (keyInRange((K) key))
+ return TreeMap.this.get(key);
+ return null;
+ }
+
+ public SortedMap<K,V> headMap(K toKey)
+ {
+ return headMap(toKey, false);
+ }
+
+ public NavigableMap<K,V> headMap(K toKey, boolean inclusive)
+ {
+ if (!keyInRange(toKey))
+ throw new IllegalArgumentException("Key outside submap range");
+ return new SubMap(minKey, (inclusive ?
+ successor(getNode(toKey)).key : toKey));
+ }
+
+ public Set<K> keySet()
+ {
+ if (this.keys == null)
+ // Create an AbstractSet with custom implementations of those methods
+ // that can be overriden easily and efficiently.
+ this.keys = new SubMap.KeySet();
+ return this.keys;
+ }
+
+ public Entry<K,V> higherEntry(K key)
+ {
+ Entry<K,V> n = TreeMap.this.higherEntry(key);
+ if (n != null && keyInRange(n.getKey()))
+ return n;
+ return null;
+ }
+
+ public K higherKey(K key)
+ {
+ K found = TreeMap.this.higherKey(key);
+ if (keyInRange(found))
+ return found;
+ else
+ return null;
+ }
+
+ public Entry<K,V> lastEntry()
+ {
+ return lowerEntry(maxKey);
+ }
+
+ public K lastKey()
+ {
+ Entry<K,V> e = lastEntry();
+ if (e == null)
+ throw new NoSuchElementException();
+ return e.getKey();
+ }
+
+ public Entry<K,V> lowerEntry(K key)
+ {
+ Entry<K,V> n = TreeMap.this.lowerEntry(key);
+ if (n != null && keyInRange(n.getKey()))
+ return n;
+ return null;
+ }
+
+ public K lowerKey(K key)
+ {
+ K found = TreeMap.this.lowerKey(key);
+ if (keyInRange(found))
+ return found;
+ else
+ return null;
+ }
+
+ public NavigableSet<K> navigableKeySet()
+ {
+ if (this.nKeys == null)
+ // Create an AbstractSet with custom implementations of those methods
+ // that can be overriden easily and efficiently.
+ this.nKeys = new SubMap.NavigableKeySet();
+ return this.nKeys;
+ }
+
+ public Entry<K,V> pollFirstEntry()
+ {
+ Entry<K,V> e = firstEntry();
+ if (e != null)
+ removeNode((Node<K,V>) e);
+ return e;
+ }
+
+ public Entry<K,V> pollLastEntry()
+ {
+ Entry<K,V> e = lastEntry();
+ if (e != null)
+ removeNode((Node<K,V>) e);
+ return e;
+ }
+
+ public V put(K key, V value)
+ {
+ if (! keyInRange(key))
+ throw new IllegalArgumentException("Key outside range");
+ return TreeMap.this.put(key, value);
+ }
+
+ public V remove(Object key)
+ {
+ if (keyInRange((K)key))
+ return TreeMap.this.remove(key);
+ return null;
+ }
+
+ public int size()
+ {
+ Node node = lowestGreaterThan(minKey, true);
+ Node max = lowestGreaterThan(maxKey, false);
+ int count = 0;
+ while (node != max)
+ {
+ count++;
+ node = successor(node);
+ }
+ return count;
+ }
+
+ public SortedMap<K,V> subMap(K fromKey, K toKey)
+ {
+ return subMap(fromKey, true, toKey, false);
+ }
+
+ public NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive,
+ K toKey, boolean toInclusive)
+ {
+ if (! keyInRange(fromKey) || ! keyInRange(toKey))
+ throw new IllegalArgumentException("key outside range");
+ return new SubMap(fromInclusive ? fromKey : successor(getNode(fromKey)).key,
+ toInclusive ? successor(getNode(toKey)).key : toKey);
+ }
+
+ public SortedMap<K, V> tailMap(K fromKey)
+ {
+ return tailMap(fromKey, true);
+ }
+
+ public NavigableMap<K,V> tailMap(K fromKey, boolean inclusive)
+ {
+ if (! keyInRange(fromKey))
+ throw new IllegalArgumentException("key outside range");
+ return new SubMap(inclusive ? fromKey : successor(getNode(fromKey)).key,
+ maxKey);
+ }
+
+ public Collection<V> values()
+ {
+ if (this.values == null)
+ // Create an AbstractCollection with custom implementations of those
+ // methods that can be overriden easily and efficiently.
+ this.values = new AbstractCollection()
+ {
+ public int size()
+ {
+ return SubMap.this.size();
+ }
+
+ public Iterator<V> iterator()
+ {
+ Node first = lowestGreaterThan(minKey, true);
+ Node max = lowestGreaterThan(maxKey, false);
+ return new TreeIterator(VALUES, first, max);
+ }
+
+ public void clear()
+ {
+ SubMap.this.clear();
+ }
+ };
+ return this.values;
+ }
+
+ private class KeySet
+ extends AbstractSet<K>
+ {
+ public int size()
+ {
+ return SubMap.this.size();
+ }
+
+ public Iterator<K> iterator()
+ {
+ Node first = lowestGreaterThan(minKey, true);
+ Node max = lowestGreaterThan(maxKey, false);
+ return new TreeIterator(KEYS, first, max);
+ }
+
+ public void clear()
+ {
+ SubMap.this.clear();
+ }
+
+ public boolean contains(Object o)
+ {
+ if (! keyInRange((K) o))
+ return false;
+ return getNode((K) o) != nil;
+ }
+
+ public boolean remove(Object o)
+ {
+ if (! keyInRange((K) o))
+ return false;
+ Node n = getNode((K) o);
+ if (n != nil)
+ {
+ removeNode(n);
+ return true;
+ }
+ return false;
+ }
+
+ } // class SubMap.KeySet
+
+ private final class NavigableKeySet
+ extends KeySet
+ implements NavigableSet<K>
+ {
+
+ public K ceiling(K k)
+ {
+ return SubMap.this.ceilingKey(k);
+ }
+
+ public Comparator<? super K> comparator()
+ {
+ return comparator;
+ }
+
+ public Iterator<K> descendingIterator()
+ {
+ return descendingSet().iterator();
+ }
+
+ public NavigableSet<K> descendingSet()
+ {
+ return new DescendingSet(this);
+ }
+
+ public K first()
+ {
+ return SubMap.this.firstKey();
+ }
+
+ public K floor(K k)
+ {
+ return SubMap.this.floorKey(k);
+ }
+
+ public SortedSet<K> headSet(K to)
+ {
+ return headSet(to, false);
+ }
+
+ public NavigableSet<K> headSet(K to, boolean inclusive)
+ {
+ return SubMap.this.headMap(to, inclusive).navigableKeySet();
+ }
+
+ public K higher(K k)
+ {
+ return SubMap.this.higherKey(k);
+ }
+
+ public K last()
+ {
+ return SubMap.this.lastKey();
+ }
+
+ public K lower(K k)
+ {
+ return SubMap.this.lowerKey(k);
+ }
+
+ public K pollFirst()
+ {
+ return SubMap.this.pollFirstEntry().getKey();
+ }
+
+ public K pollLast()
+ {
+ return SubMap.this.pollLastEntry().getKey();
+ }
+
+ public SortedSet<K> subSet(K from, K to)
+ {
+ return subSet(from, true, to, false);
+ }
+
+ public NavigableSet<K> subSet(K from, boolean fromInclusive,
+ K to, boolean toInclusive)
+ {
+ return SubMap.this.subMap(from, fromInclusive,
+ to, toInclusive).navigableKeySet();
+ }
+
+ public SortedSet<K> tailSet(K from)
+ {
+ return tailSet(from, true);
+ }
+
+ public NavigableSet<K> tailSet(K from, boolean inclusive)
+ {
+ return SubMap.this.tailMap(from, inclusive).navigableKeySet();
+ }
+
+ } // class SubMap.NavigableKeySet
+
+ /**
+ * Implementation of {@link #entrySet()}.
+ */
+ private class EntrySet
+ extends AbstractSet<Entry<K,V>>
+ {
+
+ public int size()
+ {
+ return SubMap.this.size();
+ }
+
+ public Iterator<Map.Entry<K,V>> iterator()
+ {
+ Node first = lowestGreaterThan(minKey, true);
+ Node max = lowestGreaterThan(maxKey, false);
+ return new TreeIterator(ENTRIES, first, max);
+ }
+
+ public void clear()
+ {
+ SubMap.this.clear();
+ }
+
+ public boolean contains(Object o)
+ {
+ if (! (o instanceof Map.Entry))
+ return false;
+ Map.Entry<K,V> me = (Map.Entry<K,V>) o;
+ K key = me.getKey();
+ if (! keyInRange(key))
+ return false;
+ Node<K,V> n = getNode(key);
+ return n != nil && AbstractSet.equals(me.getValue(), n.value);
+ }
+
+ public boolean remove(Object o)
+ {
+ if (! (o instanceof Map.Entry))
+ return false;
+ Map.Entry<K,V> me = (Map.Entry<K,V>) o;
+ K key = me.getKey();
+ if (! keyInRange(key))
+ return false;
+ Node<K,V> n = getNode(key);
+ if (n != nil && AbstractSet.equals(me.getValue(), n.value))
+ {
+ removeNode(n);
+ return true;
+ }
+ return false;
+ }
+ } // class SubMap.EntrySet
+
+ private final class NavigableEntrySet
+ extends EntrySet
+ implements NavigableSet<Entry<K,V>>
+ {
+
+ public Entry<K,V> ceiling(Entry<K,V> e)
+ {
+ return SubMap.this.ceilingEntry(e.getKey());
+ }
+
+ public Comparator<? super Entry<K,V>> comparator()
+ {
+ return new Comparator<Entry<K,V>>()
+ {
+ public int compare(Entry<K,V> t1, Entry<K,V> t2)
+ {
+ return comparator.compare(t1.getKey(), t2.getKey());
+ }
+ };
+ }
+
+ public Iterator<Entry<K,V>> descendingIterator()
+ {
+ return descendingSet().iterator();
+ }
+
+ public NavigableSet<Entry<K,V>> descendingSet()
+ {
+ return new DescendingSet(this);
+ }
+
+ public Entry<K,V> first()
+ {
+ return SubMap.this.firstEntry();
+ }
+
+ public Entry<K,V> floor(Entry<K,V> e)
+ {
+ return SubMap.this.floorEntry(e.getKey());
+ }
+
+ public SortedSet<Entry<K,V>> headSet(Entry<K,V> to)
+ {
+ return headSet(to, false);
+ }
+
+ public NavigableSet<Entry<K,V>> headSet(Entry<K,V> to, boolean inclusive)
+ {
+ return (NavigableSet<Entry<K,V>>)
+ SubMap.this.headMap(to.getKey(), inclusive).entrySet();
+ }
+
+ public Entry<K,V> higher(Entry<K,V> e)
+ {
+ return SubMap.this.higherEntry(e.getKey());
+ }
+
+ public Entry<K,V> last()
+ {
+ return SubMap.this.lastEntry();
+ }
+
+ public Entry<K,V> lower(Entry<K,V> e)
+ {
+ return SubMap.this.lowerEntry(e.getKey());
+ }
+
+ public Entry<K,V> pollFirst()
+ {
+ return SubMap.this.pollFirstEntry();
+ }
+
+ public Entry<K,V> pollLast()
+ {
+ return SubMap.this.pollLastEntry();
+ }
+
+ public SortedSet<Entry<K,V>> subSet(Entry<K,V> from, Entry<K,V> to)
+ {
+ return subSet(from, true, to, false);
+ }
+
+ public NavigableSet<Entry<K,V>> subSet(Entry<K,V> from, boolean fromInclusive,
+ Entry<K,V> to, boolean toInclusive)
+ {
+ return (NavigableSet<Entry<K,V>>)
+ SubMap.this.subMap(from.getKey(), fromInclusive,
+ to.getKey(), toInclusive).entrySet();
+ }
+
+ public SortedSet<Entry<K,V>> tailSet(Entry<K,V> from)
+ {
+ return tailSet(from, true);
+ }
+
+ public NavigableSet<Entry<K,V>> tailSet(Entry<K,V> from, boolean inclusive)
+ {
+ return (NavigableSet<Entry<K,V>>)
+ SubMap.this.tailMap(from.getKey(), inclusive).navigableKeySet();
+ }
+
+ } // class SubMap.NavigableEntrySet
+
+} // class SubMap
+
+ /**
+ * Returns the entry associated with the least or lowest key
+ * that is greater than or equal to the specified key, or
+ * <code>null</code> if there is no such key.
+ *
+ * @param key the key relative to the returned entry.
+ * @return the entry with the least key greater than or equal
+ * to the given key, or <code>null</code> if there is
+ * no such key.
+ * @throws ClassCastException if the specified key can not
+ * be compared with those in the map.
+ * @throws NullPointerException if the key is <code>null</code>
+ * and this map either uses natural
+ * ordering or a comparator that does
+ * not permit null keys.
+ * @since 1.6
+ */
+ public Entry<K,V> ceilingEntry(K key)
+ {
+ Node<K,V> n = lowestGreaterThan(key, false);
+ return (n == nil) ? null : n;
+ }
+
+ /**
+ * Returns the the least or lowest key that is greater than
+ * or equal to the specified key, or <code>null</code> if
+ * there is no such key.
+ *
+ * @param key the key relative to the returned entry.
+ * @return the least key greater than or equal to the given key,
+ * or <code>null</code> if there is no such key.
+ * @throws ClassCastException if the specified key can not
+ * be compared with those in the map.
+ * @throws NullPointerException if the key is <code>null</code>
+ * and this map either uses natural
+ * ordering or a comparator that does
+ * not permit null keys.
+ * @since 1.6
+ */
+ public K ceilingKey(K key)
+ {
+ Entry<K,V> e = ceilingEntry(key);
+ return (e == null) ? null : e.getKey();
+ }
+
+ /**
+ * Returns a reverse ordered {@link NavigableSet} view of this
+ * map's keys. The set is backed by the {@link TreeMap}, so changes
+ * in one show up in the other. The set supports element removal,
+ * but not element addition.
+ *
+ * @return a reverse ordered set view of the keys.
+ * @since 1.6
+ * @see #descendingMap()
+ */
+ public NavigableSet<K> descendingKeySet()
+ {
+ return descendingMap().navigableKeySet();
+ }
+
+ /**
+ * Returns a view of the map in reverse order. The descending map
+ * is backed by the original map, so that changes affect both maps.
+ * Any changes occurring to either map while an iteration is taking
+ * place (with the exception of a {@link Iterator#remove()} operation)
+ * result in undefined behaviour from the iteration. The ordering
+ * of the descending map is the same as for a map with a
+ * {@link Comparator} given by {@link Collections#reverseOrder()},
+ * and calling {@link #descendingMap()} on the descending map itself
+ * results in a view equivalent to the original map.
+ *
+ * @return a reverse order view of the map.
+ * @since 1.6
+ */
+ public NavigableMap<K,V> descendingMap()
+ {
+ if (descendingMap == null)
+ descendingMap = new DescendingMap<K,V>(this);
+ return descendingMap;
+ }
+
+ /**
+ * Returns the entry associated with the least or lowest key
+ * in the map, or <code>null</code> if the map is empty.
+ *
+ * @return the lowest entry, or <code>null</code> if the map
+ * is empty.
+ * @since 1.6
+ */
+ public Entry<K,V> firstEntry()
+ {
+ Node<K,V> n = firstNode();
+ return (n == nil) ? null : n;
+ }
+
+ /**
+ * Returns the entry associated with the greatest or highest key
+ * that is less than or equal to the specified key, or
+ * <code>null</code> if there is no such key.
+ *
+ * @param key the key relative to the returned entry.
+ * @return the entry with the greatest key less than or equal
+ * to the given key, or <code>null</code> if there is
+ * no such key.
+ * @throws ClassCastException if the specified key can not
+ * be compared with those in the map.
+ * @throws NullPointerException if the key is <code>null</code>
+ * and this map either uses natural
+ * ordering or a comparator that does
+ * not permit null keys.
+ * @since 1.6
+ */
+ public Entry<K,V> floorEntry(K key)
+ {
+ Node<K,V> n = highestLessThan(key, true);
+ return (n == nil) ? null : n;
+ }
+
+ /**
+ * Returns the the greatest or highest key that is less than
+ * or equal to the specified key, or <code>null</code> if
+ * there is no such key.
+ *
+ * @param key the key relative to the returned entry.
+ * @return the greatest key less than or equal to the given key,
+ * or <code>null</code> if there is no such key.
+ * @throws ClassCastException if the specified key can not
+ * be compared with those in the map.
+ * @throws NullPointerException if the key is <code>null</code>
+ * and this map either uses natural
+ * ordering or a comparator that does
+ * not permit null keys.
+ * @since 1.6
+ */
+ public K floorKey(K key)
+ {
+ Entry<K,V> e = floorEntry(key);
+ return (e == null) ? null : e.getKey();
+ }
+
+ /**
+ * Returns the entry associated with the least or lowest key
+ * that is strictly greater than the specified key, or
+ * <code>null</code> if there is no such key.
+ *
+ * @param key the key relative to the returned entry.
+ * @return the entry with the least key greater than
+ * the given key, or <code>null</code> if there is
+ * no such key.
+ * @throws ClassCastException if the specified key can not
+ * be compared with those in the map.
+ * @throws NullPointerException if the key is <code>null</code>
+ * and this map either uses natural
+ * ordering or a comparator that does
+ * not permit null keys.
+ * @since 1.6
+ */
+ public Entry<K,V> higherEntry(K key)
+ {
+ Node<K,V> n = lowestGreaterThan(key, false, false);
+ return (n == nil) ? null : n;
+ }
+
+ /**
+ * Returns the the least or lowest key that is strictly
+ * greater than the specified key, or <code>null</code> if
+ * there is no such key.
+ *
+ * @param key the key relative to the returned entry.
+ * @return the least key greater than the given key,
+ * or <code>null</code> if there is no such key.
+ * @throws ClassCastException if the specified key can not
+ * be compared with those in the map.
+ * @throws NullPointerException if the key is <code>null</code>
+ * and this map either uses natural
+ * ordering or a comparator that does
+ * not permit null keys.
+ * @since 1.6
+ */
+ public K higherKey(K key)
+ {
+ Entry<K,V> e = higherEntry(key);
+ return (e == null) ? null : e.getKey();
+ }
+
+ /**
+ * Returns the entry associated with the greatest or highest key
+ * in the map, or <code>null</code> if the map is empty.
+ *
+ * @return the highest entry, or <code>null</code> if the map
+ * is empty.
+ * @since 1.6
+ */
+ public Entry<K,V> lastEntry()
+ {
+ Node<K,V> n = lastNode();
+ return (n == nil) ? null : n;
+ }
+
+ /**
+ * Returns the entry associated with the greatest or highest key
+ * that is strictly less than the specified key, or
+ * <code>null</code> if there is no such key.
+ *
+ * @param key the key relative to the returned entry.
+ * @return the entry with the greatest key less than
+ * the given key, or <code>null</code> if there is
+ * no such key.
+ * @throws ClassCastException if the specified key can not
+ * be compared with those in the map.
+ * @throws NullPointerException if the key is <code>null</code>
+ * and this map either uses natural
+ * ordering or a comparator that does
+ * not permit null keys.
+ * @since 1.6
+ */
+ public Entry<K,V> lowerEntry(K key)
+ {
+ Node<K,V> n = highestLessThan(key);
+ return (n == nil) ? null : n;
+ }
+
+ /**
+ * Returns the the greatest or highest key that is strictly
+ * less than the specified key, or <code>null</code> if
+ * there is no such key.
+ *
+ * @param key the key relative to the returned entry.
+ * @return the greatest key less than the given key,
+ * or <code>null</code> if there is no such key.
+ * @throws ClassCastException if the specified key can not
+ * be compared with those in the map.
+ * @throws NullPointerException if the key is <code>null</code>
+ * and this map either uses natural
+ * ordering or a comparator that does
+ * not permit null keys.
+ * @since 1.6
+ */
+ public K lowerKey(K key)
+ {
+ Entry<K,V> e = lowerEntry(key);
+ return (e == null) ? null : e.getKey();
+ }
+
+ /**
+ * Returns a {@link NavigableSet} view of this map's keys. The set is
+ * backed by the {@link TreeMap}, so changes in one show up in the other.
+ * Any changes occurring to either while an iteration is taking
+ * place (with the exception of a {@link Iterator#remove()} operation)
+ * result in undefined behaviour from the iteration. The ordering
+ * The set supports element removal, but not element addition.
+ *
+ * @return a {@link NavigableSet} view of the keys.
+ * @since 1.6
+ */
+ public NavigableSet<K> navigableKeySet()
+ {
+ if (nKeys == null)
+ nKeys = new NavigableKeySet();
+ return nKeys;
+ }
+
+ /**
+ * Removes and returns the entry associated with the least
+ * or lowest key in the map, or <code>null</code> if the map
+ * is empty.
+ *
+ * @return the removed first entry, or <code>null</code> if the
+ * map is empty.
+ * @since 1.6
+ */
+ public Entry<K,V> pollFirstEntry()
+ {
+ Entry<K,V> e = firstEntry();
+ if (e != null)
+ removeNode((Node<K,V>)e);
+ return e;
+ }
+
+ /**
+ * Removes and returns the entry associated with the greatest
+ * or highest key in the map, or <code>null</code> if the map
+ * is empty.
+ *
+ * @return the removed last entry, or <code>null</code> if the
+ * map is empty.
+ * @since 1.6
+ */
+ public Entry<K,V> pollLastEntry()
+ {
+ Entry<K,V> e = lastEntry();
+ if (e != null)
+ removeNode((Node<K,V>)e);
+ return e;
+ }
+
+ /**
+ * Implementation of {@link #descendingMap()} and associated
+ * derivatives. This class provides a view of the
+ * original backing map in reverse order, and throws
+ * {@link IllegalArgumentException} for attempts to
+ * access beyond that range.
+ *
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ */
+ private static final class DescendingMap<DK,DV>
+ implements NavigableMap<DK,DV>
+ {
+
+ /**
+ * The cache for {@link #entrySet()}.
+ */
+ private Set<Map.Entry<DK,DV>> entries;
+
+ /**
+ * The cache for {@link #keySet()}.
+ */
+ private Set<DK> keys;
+
+ /**
+ * The cache for {@link #navigableKeySet()}.
+ */
+ private NavigableSet<DK> nKeys;
+
+ /**
+ * The cache for {@link #values()}.
+ */
+ private Collection<DV> values;
+
+ /**
+ * The backing {@link NavigableMap}.
+ */
+ private NavigableMap<DK,DV> map;
+
+ /**
+ * Create a {@link DescendingMap} around the specified
+ * map.
+ *
+ * @param map the map to wrap.
+ */
+ public DescendingMap(NavigableMap<DK,DV> map)
+ {
+ this.map = map;
+ }
+
+ public Map.Entry<DK,DV> ceilingEntry(DK key)
+ {
+ return map.floorEntry(key);
+ }
+
+ public DK ceilingKey(DK key)
+ {
+ return map.floorKey(key);
+ }
+
+ public void clear()
+ {
+ map.clear();
+ }
+
+ public Comparator<? super DK> comparator()
+ {
+ return Collections.reverseOrder(map.comparator());
+ }
+
+ public boolean containsKey(Object o)
+ {
+ return map.containsKey(o);
+ }
+
+ public boolean containsValue(Object o)
+ {
+ return map.containsValue(o);
+ }
+
+ public NavigableSet<DK> descendingKeySet()
+ {
+ return descendingMap().navigableKeySet();
+ }
+
+ public NavigableMap<DK,DV> descendingMap()
+ {
+ return map;
+ }
+
+ public Set<Entry<DK,DV>> entrySet()
+ {
+ if (entries == null)
+ entries =
+ new DescendingSet<Entry<DK,DV>>((NavigableSet<Entry<DK,DV>>)
+ map.entrySet());
+ return entries;
+ }
+
+ public boolean equals(Object o)
+ {
+ return map.equals(o);
+ }
+
+ public Entry<DK,DV> firstEntry()
+ {
+ return map.lastEntry();
+ }
+
+ public DK firstKey()
+ {
+ return map.lastKey();
+ }
+
+ public Entry<DK,DV> floorEntry(DK key)
+ {
+ return map.ceilingEntry(key);
+ }
+
+ public DK floorKey(DK key)
+ {
+ return map.ceilingKey(key);
+ }
+
+ public DV get(Object key)
+ {
+ return map.get(key);
+ }
+
+ public int hashCode()
+ {
+ return map.hashCode();
+ }
+
+ public SortedMap<DK,DV> headMap(DK toKey)
+ {
+ return headMap(toKey, false);
+ }
+
+ public NavigableMap<DK,DV> headMap(DK toKey, boolean inclusive)
+ {
+ return new DescendingMap(map.tailMap(toKey, inclusive));
+ }
+
+ public Entry<DK,DV> higherEntry(DK key)
+ {
+ return map.lowerEntry(key);
+ }
+
+ public DK higherKey(DK key)
+ {
+ return map.lowerKey(key);
+ }
+
+ public Set<DK> keySet()
+ {
+ if (keys == null)
+ keys = new DescendingSet<DK>(map.navigableKeySet());
+ return keys;
+ }
+
+ public boolean isEmpty()
+ {
+ return map.isEmpty();
+ }
+
+ public Entry<DK,DV> lastEntry()
+ {
+ return map.firstEntry();
+ }
+
+ public DK lastKey()
+ {
+ return map.firstKey();
+ }
+
+ public Entry<DK,DV> lowerEntry(DK key)
+ {
+ return map.higherEntry(key);
+ }
+
+ public DK lowerKey(DK key)
+ {
+ return map.higherKey(key);
+ }
+
+ public NavigableSet<DK> navigableKeySet()
+ {
+ if (nKeys == null)
+ nKeys = new DescendingSet<DK>(map.navigableKeySet());
+ return nKeys;
+ }
+
+ public Entry<DK,DV> pollFirstEntry()
+ {
+ return pollLastEntry();
+ }
+
+ public Entry<DK,DV> pollLastEntry()
+ {
+ return pollFirstEntry();
+ }
+
+ public DV put(DK key, DV value)
+ {
+ return map.put(key, value);
+ }
+
+ public void putAll(Map<? extends DK, ? extends DV> m)
+ {
+ map.putAll(m);
+ }
+
+ public DV remove(Object key)
+ {
+ return map.remove(key);
+ }
+
+ public int size()
+ {
+ return map.size();
+ }
+
+ public SortedMap<DK,DV> subMap(DK fromKey, DK toKey)
+ {
+ return subMap(fromKey, true, toKey, false);
+ }
+
+ public NavigableMap<DK,DV> subMap(DK fromKey, boolean fromInclusive,
+ DK toKey, boolean toInclusive)
+ {
+ return new DescendingMap(map.subMap(fromKey, fromInclusive,
+ toKey, toInclusive));
+ }
+
+ public SortedMap<DK,DV> tailMap(DK fromKey)
+ {
+ return tailMap(fromKey, true);
+ }
+
+ public NavigableMap<DK,DV> tailMap(DK fromKey, boolean inclusive)
+ {
+ return new DescendingMap(map.headMap(fromKey, inclusive));
+ }
+
+ public String toString()
+ {
+ CPStringBuilder r = new CPStringBuilder("{");
+ final Iterator<Entry<DK,DV>> it = entrySet().iterator();
+ while (it.hasNext())
+ {
+ final Entry<DK,DV> e = it.next();
+ r.append(e.getKey());
+ r.append('=');
+ r.append(e.getValue());
+ r.append(", ");
+ }
+ r.replace(r.length() - 2, r.length(), "}");
+ return r.toString();
+ }
+
+ public Collection<DV> values()
+ {
+ if (values == null)
+ // Create an AbstractCollection with custom implementations of those
+ // methods that can be overriden easily and efficiently.
+ values = new AbstractCollection()
+ {
+ public int size()
+ {
+ return DescendingMap.this.size();
+ }
+
+ public Iterator<DV> iterator()
+ {
+ return new Iterator<DV>()
+ {
+ /** The last Entry returned by a next() call. */
+ private Entry<DK,DV> last;
+
+ /** The next entry that should be returned by next(). */
+ private Entry<DK,DV> next = firstEntry();
+
+ public boolean hasNext()
+ {
+ return next != null;
+ }
+
+ public DV next()
+ {
+ if (next == null)
+ throw new NoSuchElementException();
+ last = next;
+ next = higherEntry(last.getKey());
+
+ return last.getValue();
+ }
+
+ public void remove()
+ {
+ if (last == null)
+ throw new IllegalStateException();
+
+ DescendingMap.this.remove(last.getKey());
+ last = null;
+ }
+ };
+ }
+
+ public void clear()
+ {
+ DescendingMap.this.clear();
+ }
+ };
+ return values;
+ }
+
+ } // class DescendingMap
+
+ /**
+ * Implementation of {@link #keySet()}.
+ */
+ private class KeySet
+ extends AbstractSet<K>
+ {
+
+ public int size()
+ {
+ return size;
+ }
+
+ public Iterator<K> iterator()
+ {
+ return new TreeIterator(KEYS);
+ }
+
+ public void clear()
+ {
+ TreeMap.this.clear();
+ }
+
+ public boolean contains(Object o)
+ {
+ return containsKey(o);
+ }
+
+ public boolean remove(Object key)
+ {
+ Node<K,V> n = getNode((K) key);
+ if (n == nil)
+ return false;
+ removeNode(n);
+ return true;
+ }
+ } // class KeySet
+
+ /**
+ * Implementation of {@link #navigableKeySet()}.
+ *
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ */
+ private final class NavigableKeySet
+ extends KeySet
+ implements NavigableSet<K>
+ {
+
+ public K ceiling(K k)
+ {
+ return ceilingKey(k);
+ }
+
+ public Comparator<? super K> comparator()
+ {
+ return comparator;
+ }
+
+ public Iterator<K> descendingIterator()
+ {
+ return descendingSet().iterator();
+ }
+
+ public NavigableSet<K> descendingSet()
+ {
+ return new DescendingSet<K>(this);
+ }
+
+ public K first()
+ {
+ return firstKey();
+ }
+
+ public K floor(K k)
+ {
+ return floorKey(k);
+ }
+
+ public SortedSet<K> headSet(K to)
+ {
+ return headSet(to, false);
+ }
+
+ public NavigableSet<K> headSet(K to, boolean inclusive)
+ {
+ return headMap(to, inclusive).navigableKeySet();
+ }
+
+ public K higher(K k)
+ {
+ return higherKey(k);
+ }
+
+ public K last()
+ {
+ return lastKey();
+ }
+
+ public K lower(K k)
+ {
+ return lowerKey(k);
+ }
+
+ public K pollFirst()
+ {
+ return pollFirstEntry().getKey();
+ }
+
+ public K pollLast()
+ {
+ return pollLastEntry().getKey();
+ }
+
+ public SortedSet<K> subSet(K from, K to)
+ {
+ return subSet(from, true, to, false);
+ }
+
+ public NavigableSet<K> subSet(K from, boolean fromInclusive,
+ K to, boolean toInclusive)
+ {
+ return subMap(from, fromInclusive,
+ to, toInclusive).navigableKeySet();
+ }
+
+ public SortedSet<K> tailSet(K from)
+ {
+ return tailSet(from, true);
+ }
+
+ public NavigableSet<K> tailSet(K from, boolean inclusive)
+ {
+ return tailMap(from, inclusive).navigableKeySet();
+ }
+
+
+ } // class NavigableKeySet
+
+ /**
+ * Implementation of {@link #descendingSet()} and associated
+ * derivatives. This class provides a view of the
+ * original backing set in reverse order, and throws
+ * {@link IllegalArgumentException} for attempts to
+ * access beyond that range.
+ *
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ */
+ private static final class DescendingSet<D>
+ implements NavigableSet<D>
+ {
+
+ /**
+ * The backing {@link NavigableSet}.
+ */
+ private NavigableSet<D> set;
+
+ /**
+ * Create a {@link DescendingSet} around the specified
+ * set.
+ *
+ * @param map the set to wrap.
+ */
+ public DescendingSet(NavigableSet<D> set)
+ {
+ this.set = set;
+ }
+
+ public boolean add(D e)
+ {
+ return set.add(e);
+ }
+
+ public boolean addAll(Collection<? extends D> c)
+ {
+ return set.addAll(c);
+ }
+
+ public D ceiling(D e)
+ {
+ return set.floor(e);
+ }
+
+ public void clear()
+ {
+ set.clear();
+ }
+
+ public Comparator<? super D> comparator()
+ {
+ return Collections.reverseOrder(set.comparator());
+ }
+
+ public boolean contains(Object o)
+ {
+ return set.contains(o);
+ }
+
+ public boolean containsAll(Collection<?> c)
+ {
+ return set.containsAll(c);
+ }
+
+ public Iterator<D> descendingIterator()
+ {
+ return descendingSet().iterator();
+ }
+
+ public NavigableSet<D> descendingSet()
+ {
+ return set;
+ }
+
+ public boolean equals(Object o)
+ {
+ return set.equals(o);
+ }
+
+ public D first()
+ {
+ return set.last();
+ }
+
+ public D floor(D e)
+ {
+ return set.ceiling(e);
+ }
+
+ public int hashCode()
+ {
+ return set.hashCode();
+ }
+
+ public SortedSet<D> headSet(D to)
+ {
+ return headSet(to, false);
+ }
+
+ public NavigableSet<D> headSet(D to, boolean inclusive)
+ {
+ return new DescendingSet(set.tailSet(to, inclusive));
+ }
+
+ public D higher(D e)
+ {
+ return set.lower(e);
+ }
+
+ public boolean isEmpty()
+ {
+ return set.isEmpty();
+ }
+
+ public Iterator<D> iterator()
+ {
+ return new Iterator<D>()
+ {
+
+ /** The last element returned by a next() call. */
+ private D last;
+
+ /** The next element that should be returned by next(). */
+ private D next = first();
+
+ public boolean hasNext()
+ {
+ return next != null;
+ }
+
+ public D next()
+ {
+ if (next == null)
+ throw new NoSuchElementException();
+ last = next;
+ next = higher(last);
+
+ return last;
+ }
+
+ public void remove()
+ {
+ if (last == null)
+ throw new IllegalStateException();
+
+ DescendingSet.this.remove(last);
+ last = null;
+ }
+ };
+ }
+
+ public D last()
+ {
+ return set.first();
+ }
+
+ public D lower(D e)
+ {
+ return set.higher(e);
+ }
+
+ public D pollFirst()
+ {
+ return set.pollLast();
+ }
+
+ public D pollLast()
+ {
+ return set.pollFirst();
+ }
+
+ public boolean remove(Object o)
+ {
+ return set.remove(o);
+ }
+
+ public boolean removeAll(Collection<?> c)
+ {
+ return set.removeAll(c);
+ }
+
+ public boolean retainAll(Collection<?> c)
+ {
+ return set.retainAll(c);
+ }
+
+ public int size()
+ {
+ return set.size();
+ }
+
+ public SortedSet<D> subSet(D from, D to)
+ {
+ return subSet(from, true, to, false);
+ }
+
+ public NavigableSet<D> subSet(D from, boolean fromInclusive,
+ D to, boolean toInclusive)
+ {
+ return new DescendingSet(set.subSet(from, fromInclusive,
+ to, toInclusive));
+ }
+
+ public SortedSet<D> tailSet(D from)
+ {
+ return tailSet(from, true);
+ }
+
+ public NavigableSet<D> tailSet(D from, boolean inclusive)
+ {
+ return new DescendingSet(set.headSet(from, inclusive));
+ }
+
+ public Object[] toArray()
+ {
+ D[] array = (D[]) set.toArray();
+ Arrays.sort(array, comparator());
+ return array;
+ }
+
+ public <T> T[] toArray(T[] a)
+ {
+ T[] array = set.toArray(a);
+ Arrays.sort(array, (Comparator<? super T>) comparator());
+ return array;
+ }
+
+ public String toString()
+ {
+ CPStringBuilder r = new CPStringBuilder("[");
+ final Iterator<D> it = iterator();
+ while (it.hasNext())
+ {
+ final D o = it.next();
+ if (o == this)
+ r.append("<this>");
+ else
+ r.append(o);
+ r.append(", ");
+ }
+ r.replace(r.length() - 2, r.length(), "]");
+ return r.toString();
+ }
+
+ } // class DescendingSet
+
+ private class EntrySet
+ extends AbstractSet<Entry<K,V>>
+ {
+ public int size()
+ {
+ return size;
+ }
+
+ public Iterator<Map.Entry<K,V>> iterator()
+ {
+ return new TreeIterator(ENTRIES);
+ }
+
+ public void clear()
+ {
+ TreeMap.this.clear();
+ }
+
+ public boolean contains(Object o)
+ {
+ if (! (o instanceof Map.Entry))
+ return false;
+ Map.Entry<K,V> me = (Map.Entry<K,V>) o;
+ Node<K,V> n = getNode(me.getKey());
+ return n != nil && AbstractSet.equals(me.getValue(), n.value);
+ }
+
+ public boolean remove(Object o)
+ {
+ if (! (o instanceof Map.Entry))
+ return false;
+ Map.Entry<K,V> me = (Map.Entry<K,V>) o;
+ Node<K,V> n = getNode(me.getKey());
+ if (n != nil && AbstractSet.equals(me.getValue(), n.value))
+ {
+ removeNode(n);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private final class NavigableEntrySet
+ extends EntrySet
+ implements NavigableSet<Entry<K,V>>
+ {
+
+ public Entry<K,V> ceiling(Entry<K,V> e)
+ {
+ return ceilingEntry(e.getKey());
+ }
+
+ public Comparator<? super Entry<K,V>> comparator()
+ {
+ return new Comparator<Entry<K,V>>()
+ {
+ public int compare(Entry<K,V> t1, Entry<K,V> t2)
+ {
+ return comparator.compare(t1.getKey(), t2.getKey());
+ }
+ };
+ }
+
+ public Iterator<Entry<K,V>> descendingIterator()
+ {
+ return descendingSet().iterator();
+ }
+
+ public NavigableSet<Entry<K,V>> descendingSet()
+ {
+ return new DescendingSet(this);
+ }
+
+ public Entry<K,V> first()
+ {
+ return firstEntry();
+ }
+
+ public Entry<K,V> floor(Entry<K,V> e)
+ {
+ return floorEntry(e.getKey());
+ }
+
+ public SortedSet<Entry<K,V>> headSet(Entry<K,V> to)
+ {
+ return headSet(to, false);
+ }
+
+ public NavigableSet<Entry<K,V>> headSet(Entry<K,V> to, boolean inclusive)
+ {
+ return (NavigableSet<Entry<K,V>>) headMap(to.getKey(), inclusive).entrySet();
+ }
+
+ public Entry<K,V> higher(Entry<K,V> e)
+ {
+ return higherEntry(e.getKey());
+ }
+
+ public Entry<K,V> last()
+ {
+ return lastEntry();
+ }
+
+ public Entry<K,V> lower(Entry<K,V> e)
+ {
+ return lowerEntry(e.getKey());
+ }
+
+ public Entry<K,V> pollFirst()
+ {
+ return pollFirstEntry();
+ }
+
+ public Entry<K,V> pollLast()
+ {
+ return pollLastEntry();
+ }
+
+ public SortedSet<Entry<K,V>> subSet(Entry<K,V> from, Entry<K,V> to)
+ {
+ return subSet(from, true, to, false);
+ }
+
+ public NavigableSet<Entry<K,V>> subSet(Entry<K,V> from, boolean fromInclusive,
+ Entry<K,V> to, boolean toInclusive)
+ {
+ return (NavigableSet<Entry<K,V>>) subMap(from.getKey(), fromInclusive,
+ to.getKey(), toInclusive).entrySet();
+ }
+
+ public SortedSet<Entry<K,V>> tailSet(Entry<K,V> from)
+ {
+ return tailSet(from, true);
+ }
+
+ public NavigableSet<Entry<K,V>> tailSet(Entry<K,V> from, boolean inclusive)
+ {
+ return (NavigableSet<Entry<K,V>>) tailMap(from.getKey(), inclusive).navigableKeySet();
+ }
+
+ } // class NavigableEntrySet
+
+} // class TreeMap
diff --git a/libjava/classpath/java/util/TreeSet.java b/libjava/classpath/java/util/TreeSet.java
new file mode 100644
index 000000000..06f4fa5b3
--- /dev/null
+++ b/libjava/classpath/java/util/TreeSet.java
@@ -0,0 +1,641 @@
+/* TreeSet.java -- a class providing a TreeMap-backed SortedSet
+ Copyright (C) 1999, 2000, 2001, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+/**
+ * This class provides a TreeMap-backed implementation of the SortedSet
+ * interface. The elements will be sorted according to their <i>natural
+ * order</i>, or according to the provided <code>Comparator</code>.<p>
+ *
+ * Most operations are O(log n), but there is so much overhead that this
+ * makes small sets expensive. Note that the ordering must be <i>consistent
+ * with equals</i> to correctly implement the Set interface. If this
+ * condition is violated, the set is still well-behaved, but you may have
+ * suprising results when comparing it to other sets.<p>
+ *
+ * This implementation is not synchronized. If you need to share this between
+ * multiple threads, do something like:<br>
+ * <code>SortedSet s
+ * = Collections.synchronizedSortedSet(new TreeSet(...));</code><p>
+ *
+ * The iterators are <i>fail-fast</i>, meaning that any structural
+ * modification, except for <code>remove()</code> called on the iterator
+ * itself, cause the iterator to throw a
+ * <code>ConcurrentModificationException</code> rather than exhibit
+ * non-deterministic behavior.
+ *
+ * @author Jon Zeppieri
+ * @author Bryce McKinlay
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @see Collection
+ * @see Set
+ * @see HashSet
+ * @see LinkedHashSet
+ * @see Comparable
+ * @see Comparator
+ * @see Collections#synchronizedSortedSet(SortedSet)
+ * @see TreeMap
+ * @since 1.2
+ * @status updated to 1.6
+ */
+public class TreeSet<T> extends AbstractSet<T>
+ implements NavigableSet<T>, Cloneable, Serializable
+{
+ /**
+ * Compatible with JDK 1.2.
+ */
+ private static final long serialVersionUID = -2479143000061671589L;
+
+ /**
+ * The NavigableMap which backs this Set.
+ */
+ // Not final because of readObject. This will always be one of TreeMap or
+ // TreeMap.SubMap, which both extend AbstractMap.
+ private transient NavigableMap<T, String> map;
+
+ /**
+ * Construct a new TreeSet whose backing TreeMap using the "natural"
+ * ordering of keys. Elements that are not mutually comparable will cause
+ * ClassCastExceptions down the road.
+ *
+ * @see Comparable
+ */
+ public TreeSet()
+ {
+ map = new TreeMap<T, String>();
+ }
+
+ /**
+ * Construct a new TreeSet whose backing TreeMap uses the supplied
+ * Comparator. Elements that are not mutually comparable will cause
+ * ClassCastExceptions down the road.
+ *
+ * @param comparator the Comparator this Set will use
+ */
+ public TreeSet(Comparator<? super T> comparator)
+ {
+ map = new TreeMap<T, String>(comparator);
+ }
+
+ /**
+ * Construct a new TreeSet whose backing TreeMap uses the "natural"
+ * orering of the keys and which contains all of the elements in the
+ * supplied Collection. This runs in n*log(n) time.
+ *
+ * @param collection the new Set will be initialized with all
+ * of the elements in this Collection
+ * @throws ClassCastException if the elements of the collection are not
+ * comparable
+ * @throws NullPointerException if the collection is null
+ * @see Comparable
+ */
+ public TreeSet(Collection<? extends T> collection)
+ {
+ map = new TreeMap<T, String>();
+ addAll(collection);
+ }
+
+ /**
+ * Construct a new TreeSet, using the same key ordering as the supplied
+ * SortedSet and containing all of the elements in the supplied SortedSet.
+ * This constructor runs in linear time.
+ *
+ * @param sortedSet the new TreeSet will use this SortedSet's comparator
+ * and will initialize itself with all its elements
+ * @throws NullPointerException if sortedSet is null
+ */
+ public TreeSet(SortedSet<T> sortedSet)
+ {
+ Iterator<T> itr;
+
+ map = new TreeMap<T, String>
+ ((Comparator<? super T>)sortedSet.comparator());
+ itr = ((SortedSet<T>) sortedSet).iterator();
+ ((TreeMap<T, String>) map).putKeysLinear(itr, sortedSet.size());
+ }
+
+ /**
+ * This private constructor is used to implement the subSet() calls around
+ * a backing TreeMap.SubMap.
+ *
+ * @param backingMap the submap
+ */
+ private TreeSet(NavigableMap<T,String> backingMap)
+ {
+ map = backingMap;
+ }
+
+ /**
+ * Adds the spplied Object to the Set if it is not already in the Set;
+ * returns true if the element is added, false otherwise.
+ *
+ * @param obj the Object to be added to this Set
+ * @throws ClassCastException if the element cannot be compared with objects
+ * already in the set
+ */
+ public boolean add(T obj)
+ {
+ return map.put(obj, "") == null;
+ }
+
+ /**
+ * Adds all of the elements in the supplied Collection to this TreeSet.
+ *
+ * @param c The collection to add
+ * @return true if the Set is altered, false otherwise
+ * @throws NullPointerException if c is null
+ * @throws ClassCastException if an element in c cannot be compared with
+ * objects already in the set
+ */
+ public boolean addAll(Collection<? extends T> c)
+ {
+ boolean result = false;
+ int pos = c.size();
+ Iterator<? extends T> itr = c.iterator();
+ while (--pos >= 0)
+ result |= (map.put(itr.next(), "") == null);
+ return result;
+ }
+
+ /**
+ * Removes all elements in this Set.
+ */
+ public void clear()
+ {
+ map.clear();
+ }
+
+ /**
+ * Returns a shallow copy of this Set. The elements are not cloned.
+ *
+ * @return the cloned set
+ */
+ public Object clone()
+ {
+ TreeSet<T> copy = null;
+ try
+ {
+ copy = (TreeSet<T>) super.clone();
+ // Map may be either TreeMap or TreeMap.SubMap, hence the ugly casts.
+ copy.map = (NavigableMap<T, String>) ((AbstractMap<T, String>) map).clone();
+ }
+ catch (CloneNotSupportedException x)
+ {
+ // Impossible result.
+ }
+ return copy;
+ }
+
+ /**
+ * Returns this Set's comparator.
+ *
+ * @return the comparator, or null if the set uses natural ordering
+ */
+ public Comparator<? super T> comparator()
+ {
+ return map.comparator();
+ }
+
+ /**
+ * Returns true if this Set contains the supplied Object, false otherwise.
+ *
+ * @param obj the Object to check for
+ * @return true if it is in the set
+ * @throws ClassCastException if obj cannot be compared with objects
+ * already in the set
+ */
+ public boolean contains(Object obj)
+ {
+ return map.containsKey(obj);
+ }
+
+ /**
+ * Returns the first (by order) element in this Set.
+ *
+ * @return the first element
+ * @throws NoSuchElementException if the set is empty
+ */
+ public T first()
+ {
+ return map.firstKey();
+ }
+
+ /**
+ * Returns a view of this Set including all elements less than
+ * <code>to</code>. The returned set is backed by the original, so changes
+ * in one appear in the other. The subset will throw an
+ * {@link IllegalArgumentException} for any attempt to access or add an
+ * element beyond the specified cutoff. The returned set does not include
+ * the endpoint; if you want inclusion, pass the successor element or
+ * call {@link #headSet(T,boolean)}. This call is equivalent to
+ * <code>headSet(to, false)</code>.
+ *
+ * @param to the (exclusive) cutoff point
+ * @return a view of the set less than the cutoff
+ * @throws ClassCastException if <code>to</code> is not compatible with
+ * the comparator (or is not Comparable, for natural ordering)
+ * @throws NullPointerException if to is null, but the comparator does not
+ * tolerate null elements
+ */
+ public SortedSet<T> headSet(T to)
+ {
+ return headSet(to, false);
+ }
+
+ /**
+ * Returns a view of this Set including all elements less than
+ * (or equal to, if <code>inclusive</code> is true) <code>to</code>.
+ * The returned set is backed by the original, so changes
+ * in one appear in the other. The subset will throw an
+ * {@link IllegalArgumentException} for any attempt to access or add an
+ * element beyond the specified cutoff.
+ *
+ * @param to the cutoff point
+ * @param inclusive true if <code>to</code> should be included.
+ * @return a view of the set for the specified range.
+ * @throws ClassCastException if <code>to</code> is not compatible with
+ * the comparator (or is not Comparable, for natural ordering)
+ * @throws NullPointerException if to is null, but the comparator does not
+ * tolerate null elements
+ */
+ public NavigableSet<T> headSet(T to, boolean inclusive)
+ {
+ return new TreeSet<T>(map.headMap(to, inclusive));
+ }
+
+ /**
+ * Returns true if this Set has size 0, false otherwise.
+ *
+ * @return true if the set is empty
+ */
+ public boolean isEmpty()
+ {
+ return map.isEmpty();
+ }
+
+ /**
+ * Returns in Iterator over the elements in this TreeSet, which traverses
+ * in ascending order.
+ *
+ * @return an iterator
+ */
+ public Iterator<T> iterator()
+ {
+ return map.keySet().iterator();
+ }
+
+ /**
+ * Returns the last (by order) element in this Set.
+ *
+ * @return the last element
+ * @throws NoSuchElementException if the set is empty
+ */
+ public T last()
+ {
+ return map.lastKey();
+ }
+
+ /**
+ * If the supplied Object is in this Set, it is removed, and true is
+ * returned; otherwise, false is returned.
+ *
+ * @param obj the Object to remove from this Set
+ * @return true if the set was modified
+ * @throws ClassCastException if obj cannot be compared to set elements
+ */
+ public boolean remove(Object obj)
+ {
+ return map.remove(obj) != null;
+ }
+
+ /**
+ * Returns the number of elements in this Set
+ *
+ * @return the set size
+ */
+ public int size()
+ {
+ return map.size();
+ }
+
+ /**
+ * Returns a view of this Set including all elements greater or equal to
+ * <code>from</code> and less than <code>to</code> (a half-open interval).
+ * The returned set is backed by the original, so changes in one appear in
+ * the other. The subset will throw an {@link IllegalArgumentException}
+ * for any attempt to access or add an element beyond the specified cutoffs.
+ * The returned set includes the low endpoint but not the high; if you want
+ * to reverse this behavior on either end, pass in the successor element
+ * or call {@link #subSet(T,boolean,T,boolean)}. This is equivalent to
+ * calling <code>subSet(from,true,to,false)</code>.
+ *
+ * @param from the (inclusive) low cutoff point
+ * @param to the (exclusive) high cutoff point
+ * @return a view of the set between the cutoffs
+ * @throws ClassCastException if either cutoff is not compatible with
+ * the comparator (or is not Comparable, for natural ordering)
+ * @throws NullPointerException if from or to is null, but the comparator
+ * does not tolerate null elements
+ * @throws IllegalArgumentException if from is greater than to
+ */
+ public SortedSet<T> subSet(T from, T to)
+ {
+ return subSet(from, true, to, false);
+ }
+
+ /**
+ * Returns a view of this Set including all elements greater than (or equal to,
+ * if <code>fromInclusive</code> is true</code> <code>from</code> and less than
+ * (or equal to, if <code>toInclusive</code> is true) <code>to</code>.
+ * The returned set is backed by the original, so changes in one appear in
+ * the other. The subset will throw an {@link IllegalArgumentException}
+ * for any attempt to access or add an element beyond the specified cutoffs.
+ *
+ * @param from the low cutoff point
+ * @param fromInclusive true if <code>from</code> should be included.
+ * @param to the high cutoff point
+ * @param toInclusive true if <code>to</code> should be included.
+ * @return a view of the set for the specified range.
+ * @throws ClassCastException if either cutoff is not compatible with
+ * the comparator (or is not Comparable, for natural ordering)
+ * @throws NullPointerException if from or to is null, but the comparator
+ * does not tolerate null elements
+ * @throws IllegalArgumentException if from is greater than to
+ */
+ public NavigableSet<T> subSet(T from, boolean fromInclusive,
+ T to, boolean toInclusive)
+ {
+ return new TreeSet<T>(map.subMap(from, fromInclusive,
+ to, toInclusive));
+ }
+
+ /**
+ * Returns a view of this Set including all elements greater or equal to
+ * <code>from</code>. The returned set is backed by the original, so
+ * changes in one appear in the other. The subset will throw an
+ * {@link IllegalArgumentException} for any attempt to access or add an
+ * element beyond the specified cutoff. The returned set includes the
+ * endpoint; if you want to exclude it, pass in the successor element
+ * or call {@link #tailSet(T,boolean)}. This is equivalent to calling
+ * <code>tailSet(from, true)</code>.
+ *
+ * @param from the (inclusive) low cutoff point
+ * @return a view of the set above the cutoff
+ * @throws ClassCastException if <code>from</code> is not compatible with
+ * the comparator (or is not Comparable, for natural ordering)
+ * @throws NullPointerException if from is null, but the comparator
+ * does not tolerate null elements
+ */
+ public SortedSet<T> tailSet(T from)
+ {
+ return tailSet(from, true);
+ }
+
+ /**
+ * Returns a view of this Set including all elements greater (or equal to,
+ * if <code>inclusive</code> is true) <code>from</code>. The returned set
+ * is backed by the original, so changes in one appear in the other. The
+ * subset will throw an {@link IllegalArgumentException} for any attempt
+ * to access or add an element beyond the specified cutoff.
+ *
+ * @param from the low cutoff point.
+ * @param inclusive true if <code>from</code> should be included.
+ * @return a view of the set for the specified range.
+ * @throws ClassCastException if <code>from</code> is not compatible with
+ * the comparator (or is not Comparable, for natural ordering)
+ * @throws NullPointerException if from is null, but the comparator
+ * does not tolerate null elements
+ */
+ public NavigableSet<T> tailSet(T from, boolean inclusive)
+ {
+ return new TreeSet<T>(map.tailMap(from, inclusive));
+ }
+
+ /**
+ * Serializes this object to the given stream.
+ *
+ * @param s the stream to write to
+ * @throws IOException if the underlying stream fails
+ * @serialData the <i>comparator</i> (Object), followed by the set size
+ * (int), the the elements in sorted order (Object)
+ */
+ private void writeObject(ObjectOutputStream s) throws IOException
+ {
+ s.defaultWriteObject();
+ Iterator<T> itr = map.keySet().iterator();
+ int pos = map.size();
+ s.writeObject(map.comparator());
+ s.writeInt(pos);
+ while (--pos >= 0)
+ s.writeObject(itr.next());
+ }
+
+ /**
+ * Deserializes this object from the given stream.
+ *
+ * @param s the stream to read from
+ * @throws ClassNotFoundException if the underlying stream fails
+ * @throws IOException if the underlying stream fails
+ * @serialData the <i>comparator</i> (Object), followed by the set size
+ * (int), the the elements in sorted order (Object)
+ */
+ private void readObject(ObjectInputStream s)
+ throws IOException, ClassNotFoundException
+ {
+ s.defaultReadObject();
+ Comparator<? super T> comparator = (Comparator<? super T>) s.readObject();
+ int size = s.readInt();
+ map = new TreeMap<T, String>(comparator);
+ ((TreeMap<T, String>) map).putFromObjStream(s, size, false);
+ }
+
+ /**
+ * Returns the least or lowest element in the set greater than or
+ * equal to the given element, or <code>null</code> if there is
+ * no such element.
+ *
+ * @param e the element relative to the returned element.
+ * @return the least element greater than or equal
+ * to the given element, or <code>null</code> if there is
+ * no such element.
+ * @throws ClassCastException if the specified element can not
+ * be compared with those in the map.
+ * @throws NullPointerException if the element is <code>null</code>
+ * and this set either uses natural
+ * ordering or a comparator that does
+ * not permit null elements.
+ * @since 1.6
+ */
+ public T ceiling(T e)
+ {
+ return map.ceilingKey(e);
+ }
+
+ /**
+ * Returns an iterator over the elements of this set in descending
+ * order. This is equivalent to calling
+ * <code>descendingSet().iterator()</code>.
+ *
+ * @return an iterator over the elements in descending order.
+ * @since 1.6
+ */
+ public Iterator<T> descendingIterator()
+ {
+ return descendingSet().iterator();
+ }
+
+ /**
+ * Returns a view of the set in reverse order. The descending set
+ * is backed by the original set, so that changes affect both sets.
+ * Any changes occurring to either set while an iteration is taking
+ * place (with the exception of a {@link Iterator#remove()} operation)
+ * result in undefined behaviour from the iteration. The ordering
+ * of the descending set is the same as for a set with a
+ * {@link Comparator} given by {@link Collections#reverseOrder()},
+ * and calling {@link #descendingSet()} on the descending set itself
+ * results in a view equivalent to the original set.
+ *
+ * @return a reverse order view of the set.
+ * @since 1.6
+ */
+ public NavigableSet<T> descendingSet()
+ {
+ return map.descendingKeySet();
+ }
+
+ /**
+ * Returns the greatest or highest element in the set less than or
+ * equal to the given element, or <code>null</code> if there is
+ * no such element.
+ *
+ * @param e the element relative to the returned element.
+ * @return the greatest element less than or equal
+ * to the given element, or <code>null</code> if there is
+ * no such element.
+ * @throws ClassCastException if the specified element can not
+ * be compared with those in the map.
+ * @throws NullPointerException if the element is <code>null</code>
+ * and this set either uses natural
+ * ordering or a comparator that does
+ * not permit null elements.
+ * @since 1.6
+ */
+ public T floor(T e)
+ {
+ return map.floorKey(e);
+ }
+
+ /**
+ * Returns the least or lowest element in the set strictly greater
+ * than the given element, or <code>null</code> if there is
+ * no such element.
+ *
+ * @param e the element relative to the returned element.
+ * @return the least element greater than
+ * the given element, or <code>null</code> if there is
+ * no such element.
+ * @throws ClassCastException if the specified element can not
+ * be compared with those in the map.
+ * @throws NullPointerException if the element is <code>null</code>
+ * and this set either uses natural
+ * ordering or a comparator that does
+ * not permit null elements.
+ * @since 1.6
+ */
+ public T higher(T e)
+ {
+ return map.higherKey(e);
+ }
+
+ /**
+ * Returns the greatest or highest element in the set strictly less
+ * than the given element, or <code>null</code> if there is
+ * no such element.
+ *
+ * @param e the element relative to the returned element.
+ * @return the greatest element less than
+ * the given element, or <code>null</code> if there is
+ * no such element.
+ * @throws ClassCastException if the specified element can not
+ * be compared with those in the map.
+ * @throws NullPointerException if the element is <code>null</code>
+ * and this set either uses natural
+ * ordering or a comparator that does
+ * not permit null elements.
+ * @since 1.6
+ */
+ public T lower(T e)
+ {
+ return map.lowerKey(e);
+ }
+
+ /**
+ * Removes and returns the least or lowest element in the set,
+ * or <code>null</code> if the map is empty.
+ *
+ * @return the removed first element, or <code>null</code> if the
+ * map is empty.
+ * @since 1.6
+ */
+ public T pollFirst()
+ {
+ return map.pollFirstEntry().getKey();
+ }
+
+ /**
+ * Removes and returns the greatest or highest element in the set,
+ * or <code>null</code> if the map is empty.
+ *
+ * @return the removed last element, or <code>null</code> if the
+ * map is empty.
+ * @since 1.6
+ */
+ public T pollLast()
+ {
+ return map.pollLastEntry().getKey();
+ }
+
+}
diff --git a/libjava/classpath/java/util/UUID.java b/libjava/classpath/java/util/UUID.java
new file mode 100644
index 000000000..bb25e071c
--- /dev/null
+++ b/libjava/classpath/java/util/UUID.java
@@ -0,0 +1,372 @@
+/* UUID.java -- Class that represents a UUID object.
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.io.Serializable;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * This class represents a 128-bit UUID value.
+ *
+ * There are several types of UUID, and while this class can be used to store
+ * them, only the Leach-Salz (variant 2) UUID specified in RFC-4122 will
+ * give meaningful results from the method calls.
+ * See: http://tools.ietf.org/html/4122 for the details
+ *
+ * The format of a Leach-Salz (variant 2) time-based (version 1) UUID
+ * is as follows:
+ * time_low - upper 32 bits of the most significant 64 bits,
+ * this is the least-significant part of the timestamp.
+ *
+ * time_mid - bits 16-31 of the most significant 64 bits,
+ * this is the middle portion of the timestamp.
+ *
+ * version - bits 8-15 of the most significant 64 bits.
+ *
+ * time_hi - bits 0-7 of the most significant 64 bits,
+ * the most significant portion of the timestamp.
+ *
+ * clock_and_reserved - bits 48-63 of the least significant 64 bits.
+ * a variable number of bits hold the variant
+ * (see the spec)
+ *
+ * node identifier - bits 0-47 of the least signficant 64 bits.
+ *
+ * These fields are valid only for version 1, in the remaining versions,
+ * only the version and variant fields are set, all others are used for data.
+ *
+ * @since 1.5
+ * @author Sven de Marothy
+ */
+public final class UUID
+ extends Object
+ implements Serializable, Comparable<UUID>
+{
+ private static final long serialVersionUID = -4856846361193249489L;
+
+ /**
+ * Serialized field - most significant 64 bits.
+ */
+ private long mostSigBits;
+
+ /**
+ * Serialized field - least significant 64 bits.
+ */
+ private long leastSigBits;
+
+ /**
+ * Random-number generator.
+ */
+ private static transient Random r = new Random();
+
+ /**
+ * Constructs a new UUID.
+ *
+ * @since 1.5
+ */
+ public UUID(long mostSigBits, long leastSigBits)
+ {
+ this.mostSigBits = mostSigBits;
+ this.leastSigBits = leastSigBits;
+ }
+
+ /**
+ * Returns the clock-sequence value of this UUID.
+ * This field only exists in a time-based (version 1) UUID.
+ *
+ * @throws UnsupportedOperationException if the UUID type is not 1.
+ * @returns an int containing the clock-sequence value.
+ */
+ public int clockSequence()
+ {
+ if( version() != 1 )
+ throw new UnsupportedOperationException("Not a type 1 UUID");
+ return (int)((leastSigBits & 0x3FFF000000000000L) >> 48);
+ }
+
+ /**
+ * Compare this UUID to another.
+ * The comparison is performed as between two 128-bit integers.
+ *
+ * @return -1 if this < val, 0 if they are equal, 1 if this > val.
+ */
+ public int compareTo(UUID o)
+ {
+ if( mostSigBits < o.mostSigBits )
+ return -1;
+ if( mostSigBits > o.mostSigBits )
+ return 1;
+ if( leastSigBits < o.leastSigBits )
+ return -1;
+ if( leastSigBits > o.mostSigBits )
+ return 1;
+ return 0;
+ }
+
+ /**
+ * Compare a (UUID) object to this one
+ */
+ public boolean equals(Object obj)
+ {
+ if( !(obj instanceof UUID ) )
+ return false;
+ return ( ((UUID)obj).mostSigBits == mostSigBits &&
+ ((UUID)obj).leastSigBits == leastSigBits );
+ }
+
+ /**
+ * Creates a UUID object from a Sting representation.
+ *
+ * For the format of the string,
+ * @see #toString()
+ *
+ * @return a new UUID object.
+ */
+ public static UUID fromString(String name)
+ {
+ StringTokenizer st = new StringTokenizer( name.trim(), "-" );
+ if( st.countTokens() < 5 )
+ throw new IllegalArgumentException( "Incorrect UUID string"+
+ " representation:"+name );
+
+ long msb = (Long.parseLong(st.nextToken(), 16) << 32); // time low
+ msb |= (Long.parseLong(st.nextToken(), 16) << 16); // time mid
+ msb |= Long.parseLong(st.nextToken(), 16); // time high
+
+ long lsb = (Long.parseLong(st.nextToken(), 16) << 48); // clock
+ lsb |= Long.parseLong(st.nextToken(), 16); // node
+
+ return new UUID(msb, lsb);
+ }
+
+ /**
+ * Returns a String representation of the UUID.
+ *
+ * The format of the standard string representation (given in RFC4122) is:
+ *
+ * time-low "-" time-mid "-"
+ * time-high-and-version "-"
+ * clock-seq-and-reserved
+ * clock-seq-low "-" node
+ *
+ * Where each field is represented as a hex string.
+ *
+ * @return the String representation.
+ */
+ public String toString()
+ {
+ return // time-low first
+ padHex( (( mostSigBits & 0xFFFFFFFF00000000L) >> 32) & 0xFFFFFFFFL, 8)
+ + "-" + // then time-mid
+ padHex( (( mostSigBits & 0xFFFF0000L ) >> 16), 4 )
+ + "-" + // time-high
+ padHex( ( mostSigBits & 0x0000000000000000FFFFL ), 4 )
+ + "-" + // clock (note - no reason to separate high and low here)
+ padHex( (((leastSigBits & 0xFFFF000000000000L) >> 48) & 0xFFFF), 4 )
+ + "-" + // finally the node value.
+ padHex(leastSigBits & 0xFFFFFFFFFFFFL, 12);
+ }
+
+ /**
+ * Returns the least significant 64 bits of the UUID as a <code>long</code>.
+ */
+ public long getLeastSignificantBits()
+ {
+ return leastSigBits;
+ }
+
+ /**
+ * Returns the most significant 64 bits of the UUID as a <code>long</code>.
+ */
+ public long getMostSignificantBits()
+ {
+ return mostSigBits;
+ }
+
+ /**
+ * Returns a hash of this UUID.
+ */
+ public int hashCode()
+ {
+ int l1 = (int)(leastSigBits & 0xFFFFFFFFL);
+ int l2 = (int)((leastSigBits & 0xFFFFFFFF00000000L) >> 32);
+ int m1 = (int)(mostSigBits & 0xFFFFFFFFL);
+ int m2 = (int)((mostSigBits & 0xFFFFFFFF00000000L) >> 32);
+
+ return (l1 ^ l2) ^ (m1 ^ m2);
+ }
+
+ /**
+ * Creates a UUID version 3 object (name based with MD5 hashing)
+ * from a series of bytes representing a name.
+ */
+ public static UUID nameUUIDFromBytes(byte[] name)
+ {
+ long msb, lsb;
+ byte[] hash;
+
+ try
+ {
+ MessageDigest md5 = MessageDigest.getInstance("MD5");
+ hash = md5.digest( name );
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ throw new UnsupportedOperationException("No MD5 algorithm available.");
+ }
+
+ msb = ((hash[0] & 0xFFL) << 56) | ((hash[1] & 0xFFL) << 48) |
+ ((hash[2] & 0xFFL) << 40) | ((hash[3] & 0xFFL) << 32) |
+ ((hash[4] & 0xFFL) << 24) | ((hash[5] & 0xFFL) << 16) |
+ ((hash[6] & 0xFFL) << 8) | (hash[7] & 0xFFL);
+
+ lsb = ((hash[8] & 0xFFL) << 56) | ((hash[9] & 0xFFL) << 48) |
+ ((hash[10] & 0xFFL) << 40) | ((hash[11] & 0xFFL) << 32) |
+ ((hash[12] & 0xFFL) << 24) | ((hash[13] & 0xFFL) << 16) |
+ ((hash[14] & 0xFFL) << 8) | (hash[15] & 0xFFL);
+
+ lsb &= 0x3FFFFFFFFFFFFFFFL;
+ lsb |= 0x8000000000000000L; // set top two bits to variant 2
+
+ msb &= 0xFFFFFFFFFFFF0FFFL;
+ msb |= 0x3000; // Version 3;
+
+ return new UUID(msb, lsb);
+ }
+
+ /**
+ * Returns the 48-bit node value in a long.
+ * This field only exists in a time-based (version 1) UUID.
+ *
+ * @throws UnsupportedOperationException if the UUID type is not 1.
+ * @returns a long with the node value in the lower 48 bits.
+ */
+ public long node()
+ {
+ if( version() != 1 )
+ throw new UnsupportedOperationException("Not a type 1 UUID");
+ return (leastSigBits & 0xFFFFFFFFFFFFL);
+ }
+
+ /**
+ * Returns the 60-bit timestamp value of the UUID in a long.
+ * This field only exists in a time-based (version 1) UUID.
+ *
+ * @throws UnsupportedOperationException if the UUID type is not 1.
+ * @returns a long with the timestamp value.
+ */
+ public long timestamp()
+ {
+ if( version() != 1 )
+ throw new UnsupportedOperationException("Not a type 1 UUID");
+ long time = (( mostSigBits & 0xFFFFFFFF00000000L) >> 32);
+ time |= (( mostSigBits & 0xFFFF0000L ) << 16);
+ long time_hi = ( mostSigBits & 0xFFFL );
+ time |= (time_hi << 48);
+ return time;
+ }
+
+ /**
+ * Generate a Leach-Salz (Variant 2) randomly generated (version 4)
+ * UUID.
+ *
+ */
+ public static UUID randomUUID()
+ {
+ long lsb = r.nextLong();
+ long msb = r.nextLong();
+
+ lsb &= 0x3FFFFFFFFFFFFFFFL;
+ lsb |= 0x8000000000000000L; // set top two bits to variant 2
+
+ msb &= 0xFFFFFFFFFFFF0FFFL;
+ msb |= 0x4000; // Version 4;
+
+ return new UUID( msb, lsb );
+ }
+
+ /**
+ * Returns a hex String from l, padded to n spaces.
+ */
+ private String padHex( long l, int n )
+ {
+ String s = Long.toHexString( l );
+ while( s.length() < n )
+ s = "0" + s;
+ return s;
+ }
+
+ /**
+ * Returns the variant of the UUID
+ *
+ * This may be:
+ * 0 = Reserved for NCS backwards-compatibility
+ * 2 = Leach-Salz (supports the other methods in this class)
+ * 6 = Reserved for Microsoft backwards-compatibility
+ * 7 = (reserved for future use)
+ */
+ public int variant()
+ {
+ // Get the top 3 bits (not all may be part of the variant)
+ int v = (int)((leastSigBits & 0xE000000000000000L) >> 61);
+ if( (v & 0x04) == 0 ) // msb of the variant is 0
+ return 0;
+ if( (v & 0x02) == 0 ) // variant is 0 1 (Leach-Salz)
+ return 2;
+ return v; // 6 or 7
+ }
+
+ /**
+ * Returns the version # of the UUID.
+ *
+ * Valid version numbers for a variant 2 UUID are:
+ * 1 = Time based UUID
+ * 2 = DCE security UUID
+ * 3 = Name-based UUID using MD5 hashing
+ * 4 = Randomly generated UUID
+ * 5 = Name-based UUID using SHA-1 hashing
+ *
+ * @return the version number
+ */
+ public int version()
+ {
+ return (int)((mostSigBits & 0xF000L) >> 12);
+ }
+}
diff --git a/libjava/classpath/java/util/UnknownFormatConversionException.java b/libjava/classpath/java/util/UnknownFormatConversionException.java
new file mode 100644
index 000000000..7326a8250
--- /dev/null
+++ b/libjava/classpath/java/util/UnknownFormatConversionException.java
@@ -0,0 +1,86 @@
+/* UnknownFormatConversionException.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Thrown when a {@link Formatter} is supplied with an
+ * unknown conversion.
+ *
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+public class UnknownFormatConversionException
+ extends IllegalFormatException
+{
+ private static final long serialVersionUID = 19060418L;
+
+ /**
+ * The unknown conversion.
+ *
+ * @serial the unknown conversion.
+ */
+ // Note: name fixed by serialization.
+ private String s;
+
+ /**
+ * Constructs a new <code>UnknownFormatConversionException</code>
+ * for the specified conversion string.
+ *
+ * @param s the conversion string.
+ * @throws NullPointerException if the conversion string is null.
+ */
+ public UnknownFormatConversionException(String s)
+ {
+ super("Unknown format conversion: " + s);
+ if (s == null)
+ throw new NullPointerException("The conversion string is null.");
+ this.s = s;
+ }
+
+ /**
+ * Returns the conversion string.
+ *
+ * @return the conversion string.
+ */
+ public String getConversion()
+ {
+ return s;
+ }
+}
diff --git a/libjava/classpath/java/util/UnknownFormatFlagsException.java b/libjava/classpath/java/util/UnknownFormatFlagsException.java
new file mode 100644
index 000000000..a7f4fc6ab
--- /dev/null
+++ b/libjava/classpath/java/util/UnknownFormatFlagsException.java
@@ -0,0 +1,88 @@
+/* UnknownFormatFlagsException.java
+ Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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;
+
+/**
+ * Thrown when a {@link Formatter} is supplied with an
+ * unknown flag.
+ *
+ * @author Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.5
+ */
+public class UnknownFormatFlagsException
+ extends IllegalFormatException
+{
+ private static final long serialVersionUID = 19370506L;
+
+ /**
+ * The set of flags containing the unknown flag.
+ *
+ * @serial the unknown conversion.
+ */
+ // Note: name fixed by serialization.
+ private String flags;
+
+ /**
+ * Constructs a new <code>UnknownFormatFlagsException</code>
+ * which specifies that the supplied set of flags contains a
+ * unknown.
+ *
+ * @param flags the flags containing a unknown.
+ * @throws NullPointerException if <code>flags</code> is null.
+ */
+ public UnknownFormatFlagsException(String s)
+ {
+ super("Unknown flag passed in " + s);
+ if (s == null)
+ throw new
+ NullPointerException("Null flags value passed to constructor.");
+ this.flags = s;
+ }
+
+ /**
+ * Returns the flags which contain a unknown.
+ *
+ * @return the flags.
+ */
+ public String getFlags()
+ {
+ return flags;
+ }
+}
diff --git a/libjava/classpath/java/util/Vector.java b/libjava/classpath/java/util/Vector.java
new file mode 100644
index 000000000..44370a1e4
--- /dev/null
+++ b/libjava/classpath/java/util/Vector.java
@@ -0,0 +1,958 @@
+/* Vector.java -- Class that provides growable arrays.
+ Copyright (C) 1998, 1999, 2000, 2001, 2004, 2005, 2006,
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Array;
+
+/**
+ * The <code>Vector</code> classes implements growable arrays of Objects.
+ * You can access elements in a Vector with an index, just as you
+ * can in a built in array, but Vectors can grow and shrink to accommodate
+ * more or fewer objects.<p>
+ *
+ * Vectors try to mantain efficiency in growing by having a
+ * <code>capacityIncrement</code> that can be specified at instantiation.
+ * When a Vector can no longer hold a new Object, it grows by the amount
+ * in <code>capacityIncrement</code>. If this value is 0, the vector doubles in
+ * size.<p>
+ *
+ * Vector implements the JDK 1.2 List interface, and is therefore a fully
+ * compliant Collection object. The iterators are fail-fast - if external
+ * code structurally modifies the vector, any operation on the iterator will
+ * then throw a {@link ConcurrentModificationException}. The Vector class is
+ * fully synchronized, but the iterators are not. So, when iterating over a
+ * vector, be sure to synchronize on the vector itself. If you don't want the
+ * expense of synchronization, use ArrayList instead. On the other hand, the
+ * Enumeration of elements() is not thread-safe, nor is it fail-fast; so it
+ * can lead to undefined behavior even in a single thread if you modify the
+ * vector during iteration.<p>
+ *
+ * Note: Some methods, especially those specified by List, specify throwing
+ * {@link IndexOutOfBoundsException}, but it is easier to implement by
+ * throwing the subclass {@link ArrayIndexOutOfBoundsException}. Others
+ * directly specify this subclass.
+ *
+ * @author Scott G. Miller
+ * @author Bryce McKinlay
+ * @author Eric Blake (ebb9@email.byu.edu)
+ * @see Collection
+ * @see List
+ * @see ArrayList
+ * @see LinkedList
+ * @since 1.0
+ * @status updated to 1.4
+ */
+public class Vector<T> extends AbstractList<T>
+ implements List<T>, RandomAccess, Cloneable, Serializable
+{
+ /**
+ * Compatible with JDK 1.0+.
+ */
+ private static final long serialVersionUID = -2767605614048989439L;
+
+ /**
+ * The internal array used to hold members of a Vector. The elements are
+ * in positions 0 through elementCount - 1, and all remaining slots are null.
+ * @serial the elements
+ */
+ protected Object[] elementData;
+
+ /**
+ * The number of elements currently in the vector, also returned by
+ * {@link #size}.
+ * @serial the size
+ */
+ protected int elementCount;
+
+ /**
+ * The amount the Vector's internal array should be increased in size when
+ * a new element is added that exceeds the current size of the array,
+ * or when {@link #ensureCapacity} is called. If &lt;= 0, the vector just
+ * doubles in size.
+ * @serial the amount to grow the vector by
+ */
+ protected int capacityIncrement;
+
+ /**
+ * Constructs an empty vector with an initial size of 10, and
+ * a capacity increment of 0
+ */
+ public Vector()
+ {
+ this(10, 0);
+ }
+
+ /**
+ * Constructs a vector containing the contents of Collection, in the
+ * order given by the collection.
+ *
+ * @param c collection of elements to add to the new vector
+ * @throws NullPointerException if c is null
+ * @since 1.2
+ */
+ public Vector(Collection<? extends T> c)
+ {
+ elementCount = c.size();
+ elementData = c.toArray(new Object[elementCount]);
+ }
+
+ /**
+ * Constructs a Vector with the initial capacity and capacity
+ * increment specified.
+ *
+ * @param initialCapacity the initial size of the Vector's internal array
+ * @param capacityIncrement the amount the internal array should be
+ * increased by when necessary, 0 to double the size
+ * @throws IllegalArgumentException if initialCapacity &lt; 0
+ */
+ public Vector(int initialCapacity, int capacityIncrement)
+ {
+ if (initialCapacity < 0)
+ throw new IllegalArgumentException();
+ elementData = new Object[initialCapacity];
+ this.capacityIncrement = capacityIncrement;
+ }
+
+ /**
+ * Constructs a Vector with the initial capacity specified, and a capacity
+ * increment of 0 (double in size).
+ *
+ * @param initialCapacity the initial size of the Vector's internal array
+ * @throws IllegalArgumentException if initialCapacity &lt; 0
+ */
+ public Vector(int initialCapacity)
+ {
+ this(initialCapacity, 0);
+ }
+
+ /**
+ * Copies the contents of the Vector into the provided array. If the
+ * array is too small to fit all the elements in the Vector, an
+ * {@link IndexOutOfBoundsException} is thrown without modifying the array.
+ * Old elements in the array are overwritten by the new elements.
+ *
+ * @param a target array for the copy
+ * @throws IndexOutOfBoundsException the array is not large enough
+ * @throws NullPointerException the array is null
+ * @see #toArray(Object[])
+ */
+ public synchronized void copyInto(Object[] a)
+ {
+ System.arraycopy(elementData, 0, a, 0, elementCount);
+ }
+
+ /**
+ * Trims the Vector down to size. If the internal data array is larger
+ * than the number of Objects its holding, a new array is constructed
+ * that precisely holds the elements. Otherwise this does nothing.
+ */
+ public synchronized void trimToSize()
+ {
+ // Don't bother checking for the case where size() == the capacity of the
+ // vector since that is a much less likely case; it's more efficient to
+ // not do the check and lose a bit of performance in that infrequent case
+
+ T[] newArray = (T[]) new Object[elementCount];
+ System.arraycopy(elementData, 0, newArray, 0, elementCount);
+ elementData = newArray;
+ }
+
+ /**
+ * Ensures that <code>minCapacity</code> elements can fit within this Vector.
+ * If <code>elementData</code> is too small, it is expanded as follows:
+ * If the <code>elementCount + capacityIncrement</code> is adequate, that
+ * is the new size. If <code>capacityIncrement</code> is non-zero, the
+ * candidate size is double the current. If that is not enough, the new
+ * size is <code>minCapacity</code>.
+ *
+ * @param minCapacity the desired minimum capacity, negative values ignored
+ */
+ public synchronized void ensureCapacity(int minCapacity)
+ {
+ if (elementData.length >= minCapacity)
+ return;
+
+ int newCapacity;
+ if (capacityIncrement <= 0)
+ newCapacity = elementData.length * 2;
+ else
+ newCapacity = elementData.length + capacityIncrement;
+
+ T[] newArray = (T[]) new Object[Math.max(newCapacity, minCapacity)];
+
+ System.arraycopy(elementData, 0, newArray, 0, elementCount);
+ elementData = newArray;
+ }
+
+ /**
+ * Explicitly sets the size of the vector (but not necessarily the size of
+ * the internal data array). If the new size is smaller than the old one,
+ * old values that don't fit are lost. If the new size is larger than the
+ * old one, the vector is padded with null entries.
+ *
+ * @param newSize The new size of the internal array
+ * @throws ArrayIndexOutOfBoundsException if the new size is negative
+ */
+ public synchronized void setSize(int newSize)
+ {
+ // Don't bother checking for the case where size() == the capacity of the
+ // vector since that is a much less likely case; it's more efficient to
+ // not do the check and lose a bit of performance in that infrequent case
+ modCount++;
+ ensureCapacity(newSize);
+ if (newSize < elementCount)
+ Arrays.fill(elementData, newSize, elementCount, null);
+ elementCount = newSize;
+ }
+
+ /**
+ * Returns the size of the internal data array (not the amount of elements
+ * contained in the Vector).
+ *
+ * @return capacity of the internal data array
+ */
+ public synchronized int capacity()
+ {
+ return elementData.length;
+ }
+
+ /**
+ * Returns the number of elements stored in this Vector.
+ *
+ * @return the number of elements in this Vector
+ */
+ public synchronized int size()
+ {
+ return elementCount;
+ }
+
+ /**
+ * Returns true if this Vector is empty, false otherwise
+ *
+ * @return true if the Vector is empty, false otherwise
+ */
+ public synchronized boolean isEmpty()
+ {
+ return elementCount == 0;
+ }
+
+ /**
+ * Returns an Enumeration of the elements of this Vector. The enumeration
+ * visits the elements in increasing index order, but is NOT thread-safe.
+ *
+ * @return an Enumeration
+ * @see #iterator()
+ */
+ // No need to synchronize as the Enumeration is not thread-safe!
+ public Enumeration<T> elements()
+ {
+ return new Enumeration<T>()
+ {
+ private int i = 0;
+
+ public boolean hasMoreElements()
+ {
+ return i < elementCount;
+ }
+
+ @SuppressWarnings("unchecked")
+ public T nextElement()
+ {
+ if (i >= elementCount)
+ throw new NoSuchElementException();
+ return (T) elementData[i++];
+ }
+ };
+ }
+
+ /**
+ * Returns true when <code>elem</code> is contained in this Vector.
+ *
+ * @param elem the element to check
+ * @return true if the object is contained in this Vector, false otherwise
+ */
+ public boolean contains(Object elem)
+ {
+ return indexOf(elem, 0) >= 0;
+ }
+
+ /**
+ * Returns the first occurrence of <code>elem</code> in the Vector, or -1 if
+ * <code>elem</code> is not found.
+ *
+ * @param elem the object to search for
+ * @return the index of the first occurrence, or -1 if not found
+ */
+ public int indexOf(Object elem)
+ {
+ return indexOf(elem, 0);
+ }
+
+ /**
+ * Searches the vector starting at <code>index</code> for object
+ * <code>elem</code> and returns the index of the first occurrence of this
+ * Object. If the object is not found, or index is larger than the size
+ * of the vector, -1 is returned.
+ *
+ * @param e the Object to search for
+ * @param index start searching at this index
+ * @return the index of the next occurrence, or -1 if it is not found
+ * @throws IndexOutOfBoundsException if index &lt; 0
+ */
+ public synchronized int indexOf(Object e, int index)
+ {
+ for (int i = index; i < elementCount; i++)
+ if (equals(e, elementData[i]))
+ return i;
+ return -1;
+ }
+
+ /**
+ * Returns the last index of <code>elem</code> within this Vector, or -1
+ * if the object is not within the Vector.
+ *
+ * @param elem the object to search for
+ * @return the last index of the object, or -1 if not found
+ */
+ public int lastIndexOf(Object elem)
+ {
+ return lastIndexOf(elem, elementCount - 1);
+ }
+
+ /**
+ * Returns the index of the first occurrence of <code>elem</code>, when
+ * searching backwards from <code>index</code>. If the object does not
+ * occur in this Vector, or index is less than 0, -1 is returned.
+ *
+ * @param e the object to search for
+ * @param index the index to start searching in reverse from
+ * @return the index of the Object if found, -1 otherwise
+ * @throws IndexOutOfBoundsException if index &gt;= size()
+ */
+ public synchronized int lastIndexOf(Object e, int index)
+ {
+ checkBoundExclusive(index);
+ for (int i = index; i >= 0; i--)
+ if (equals(e, elementData[i]))
+ return i;
+ return -1;
+ }
+
+ /**
+ * Returns the Object stored at <code>index</code>.
+ *
+ * @param index the index of the Object to retrieve
+ * @return the object at <code>index</code>
+ * @throws ArrayIndexOutOfBoundsException index &lt; 0 || index &gt;= size()
+ * @see #get(int)
+ */
+ @SuppressWarnings("unchecked")
+ public synchronized T elementAt(int index)
+ {
+ checkBoundExclusive(index);
+ return (T) elementData[index];
+ }
+
+ /**
+ * Returns the first element (index 0) in the Vector.
+ *
+ * @return the first Object in the Vector
+ * @throws NoSuchElementException the Vector is empty
+ */
+ @SuppressWarnings("unchecked")
+ public synchronized T firstElement()
+ {
+ if (elementCount == 0)
+ throw new NoSuchElementException();
+
+ return (T) elementData[0];
+ }
+
+ /**
+ * Returns the last element in the Vector.
+ *
+ * @return the last Object in the Vector
+ * @throws NoSuchElementException the Vector is empty
+ */
+ @SuppressWarnings("unchecked")
+ public synchronized T lastElement()
+ {
+ if (elementCount == 0)
+ throw new NoSuchElementException();
+
+ return (T) elementData[elementCount - 1];
+ }
+
+ /**
+ * Changes the element at <code>index</code> to be <code>obj</code>
+ *
+ * @param obj the object to store
+ * @param index the position in the Vector to store the object
+ * @throws ArrayIndexOutOfBoundsException the index is out of range
+ * @see #set(int, Object)
+ */
+ public void setElementAt(T obj, int index)
+ {
+ set(index, obj);
+ }
+
+ /**
+ * Removes the element at <code>index</code>, and shifts all elements at
+ * positions greater than index to their index - 1.
+ *
+ * @param index the index of the element to remove
+ * @throws ArrayIndexOutOfBoundsException index &lt; 0 || index &gt;= size();
+ * @see #remove(int)
+ */
+ public void removeElementAt(int index)
+ {
+ remove(index);
+ }
+
+ /**
+ * Inserts a new element into the Vector at <code>index</code>. Any elements
+ * at or greater than index are shifted up one position.
+ *
+ * @param obj the object to insert
+ * @param index the index at which the object is inserted
+ * @throws ArrayIndexOutOfBoundsException index &lt; 0 || index &gt; size()
+ * @see #add(int, Object)
+ */
+ public synchronized void insertElementAt(T obj, int index)
+ {
+ checkBoundInclusive(index);
+ if (elementCount == elementData.length)
+ ensureCapacity(elementCount + 1);
+ modCount++;
+ System.arraycopy(elementData, index, elementData, index + 1,
+ elementCount - index);
+ elementCount++;
+ elementData[index] = obj;
+ }
+
+ /**
+ * Adds an element to the Vector at the end of the Vector. The vector
+ * is increased by ensureCapacity(size() + 1) if needed.
+ *
+ * @param obj the object to add to the Vector
+ */
+ public synchronized void addElement(T obj)
+ {
+ if (elementCount == elementData.length)
+ ensureCapacity(elementCount + 1);
+ modCount++;
+ elementData[elementCount++] = obj;
+ }
+
+ /**
+ * Removes the first (the lowest index) occurrence of the given object from
+ * the Vector. If such a remove was performed (the object was found), true
+ * is returned. If there was no such object, false is returned.
+ *
+ * @param obj the object to remove from the Vector
+ * @return true if the Object was in the Vector, false otherwise
+ * @see #remove(Object)
+ */
+ public synchronized boolean removeElement(Object obj)
+ {
+ int idx = indexOf(obj, 0);
+ if (idx >= 0)
+ {
+ remove(idx);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Removes all elements from the Vector. Note that this does not
+ * resize the internal data array.
+ *
+ * @see #clear()
+ */
+ public synchronized void removeAllElements()
+ {
+ if (elementCount == 0)
+ return;
+
+ modCount++;
+ Arrays.fill(elementData, 0, elementCount, null);
+ elementCount = 0;
+ }
+
+ /**
+ * Creates a new Vector with the same contents as this one. The clone is
+ * shallow; elements are not cloned.
+ *
+ * @return the clone of this vector
+ */
+ public synchronized Object clone()
+ {
+ try
+ {
+ Vector clone = (Vector) super.clone();
+ clone.elementData = (Object[]) elementData.clone();
+ return clone;
+ }
+ catch (CloneNotSupportedException ex)
+ {
+ // Impossible to get here.
+ throw new InternalError(ex.toString());
+ }
+ }
+
+ /**
+ * Returns an Object array with the contents of this Vector, in the order
+ * they are stored within this Vector. Note that the Object array returned
+ * is not the internal data array, and that it holds only the elements
+ * within the Vector. This is similar to creating a new Object[] with the
+ * size of this Vector, then calling Vector.copyInto(yourArray).
+ *
+ * @return an Object[] containing the contents of this Vector in order
+ * @since 1.2
+ */
+ public synchronized Object[] toArray()
+ {
+ Object[] newArray = new Object[elementCount];
+ copyInto(newArray);
+ return newArray;
+ }
+
+ /**
+ * Returns an array containing the contents of this Vector.
+ * If the provided array is large enough, the contents are copied
+ * into that array, and a null is placed in the position size().
+ * In this manner, you can obtain the size of a Vector by the position
+ * of the null element, if you know the vector does not itself contain
+ * null entries. If the array is not large enough, reflection is used
+ * to create a bigger one of the same runtime type.
+ *
+ * @param a an array to copy the Vector into if large enough
+ * @return an array with the contents of this Vector in order
+ * @throws ArrayStoreException the runtime type of the provided array
+ * cannot hold the elements of the Vector
+ * @throws NullPointerException if <code>a</code> is null
+ * @since 1.2
+ */
+ public synchronized <S> S[] toArray(S[] a)
+ {
+ if (a.length < elementCount)
+ a = (S[]) Array.newInstance(a.getClass().getComponentType(),
+ elementCount);
+ else if (a.length > elementCount)
+ a[elementCount] = null;
+ System.arraycopy(elementData, 0, a, 0, elementCount);
+ return a;
+ }
+
+ /**
+ * Returns the element at position <code>index</code>.
+ *
+ * @param index the position from which an element will be retrieved
+ * @return the element at that position
+ * @throws ArrayIndexOutOfBoundsException index &lt; 0 || index &gt;= size()
+ * @since 1.2
+ */
+ public T get(int index)
+ {
+ return elementAt(index);
+ }
+
+ /**
+ * Puts <code>element</code> into the Vector at position <code>index</code>
+ * and returns the Object that previously occupied that position.
+ *
+ * @param index the index within the Vector to place the Object
+ * @param element the Object to store in the Vector
+ * @return the previous object at the specified index
+ * @throws ArrayIndexOutOfBoundsException index &lt; 0 || index &gt;= size()
+ * @since 1.2
+ */
+ @SuppressWarnings("unchecked")
+ public synchronized T set(int index, T element)
+ {
+ checkBoundExclusive(index);
+ T temp = (T) elementData[index];
+ elementData[index] = element;
+ return temp;
+ }
+
+ /**
+ * Adds an object to the Vector.
+ *
+ * @param o the element to add to the Vector
+ * @return true, as specified by List
+ * @since 1.2
+ */
+ public boolean add(T o)
+ {
+ addElement(o);
+ return true;
+ }
+
+ /**
+ * Removes the given Object from the Vector. If it exists, true
+ * is returned, if not, false is returned.
+ *
+ * @param o the object to remove from the Vector
+ * @return true if the Object existed in the Vector, false otherwise
+ * @since 1.2
+ */
+ public boolean remove(Object o)
+ {
+ return removeElement(o);
+ }
+
+ /**
+ * Adds an object at the specified index. Elements at or above
+ * index are shifted up one position.
+ *
+ * @param index the index at which to add the element
+ * @param element the element to add to the Vector
+ * @throws ArrayIndexOutOfBoundsException index &lt; 0 || index &gt; size()
+ * @since 1.2
+ */
+ public void add(int index, T element)
+ {
+ insertElementAt(element, index);
+ }
+
+ /**
+ * Removes the element at the specified index, and returns it.
+ *
+ * @param index the position from which to remove the element
+ * @return the object removed
+ * @throws ArrayIndexOutOfBoundsException index &lt; 0 || index &gt;= size()
+ * @since 1.2
+ */
+ @SuppressWarnings("unchecked")
+ public synchronized T remove(int index)
+ {
+ checkBoundExclusive(index);
+ T temp = (T) elementData[index];
+ modCount++;
+ elementCount--;
+ if (index < elementCount)
+ System.arraycopy(elementData, index + 1, elementData, index,
+ elementCount - index);
+ elementData[elementCount] = null;
+ return temp;
+ }
+
+ /**
+ * Clears all elements in the Vector and sets its size to 0.
+ */
+ public void clear()
+ {
+ removeAllElements();
+ }
+
+ /**
+ * Returns true if this Vector contains all the elements in c.
+ *
+ * @param c the collection to compare to
+ * @return true if this vector contains all elements of c
+ * @throws NullPointerException if c is null
+ * @since 1.2
+ */
+ public synchronized boolean containsAll(Collection<?> c)
+ {
+ // Here just for the sychronization.
+ return super.containsAll(c);
+ }
+
+ /**
+ * Appends all elements of the given collection to the end of this Vector.
+ * Behavior is undefined if the collection is modified during this operation
+ * (for example, if this == c).
+ *
+ * @param c the collection to append
+ * @return true if this vector changed, in other words c was not empty
+ * @throws NullPointerException if c is null
+ * @since 1.2
+ */
+ public synchronized boolean addAll(Collection<? extends T> c)
+ {
+ return addAll(elementCount, c);
+ }
+
+ /**
+ * Remove from this vector all elements contained in the given collection.
+ *
+ * @param c the collection to filter out
+ * @return true if this vector changed
+ * @throws NullPointerException if c is null
+ * @since 1.2
+ */
+ public synchronized boolean removeAll(Collection<?> c)
+ {
+ // The NullPointerException is thrown implicitly when the Vector
+ // is not empty and c is null. The RI allows null arguments when
+ // the vector is empty. See Mauve test:
+ // gnu/testlet/java/util/Vector/removeAll.java
+
+ int i;
+ int j;
+ for (i = 0; i < elementCount; i++)
+ if (c.contains(elementData[i]))
+ break;
+ if (i == elementCount)
+ return false;
+
+ modCount++;
+ for (j = i++; i < elementCount; i++)
+ if (! c.contains(elementData[i]))
+ elementData[j++] = elementData[i];
+ elementCount -= i - j;
+ return true;
+ }
+
+ /**
+ * Retain in this vector only the elements contained in the given collection.
+ *
+ * @param c the collection to filter by
+ * @return true if this vector changed
+ * @throws NullPointerException if c is null
+ * @since 1.2
+ */
+ public synchronized boolean retainAll(Collection<?> c)
+ {
+ // The NullPointerException is thrown implicitly when the Vector
+ // is not empty and c is null. The RI allows null arguments when
+ // the vector is empty. See Mauve test:
+ // gnu/testlet/java/util/Vector/retainAll.java
+
+ int i;
+ int j;
+ for (i = 0; i < elementCount; i++)
+ if (! c.contains(elementData[i]))
+ break;
+ if (i == elementCount)
+ return false;
+
+ modCount++;
+ for (j = i++; i < elementCount; i++)
+ if (c.contains(elementData[i]))
+ elementData[j++] = elementData[i];
+ elementCount -= i - j;
+ return true;
+ }
+
+ /**
+ * Inserts all elements of the given collection at the given index of
+ * this Vector. Behavior is undefined if the collection is modified during
+ * this operation (for example, if this == c).
+ *
+ * @param c the collection to append
+ * @return true if this vector changed, in other words c was not empty
+ * @throws NullPointerException if c is null
+ * @throws ArrayIndexOutOfBoundsException index &lt; 0 || index &gt; size()
+ * @since 1.2
+ */
+ public synchronized boolean addAll(int index, Collection<? extends T> c)
+ {
+ checkBoundInclusive(index);
+ Iterator<? extends T> itr = c.iterator();
+ int csize = c.size();
+
+ modCount++;
+ ensureCapacity(elementCount + csize);
+ int end = index + csize;
+ if (elementCount > 0 && index != elementCount)
+ System.arraycopy(elementData, index,
+ elementData, end, elementCount - index);
+ elementCount += csize;
+ for ( ; index < end; index++)
+ elementData[index] = itr.next();
+ return (csize > 0);
+ }
+
+ /**
+ * Compares this to the given object.
+ *
+ * @param o the object to compare to
+ * @return true if the two are equal
+ * @since 1.2
+ */
+ public synchronized boolean equals(Object o)
+ {
+ // Here just for the sychronization.
+ return super.equals(o);
+ }
+
+ /**
+ * Computes the hashcode of this object.
+ *
+ * @return the hashcode
+ * @since 1.2
+ */
+ public synchronized int hashCode()
+ {
+ // Here just for the sychronization.
+ return super.hashCode();
+ }
+
+ /**
+ * Returns a string representation of this Vector in the form
+ * "[element0, element1, ... elementN]".
+ *
+ * @return the String representation of this Vector
+ */
+ public synchronized String toString()
+ {
+ // Here just for the sychronization.
+ return super.toString();
+ }
+
+ /**
+ * Obtain a List view of a subsection of this list, from fromIndex
+ * (inclusive) to toIndex (exclusive). If the two indices are equal, the
+ * sublist is empty. The returned list is modifiable, and changes in one
+ * reflect in the other. If this list is structurally modified in
+ * any way other than through the returned list, the result of any subsequent
+ * operations on the returned list is undefined.
+ * <p>
+ *
+ * @param fromIndex the index that the returned list should start from
+ * (inclusive)
+ * @param toIndex the index that the returned list should go to (exclusive)
+ * @return a List backed by a subsection of this vector
+ * @throws IndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; size()
+ * @throws IllegalArgumentException if fromIndex &gt; toIndex
+ * @see ConcurrentModificationException
+ * @since 1.2
+ */
+ public synchronized List<T> subList(int fromIndex, int toIndex)
+ {
+ List<T> sub = super.subList(fromIndex, toIndex);
+ // We must specify the correct object to synchronize upon, hence the
+ // use of a non-public API
+ return new Collections.SynchronizedList<T>(this, sub);
+ }
+
+ /**
+ * Removes a range of elements from this list.
+ * Does nothing when toIndex is equal to fromIndex.
+ *
+ * @param fromIndex the index to start deleting from (inclusive)
+ * @param toIndex the index to delete up to (exclusive)
+ * @throws IndexOutOfBoundsException if fromIndex &gt; toIndex
+ */
+ // This does not need to be synchronized, because it is only called through
+ // clear() of a sublist, and clear() had already synchronized.
+ protected void removeRange(int fromIndex, int toIndex)
+ {
+ int change = toIndex - fromIndex;
+ if (change > 0)
+ {
+ modCount++;
+ System.arraycopy(elementData, toIndex, elementData, fromIndex,
+ elementCount - toIndex);
+ int save = elementCount;
+ elementCount -= change;
+ Arrays.fill(elementData, elementCount, save, null);
+ }
+ else if (change < 0)
+ throw new IndexOutOfBoundsException();
+ }
+
+ /**
+ * Checks that the index is in the range of possible elements (inclusive).
+ *
+ * @param index the index to check
+ * @throws ArrayIndexOutOfBoundsException if index &gt; size
+ */
+ private void checkBoundInclusive(int index)
+ {
+ // Implementation note: we do not check for negative ranges here, since
+ // use of a negative index will cause an ArrayIndexOutOfBoundsException
+ // with no effort on our part.
+ if (index > elementCount)
+ raiseBoundsError(index, " > ");
+ }
+
+ /**
+ * Checks that the index is in the range of existing elements (exclusive).
+ *
+ * @param index the index to check
+ * @throws ArrayIndexOutOfBoundsException if index &gt;= size
+ */
+ private void checkBoundExclusive(int index)
+ {
+ // Implementation note: we do not check for negative ranges here, since
+ // use of a negative index will cause an ArrayIndexOutOfBoundsException
+ // with no effort on our part.
+ if (index >= elementCount)
+ raiseBoundsError(index, " >= ");
+ }
+
+ /**
+ * Raise the ArrayIndexOfOutBoundsException.
+ *
+ * @param index the index of the access
+ * @param operator the operator to include in the error message
+ * @throws IndexOutOfBoundsException unconditionally
+ */
+ private void raiseBoundsError(int index, String operator)
+ {
+ // Implementaion note: put in a separate method to make the JITs job easier
+ // (separate common from uncommon code at method boundaries when trivial to
+ // do so).
+ throw new ArrayIndexOutOfBoundsException(index + operator + elementCount);
+ }
+
+ /**
+ * Serializes this object to the given stream.
+ *
+ * @param s the stream to write to
+ * @throws IOException if the underlying stream fails
+ * @serialData just calls default write function
+ */
+ private synchronized void writeObject(ObjectOutputStream s)
+ throws IOException
+ {
+ s.defaultWriteObject();
+ }
+
+}
diff --git a/libjava/classpath/java/util/WeakHashMap.java b/libjava/classpath/java/util/WeakHashMap.java
new file mode 100644
index 000000000..3e4d34796
--- /dev/null
+++ b/libjava/classpath/java/util/WeakHashMap.java
@@ -0,0 +1,880 @@
+/* WeakHashMap -- a hashtable that keeps only weak references
+ to its keys, allowing the virtual machine to reclaim them
+ Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+
+/**
+ * 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.
+ *
+ * <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 when the <code>equals</code>
+ * method is overloaded, such as String keys, but you should be prepared
+ * to deal with some entries disappearing spontaneously.
+ *
+ * <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.
+ *
+ * <p>A weak hash map is not meant for caches; use a normal map, with
+ * soft references as values instead, or try {@link LinkedHashMap}.
+ *
+ * <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.
+ *
+ * <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 Tom Tromey (tromey@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ *
+ * @see HashMap
+ * @see WeakReference
+ * @see LinkedHashMap
+ * @since 1.2
+ * @status updated to 1.4 (partial 1.5)
+ */
+public class WeakHashMap<K,V> extends AbstractMap<K,V>
+{
+ // 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
+ * value.
+ */
+ private static final int DEFAULT_CAPACITY = 11;
+
+ /**
+ * The default load factor of a HashMap.
+ */
+ private static final float DEFAULT_LOAD_FACTOR = 0.75F;
+
+ /**
+ * This is used instead of the key value <i>null</i>. It is needed
+ * to distinguish between an null key and a removed key.
+ */
+ // Package visible for use by nested classes.
+ static final Object NULL_KEY = new Object()
+ {
+ /**
+ * Sets the hashCode to 0, since that's what null would map to.
+ * @return the hash code 0
+ */
+ public int hashCode()
+ {
+ return 0;
+ }
+
+ /**
+ * Compares this key to the given object. Normally, an object should
+ * NEVER compare equal to null, but since we don't publicize NULL_VALUE,
+ * it saves bytecode to do so here.
+ * @return true iff o is this or null
+ */
+ public boolean equals(Object o)
+ {
+ return null == o || this == o;
+ }
+ };
+
+ /**
+ * The reference queue where our buckets (which are WeakReferences) are
+ * registered to.
+ */
+ private final ReferenceQueue queue;
+
+ /**
+ * The number of entries in this hash map.
+ */
+ // Package visible for use by nested classes.
+ int size;
+
+ /**
+ * The load factor of this WeakHashMap. This is the maximum ratio of
+ * size versus number of buckets. If size grows the number of buckets
+ * must grow, too.
+ */
+ private float loadFactor;
+
+ /**
+ * 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 <code>rehash()</code>.
+ */
+ private int threshold;
+
+ /**
+ * The number of structural modifications. This is used by
+ * iterators, to see if they should fail. This doesn't count
+ * the silent key removals, when a weak reference is cleared
+ * by the garbage collection. Instead the iterators must make
+ * sure to have strong references to the entries they rely on.
+ */
+ // Package visible for use by nested classes.
+ int modCount;
+
+ /**
+ * The entry set. There is only one instance per hashmap, namely
+ * theEntrySet. Note that the entry set may silently shrink, just
+ * like the WeakHashMap.
+ */
+ private final class WeakEntrySet extends AbstractSet
+ {
+ /**
+ * Non-private constructor to reduce bytecode emitted.
+ */
+ WeakEntrySet()
+ {
+ }
+
+ /**
+ * Returns the size of this set.
+ *
+ * @return the set size
+ */
+ public int size()
+ {
+ return size;
+ }
+
+ /**
+ * Returns an iterator for all entries.
+ *
+ * @return an Entry iterator
+ */
+ public Iterator iterator()
+ {
+ return new Iterator()
+ {
+ /**
+ * The entry that was returned by the last
+ * <code>next()</code> call. This is also the entry whose
+ * bucket should be removed by the <code>remove</code> call. <br>
+ *
+ * It is null, if the <code>next</code> method wasn't
+ * called yet, or if the entry was already removed. <br>
+ *
+ * Remembering this entry here will also prevent it from
+ * being removed under us, since the entry strongly refers
+ * to the key.
+ */
+ WeakBucket.WeakEntry lastEntry;
+
+ /**
+ * The entry that will be returned by the next
+ * <code>next()</code> call. It is <code>null</code> if there
+ * is no further entry. <br>
+ *
+ * Remembering this entry here will also prevent it from
+ * being removed under us, since the entry strongly refers
+ * to the key.
+ */
+ WeakBucket.WeakEntry nextEntry = findNext(null);
+
+ /**
+ * The known number of modification to the list, if it differs
+ * from the real number, we throw an exception.
+ */
+ int knownMod = modCount;
+
+ /**
+ * Check the known number of modification to the number of
+ * modifications of the table. If it differs from the real
+ * number, we throw an exception.
+ * @throws ConcurrentModificationException if the number
+ * of modifications doesn't match.
+ */
+ private void checkMod()
+ {
+ // This method will get inlined.
+ cleanQueue();
+ if (knownMod != modCount)
+ throw new ConcurrentModificationException(knownMod + " != "
+ + modCount);
+ }
+
+ /**
+ * Get a strong reference to the next entry after
+ * lastBucket.
+ * @param lastEntry the previous bucket, or null if we should
+ * get the first entry.
+ * @return the next entry.
+ */
+ private WeakBucket.WeakEntry findNext(WeakBucket.WeakEntry lastEntry)
+ {
+ int slot;
+ WeakBucket nextBucket;
+ if (lastEntry != null)
+ {
+ nextBucket = lastEntry.getBucket().next;
+ slot = lastEntry.getBucket().slot;
+ }
+ else
+ {
+ nextBucket = buckets[0];
+ slot = 0;
+ }
+
+ while (true)
+ {
+ while (nextBucket != null)
+ {
+ WeakBucket.WeakEntry entry = nextBucket.getEntry();
+ if (entry != null)
+ // This is the next entry.
+ return entry;
+
+ // Entry was cleared, try next.
+ nextBucket = nextBucket.next;
+ }
+
+ slot++;
+ if (slot == buckets.length)
+ // No more buckets, we are through.
+ return null;
+
+ nextBucket = buckets[slot];
+ }
+ }
+
+ /**
+ * Checks if there are more entries.
+ * @return true, iff there are more elements.
+ */
+ public boolean hasNext()
+ {
+ return nextEntry != null;
+ }
+
+ /**
+ * Returns the next entry.
+ * @return the next entry.
+ * @throws ConcurrentModificationException if the hash map was
+ * modified.
+ * @throws NoSuchElementException if there is no entry.
+ */
+ public Object next()
+ {
+ checkMod();
+ if (nextEntry == null)
+ throw new NoSuchElementException();
+ lastEntry = nextEntry;
+ nextEntry = findNext(lastEntry);
+ return lastEntry;
+ }
+
+ /**
+ * Removes the last returned entry from this set. This will
+ * also remove the bucket of the underlying weak hash map.
+ * @throws ConcurrentModificationException if the hash map was
+ * modified.
+ * @throws IllegalStateException if <code>next()</code> was
+ * never called or the element was already removed.
+ */
+ public void remove()
+ {
+ checkMod();
+ if (lastEntry == null)
+ throw new IllegalStateException();
+ modCount++;
+ internalRemove(lastEntry.getBucket());
+ lastEntry = null;
+ knownMod++;
+ }
+ };
+ }
+ }
+
+ /**
+ * A bucket is a weak reference to the key, that contains a strong
+ * reference to the value, a pointer to the next bucket and its slot
+ * number. <br>
+ *
+ * It would be cleaner to have a WeakReference as field, instead of
+ * extending it, but if a weak reference gets cleared, we only get
+ * the weak reference (by queue.poll) and wouldn't know where to
+ * look for this reference in the hashtable, to remove that entry.
+ *
+ * @author Jochen Hoenicke
+ */
+ private static class WeakBucket<K, V> extends WeakReference<K>
+ {
+ /**
+ * The value of this entry. The key is stored in the weak
+ * reference that we extend.
+ */
+ V value;
+
+ /**
+ * The next bucket describing another entry that uses the same
+ * slot.
+ */
+ WeakBucket<K, V> next;
+
+ /**
+ * The slot of this entry. This should be
+ * <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.
+ */
+ int slot;
+
+ /**
+ * Creates a new bucket for the given key/value pair and the specified
+ * slot.
+ * @param key the key
+ * @param queue the queue the weak reference belongs to
+ * @param value the value
+ * @param slot the slot. This must match the slot where this bucket
+ * will be enqueued.
+ */
+ public WeakBucket(K key, ReferenceQueue queue, V value,
+ int slot)
+ {
+ super(key, queue);
+ this.value = value;
+ this.slot = slot;
+ }
+
+ /**
+ * This class gives the <code>Entry</code> representation of the
+ * current bucket. It also keeps a strong reference to the
+ * key; bad things may happen otherwise.
+ */
+ class WeakEntry implements Map.Entry<K, V>
+ {
+ /**
+ * The strong ref to the key.
+ */
+ K key;
+
+ /**
+ * Creates a new entry for the key.
+ * @param key the key
+ */
+ public WeakEntry(K key)
+ {
+ this.key = key;
+ }
+
+ /**
+ * Returns the underlying bucket.
+ * @return the owning bucket
+ */
+ public WeakBucket getBucket()
+ {
+ return WeakBucket.this;
+ }
+
+ /**
+ * Returns the key.
+ * @return the key
+ */
+ public K getKey()
+ {
+ return key == NULL_KEY ? null : key;
+ }
+
+ /**
+ * Returns the value.
+ * @return the value
+ */
+ public V getValue()
+ {
+ return value;
+ }
+
+ /**
+ * This changes the value. This change takes place in
+ * the underlying hash map.
+ * @param newVal the new value
+ * @return the old value
+ */
+ public V setValue(V newVal)
+ {
+ V oldVal = value;
+ value = newVal;
+ return oldVal;
+ }
+
+ /**
+ * The hashCode as specified in the Entry interface.
+ * @return the hash code
+ */
+ public int hashCode()
+ {
+ return key.hashCode() ^ WeakHashMap.hashCode(value);
+ }
+
+ /**
+ * The equals method as specified in the Entry interface.
+ * @param o the object to compare to
+ * @return true iff o represents the same key/value pair
+ */
+ public boolean equals(Object o)
+ {
+ if (o instanceof Map.Entry)
+ {
+ Map.Entry e = (Map.Entry) o;
+ return WeakHashMap.equals(getKey(), e.getKey())
+ && WeakHashMap.equals(value, e.getValue());
+ }
+ return false;
+ }
+
+ public String toString()
+ {
+ return getKey() + "=" + value;
+ }
+ }
+
+ /**
+ * This returns the entry stored in this bucket, or null, if the
+ * bucket got cleared in the mean time.
+ * @return the Entry for this bucket, if it exists
+ */
+ WeakEntry getEntry()
+ {
+ final K key = this.get();
+ if (key == null)
+ return null;
+ return new WeakEntry(key);
+ }
+ }
+
+ /**
+ * The entry set returned by <code>entrySet()</code>.
+ */
+ private final WeakEntrySet theEntrySet;
+
+ /**
+ * The hash buckets. These are linked lists. Package visible for use in
+ * nested classes.
+ */
+ WeakBucket[] buckets;
+
+ /**
+ * Creates a new weak hash map with default load factor and default
+ * capacity.
+ */
+ public WeakHashMap()
+ {
+ this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR);
+ }
+
+ /**
+ * Creates a new weak hash map with default load factor and the given
+ * capacity.
+ * @param initialCapacity the initial capacity
+ * @throws IllegalArgumentException if initialCapacity is negative
+ */
+ public WeakHashMap(int initialCapacity)
+ {
+ this(initialCapacity, DEFAULT_LOAD_FACTOR);
+ }
+
+ /**
+ * Creates a new weak hash map with the given initial capacity and
+ * load factor.
+ * @param initialCapacity the initial capacity.
+ * @param loadFactor the load factor (see class description of HashMap).
+ * @throws IllegalArgumentException if initialCapacity is negative, or
+ * loadFactor is non-positive
+ */
+ public WeakHashMap(int initialCapacity, float loadFactor)
+ {
+ // Check loadFactor for NaN as well.
+ if (initialCapacity < 0 || ! (loadFactor > 0))
+ throw new IllegalArgumentException();
+ if (initialCapacity == 0)
+ initialCapacity = 1;
+ this.loadFactor = loadFactor;
+ threshold = (int) (initialCapacity * loadFactor);
+ theEntrySet = new WeakEntrySet();
+ queue = new ReferenceQueue();
+ buckets = new WeakBucket[initialCapacity];
+ }
+
+ /**
+ * Construct a new WeakHashMap with the same mappings as the given map.
+ * The WeakHashMap has a default load factor of 0.75.
+ *
+ * @param m the map to copy
+ * @throws NullPointerException if m is null
+ * @since 1.3
+ */
+ public WeakHashMap(Map<? extends K, ? extends V> m)
+ {
+ this(m.size(), DEFAULT_LOAD_FACTOR);
+ putAll(m);
+ }
+
+ /**
+ * Simply hashes a non-null Object to its array index.
+ * @param key the key to hash
+ * @return its slot number
+ */
+ private int hash(Object key)
+ {
+ return Math.abs(key.hashCode() % buckets.length);
+ }
+
+ /**
+ * Cleans the reference queue. This will poll all references (which
+ * are WeakBuckets) from the queue and remove them from this map.
+ * This will not change modCount, even if it modifies the map. The
+ * iterators have to make sure that nothing bad happens. <br>
+ *
+ * Currently the iterator maintains a strong reference to the key, so
+ * that is no problem.
+ */
+ // Package visible for use by nested classes.
+ void cleanQueue()
+ {
+ Object bucket = queue.poll();
+ while (bucket != null)
+ {
+ internalRemove((WeakBucket) bucket);
+ bucket = queue.poll();
+ }
+ }
+
+ /**
+ * Rehashes this hashtable. This will be called by the
+ * <code>add()</code> method if the size grows beyond the threshold.
+ * It will grow the bucket size at least by factor two and allocates
+ * new buckets.
+ */
+ private void rehash()
+ {
+ WeakBucket[] oldBuckets = buckets;
+ int newsize = buckets.length * 2 + 1; // XXX should be prime.
+ threshold = (int) (newsize * loadFactor);
+ buckets = new WeakBucket[newsize];
+
+ // Now we have to insert the buckets again.
+ for (int i = 0; i < oldBuckets.length; i++)
+ {
+ WeakBucket bucket = oldBuckets[i];
+ WeakBucket nextBucket;
+ while (bucket != null)
+ {
+ nextBucket = bucket.next;
+
+ Object key = bucket.get();
+ if (key == null)
+ {
+ // This bucket should be removed; it is probably
+ // already on the reference queue. We don't insert it
+ // at all, and mark it as cleared.
+ bucket.slot = -1;
+ size--;
+ }
+ else
+ {
+ // Add this bucket to its new slot.
+ int slot = hash(key);
+ bucket.slot = slot;
+ bucket.next = buckets[slot];
+ buckets[slot] = bucket;
+ }
+ bucket = nextBucket;
+ }
+ }
+ }
+
+ /**
+ * Finds the entry corresponding to key. Since it returns an Entry
+ * it will also prevent the key from being removed under us.
+ * @param key the key, may be null
+ * @return The WeakBucket.WeakEntry or null, if the key wasn't found.
+ */
+ private WeakBucket.WeakEntry internalGet(Object key)
+ {
+ if (key == null)
+ key = NULL_KEY;
+ int slot = hash(key);
+ WeakBucket bucket = buckets[slot];
+ while (bucket != null)
+ {
+ WeakBucket.WeakEntry entry = bucket.getEntry();
+ if (entry != null && equals(key, entry.key))
+ return entry;
+
+ bucket = bucket.next;
+ }
+ return null;
+ }
+
+ /**
+ * Adds a new key/value pair to the hash map.
+ * @param key the key. This mustn't exists in the map. It may be null.
+ * @param value the value.
+ */
+ private void internalAdd(Object key, Object value)
+ {
+ if (key == null)
+ key = NULL_KEY;
+ int slot = hash(key);
+ WeakBucket bucket = new WeakBucket(key, queue, value, slot);
+ bucket.next = buckets[slot];
+ buckets[slot] = bucket;
+ size++;
+ }
+
+ /**
+ * 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).
+ * Package visible for use in nested classes.
+ *
+ * @param bucket the bucket to remove.
+ */
+ void internalRemove(WeakBucket bucket)
+ {
+ int slot = bucket.slot;
+ if (slot == -1)
+ // This bucket was already removed.
+ return;
+
+ // Mark the bucket as removed. This is necessary, since the
+ // bucket may be enqueued later by the garbage collection, and
+ // internalRemove will be called a second time.
+ bucket.slot = -1;
+
+ WeakBucket prev = null;
+ WeakBucket next = buckets[slot];
+ while (next != bucket)
+ {
+ if (next == null) throw new InternalError("WeakHashMap in incosistent state");
+ prev = next;
+ next = prev.next;
+ }
+ if (prev == null)
+ buckets[slot] = bucket.next;
+ else
+ prev.next = bucket.next;
+
+ size--;
+ }
+
+ /**
+ * Returns the size of this hash map. Note that the size() may shrink
+ * spontaneously, if the some of the keys were only weakly reachable.
+ * @return the number of entries in this hash map.
+ */
+ public int size()
+ {
+ cleanQueue();
+ return size;
+ }
+
+ /**
+ * Tells if the map is empty. Note that the result may change
+ * spontanously, if all of the keys were only weakly reachable.
+ * @return true, iff the map is empty.
+ */
+ public boolean isEmpty()
+ {
+ cleanQueue();
+ return size == 0;
+ }
+
+ /**
+ * Tells if the map contains the given key. Note that the result
+ * may change spontanously, if the key was only weakly
+ * reachable.
+ * @param key the key to look for
+ * @return true, iff the map contains an entry for the given key.
+ */
+ public boolean containsKey(Object key)
+ {
+ cleanQueue();
+ return internalGet(key) != null;
+ }
+
+ /**
+ * Gets the value the key is mapped to.
+ * @return the value the key was mapped to. It returns null if
+ * the key wasn't in this map, or if the mapped value was
+ * explicitly set to null.
+ */
+ public V get(Object key)
+ {
+ cleanQueue();
+ WeakBucket<K, V>.WeakEntry entry = internalGet(key);
+ return entry == null ? null : entry.getValue();
+ }
+
+ /**
+ * Adds a new key/value mapping to this map.
+ * @param key the key, may be null
+ * @param value the value, may be null
+ * @return the value the key was mapped to previously. It returns
+ * null if the key wasn't in this map, or if the mapped value
+ * was explicitly set to null.
+ */
+ public V put(K key, V value)
+ {
+ cleanQueue();
+ WeakBucket<K, V>.WeakEntry entry = internalGet(key);
+ if (entry != null)
+ return entry.setValue(value);
+
+ modCount++;
+ if (size >= threshold)
+ rehash();
+
+ internalAdd(key, value);
+ return null;
+ }
+
+ /**
+ * Removes the key and the corresponding value from this map.
+ * @param key the key. This may be null.
+ * @return the value the key was mapped to previously. It returns
+ * null if the key wasn't in this map, or if the mapped value was
+ * explicitly set to null.
+ */
+ public V remove(Object key)
+ {
+ cleanQueue();
+ WeakBucket<K, V>.WeakEntry entry = internalGet(key);
+ if (entry == null)
+ return null;
+
+ modCount++;
+ internalRemove(entry.getBucket());
+ return entry.getValue();
+ }
+
+ /**
+ * Returns a set representation of the entries in this map. This
+ * set will not have strong references to the keys, so they can be
+ * silently removed. The returned set has therefore the same
+ * strange behaviour (shrinking size(), disappearing entries) as
+ * this weak hash map.
+ * @return a set representation of the entries.
+ */
+ public Set<Map.Entry<K,V>> entrySet()
+ {
+ cleanQueue();
+ return theEntrySet;
+ }
+
+ /**
+ * Clears all entries from this map.
+ */
+ public void clear()
+ {
+ super.clear();
+ }
+
+ /**
+ * Returns true if the map contains at least one key which points to
+ * the specified object as a value. Note that the result
+ * may change spontanously, if its key was only weakly reachable.
+ * @param value the value to search for
+ * @return true if it is found in the set.
+ */
+ public boolean containsValue(Object value)
+ {
+ cleanQueue();
+ return super.containsValue(value);
+ }
+
+ /**
+ * Returns a set representation of the keys in this map. This
+ * set will not have strong references to the keys, so they can be
+ * silently removed. The returned set has therefore the same
+ * strange behaviour (shrinking size(), disappearing entries) as
+ * this weak hash map.
+ * @return a set representation of the keys.
+ */
+ public Set<K> keySet()
+ {
+ cleanQueue();
+ return super.keySet();
+ }
+
+ /**
+ * Puts all of the mappings from the given map into this one. If the
+ * key already exists in this map, its value is replaced.
+ * @param m the map to copy in
+ */
+ public void putAll(Map<? extends K, ? extends V> m)
+ {
+ super.putAll(m);
+ }
+
+ /**
+ * Returns a collection representation of the values in this map. This
+ * collection will not have strong references to the keys, so mappings
+ * can be silently removed. The returned collection has therefore the same
+ * strange behaviour (shrinking size(), disappearing entries) as
+ * this weak hash map.
+ * @return a collection representation of the values.
+ */
+ public Collection<V> values()
+ {
+ cleanQueue();
+ return super.values();
+ }
+} // class WeakHashMap
diff --git a/libjava/classpath/java/util/concurrent/CopyOnWriteArrayList.java b/libjava/classpath/java/util/concurrent/CopyOnWriteArrayList.java
new file mode 100644
index 000000000..f9f1ac1b4
--- /dev/null
+++ b/libjava/classpath/java/util/concurrent/CopyOnWriteArrayList.java
@@ -0,0 +1,1463 @@
+/* CopyOnWriteArrayList.java
+ Copyright (C) 2006 Free Software Foundation
+
+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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.concurrent;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+import java.lang.reflect.Array;
+
+import java.util.AbstractList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+import java.util.RandomAccess;
+
+/**
+ * A thread-safe implementation of an ArrayList. A CopyOnWriteArrayList is
+ * as special ArrayList which performs copies of the underlying storage
+ * each time a write (<code>remove</code>, <code>add</code> etc..) operation
+ * is performed.<br />
+ * <br />
+ * The update operation in this class run usually in <code>O(n)</code> or worse,
+ * but traversal operations are fast and efficient, especially when running in
+ * a multi-thread environment without the need to design complex synchronize
+ * mechanisms.<br />
+ * <br />
+ * <code>Iterator</code>s in this class work on a snapshot of the backing store
+ * at the moment the iterator itself was created, hence the iterator will not
+ * reflect changes in the underlying storage. Thus, update operation on the
+ * <code>Iterator</code>s are not supported, but as interferences from other
+ * threads are impossible, no <code>ConcurrentModificationException</code>
+ * will be ever thrown from within the <code>Iterator</code>.
+ * <br /><br />
+ * This class is especially useful when used with event handling, like the
+ * following code demonstrates:<br />
+ * <code><pre>
+ *
+ * CopyOnWriteArrayList<EventListener> listeners =
+ * new CopyOnWriteArrayList<EventListener>();
+ *
+ * [...]
+ *
+ * for (final EventListener listener : listeners)
+ * {
+ * Runnable dispatcher = new Runnable() {
+ * public void run()
+ * {
+ * listener.preferenceChange(event);
+ * }
+ * };
+ *
+ * Executor executor = Executors.newSingleThreadExecutor();
+ * executor.execute(dispatcher);
+ * }
+ * </pre></code>
+ *
+ * @since 1.5
+ */
+public class CopyOnWriteArrayList<E>
+ implements List<E>, RandomAccess, Cloneable, Serializable
+{
+ /**
+ *
+ */
+ private static final long serialVersionUID = 8673264195747942595L;
+
+ /**
+ * Where the data is stored.
+ */
+ private transient E[] data;
+
+ /**
+ * Construct a new ArrayList with the default capacity (16).
+ */
+ public CopyOnWriteArrayList()
+ {
+ data = (E[]) new Object[0];
+ }
+
+ /**
+ * Construct a new ArrayList, and initialize it with the elements in the
+ * supplied Collection. The initial capacity is 110% of the Collection's size.
+ *
+ * @param c
+ * the collection whose elements will initialize this list
+ * @throws NullPointerException
+ * if c is null
+ */
+ public CopyOnWriteArrayList(Collection< ? extends E> c)
+ {
+ // FIXME ... correct? use c.toArray()
+ data = (E[]) new Object[c.size()];
+ int index = 0;
+ for (E value : c)
+ data[index++] = value;
+ }
+
+ /**
+ * Construct a new ArrayList, and initialize it with the elements in the
+ * supplied array.
+ *
+ * @param array
+ * the array used to initialize this list
+ * @throws NullPointerException
+ * if array is null
+ */
+ public CopyOnWriteArrayList(E[] array)
+ {
+ data = (E[]) array.clone();
+ }
+
+ /**
+ * Returns the number of elements in this list.
+ *
+ * @return the list size
+ */
+ public int size()
+ {
+ return data.length;
+ }
+
+ /**
+ * Checks if the list is empty.
+ *
+ * @return true if there are no elements
+ */
+ public boolean isEmpty()
+ {
+ return data.length == 0;
+ }
+
+ /**
+ * Returns true if element is in this ArrayList.
+ *
+ * @param e
+ * the element whose inclusion in the List is being tested
+ * @return true if the list contains e
+ */
+ public boolean contains(Object e)
+ {
+ return indexOf(e) != -1;
+ }
+
+ /**
+ * Tests whether this collection contains all the elements in a given
+ * collection. This implementation iterates over the given collection,
+ * testing whether each element is contained in this collection. If any one
+ * is not, false is returned. Otherwise true is returned.
+ *
+ * @param c the collection to test against
+ * @return true if this collection contains all the elements in the given
+ * collection
+ * @throws NullPointerException if the given collection is null
+ * @see #contains(Object)
+ */
+ public boolean containsAll(Collection<?> c)
+ {
+ Iterator<?> itr = c.iterator();
+ int pos = c.size();
+ while (--pos >= 0)
+ if (!contains(itr.next()))
+ return false;
+ return true;
+ }
+
+ /**
+ * Returns the lowest index at which element appears in this List, or -1 if it
+ * does not appear.
+ *
+ * @param e
+ * the element whose inclusion in the List is being tested
+ * @return the index where e was found
+ */
+ public int indexOf(Object e)
+ {
+ E[] data = this.data;
+ for (int i = 0; i < data.length; i++)
+ if (equals(e, data[i]))
+ return i;
+ return -1;
+ }
+
+ /**
+ * Return the lowest index greater equal <code>index</code> at which
+ * <code>e</code> appears in this List, or -1 if it does not
+ * appear.
+ *
+ * @param e the element whose inclusion in the list is being tested
+ * @param index the index at which the search begins
+ * @return the index where <code>e</code> was found
+ */
+ public int indexOf(E e, int index)
+ {
+ E[] data = this.data;
+
+ for (int i = index; i < data.length; i++)
+ if (equals(e, data[i]))
+ return i;
+ return -1;
+ }
+
+ /**
+ * Returns the highest index at which element appears in this List, or -1 if
+ * it does not appear.
+ *
+ * @param e
+ * the element whose inclusion in the List is being tested
+ * @return the index where e was found
+ */
+ public int lastIndexOf(Object e)
+ {
+ E[] data = this.data;
+ for (int i = data.length - 1; i >= 0; i--)
+ if (equals(e, data[i]))
+ return i;
+ return -1;
+ }
+
+ /**
+ * Returns the highest index lesser equal <code>index</code> at
+ * which <code>e</code> appears in this List, or -1 if it does not
+ * appear.
+ *
+ * @param e the element whose inclusion in the list is being tested
+ * @param index the index at which the search begins
+ * @return the index where <code>e</code> was found
+ */
+ public int lastIndexOf(E e, int index)
+ {
+ E[] data = this.data;
+
+ for (int i = index; i >= 0; i--)
+ if (equals(e, data[i]))
+ return i;
+ return -1;
+ }
+
+ /**
+ * Creates a shallow copy of this ArrayList (elements are not cloned).
+ *
+ * @return the cloned object
+ */
+ public Object clone()
+ {
+ CopyOnWriteArrayList<E> clone = null;
+ try
+ {
+ clone = (CopyOnWriteArrayList<E>) super.clone();
+ }
+ catch (CloneNotSupportedException e)
+ {
+ // Impossible to get here.
+ }
+ return clone;
+ }
+
+ /**
+ * Returns an Object array containing all of the elements in this ArrayList.
+ * The array is independent of this list.
+ *
+ * @return an array representation of this list
+ */
+ public Object[] toArray()
+ {
+ E[] data = this.data;
+ E[] array = (E[]) new Object[data.length];
+ System.arraycopy(data, 0, array, 0, data.length);
+ return array;
+ }
+
+ /**
+ * Returns an Array whose component type is the runtime component type of the
+ * passed-in Array. The returned Array is populated with all of the elements
+ * in this ArrayList. If the passed-in Array is not large enough to store all
+ * of the elements in this List, a new Array will be created and returned; if
+ * the passed-in Array is <i>larger</i> than the size of this List, then
+ * size() index will be set to null.
+ *
+ * @param a
+ * the passed-in Array
+ * @return an array representation of this list
+ * @throws ArrayStoreException
+ * if the runtime type of a does not allow an element in this list
+ * @throws NullPointerException
+ * if a is null
+ */
+ public <T> T[] toArray(T[] a)
+ {
+ E[] data = this.data;
+ if (a.length < data.length)
+ a = (T[]) Array.newInstance(a.getClass().getComponentType(), data.length);
+ else if (a.length > data.length)
+ a[data.length] = null;
+ System.arraycopy(data, 0, a, 0, data.length);
+ return a;
+ }
+
+ /**
+ * Retrieves the element at the user-supplied index.
+ *
+ * @param index
+ * the index of the element we are fetching
+ * @throws IndexOutOfBoundsException
+ * if index &lt; 0 || index &gt;= size()
+ */
+ public E get(int index)
+ {
+ return data[index];
+ }
+
+ /**
+ * Sets the element at the specified index. The new element, e, can be an
+ * object of any type or null.
+ *
+ * @param index
+ * the index at which the element is being set
+ * @param e
+ * the element to be set
+ * @return the element previously at the specified index
+ * @throws IndexOutOfBoundsException
+ * if index &lt; 0 || index &gt;= 0
+ */
+ public synchronized E set(int index, E e)
+ {
+ E result = data[index];
+ E[] newData = (E[]) data.clone();
+ newData[index] = e;
+ data = newData;
+ return result;
+ }
+
+ /**
+ * Appends the supplied element to the end of this list. The element, e, can
+ * be an object of any type or null.
+ *
+ * @param e
+ * the element to be appended to this list
+ * @return true, the add will always succeed
+ */
+ public synchronized boolean add(E e)
+ {
+ E[] data = this.data;
+ E[] newData = (E[]) new Object[data.length + 1];
+ System.arraycopy(data, 0, newData, 0, data.length);
+ newData[data.length] = e;
+ this.data = newData;
+ return true;
+ }
+
+ /**
+ * Adds the supplied element at the specified index, shifting all elements
+ * currently at that index or higher one to the right. The element, e, can be
+ * an object of any type or null.
+ *
+ * @param index
+ * the index at which the element is being added
+ * @param e
+ * the item being added
+ * @throws IndexOutOfBoundsException
+ * if index &lt; 0 || index &gt; size()
+ */
+ public synchronized void add(int index, E e)
+ {
+ E[] data = this.data;
+ E[] newData = (E[]) new Object[data.length + 1];
+ System.arraycopy(data, 0, newData, 0, index);
+ newData[index] = e;
+ System.arraycopy(data, index, newData, index + 1, data.length - index);
+ this.data = newData;
+ }
+
+ /**
+ * Removes the element at the user-supplied index.
+ *
+ * @param index
+ * the index of the element to be removed
+ * @return the removed Object
+ * @throws IndexOutOfBoundsException
+ * if index &lt; 0 || index &gt;= size()
+ */
+ public synchronized E remove(int index)
+ {
+ if (index < 0 || index >= this.size())
+ throw new IndexOutOfBoundsException("index = " + index);
+
+ E[] snapshot = this.data;
+ E[] newData = (E[]) new Object[snapshot.length - 1];
+
+ E result = snapshot[index];
+
+ if (index > 0)
+ System.arraycopy(snapshot, 0, newData, 0, index);
+
+ System.arraycopy(snapshot, index + 1, newData, index,
+ snapshot.length - index - 1);
+
+ this.data = newData;
+
+ return result;
+ }
+
+ /**
+ * Remove the first occurrence, if any, of the given object from this list,
+ * returning <code>true</code> if the object was removed, <code>false</code>
+ * otherwise.
+ *
+ * @param element the object to be removed.
+ * @return true if element was removed, false otherwise. false means also that
+ * the underlying storage was unchanged after this operation concluded.
+ */
+ public synchronized boolean remove(Object element)
+ {
+ E[] snapshot = this.data;
+ int len = snapshot.length;
+
+ if (len == 0)
+ return false;
+
+ E[] newData = (E[]) new Object[len - 1];
+
+ // search the element to remove while filling the backup array
+ // this way we can run this method in O(n)
+ int elementIndex = -1;
+ for (int i = 0; i < snapshot.length; i++)
+ {
+ if (equals(element, snapshot[i]))
+ {
+ elementIndex = i;
+ break;
+ }
+
+ if (i < newData.length)
+ newData[i] = snapshot[i];
+ }
+
+ if (elementIndex < 0)
+ return false;
+
+ System.arraycopy(snapshot, elementIndex + 1, newData, elementIndex,
+ snapshot.length - elementIndex - 1);
+ this.data = newData;
+
+ return true;
+ }
+
+ /**
+ * Removes all the elements contained in the given collection.
+ * This method removes the elements that are contained in both
+ * this list and in the given collection.
+ *
+ * @param c the collection containing the elements to be removed from this
+ * list.
+ * @return true if at least one element was removed, indicating that
+ * the list internal storage changed as a result, false otherwise.
+ */
+ public synchronized boolean removeAll(Collection<?> c)
+ {
+ if (c.size() == 0)
+ return false;
+
+ E [] snapshot = this.data;
+ E [] storage = (E[]) new Object[this.data.length];
+ boolean changed = false;
+
+ int length = 0;
+ for (E element : snapshot)
+ {
+ // copy all the elements, including null values
+ // if the collection can hold it
+ // FIXME: slow operation
+ if (c.contains(element))
+ changed = true;
+ else
+ storage[length++] = element;
+ }
+
+ if (!changed)
+ return false;
+
+ E[] newData = (E[]) new Object[length];
+ System.arraycopy(storage, 0, newData, 0, length);
+
+ this.data = newData;
+
+ return true;
+ }
+
+ /**
+ * Removes all the elements that are not in the passed collection.
+ * If the collection is void, this method has the same effect of
+ * <code>clear()</code>.
+ * Please, note that this method is extremely slow (unless the argument has
+ * <code>size == 0</code>) and has bad performance is both space and time
+ * usage.
+ *
+ * @param c the collection containing the elements to be retained by this
+ * list.
+ * @return true the list internal storage changed as a result of this
+ * operation, false otherwise.
+ */
+ public synchronized boolean retainAll(Collection<?> c)
+ {
+ // if the given collection does not contain elements
+ // we remove all the elements from our storage
+ if (c.size() == 0)
+ {
+ this.clear();
+ return true;
+ }
+
+ E [] snapshot = this.data;
+ E [] storage = (E[]) new Object[this.data.length];
+
+ int length = 0;
+ for (E element : snapshot)
+ {
+ if (c.contains(element))
+ storage[length++] = element;
+ }
+
+ // means we retained all the elements previously in our storage
+ // we are running already slow here, but at least we avoid copying
+ // another array and changing the internal storage
+ if (length == snapshot.length)
+ return false;
+
+ E[] newData = (E[]) new Object[length];
+ System.arraycopy(storage, 0, newData, 0, length);
+
+ this.data = newData;
+
+ return true;
+ }
+
+ /**
+ * Removes all elements from this List
+ */
+ public synchronized void clear()
+ {
+ data = (E[]) new Object[0];
+ }
+
+ /**
+ * Add each element in the supplied Collection to this List. It is undefined
+ * what happens if you modify the list while this is taking place; for
+ * example, if the collection contains this list. c can contain objects of any
+ * type, as well as null values.
+ *
+ * @param c
+ * a Collection containing elements to be added to this List
+ * @return true if the list was modified, in other words c is not empty
+ * @throws NullPointerException
+ * if c is null
+ */
+ public synchronized boolean addAll(Collection< ? extends E> c)
+ {
+ return addAll(data.length, c);
+ }
+
+ /**
+ * Add all elements in the supplied collection, inserting them beginning at
+ * the specified index. c can contain objects of any type, as well as null
+ * values.
+ *
+ * @param index
+ * the index at which the elements will be inserted
+ * @param c
+ * the Collection containing the elements to be inserted
+ * @throws IndexOutOfBoundsException
+ * if index &lt; 0 || index &gt; 0
+ * @throws NullPointerException
+ * if c is null
+ */
+ public synchronized boolean addAll(int index, Collection< ? extends E> c)
+ {
+ if (index < 0 || index > this.size())
+ throw new IndexOutOfBoundsException("index = " + index);
+
+ int csize = c.size();
+ if (csize == 0)
+ return false;
+
+ E[] data = this.data;
+ Iterator<? extends E> itr = c.iterator();
+
+ E[] newData = (E[]) new Object[data.length + csize];
+
+ // avoid this call at all if we were asked to put the elements at the
+ // beginning of our storage
+ if (index != 0)
+ System.arraycopy(data, 0, newData, 0, index);
+
+ int itemsLeft = index;
+
+ for (E value : c)
+ newData[index++] = value;
+
+ // now copy the remaining elements
+ System.arraycopy(data, itemsLeft, newData, 0, data.length - itemsLeft);
+
+ this.data = newData;
+
+ return true;
+ }
+
+ /**
+ * Adds an element if the list does not contains it already.
+ *
+ * @param val the element to add to the list.
+ * @return true if the element was added, false otherwise.
+ */
+ public synchronized boolean addIfAbsent(E val)
+ {
+ if (contains(val))
+ return false;
+ add(val);
+ return true;
+ }
+
+ /**
+ * Adds all the element from the given collection that are not already
+ * in this list.
+ *
+ * @param c the Collection containing the elements to be inserted
+ * @return true the list internal storage changed as a result of this
+ * operation, false otherwise.
+ */
+ public synchronized int addAllAbsent(Collection<? extends E> c)
+ {
+ int size = c.size();
+ if (size == 0)
+ return 0;
+
+ E [] snapshot = this.data;
+ E [] storage = (E[]) new Object[size];
+
+ size = 0;
+ for (E val : c)
+ {
+ if (!this.contains(val))
+ storage[size++] = val;
+ }
+
+ if (size == 0)
+ return 0;
+
+ // append storage to data
+ E [] newData = (E[]) new Object[snapshot.length + size];
+
+ System.arraycopy(snapshot, 0, newData, 0, snapshot.length);
+ System.arraycopy(storage, 0, newData, snapshot.length, size);
+
+ this.data = newData;
+
+ return size;
+ }
+
+ public String toString()
+ {
+ return Arrays.toString(this.data);
+ }
+
+ public boolean equals(Object o)
+ {
+ if (o == null)
+ return false;
+
+ if (this == o)
+ return true;
+
+ // let's see if 'o' is a list, if so, we need to compare the elements
+ // as returned by the iterator
+ if (o instanceof List)
+ {
+ List<?> source = (List<?>) o;
+
+ if (source.size() != this.size())
+ return false;
+
+ Iterator<?> sourceIterator = source.iterator();
+ for (E element : this)
+ {
+ if (!element.equals(sourceIterator.next()))
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public int hashCode()
+ {
+ // see http://java.sun.com/6/docs/api/java/util/List.html#hashcode()
+ int hashcode = 1;
+ for (E element : this)
+ {
+ hashcode = 31 * hashcode + (element == null ? 0 : element.hashCode());
+ }
+ return hashcode;
+ }
+
+ /**
+ * Return an Iterator containing the elements of this list.
+ * The Iterator uses a snapshot of the state of the internal storage
+ * at the moment this method is called and does <strong>not</strong> support
+ * update operations, so no synchronization is needed to traverse the
+ * iterator.
+ *
+ * @return an Iterator containing the elements of this list in sequence.
+ */
+ public Iterator<E> iterator()
+ {
+ return new Iterator<E>()
+ {
+ E [] iteratorData = CopyOnWriteArrayList.this.data;
+ int currentElement = 0;
+
+ public boolean hasNext()
+ {
+ return (currentElement < iteratorData.length);
+ }
+
+ public E next()
+ {
+ return iteratorData[currentElement++];
+ }
+
+ public void remove()
+ {
+ throw new UnsupportedOperationException("updating of elements in " +
+ "iterators is not supported " +
+ "by this class");
+ }
+ };
+ }
+
+ /**
+ * Return a ListIterator containing the elements of this list.
+ * The Iterator uses a snapshot of the state of the internal storage
+ * at the moment this method is called and does <strong>not</strong> support
+ * update operations, so no synchronization is needed to traverse the
+ * iterator.
+ *
+ * @return a ListIterator containing the elements of this list in sequence.
+ */
+ public ListIterator<E> listIterator()
+ {
+ return listIterator(0);
+ }
+
+ /**
+ * Return a ListIterator over the elements of this list starting at
+ * the specified index. An initial call to {@code next()} will thus
+ * return the element at {@code index}, while an initial call to
+ * {@code previous()} will return the element at {@code index-1}. The
+ * Iterator uses a snapshot of the state of the internal storage
+ * at the moment this method is called and does <strong>not</strong> support
+ * update operations, so no synchronization is needed to traverse the
+ * iterator.
+ *
+ * @param index the index at which to start iterating.
+ * @return a ListIterator containing the elements of this list in sequence.
+ */
+ public ListIterator<E> listIterator(final int index)
+ {
+ if (index < 0 || index > size())
+ throw new IndexOutOfBoundsException("Index: " + index + ", Size:"
+ + size());
+
+ return new ListIterator<E>()
+ {
+ E [] iteratorData = CopyOnWriteArrayList.this.data;
+ int currentElement = index;
+
+ public void add(E o)
+ {
+ throw new UnsupportedOperationException("updating of elements in " +
+ "iterators is not supported " +
+ "by this class");
+ }
+
+ public boolean hasNext()
+ {
+ return (currentElement < iteratorData.length);
+ }
+
+ public boolean hasPrevious()
+ {
+ return (currentElement > 0);
+ }
+
+ public E next()
+ {
+ if (hasNext() == false)
+ throw new java.util.NoSuchElementException();
+
+ return iteratorData[currentElement++];
+ }
+
+ public int nextIndex()
+ {
+ return (currentElement + 1);
+ }
+
+ public E previous()
+ {
+ if (hasPrevious() == false)
+ throw new java.util.NoSuchElementException();
+
+ return iteratorData[--currentElement];
+ }
+
+ public int previousIndex()
+ {
+ return (currentElement - 1);
+ }
+
+ public void remove()
+ {
+ throw new UnsupportedOperationException("updating of elements in " +
+ "iterators is not supported " +
+ "by this class");
+ }
+
+ public void set(E o)
+ {
+ throw new UnsupportedOperationException("updating of elements in " +
+ "iterators is not supported " +
+ "by this class");
+ }
+
+ };
+ }
+
+ /**
+ * Obtain a List view of a subsection of this list, from fromIndex
+ * (inclusive) to toIndex (exclusive). If the two indices are equal, the
+ * sublist is empty. The returned list should be modifiable if and only
+ * if this list is modifiable. Changes to the returned list should be
+ * reflected in this list. If this list is structurally modified in
+ * any way other than through the returned list, the result of any subsequent
+ * operations on the returned list is undefined.
+ * <p>
+ *
+ * This implementation returns a subclass of AbstractList. It stores, in
+ * private fields, the offset and size of the sublist, and the expected
+ * modCount of the backing list. If the backing list implements RandomAccess,
+ * the sublist will also.
+ * <p>
+ *
+ * The subclass's <code>set(int, Object)</code>, <code>get(int)</code>,
+ * <code>add(int, Object)</code>, <code>remove(int)</code>,
+ * <code>addAll(int, Collection)</code> and
+ * <code>removeRange(int, int)</code> methods all delegate to the
+ * corresponding methods on the backing abstract list, after
+ * bounds-checking the index and adjusting for the offset. The
+ * <code>addAll(Collection c)</code> method merely returns addAll(size, c).
+ * The <code>listIterator(int)</code> method returns a "wrapper object"
+ * over a list iterator on the backing list, which is created with the
+ * corresponding method on the backing list. The <code>iterator()</code>
+ * method merely returns listIterator(), and the <code>size()</code> method
+ * merely returns the subclass's size field.
+ * <p>
+ *
+ * All methods first check to see if the actual modCount of the backing
+ * list is equal to its expected value, and throw a
+ * ConcurrentModificationException if it is not.
+ *
+ * @param fromIndex the index that the returned list should start from
+ * (inclusive)
+ * @param toIndex the index that the returned list should go to (exclusive)
+ * @return a List backed by a subsection of this list
+ * @throws IndexOutOfBoundsException if fromIndex &lt; 0
+ * || toIndex &gt; size()
+ * @throws IndexOutOfBoundsException if fromIndex &gt; toIndex
+ * @see ConcurrentModificationException
+ * @see RandomAccess
+ */
+ public synchronized List<E> subList(int fromIndex, int toIndex)
+ {
+ // This follows the specification of AbstractList, but is inconsistent
+ // with the one in List. Don't you love Sun's inconsistencies?
+ if (fromIndex > toIndex)
+ throw new IndexOutOfBoundsException(fromIndex + " > " + toIndex);
+ if (fromIndex < 0 || toIndex > size())
+ throw new IndexOutOfBoundsException();
+
+ if (this instanceof RandomAccess)
+ return new RandomAccessSubList<E>(this, fromIndex, toIndex);
+ return new SubList<E>(this, fromIndex, toIndex);
+ }
+
+ /**
+ * This class follows the implementation requirements set forth in
+ * {@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)
+ */
+ private static class SubList<E>
+ extends AbstractList<E>
+ {
+ // Package visible, for use by iterator.
+ /** The original list. */
+ final CopyOnWriteArrayList<E> backingList;
+ /** The index of the first element of the sublist. */
+ final int offset;
+ /** The size of the sublist. */
+ int size;
+ /** The backing data */
+ E[] data;
+
+ /**
+ * Construct the sublist.
+ *
+ * @param backing the list this comes from
+ * @param fromIndex the lower bound, inclusive
+ * @param toIndex the upper bound, exclusive
+ */
+ SubList(CopyOnWriteArrayList<E> backing, int fromIndex, int toIndex)
+ {
+ backingList = backing;
+ data = backing.data;
+ offset = fromIndex;
+ size = toIndex - fromIndex;
+ }
+
+ /**
+ * This method checks the two modCount fields to ensure that there has
+ * not been a concurrent modification, returning if all is okay.
+ *
+ * @throws ConcurrentModificationException if the backing list has been
+ * modified externally to this sublist
+ */
+ // This can be inlined. Package visible, for use by iterator.
+ void checkMod()
+ {
+ if (data != backingList.data)
+ throw new ConcurrentModificationException();
+ }
+
+ /**
+ * This method checks that a value is between 0 and size (inclusive). If
+ * it is not, an exception is thrown.
+ *
+ * @param index the value to check
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ */
+ // This will get inlined, since it is private.
+ private void checkBoundsInclusive(int index)
+ {
+ if (index < 0 || index > size)
+ throw new IndexOutOfBoundsException("Index: " + index +
+ ", Size:" + size);
+ }
+
+ /**
+ * This method checks that a value is between 0 (inclusive) and size
+ * (exclusive). If it is not, an exception is thrown.
+ *
+ * @param index the value to check
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ */
+ // This will get inlined, since it is private.
+ private void checkBoundsExclusive(int index)
+ {
+ if (index < 0 || index >= size)
+ throw new IndexOutOfBoundsException("Index: " + index +
+ ", Size:" + size);
+ }
+
+ /**
+ * Specified by AbstractList.subList to return the private field size.
+ *
+ * @return the sublist size
+ * @throws ConcurrentModificationException if the backing list has been
+ * modified externally to this sublist
+ */
+ public int size()
+ {
+ synchronized (backingList)
+ {
+ checkMod();
+ return size;
+ }
+ }
+
+ public void clear()
+ {
+ synchronized (backingList)
+ {
+ E[] snapshot = backingList.data;
+ E[] newData = (E[]) new Object[snapshot.length - size];
+
+ int toIndex = size + offset;
+
+ System.arraycopy(snapshot, 0, newData, 0, offset);
+ System.arraycopy(snapshot, toIndex, newData, offset,
+ snapshot.length - toIndex);
+
+ backingList.data = newData;
+ this.data = backingList.data;
+ this.size = 0;
+ }
+ }
+
+ /**
+ * Specified by AbstractList.subList to delegate to the backing list.
+ *
+ * @param index the location to modify
+ * @param o the new value
+ * @return the old value
+ * @throws ConcurrentModificationException if the backing list has been
+ * modified externally to this sublist
+ * @throws UnsupportedOperationException if the backing list does not
+ * support the set operation
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ * @throws ClassCastException if o cannot be added to the backing list due
+ * to its type
+ * @throws IllegalArgumentException if o cannot be added to the backing list
+ * for some other reason
+ */
+ public E set(int index, E o)
+ {
+ synchronized (backingList)
+ {
+ checkMod();
+ checkBoundsExclusive(index);
+
+ E el = backingList.set(index + offset, o);
+ this.data = backingList.data;
+
+ return el;
+ }
+ }
+
+ /**
+ * Specified by AbstractList.subList to delegate to the backing list.
+ *
+ * @param index the location to get from
+ * @return the object at that location
+ * @throws ConcurrentModificationException if the backing list has been
+ * modified externally to this sublist
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ */
+ public E get(int index)
+ {
+ synchronized (backingList)
+ {
+ checkMod();
+ checkBoundsExclusive(index);
+
+ return backingList.get(index + offset);
+ }
+ }
+
+ /**
+ * Specified by AbstractList.subList to delegate to the backing list.
+ *
+ * @param index the index to insert at
+ * @param o the object to add
+ * @throws ConcurrentModificationException if the backing list has been
+ * modified externally to this sublist
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ * @throws UnsupportedOperationException if the backing list does not
+ * support the add operation.
+ * @throws ClassCastException if o cannot be added to the backing list due
+ * to its type.
+ * @throws IllegalArgumentException if o cannot be added to the backing
+ * list for some other reason.
+ */
+ public void add(int index, E o)
+ {
+ synchronized (backingList)
+ {
+ checkMod();
+ checkBoundsInclusive(index);
+
+ backingList.add(index + offset, o);
+
+ this.data = backingList.data;
+ size++;
+ }
+ }
+
+ /**
+ * Specified by AbstractList.subList to delegate to the backing list.
+ *
+ * @param index the index to remove
+ * @return the removed object
+ * @throws ConcurrentModificationException if the backing list has been
+ * modified externally to this sublist
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt;= size()
+ * @throws UnsupportedOperationException if the backing list does not
+ * support the remove operation
+ */
+ public E remove(int index)
+ {
+ synchronized (backingList)
+ {
+ checkMod();
+ checkBoundsExclusive(index);
+ E o = backingList.remove(index + offset);
+
+ this.data = backingList.data;
+ size--;
+
+ return o;
+ }
+ }
+
+ /**
+ * Specified by AbstractList.subList to delegate to the backing list.
+ *
+ * @param index the location to insert at
+ * @param c the collection to insert
+ * @return true if this list was modified, in other words, c is non-empty
+ * @throws ConcurrentModificationException if the backing list has been
+ * modified externally to this sublist
+ * @throws IndexOutOfBoundsException if index &lt; 0 || index &gt; size()
+ * @throws UnsupportedOperationException if this list does not support the
+ * addAll operation
+ * @throws ClassCastException if some element of c cannot be added to this
+ * list due to its type
+ * @throws IllegalArgumentException if some element of c cannot be added
+ * to this list for some other reason
+ * @throws NullPointerException if the specified collection is null
+ */
+ public boolean addAll(int index, Collection<? extends E> c)
+ {
+ synchronized (backingList)
+ {
+ checkMod();
+ checkBoundsInclusive(index);
+ int csize = c.size();
+ boolean result = backingList.addAll(offset + index, c);
+
+ this.data = backingList.data;
+ size += csize;
+
+ return result;
+ }
+ }
+
+ /**
+ * Specified by AbstractList.subList to return addAll(size, c).
+ *
+ * @param c the collection to insert
+ * @return true if this list was modified, in other words, c is non-empty
+ * @throws ConcurrentModificationException if the backing list has been
+ * modified externally to this sublist
+ * @throws UnsupportedOperationException if this list does not support the
+ * addAll operation
+ * @throws ClassCastException if some element of c cannot be added to this
+ * list due to its type
+ * @throws IllegalArgumentException if some element of c cannot be added
+ * to this list for some other reason
+ * @throws NullPointerException if the specified collection is null
+ */
+ public boolean addAll(Collection<? extends E> c)
+ {
+ synchronized (backingList)
+ {
+ return addAll(size, c);
+ }
+ }
+
+ /**
+ * Specified by AbstractList.subList to return listIterator().
+ *
+ * @return an iterator over the sublist
+ */
+ public Iterator<E> iterator()
+ {
+ return listIterator();
+ }
+
+ /**
+ * Specified by AbstractList.subList to return a wrapper around the
+ * backing list's iterator.
+ *
+ * @param index the start location of the iterator
+ * @return a list iterator over the sublist
+ * @throws ConcurrentModificationException if the backing list has been
+ * modified externally to this sublist
+ * @throws IndexOutOfBoundsException if the value is out of range
+ */
+ public ListIterator<E> listIterator(final int index)
+ {
+ checkMod();
+ checkBoundsInclusive(index);
+
+ return new ListIterator<E>()
+ {
+ private final ListIterator<E> i =
+ backingList.listIterator(index + offset);
+ private int position = index;
+
+ /**
+ * Tests to see if there are any more objects to
+ * return.
+ *
+ * @return True if the end of the list has not yet been
+ * reached.
+ */
+ public boolean hasNext()
+ {
+ return position < size;
+ }
+
+ /**
+ * Tests to see if there are objects prior to the
+ * current position in the list.
+ *
+ * @return True if objects exist prior to the current
+ * position of the iterator.
+ */
+ public boolean hasPrevious()
+ {
+ return position > 0;
+ }
+
+ /**
+ * Retrieves the next object from the list.
+ *
+ * @return The next object.
+ * @throws NoSuchElementException if there are no
+ * more objects to retrieve.
+ * @throws ConcurrentModificationException if the
+ * list has been modified elsewhere.
+ */
+ public E next()
+ {
+ if (position == size)
+ throw new NoSuchElementException();
+ position++;
+ return i.next();
+ }
+
+ /**
+ * Retrieves the previous object from the list.
+ *
+ * @return The next object.
+ * @throws NoSuchElementException if there are no
+ * previous objects to retrieve.
+ * @throws ConcurrentModificationException if the
+ * list has been modified elsewhere.
+ */
+ public E previous()
+ {
+ if (position == 0)
+ throw new NoSuchElementException();
+ position--;
+ return i.previous();
+ }
+
+ /**
+ * Returns the index of the next element in the
+ * list, which will be retrieved by <code>next()</code>
+ *
+ * @return The index of the next element.
+ */
+ public int nextIndex()
+ {
+ return i.nextIndex() - offset;
+ }
+
+ /**
+ * Returns the index of the previous element in the
+ * list, which will be retrieved by <code>previous()</code>
+ *
+ * @return The index of the previous element.
+ */
+ public int previousIndex()
+ {
+ return i.previousIndex() - offset;
+ }
+
+ /**
+ * Removes the last object retrieved by <code>next()</code>
+ * from the list, if the list supports object removal.
+ *
+ * @throws IllegalStateException if the iterator is positioned
+ * before the start of the list or the last object has already
+ * been removed.
+ * @throws UnsupportedOperationException if the list does
+ * not support removing elements.
+ */
+ public void remove()
+ {
+ throw new UnsupportedOperationException("Modification not supported " +
+ "on CopyOnWriteArrayList iterators");
+ }
+
+ /**
+ * Replaces the last object retrieved by <code>next()</code>
+ * or <code>previous</code> with o, if the list supports object
+ * replacement and an add or remove operation has not already
+ * been performed.
+ *
+ * @throws IllegalStateException if the iterator is positioned
+ * before the start of the list or the last object has already
+ * been removed.
+ * @throws UnsupportedOperationException if the list doesn't support
+ * the addition or removal of elements.
+ * @throws ClassCastException if the type of o is not a valid type
+ * for this list.
+ * @throws IllegalArgumentException if something else related to o
+ * prevents its addition.
+ * @throws ConcurrentModificationException if the list
+ * has been modified elsewhere.
+ */
+ public void set(E o)
+ {
+ throw new UnsupportedOperationException("Modification not supported " +
+ "on CopyOnWriteArrayList iterators");
+ }
+
+ /**
+ * Adds the supplied object before the element that would be returned
+ * by a call to <code>next()</code>, if the list supports addition.
+ *
+ * @param o The object to add to the list.
+ * @throws UnsupportedOperationException if the list doesn't support
+ * the addition of new elements.
+ * @throws ClassCastException if the type of o is not a valid type
+ * for this list.
+ * @throws IllegalArgumentException if something else related to o
+ * prevents its addition.
+ * @throws ConcurrentModificationException if the list
+ * has been modified elsewhere.
+ */
+ public void add(E o)
+ {
+ throw new UnsupportedOperationException("Modification not supported " +
+ "on CopyOnWriteArrayList iterators");
+ }
+ };
+ }
+ } // class SubList
+
+ /**
+ * This class is a RandomAccess version of SubList, as required by
+ * {@link AbstractList#subList(int, int)}.
+ *
+ * @author Eric Blake (ebb9@email.byu.edu)
+ */
+ private static final class RandomAccessSubList<E> extends SubList<E>
+ implements RandomAccess
+ {
+ /**
+ * Construct the sublist.
+ *
+ * @param backing the list this comes from
+ * @param fromIndex the lower bound, inclusive
+ * @param toIndex the upper bound, exclusive
+ */
+ RandomAccessSubList(CopyOnWriteArrayList<E> backing, int fromIndex, int toIndex)
+ {
+ super(backing, fromIndex, toIndex);
+ }
+ } // class RandomAccessSubList
+
+ /**
+ * Serializes this object to the given stream.
+ *
+ * @param s
+ * the stream to write to
+ * @throws IOException
+ * if the underlying stream fails
+ * @serialData the size field (int), the length of the backing array (int),
+ * followed by its elements (Objects) in proper order.
+ */
+ private void writeObject(ObjectOutputStream s) throws IOException
+ {
+ // The 'size' field.
+ s.defaultWriteObject();
+ // We serialize unused list entries to preserve capacity.
+ int len = data.length;
+ s.writeInt(len);
+ // it would be more efficient to just write "size" items,
+ // this need readObject read "size" items too.
+ for (int i = 0; i < data.length; i++)
+ s.writeObject(data[i]);
+ }
+
+ /**
+ * Deserializes this object from the given stream.
+ *
+ * @param s
+ * the stream to read from
+ * @throws ClassNotFoundException
+ * if the underlying stream fails
+ * @throws IOException
+ * if the underlying stream fails
+ * @serialData the size field (int), the length of the backing array (int),
+ * followed by its elements (Objects) in proper order.
+ */
+ private void readObject(ObjectInputStream s) throws IOException,
+ ClassNotFoundException
+ {
+ // the `size' field.
+ s.defaultReadObject();
+ int capacity = s.readInt();
+ data = (E[]) new Object[capacity];
+ for (int i = 0; i < capacity; i++)
+ data[i] = (E) s.readObject();
+ }
+
+ static final boolean equals(Object o1, Object o2)
+ {
+ return o1 == null ? o2 == null : o1.equals(o2);
+ }
+
+ Object[] getArray()
+ {
+ return data;
+ }
+}
diff --git a/libjava/classpath/java/util/jar/Attributes.java b/libjava/classpath/java/util/jar/Attributes.java
new file mode 100644
index 000000000..88800294c
--- /dev/null
+++ b/libjava/classpath/java/util/jar/Attributes.java
@@ -0,0 +1,629 @@
+/* Attributes.java -- Represents attribute name/value pairs from a Manifest
+ Copyright (C) 2000, 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.jar;
+
+import gnu.java.util.jar.JarUtils;
+
+import java.util.Collection;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Represents attribute name/value pairs from a Manifest as a Map.
+ * The names of an attribute are represented by the
+ * <code>Attributes.Name</code> class and should confirm to the restrictions
+ * described in that class. Note that the Map interface that Attributes
+ * implements allows you to put names and values into the attribute that don't
+ * follow these restriction (and are not really Atrribute.Names, but if you do
+ * that it might cause undefined behaviour later).
+ * <p>
+ * If you use the constants defined in the inner class Name then you can be
+ * sure that you always access the right attribute names. This makes
+ * manipulating the Attributes more or less type safe.
+ * <p>
+ * Most of the methods are wrappers to implement the Map interface. The really
+ * useful and often used methods are <code>getValue(Name)</code> and
+ * <code>getValue(String)</code>. If you actually want to set attributes you
+ * may want to use the <code>putValue(String, String)</code> method
+ * (sorry there is no public type safe <code>putValue(Name, String)</code>
+ * method).
+ *
+ * @see java.util.jar.Attributes.Name
+ * @author Mark Wielaard (mark@klomp.org)
+ */
+public class Attributes
+ implements Cloneable, Map<Object, Object>
+{
+
+ // Fields
+
+ /**
+ * The map that holds all the attribute name/value pairs. In this
+ * implementation it is actually a Hashtable, but that can be different in
+ * other implementations.
+ */
+ protected Map<Object, Object> map;
+
+ // Inner class
+
+ /**
+ * Represents a name of a Manifest Attribute. Defines a couple of well
+ * know names for the general main attributes, stand alone application
+ * attributes, applet attributes, extension identification attributes,
+ * package versioning and sealing attributes, file contents attributes,
+ * bean objects attribute and signing attributes. See the
+ *
+ * <p>The characters of a Name must obey the following restrictions:</p>
+ *
+ * <ul>
+ * <li>Must contain at least one character</li>
+ * <li>The first character must be alphanumeric (a-z, A-Z, 0-9)</li>
+ * <li>All other characters must be alphanumeric, a '-' or a '_'</li>
+ * </ul>
+ *
+ * <p>When comparing Names (with <code>equals</code>) all characters are
+ * converted to lowercase. But you can get the original case sensitive
+ * string with the <code>toString()</code> method.</p>
+ *
+ * <p>Most important attributes have a constant defined in this
+ * class. Some other attributes used in Manifest files are:
+ * <ul>
+ * <li> "Created-By" - General main attribute, tool and version
+ * that created this Manifest file.</li>
+ * <li> "Java-Bean" - Bean objects attribute, whether the entry is a Bean.
+ * Value is either "true" or "false".</li>
+ * <li> "Magic" - Signing attribute, application specific signing attribute.
+ * Must be understood by the manifest parser when present to validate the
+ * jar (entry).</li>
+ * </ul>
+ *
+ * @since 1.2
+ * @author Mark Wielaard (mark@klomp.org)
+ */
+ public static class Name
+ {
+ // General Main Attributes
+
+ /**
+ * General main attribute -
+ * the version of this Manifest file.
+ */
+ public static final Name MANIFEST_VERSION = new Name(JarUtils.MANIFEST_VERSION);
+
+ /**
+ * General main attribute -
+ * the version of the jar file signature.
+ */
+ public static final Name SIGNATURE_VERSION = new Name(JarUtils.SIGNATURE_VERSION);
+
+ /**
+ * General main attribute -
+ * (relative) file paths of the libraries/classpaths that the Classes in
+ * this jar file depend on. Paths are separated by spaces.
+ */
+ public static final Name CLASS_PATH = new Name("Class-Path");
+
+ /**
+ * Stand alone application attribute -
+ * the entry (without the .class ending) that is the main
+ * class of this jar file.
+ */
+ public static final Name MAIN_CLASS = new Name("Main-Class");
+
+ /**
+ * Applet attribute -
+ * a list of extension libraries that the applet in this
+ * jar file depends on.
+ * For every named extension there should be some Attributes in the
+ * Manifest manifest file with the following Names:
+ * <ul>
+ * <li> &lt;extension&gt;-Extension-Name:
+ * unique name of the extension</li>
+ * <li> &lt;extension&gt;-Specification-Version:
+ * minimum specification version</li>
+ * <li> &lt;extension&gt;-Implementation-Version:
+ * minimum implementation version</li>
+ * <li> &lt;extension&gt;-Implementation-Vendor-Id:
+ * unique id of implementation vendor</li>
+ * <li> &lt;extension&gt;-Implementation-URL:
+ * where the latest version of the extension library can be found</li>
+ * </ul>
+ */
+ public static final Name EXTENSION_LIST = new Name("Extension-List");
+
+ /**
+ * Extension identification attribute -
+ * the name if the extension library contained in the jar.
+ */
+ public static final Name EXTENSION_NAME = new Name("Extension-Name");
+
+ /**
+ * Extension identification attribute -
+ * synonym for <code>EXTENSTION_NAME</code>.
+ */
+ public static final Name EXTENSION_INSTALLATION = EXTENSION_NAME;
+
+ // Package versioning and sealing attributes
+
+ /**
+ * Package versioning -
+ * name of extension library contained in this jar.
+ */
+ public static final Name IMPLEMENTATION_TITLE
+ = new Name("Implementation-Title");
+
+ /**
+ * Package versioning -
+ * version of the extension library contained in this jar.
+ */
+ public static final Name IMPLEMENTATION_VERSION
+ = new Name("Implementation-Version");
+
+ /**
+ * Package versioning -
+ * name of extension library creator contained in this jar.
+ */
+ public static final Name IMPLEMENTATION_VENDOR
+ = new Name("Implementation-Vendor");
+
+ /**
+ * Package versioning -
+ * unique id of extension library creator.
+ */
+ public static final Name IMPLEMENTATION_VENDOR_ID
+ = new Name("Implementation-Vendor-Id");
+
+ /**
+ * Package versioning -
+ * location where this implementation can be downloaded.
+ */
+ public static final Name IMPLEMENTATION_URL
+ = new Name("Implementation-URL");
+
+ /**
+ * Package versioning -
+ * title of the specification contained in this jar.
+ */
+ public static final Name SPECIFICATION_TITLE
+ = new Name("Specification-Title");
+
+ /**
+ * Package versioning -
+ * version of the specification contained in this jar.
+ */
+ public static final Name SPECIFICATION_VERSION
+ = new Name("Specification-Version");
+
+ /**
+ * Package versioning -
+ * organisation that maintains the specification contains in this
+ * jar.
+ */
+ public static final Name SPECIFICATION_VENDOR
+ = new Name("Specification-Vendor");
+
+ /**
+ * Package sealing -
+ * whether (all) package(s) is(/are) sealed. Value is either "true"
+ * or "false".
+ */
+ public static final Name SEALED = new Name("Sealed");
+
+ /**
+ * File contents attribute -
+ * Mime type and subtype for the jar entry.
+ */
+ public static final Name CONTENT_TYPE = new Name("Content-Type");
+
+ /** The (lowercase) String representation of this Name */
+ private final String name;
+
+ /** The original String given to the constructor */
+ private final String origName;
+
+ // Constructor
+
+ /**
+ * Creates a new Name from the given String.
+ * Throws an IllegalArgumentException if the given String is empty or
+ * contains any illegal Name characters.
+ *
+ * @param name the name of the new Name
+ * @exception IllegalArgumentException if name isn't a valid String
+ * representation of a Name
+ * @exception NullPointerException if name is null
+ */
+ public Name(String name) throws IllegalArgumentException,
+ NullPointerException
+ {
+ // name must not be null
+ // this will throw a NullPointerException if it is
+ char chars[] = name.toCharArray();
+
+ // there must be at least one character
+ if (chars.length == 0)
+ throw new
+ IllegalArgumentException
+ ("There must be at least one character in a name");
+
+ // first character must be alphanum
+ char c = chars[0];
+ if (!((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')))
+ throw new
+ IllegalArgumentException("First character must be alphanum");
+
+ // all other characters must be alphanums, '-' or '_'
+ for (int i = 1; i < chars.length; i++)
+ {
+ c = chars[i];
+ if (!((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') || (c == '-') || (c == '_')))
+ throw new
+ IllegalArgumentException
+ ("Characters must be alphanums, '-' or '_'");
+ }
+
+ // Still here? Then convert to lower case and be done.
+ // Store the original name for toString();
+ this.origName = name;
+ this.name = name.toLowerCase();
+ }
+
+ /**
+ * Returns the hash code of the (lowercase) String representation of
+ * this Name.
+ */
+ public int hashCode()
+ {
+ return name.hashCode();
+ }
+
+ /**
+ * Checks if another object is equal to this Name object.
+ * Another object is equal to this Name object if it is an instance of
+ * Name and the (lowercase) string representation of the name is equal.
+ */
+ public boolean equals(Object o)
+ {
+ // Quick and dirty check
+ if (name == o)
+ return true;
+
+ try
+ {
+ // Note that the constructor already converts the strings to
+ // lowercase.
+ String otherName = ((Name) o).name;
+ return name.equals(otherName);
+ }
+ catch (ClassCastException cce)
+ {
+ return false;
+ }
+ catch (NullPointerException npe)
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the string representation of this Name as given to the
+ * constructor (not neccesarily the lower case representation).
+ */
+ public String toString()
+ {
+ return origName;
+ }
+ }
+
+ // Constructors
+
+ /**
+ * Creates an empty Attributes map.
+ */
+ public Attributes()
+ {
+ map = new Hashtable();
+ }
+
+ /**
+ * Creates an empty Attributes map with the given initial size.
+ * @param size the initial size of the underlying map
+ */
+ public Attributes(int size)
+ {
+ map = new Hashtable(size);
+ }
+
+ /**
+ * Creates an Attributes map with the initial values taken from another
+ * Attributes map.
+ * @param attr Attributes map to take the initial values from
+ */
+ public Attributes(Attributes attr)
+ {
+ map = new Hashtable(attr.map);
+ }
+
+ // Methods
+
+ /**
+ * Gets the value of an attribute name given as a String.
+ *
+ * @param name a String describing the Name to look for
+ * @return the value gotten from the map of null when not found
+ */
+ public String getValue(String name)
+ {
+ return (String) get(new Name(name));
+ }
+
+ /**
+ * Gets the value of the given attribute name.
+ *
+ * @param name the Name to look for
+ * @return the value gotten from the map of null when not found
+ */
+ public String getValue(Name name)
+ {
+ return (String) get(name);
+ }
+
+ /**
+ * Stores an attribute name (represented by a String) and value in this
+ * Attributes map.
+ * When the (case insensitive string) name already exists the value is
+ * replaced and the old value is returned.
+ *
+ * @param name a (case insensitive) String representation of the attribite
+ * name to add/replace
+ * @param value the (new) value of the attribute name
+ * @returns the old value of the attribute name or null if it didn't exist
+ * yet
+ */
+ public String putValue(String name, String value)
+ {
+ return putValue(new Name(name), value);
+ }
+
+ /**
+ * Stores an attribute name (represented by a String) and value in this
+ * Attributes map.
+ * When the name already exists the value is replaced and the old value
+ * is returned.
+ *
+ * @param name the attribite name to add/replace
+ * @param value the (new) value of the attribute name
+ * @returns the old value of the attribute name or null if it didn't exist
+ * yet
+ */
+ private String putValue(Name name, String value)
+ {
+ return (String) put(name, value);
+ }
+
+ // Methods from Cloneable interface
+
+ /**
+ * Return a clone of this attribute map.
+ */
+ public Object clone()
+ {
+ return new Attributes(this);
+ }
+
+ // Methods from Map interface
+
+ /**
+ * Removes all attributes.
+ */
+ public void clear()
+ {
+ map.clear();
+ }
+
+ /**
+ * Checks to see if there is an attribute with the specified name.
+ * XXX - what if the object is a String?
+ *
+ * @param attrName the name of the attribute to check
+ * @return true if there is an attribute with the specified name, false
+ * otherwise
+ */
+ public boolean containsKey(Object attrName)
+ {
+ return map.containsKey(attrName);
+ }
+
+ /**
+ * Checks to see if there is an attribute name with the specified value.
+ *
+ * @param attrValue the value of a attribute to check
+ * @return true if there is an attribute name with the specified value,
+ * false otherwise
+ */
+ public boolean containsValue(Object attrValue)
+ {
+ return map.containsValue(attrValue);
+ }
+
+ /**
+ * Gives a Set of attribute name and values pairs as MapEntries.
+ * @see java.util.Map.Entry
+ * @see java.util.Map#entrySet()
+ *
+ * @return a set of attribute name value pairs
+ */
+ public Set<Map.Entry<Object, Object>> entrySet()
+ {
+ return map.entrySet();
+ }
+
+ /**
+ * Checks to see if two Attributes are equal. The supplied object must be
+ * a real instance of Attributes and contain the same attribute name/value
+ * pairs.
+ *
+ * @param o another Attribute object which should be checked for equality
+ * @return true if the object is an instance of Attributes and contains the
+ * same name/value pairs, false otherwise
+ */
+ public boolean equals(Object o)
+ {
+ // quick and dirty check
+ if (this == o)
+ return true;
+
+ try
+ {
+ return map.equals(((Attributes) o).map);
+ }
+ catch (ClassCastException cce)
+ {
+ return false;
+ }
+ catch (NullPointerException npe)
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Gets the value of a specified attribute name.
+ * XXX - what if the object is a String?
+ *
+ * @param attrName the name of the attribute we want the value of
+ * @return the value of the specified attribute name or null when there is
+ * no such attribute name
+ */
+ public Object get(Object attrName)
+ {
+ return map.get(attrName);
+ }
+
+ /**
+ * Returns the hashcode of the attribute name/value map.
+ */
+ public int hashCode()
+ {
+ return map.hashCode();
+ }
+
+ /**
+ * Returns true if there are no attributes set, false otherwise.
+ */
+ public boolean isEmpty()
+ {
+ return map.isEmpty();
+ }
+
+ /**
+ * Gives a Set of all the values of defined attribute names.
+ */
+ public Set<Object> keySet()
+ {
+ return map.keySet();
+ }
+
+ /**
+ * Adds or replaces a attribute name/value pair.
+ * XXX - What if the name is a string? What if the name is neither a Name
+ * nor a String? What if the value is not a string?
+ *
+ * @param name the name of the attribute
+ * @param value the (new) value of the attribute
+ * @return the old value of the attribute or null when there was no old
+ * attribute with this name
+ */
+ public Object put(Object name, Object value)
+ {
+ return map.put(name, value);
+ }
+
+ /**
+ * Adds or replaces all attribute name/value pairs from another
+ * Attributes object to this one. The supplied Map must be an instance of
+ * Attributes.
+ *
+ * @param attr the Attributes object to merge with this one
+ * @exception ClassCastException if the supplied map is not an instance of
+ * Attributes
+ */
+ public void putAll(Map<?, ?> attr)
+ {
+ if (!(attr instanceof Attributes))
+ {
+ throw new
+ ClassCastException("Supplied Map is not an instance of Attributes");
+ }
+ map.putAll(attr);
+ }
+
+ /**
+ * Remove a attribute name/value pair.
+ * XXX - What if the name is a String?
+ *
+ * @param name the name of the attribute name/value pair to remove
+ * @return the old value of the attribute or null if the attribute didn't
+ * exist
+ */
+ public Object remove(Object name)
+ {
+ return map.remove(name);
+ }
+
+ /**
+ * Returns the number of defined attribute name/value pairs.
+ */
+ public int size()
+ {
+ return map.size();
+ }
+
+ /**
+ * Returns all the values of the defined attribute name/value pairs as a
+ * Collection.
+ */
+ public Collection<Object> values()
+ {
+ return map.values();
+ }
+}
diff --git a/libjava/classpath/java/util/jar/JarEntry.java b/libjava/classpath/java/util/jar/JarEntry.java
new file mode 100644
index 000000000..52cb2c31c
--- /dev/null
+++ b/libjava/classpath/java/util/jar/JarEntry.java
@@ -0,0 +1,172 @@
+/* JarEntry.java - Represents an entry in a jar file
+ Copyright (C) 2000, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.jar;
+
+import java.io.IOException;
+import java.security.cert.Certificate;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+
+/**
+ * Extension to a ZipEntry that contains manifest attributes and certificates.
+ * Both the Atrributes and the Certificates can be null when not set.
+ * Note that the <code>getCertificates()</code> method only returns a
+ * valid value after all of the data of the entry has been read.
+ * <p>
+ * There are no public methods to set the attributes or certificate of an
+ * Entru. Only JarEntries created by the classes in <code>java.util.jar</code>
+ * will have these properties set.
+ *
+ * @since 1.2
+ * @author Mark Wielaard (mark@klomp.org)
+ */
+
+public class JarEntry extends ZipEntry
+{
+ // (Package local) fields
+
+ Attributes attr;
+ JarFile jarfile;
+
+ // Constructors
+
+ /**
+ * Creates a new JarEntry with the specified name and no attributes or
+ * or certificates. Calls <code>super(name)</code> so all other (zip)entry
+ * fields are null or -1.
+ *
+ * @param name the name of the new jar entry
+ * @exception NullPointerException when the supplied name is null
+ * @exception IllegalArgumentException when the supplied name is longer
+ * than 65535 bytes
+ */
+ public JarEntry(String name) throws NullPointerException,
+ IllegalArgumentException
+ {
+ super(name);
+ attr = null;
+ jarfile = null;
+ }
+
+ /**
+ * Creates a new JarEntry with the specified ZipEntry as template for
+ * all properties of the entry. Both attributes and certificates will be
+ * null.
+ *
+ * @param entry the ZipEntry whose fields should be copied
+ */
+ public JarEntry(ZipEntry entry)
+ {
+ super(entry);
+ attr = null;
+ jarfile = null;
+ }
+
+ /**
+ * Creates a new JarEntry with the specified JarEntry as template for
+ * all properties of the entry.
+ *
+ * @param entry the jarEntry whose fields should be copied
+ */
+ public JarEntry(JarEntry entry)
+ {
+ super(entry);
+ try
+ {
+ attr = entry.getAttributes();
+ }
+ catch (IOException _)
+ {
+ }
+ jarfile = entry.jarfile;
+ }
+
+ // Methods
+
+ /**
+ * Returns a copy of the Attributes set for this entry.
+ * When no Attributes are set in the manifest null is returned.
+ *
+ * @return a copy of the Attributes set for this entry
+ * @exception IOException This will never be thrown. It is here for
+ * binary compatibility.
+ */
+ public Attributes getAttributes() throws IOException
+ {
+ if (attr != null)
+ {
+ return (Attributes) attr.clone();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Returns a copy of the certificates set for this entry.
+ * When no certificates are set or when not all data of this entry has
+ * been read null is returned.
+ * <p>
+ * To make sure that this call returns a valid value you must read all
+ * data from the JarInputStream for this entry.
+ * When you don't need the data for an entry but want to know the
+ * certificates that are set for the entry then you can skip all data by
+ * calling <code>skip(entry.getSize())</code> on the JarInputStream for
+ * the entry.
+ *
+ * @return a copy of the certificates set for this entry
+ */
+ public Certificate[] getCertificates()
+ {
+ if (jarfile != null)
+ {
+ synchronized (jarfile)
+ {
+ if (jarfile.entryCerts != null)
+ {
+ Set certs = (Set) jarfile.entryCerts.get(getName());
+ if (certs != null
+ && jarfile.verified.get(getName()) == Boolean.TRUE)
+ return (Certificate[]) certs.toArray(new Certificate[certs.size()]);
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/libjava/classpath/java/util/jar/JarException.java b/libjava/classpath/java/util/jar/JarException.java
new file mode 100644
index 000000000..d6f0634fe
--- /dev/null
+++ b/libjava/classpath/java/util/jar/JarException.java
@@ -0,0 +1,77 @@
+/* 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.
+
+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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.jar;
+
+import java.util.zip.ZipException;
+
+/**
+ * This exception is thrown to indicate an problem with a jar file.
+ * 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.
+ *
+ * @author Mark Wielaard (mark@klomp.org)
+ * @since 1.2
+ */
+public class JarException extends ZipException
+{
+ /**
+ * Compatible with JDK 1.2+.
+ */
+ private static final long serialVersionUID = 7159778400963954473L;
+
+ /**
+ * Create a new JarException without a descriptive error message.
+ */
+ public JarException()
+ {
+ }
+
+ /**
+ * 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.
+ *
+ * @param message The descriptive error message
+ * @see #getMessage()
+ */
+ public JarException(String message)
+ {
+ super(message);
+ }
+}
diff --git a/libjava/classpath/java/util/jar/JarFile.java b/libjava/classpath/java/util/jar/JarFile.java
new file mode 100644
index 000000000..b67c95346
--- /dev/null
+++ b/libjava/classpath/java/util/jar/JarFile.java
@@ -0,0 +1,981 @@
+/* JarFile.java - Representation of a jar file
+ Copyright (C) 2000, 2003, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.jar;
+
+import gnu.java.io.Base64InputStream;
+import gnu.java.security.OID;
+import gnu.java.security.pkcs.PKCS7SignedData;
+import gnu.java.security.pkcs.SignerInfo;
+import gnu.java.security.provider.Gnu;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.CRLException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+
+/**
+ * Representation of a jar file.
+ * <p>
+ * Note that this class is not a subclass of java.io.File but a subclass of
+ * java.util.zip.ZipFile and you can only read JarFiles with it (although
+ * there are constructors that take a File object).
+ *
+ * @since 1.2
+ * @author Mark Wielaard (mark@klomp.org)
+ * @author Casey Marshall (csm@gnu.org) wrote the certificate and entry
+ * verification code.
+ */
+public class JarFile extends ZipFile
+{
+ // Fields
+
+ /** The name of the manifest entry: META-INF/MANIFEST.MF */
+ public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
+
+ /** The META-INF directory entry. */
+ private static final String META_INF = "META-INF/";
+
+ /** The suffix for PKCS7 DSA signature entries. */
+ private static final String PKCS7_DSA_SUFFIX = ".DSA";
+
+ /** The suffix for PKCS7 RSA signature entries. */
+ private static final String PKCS7_RSA_SUFFIX = ".RSA";
+
+ /** The suffix for digest attributes. */
+ private static final String DIGEST_KEY_SUFFIX = "-Digest";
+
+ /** The suffix for signature files. */
+ private static final String SF_SUFFIX = ".SF";
+
+ /**
+ * The security provider to use for signature verification.
+ * We need a known fallback to be able to read any signed jar file
+ * (which might contain the user selected security provider).
+ * This is package-private to avoid accessor methods for inner classes.
+ */
+ static final Gnu provider = new Gnu();
+
+ // Signature OIDs.
+ private static final OID MD2_OID = new OID("1.2.840.113549.2.2");
+ private static final OID MD4_OID = new OID("1.2.840.113549.2.4");
+ private static final OID MD5_OID = new OID("1.2.840.113549.2.5");
+ private static final OID SHA1_OID = new OID("1.3.14.3.2.26");
+ private static final OID DSA_ENCRYPTION_OID = new OID("1.2.840.10040.4.1");
+ private static final OID RSA_ENCRYPTION_OID = new OID("1.2.840.113549.1.1.1");
+
+ /**
+ * The manifest of this file, if any, otherwise null.
+ * Read when first needed.
+ */
+ private Manifest manifest;
+
+ /** Whether to verify the manifest and all entries. */
+ boolean verify;
+
+ /** Whether the has already been loaded. */
+ private boolean manifestRead = false;
+
+ /** Whether the signature files have been loaded. */
+ boolean signaturesRead = false;
+
+ /**
+ * A map between entry names and booleans, signaling whether or
+ * not that entry has been verified.
+ * Only be accessed with lock on this JarFile*/
+ HashMap verified = new HashMap();
+
+ /**
+ * A mapping from entry name to certificates, if any.
+ * Only accessed with lock on this JarFile.
+ */
+ HashMap entryCerts;
+
+ /**
+ * A {@link Map} of message digest algorithm names to their implementation.
+ * Used to reduce object (algorithm implementation) instantiation.
+ */
+ private HashMap digestAlgorithms = new HashMap();
+
+ static boolean DEBUG = false;
+ static void debug(Object msg)
+ {
+ System.err.print(JarFile.class.getName());
+ System.err.print(" >>> ");
+ System.err.println(msg);
+ }
+
+ // Constructors
+
+ /**
+ * Creates a new JarFile. All jar entries are verified (when a Manifest file
+ * for this JarFile exists). You need to actually open and read the complete
+ * jar entry (with <code>getInputStream()</code>) to check its signature.
+ *
+ * @param fileName the name of the file to open
+ * @exception FileNotFoundException if the fileName cannot be found
+ * @exception IOException if another IO exception occurs while reading
+ */
+ public JarFile(String fileName) throws FileNotFoundException, IOException
+ {
+ this(fileName, true);
+ }
+
+ /**
+ * Creates a new JarFile. If verify is true then all jar entries are
+ * verified (when a Manifest file for this JarFile exists). You need to
+ * actually open and read the complete jar entry
+ * (with <code>getInputStream()</code>) to check its signature.
+ *
+ * @param fileName the name of the file to open
+ * @param verify checks manifest and entries when true and a manifest
+ * exists, when false no checks are made
+ * @exception FileNotFoundException if the fileName cannot be found
+ * @exception IOException if another IO exception occurs while reading
+ */
+ public JarFile(String fileName, boolean verify) throws
+ FileNotFoundException, IOException
+ {
+ super(fileName);
+ if (verify)
+ {
+ manifest = readManifest();
+ verify();
+ }
+ }
+
+ /**
+ * Creates a new JarFile. All jar entries are verified (when a Manifest file
+ * for this JarFile exists). You need to actually open and read the complete
+ * jar entry (with <code>getInputStream()</code>) to check its signature.
+ *
+ * @param file the file to open as a jar file
+ * @exception FileNotFoundException if the file does not exits
+ * @exception IOException if another IO exception occurs while reading
+ */
+ public JarFile(File file) throws FileNotFoundException, IOException
+ {
+ this(file, true);
+ }
+
+ /**
+ * Creates a new JarFile. If verify is true then all jar entries are
+ * verified (when a Manifest file for this JarFile exists). You need to
+ * actually open and read the complete jar entry
+ * (with <code>getInputStream()</code>) to check its signature.
+ *
+ * @param file the file to open to open as a jar file
+ * @param verify checks manifest and entries when true and a manifest
+ * exists, when false no checks are made
+ * @exception FileNotFoundException if file does not exist
+ * @exception IOException if another IO exception occurs while reading
+ */
+ public JarFile(File file, boolean verify) throws FileNotFoundException,
+ IOException
+ {
+ super(file);
+ if (verify)
+ {
+ manifest = readManifest();
+ verify();
+ }
+ }
+
+ /**
+ * Creates a new JarFile with the indicated mode. If verify is true then
+ * all jar entries are verified (when a Manifest file for this JarFile
+ * exists). You need to actually open and read the complete jar entry
+ * (with <code>getInputStream()</code>) to check its signature.
+ * manifest and if the manifest exists and verify is true verfies it.
+ *
+ * @param file the file to open to open as a jar file
+ * @param verify checks manifest and entries when true and a manifest
+ * exists, when false no checks are made
+ * @param mode either ZipFile.OPEN_READ or
+ * (ZipFile.OPEN_READ | ZipFile.OPEN_DELETE)
+ * @exception FileNotFoundException if the file does not exist
+ * @exception IOException if another IO exception occurs while reading
+ * @exception IllegalArgumentException when given an illegal mode
+ *
+ * @since 1.3
+ */
+ public JarFile(File file, boolean verify, int mode) throws
+ FileNotFoundException, IOException, IllegalArgumentException
+ {
+ super(file, mode);
+ if (verify)
+ {
+ manifest = readManifest();
+ verify();
+ }
+ }
+
+ // Methods
+
+ /**
+ * XXX - should verify the manifest file
+ */
+ private void verify()
+ {
+ // only check if manifest is not null
+ if (manifest == null)
+ {
+ verify = false;
+ return;
+ }
+
+ verify = true;
+ // XXX - verify manifest
+ }
+
+ /**
+ * Parses and returns the manifest if it exists, otherwise returns null.
+ */
+ private Manifest readManifest()
+ {
+ try
+ {
+ ZipEntry manEntry = super.getEntry(MANIFEST_NAME);
+ if (manEntry != null)
+ {
+ InputStream in = super.getInputStream(manEntry);
+ manifestRead = true;
+ return new Manifest(in);
+ }
+ else
+ {
+ manifestRead = true;
+ return null;
+ }
+ }
+ catch (IOException ioe)
+ {
+ manifestRead = true;
+ return null;
+ }
+ }
+
+ /**
+ * Returns a enumeration of all the entries in the JarFile.
+ * Note that also the Jar META-INF entries are returned.
+ *
+ * @exception IllegalStateException when the JarFile is already closed
+ */
+ public Enumeration<JarEntry> entries() throws IllegalStateException
+ {
+ return new JarEnumeration(super.entries(), this);
+ }
+
+ /**
+ * Wraps a given Zip Entries Enumeration. For every zip entry a
+ * JarEntry is created and the corresponding Attributes are looked up.
+ */
+ private static class JarEnumeration implements Enumeration<JarEntry>
+ {
+
+ private final Enumeration<? extends ZipEntry> entries;
+ private final JarFile jarfile;
+
+ JarEnumeration(Enumeration<? extends ZipEntry> e, JarFile f)
+ {
+ entries = e;
+ jarfile = f;
+ }
+
+ public boolean hasMoreElements()
+ {
+ return entries.hasMoreElements();
+ }
+
+ public JarEntry nextElement()
+ {
+ ZipEntry zip = (ZipEntry) entries.nextElement();
+ JarEntry jar = new JarEntry(zip);
+ Manifest manifest;
+ try
+ {
+ manifest = jarfile.getManifest();
+ }
+ catch (IOException ioe)
+ {
+ manifest = null;
+ }
+
+ if (manifest != null)
+ {
+ jar.attr = manifest.getAttributes(jar.getName());
+ }
+
+ synchronized(jarfile)
+ {
+ if (jarfile.verify && !jarfile.signaturesRead)
+ try
+ {
+ jarfile.readSignatures();
+ }
+ catch (IOException ioe)
+ {
+ if (JarFile.DEBUG)
+ {
+ JarFile.debug(ioe);
+ ioe.printStackTrace();
+ }
+ jarfile.signaturesRead = true; // fudge it.
+ }
+ }
+ jar.jarfile = jarfile;
+ return jar;
+ }
+ }
+
+ /**
+ * XXX
+ * It actually returns a JarEntry not a zipEntry
+ * @param name XXX
+ */
+ public synchronized ZipEntry getEntry(String name)
+ {
+ ZipEntry entry = super.getEntry(name);
+ if (entry != null)
+ {
+ JarEntry jarEntry = new JarEntry(entry);
+ Manifest manifest;
+ try
+ {
+ manifest = getManifest();
+ }
+ catch (IOException ioe)
+ {
+ manifest = null;
+ }
+
+ if (manifest != null)
+ {
+ jarEntry.attr = manifest.getAttributes(name);
+ }
+
+ if (verify && !signaturesRead)
+ try
+ {
+ readSignatures();
+ }
+ catch (IOException ioe)
+ {
+ if (DEBUG)
+ {
+ debug(ioe);
+ ioe.printStackTrace();
+ }
+ signaturesRead = true;
+ }
+ jarEntry.jarfile = this;
+ return jarEntry;
+ }
+ return null;
+ }
+
+ /**
+ * Returns an input stream for the given entry. If configured to
+ * verify entries, the input stream returned will verify them while
+ * the stream is read, but only on the first time.
+ *
+ * @param entry The entry to get the input stream for.
+ * @exception ZipException XXX
+ * @exception IOException XXX
+ */
+ public synchronized InputStream getInputStream(ZipEntry entry) throws
+ ZipException, IOException
+ {
+ // If we haven't verified the hash, do it now.
+ if (!verified.containsKey(entry.getName()) && verify)
+ {
+ if (DEBUG)
+ debug("reading and verifying " + entry);
+ return new EntryInputStream(entry, super.getInputStream(entry), this);
+ }
+ else
+ {
+ if (DEBUG)
+ debug("reading already verified entry " + entry);
+ if (verify && verified.get(entry.getName()) == Boolean.FALSE)
+ throw new ZipException("digest for " + entry + " is invalid");
+ return super.getInputStream(entry);
+ }
+ }
+
+ /**
+ * Returns the JarEntry that belongs to the name if such an entry
+ * exists in the JarFile. Returns null otherwise
+ * Convenience method that just casts the result from <code>getEntry</code>
+ * to a JarEntry.
+ *
+ * @param name the jar entry name to look up
+ * @return the JarEntry if it exists, null otherwise
+ */
+ public JarEntry getJarEntry(String name)
+ {
+ return (JarEntry) getEntry(name);
+ }
+
+ /**
+ * Returns the manifest for this JarFile or null when the JarFile does not
+ * contain a manifest file.
+ */
+ public synchronized Manifest getManifest() throws IOException
+ {
+ if (!manifestRead)
+ manifest = readManifest();
+
+ return manifest;
+ }
+
+ // Only called with lock on this JarFile.
+ // Package private for use in inner classes.
+ void readSignatures() throws IOException
+ {
+ Map pkcs7Dsa = new HashMap();
+ Map pkcs7Rsa = new HashMap();
+ Map sigFiles = new HashMap();
+
+ // Phase 1: Read all signature files. These contain the user
+ // certificates as well as the signatures themselves.
+ for (Enumeration e = super.entries(); e.hasMoreElements(); )
+ {
+ ZipEntry ze = (ZipEntry) e.nextElement();
+ String name = ze.getName();
+ if (name.startsWith(META_INF))
+ {
+ String alias = name.substring(META_INF.length());
+ if (alias.lastIndexOf('.') >= 0)
+ alias = alias.substring(0, alias.lastIndexOf('.'));
+
+ if (name.endsWith(PKCS7_DSA_SUFFIX) || name.endsWith(PKCS7_RSA_SUFFIX))
+ {
+ if (DEBUG)
+ debug("reading PKCS7 info from " + name + ", alias=" + alias);
+ PKCS7SignedData sig = null;
+ try
+ {
+ sig = new PKCS7SignedData(super.getInputStream(ze));
+ }
+ catch (CertificateException ce)
+ {
+ IOException ioe = new IOException("certificate parsing error");
+ ioe.initCause(ce);
+ throw ioe;
+ }
+ catch (CRLException crle)
+ {
+ IOException ioe = new IOException("CRL parsing error");
+ ioe.initCause(crle);
+ throw ioe;
+ }
+ if (name.endsWith(PKCS7_DSA_SUFFIX))
+ pkcs7Dsa.put(alias, sig);
+ else if (name.endsWith(PKCS7_RSA_SUFFIX))
+ pkcs7Rsa.put(alias, sig);
+ }
+ else if (name.endsWith(SF_SUFFIX))
+ {
+ if (DEBUG)
+ debug("reading signature file for " + alias + ": " + name);
+ Manifest sf = new Manifest(super.getInputStream(ze));
+ sigFiles.put(alias, sf);
+ if (DEBUG)
+ debug("result: " + sf);
+ }
+ }
+ }
+
+ // Phase 2: verify the signatures on any signature files.
+ Set validCerts = new HashSet();
+ Map entryCerts = new HashMap();
+ for (Iterator it = sigFiles.entrySet().iterator(); it.hasNext(); )
+ {
+ int valid = 0;
+ Map.Entry e = (Map.Entry) it.next();
+ String alias = (String) e.getKey();
+
+ PKCS7SignedData sig = (PKCS7SignedData) pkcs7Dsa.get(alias);
+ if (sig != null)
+ {
+ Certificate[] certs = sig.getCertificates();
+ Set signerInfos = sig.getSignerInfos();
+ for (Iterator it2 = signerInfos.iterator(); it2.hasNext(); )
+ verify(certs, (SignerInfo) it2.next(), alias, validCerts);
+ }
+
+ sig = (PKCS7SignedData) pkcs7Rsa.get(alias);
+ if (sig != null)
+ {
+ Certificate[] certs = sig.getCertificates();
+ Set signerInfos = sig.getSignerInfos();
+ for (Iterator it2 = signerInfos.iterator(); it2.hasNext(); )
+ verify(certs, (SignerInfo) it2.next(), alias, validCerts);
+ }
+
+ // It isn't a signature for anything. Punt it.
+ if (validCerts.isEmpty())
+ {
+ it.remove();
+ continue;
+ }
+
+ entryCerts.put(e.getValue(), new HashSet(validCerts));
+ validCerts.clear();
+ }
+
+ // Read the manifest into a HashMap (String fileName, String entry)
+ // The fileName might be split into multiple lines in the manifest.
+ // Such additional lines will start with a space.
+ InputStream in = super.getInputStream(super.getEntry(MANIFEST_NAME));
+ ByteArrayOutputStream baStream = new ByteArrayOutputStream();
+ byte[] ba = new byte[1024];
+ while (true)
+ {
+ int len = in.read(ba);
+ if (len < 0)
+ break;
+ baStream.write(ba, 0, len);
+ }
+ in.close();
+
+ HashMap hmManifestEntries = new HashMap();
+ Pattern p = Pattern.compile("Name: (.+?\r?\n(?: .+?\r?\n)*)"
+ + ".+?-Digest: .+?\r?\n\r?\n");
+ Matcher m = p.matcher(baStream.toString());
+ while (m.find())
+ {
+ String fileName = m.group(1).replaceAll("\r?\n ?", "");
+ hmManifestEntries.put(fileName, m.group());
+ }
+
+ // Phase 3: verify the signature file signatures against the manifest,
+ // mapping the entry name to the target certificates.
+ this.entryCerts = new HashMap();
+ for (Iterator it = entryCerts.entrySet().iterator(); it.hasNext(); )
+ {
+ Map.Entry e = (Map.Entry) it.next();
+ Manifest sigfile = (Manifest) e.getKey();
+ Map entries = sigfile.getEntries();
+ Set certificates = (Set) e.getValue();
+
+ for (Iterator it2 = entries.entrySet().iterator(); it2.hasNext(); )
+ {
+ Map.Entry e2 = (Map.Entry) it2.next();
+ String entryname = String.valueOf(e2.getKey());
+ Attributes attr = (Attributes) e2.getValue();
+ if (verifyHashes(entryname, attr, hmManifestEntries))
+ {
+ if (DEBUG)
+ debug("entry " + entryname + " has certificates " + certificates);
+ Set s = (Set) this.entryCerts.get(entryname);
+ if (s != null)
+ s.addAll(certificates);
+ else
+ this.entryCerts.put(entryname, new HashSet(certificates));
+ }
+ }
+ }
+
+ signaturesRead = true;
+ }
+
+ /**
+ * Tell if the given signer info is over the given alias's signature file,
+ * given one of the certificates specified.
+ */
+ private void verify(Certificate[] certs, SignerInfo signerInfo,
+ String alias, Set validCerts)
+ {
+ Signature sig = null;
+ try
+ {
+ OID alg = signerInfo.getDigestEncryptionAlgorithmId();
+ if (alg.equals(DSA_ENCRYPTION_OID))
+ {
+ if (!signerInfo.getDigestAlgorithmId().equals(SHA1_OID))
+ return;
+ sig = Signature.getInstance("SHA1withDSA", provider);
+ }
+ else if (alg.equals(RSA_ENCRYPTION_OID))
+ {
+ OID hash = signerInfo.getDigestAlgorithmId();
+ if (hash.equals(MD2_OID))
+ sig = Signature.getInstance("md2WithRsaEncryption", provider);
+ else if (hash.equals(MD4_OID))
+ sig = Signature.getInstance("md4WithRsaEncryption", provider);
+ else if (hash.equals(MD5_OID))
+ sig = Signature.getInstance("md5WithRsaEncryption", provider);
+ else if (hash.equals(SHA1_OID))
+ sig = Signature.getInstance("sha1WithRsaEncryption", provider);
+ else
+ return;
+ }
+ else
+ {
+ if (DEBUG)
+ debug("unsupported signature algorithm: " + alg);
+ return;
+ }
+ }
+ catch (NoSuchAlgorithmException nsae)
+ {
+ if (DEBUG)
+ {
+ debug(nsae);
+ nsae.printStackTrace();
+ }
+ return;
+ }
+ ZipEntry sigFileEntry = super.getEntry(META_INF + alias + SF_SUFFIX);
+ if (sigFileEntry == null)
+ return;
+ for (int i = 0; i < certs.length; i++)
+ {
+ if (!(certs[i] instanceof X509Certificate))
+ continue;
+ X509Certificate cert = (X509Certificate) certs[i];
+ if (!cert.getIssuerX500Principal().equals(signerInfo.getIssuer()) ||
+ !cert.getSerialNumber().equals(signerInfo.getSerialNumber()))
+ continue;
+ try
+ {
+ sig.initVerify(cert.getPublicKey());
+ InputStream in = super.getInputStream(sigFileEntry);
+ if (in == null)
+ continue;
+ byte[] buf = new byte[1024];
+ int len = 0;
+ while ((len = in.read(buf)) != -1)
+ sig.update(buf, 0, len);
+ if (sig.verify(signerInfo.getEncryptedDigest()))
+ {
+ if (DEBUG)
+ debug("signature for " + cert.getSubjectDN() + " is good");
+ validCerts.add(cert);
+ }
+ }
+ catch (IOException ioe)
+ {
+ continue;
+ }
+ catch (InvalidKeyException ike)
+ {
+ continue;
+ }
+ catch (SignatureException se)
+ {
+ continue;
+ }
+ }
+ }
+
+ /**
+ * Verifies that the digest(s) in a signature file were, in fact, made over
+ * the manifest entry for ENTRY.
+ *
+ * @param entry The entry name.
+ * @param attr The attributes from the signature file to verify.
+ * @param hmManifestEntries Mappings of Jar file entry names to their manifest
+ * entry text; i.e. the base-64 encoding of their
+ */
+ private boolean verifyHashes(String entry, Attributes attr,
+ HashMap hmManifestEntries)
+ {
+ int verified = 0;
+
+ String stringEntry = (String) hmManifestEntries.get(entry);
+ if (stringEntry == null)
+ {
+ if (DEBUG)
+ debug("could not find " + entry + " in manifest");
+ return false;
+ }
+ // The bytes for ENTRY's manifest entry, which are signed in the
+ // signature file.
+ byte[] entryBytes = stringEntry.getBytes();
+
+ for (Iterator it = attr.entrySet().iterator(); it.hasNext(); )
+ {
+ Map.Entry e = (Map.Entry) it.next();
+ String key = String.valueOf(e.getKey());
+ if (!key.endsWith(DIGEST_KEY_SUFFIX))
+ continue;
+ String alg = key.substring(0, key.length() - DIGEST_KEY_SUFFIX.length());
+ try
+ {
+ byte[] hash = Base64InputStream.decode((String) e.getValue());
+ MessageDigest md = (MessageDigest) digestAlgorithms.get(alg);
+ if (md == null)
+ {
+ md = MessageDigest.getInstance(alg, provider);
+ digestAlgorithms.put(alg, md);
+ }
+ md.reset();
+ byte[] hash2 = md.digest(entryBytes);
+ if (DEBUG)
+ debug("verifying SF entry " + entry + " alg: " + md.getAlgorithm()
+ + " expect=" + new java.math.BigInteger(hash).toString(16)
+ + " comp=" + new java.math.BigInteger(hash2).toString(16));
+ if (!Arrays.equals(hash, hash2))
+ return false;
+ verified++;
+ }
+ catch (IOException ioe)
+ {
+ if (DEBUG)
+ {
+ debug(ioe);
+ ioe.printStackTrace();
+ }
+ return false;
+ }
+ catch (NoSuchAlgorithmException nsae)
+ {
+ if (DEBUG)
+ {
+ debug(nsae);
+ nsae.printStackTrace();
+ }
+ return false;
+ }
+ }
+
+ // We have to find at least one valid digest.
+ return verified > 0;
+ }
+
+ /**
+ * A utility class that verifies jar entries as they are read.
+ */
+ private static class EntryInputStream extends FilterInputStream
+ {
+ private final JarFile jarfile;
+ private final long length;
+ private long pos;
+ private final ZipEntry entry;
+ private final byte[][] hashes;
+ private final MessageDigest[] md;
+ private boolean checked;
+
+ EntryInputStream(final ZipEntry entry,
+ final InputStream in,
+ final JarFile jar)
+ throws IOException
+ {
+ super(in);
+ this.entry = entry;
+ this.jarfile = jar;
+
+ length = entry.getSize();
+ pos = 0;
+ checked = false;
+
+ Attributes attr;
+ Manifest manifest = jarfile.getManifest();
+ if (manifest != null)
+ attr = manifest.getAttributes(entry.getName());
+ else
+ attr = null;
+ if (DEBUG)
+ debug("verifying entry " + entry + " attr=" + attr);
+ if (attr == null)
+ {
+ hashes = new byte[0][];
+ md = new MessageDigest[0];
+ }
+ else
+ {
+ List hashes = new LinkedList();
+ List md = new LinkedList();
+ for (Iterator it = attr.entrySet().iterator(); it.hasNext(); )
+ {
+ Map.Entry e = (Map.Entry) it.next();
+ String key = String.valueOf(e.getKey());
+ if (key == null)
+ continue;
+ if (!key.endsWith(DIGEST_KEY_SUFFIX))
+ continue;
+ hashes.add(Base64InputStream.decode((String) e.getValue()));
+ try
+ {
+ int length = key.length() - DIGEST_KEY_SUFFIX.length();
+ String alg = key.substring(0, length);
+ md.add(MessageDigest.getInstance(alg, provider));
+ }
+ catch (NoSuchAlgorithmException nsae)
+ {
+ IOException ioe = new IOException("no such message digest: " + key);
+ ioe.initCause(nsae);
+ throw ioe;
+ }
+ }
+ if (DEBUG)
+ debug("digests=" + md);
+ this.hashes = (byte[][]) hashes.toArray(new byte[hashes.size()][]);
+ this.md = (MessageDigest[]) md.toArray(new MessageDigest[md.size()]);
+ }
+ }
+
+ public boolean markSupported()
+ {
+ return false;
+ }
+
+ public void mark(int readLimit)
+ {
+ }
+
+ public void reset()
+ {
+ }
+
+ public int read() throws IOException
+ {
+ int b = super.read();
+ if (b == -1)
+ {
+ eof();
+ return -1;
+ }
+ for (int i = 0; i < md.length; i++)
+ md[i].update((byte) b);
+ pos++;
+ if (length > 0 && pos >= length)
+ eof();
+ return b;
+ }
+
+ public int read(byte[] buf, int off, int len) throws IOException
+ {
+ int count = super.read(buf, off, (int) Math.min(len, (length != 0
+ ? length - pos
+ : Integer.MAX_VALUE)));
+ if (count == -1 || (length > 0 && pos >= length))
+ {
+ eof();
+ return -1;
+ }
+ for (int i = 0; i < md.length; i++)
+ md[i].update(buf, off, count);
+ pos += count;
+ if (length != 0 && pos >= length)
+ eof();
+ return count;
+ }
+
+ public int read(byte[] buf) throws IOException
+ {
+ return read(buf, 0, buf.length);
+ }
+
+ public long skip(long bytes) throws IOException
+ {
+ byte[] b = new byte[1024];
+ long amount = 0;
+ while (amount < bytes)
+ {
+ int l = read(b, 0, (int) Math.min(b.length, bytes - amount));
+ if (l == -1)
+ break;
+ amount += l;
+ }
+ return amount;
+ }
+
+ private void eof() throws IOException
+ {
+ if (checked)
+ return;
+ checked = true;
+ for (int i = 0; i < md.length; i++)
+ {
+ byte[] hash = md[i].digest();
+ if (DEBUG)
+ debug("verifying " + md[i].getAlgorithm() + " expect="
+ + new java.math.BigInteger(hashes[i]).toString(16)
+ + " comp=" + new java.math.BigInteger(hash).toString(16));
+ if (!Arrays.equals(hash, hashes[i]))
+ {
+ synchronized(jarfile)
+ {
+ if (DEBUG)
+ debug(entry + " could NOT be verified");
+ jarfile.verified.put(entry.getName(), Boolean.FALSE);
+ }
+ return;
+ // XXX ??? what do we do here?
+ // throw new ZipException("message digest mismatch");
+ }
+ }
+
+ synchronized(jarfile)
+ {
+ if (DEBUG)
+ debug(entry + " has been VERIFIED");
+ jarfile.verified.put(entry.getName(), Boolean.TRUE);
+ }
+ }
+ }
+}
diff --git a/libjava/classpath/java/util/jar/JarInputStream.java b/libjava/classpath/java/util/jar/JarInputStream.java
new file mode 100644
index 000000000..4de6609a5
--- /dev/null
+++ b/libjava/classpath/java/util/jar/JarInputStream.java
@@ -0,0 +1,200 @@
+/* JarInputStream.java - InputStream for reading jar files
+ Copyright (C) 2000, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.jar;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * InputStream for reading jar files.
+ * XXX - verification of the signatures in the Manifest file is not yet
+ * implemented.
+ *
+ * @since 1.2
+ * @author Mark Wielaard (mark@klomp.org)
+ */
+
+public class JarInputStream extends ZipInputStream
+{
+ // Fields
+
+ /** The manifest for this file or null when there was no manifest. */
+ private Manifest manifest;
+
+ /** The first real JarEntry for this file. Used by readManifest() to store
+ an entry that isn't the manifest but that should be returned by
+ getNextEntry next time it is called. Null when no firstEntry was read
+ while searching for the manifest entry, or when it has already been
+ returned by getNextEntry(). */
+ private JarEntry firstEntry;
+
+ // Constructors
+
+ /**
+ * Creates a new JarInputStream and tries to read the manifest.
+ * If such a manifest is present the JarInputStream tries to verify all
+ * the entry signatures while reading.
+ *
+ * @param in InputStream to read the jar from
+ * @exception IOException when an error occurs when opening or reading
+ */
+ public JarInputStream(InputStream in) throws IOException
+ {
+ this(in, true);
+ }
+
+ /**
+ * Creates a new JarInputStream and tries to read the manifest.
+ * If such a manifest is present and verify is true, the JarInputStream
+ * tries to verify all the entry signatures while reading.
+ *
+ * @param in InputStream to read the jar from
+ * @param verify whether or not to verify the manifest entries
+ * @exception IOException when an error occurs when opening or reading
+ */
+ public JarInputStream(InputStream in, boolean verify) throws IOException
+ {
+ super(in);
+ readManifest(verify);
+ }
+
+ // Methods
+
+ /**
+ * Set the manifest if found. Skips all entries that start with "META-INF/"
+ *
+ * @param verify when true (and a Manifest is found) checks the Manifest,
+ * when false no check is performed
+ * @exception IOException if an error occurs while reading
+ */
+ private void readManifest(boolean verify) throws IOException
+ {
+ firstEntry = (JarEntry) super.getNextEntry();
+ while ((firstEntry != null) &&
+ firstEntry.getName().startsWith("META-INF/"))
+ {
+ if (firstEntry.getName().equals(JarFile.MANIFEST_NAME))
+ {
+ manifest = new Manifest(this);
+ }
+ firstEntry = (JarEntry) super.getNextEntry();
+ }
+
+ if (verify)
+ {
+ // XXX
+ }
+ }
+
+ /**
+ * Creates a JarEntry for a particular name and consults the manifest
+ * for the Attributes of the entry.
+ * Used by <code>ZipEntry.getNextEntry()</code>
+ *
+ * @param name the name of the new entry
+ */
+ protected ZipEntry createZipEntry(String name)
+ {
+ ZipEntry zipEntry = super.createZipEntry(name);
+ JarEntry jarEntry = new JarEntry(zipEntry);
+ if (manifest != null)
+ {
+ jarEntry.attr = manifest.getAttributes(name);
+ }
+ return jarEntry;
+ }
+
+ /**
+ * Returns the Manifest for the jar file or null if there was no Manifest.
+ */
+ public Manifest getManifest()
+ {
+ return manifest;
+ }
+
+ /**
+ * Returns the next entry or null when there are no more entries.
+ * Does actually return a JarEntry, if you don't want to cast it yourself
+ * use <code>getNextJarEntry()</code>. Does not return any entries found
+ * at the beginning of the ZipFile that are special
+ * (those that start with "META-INF/").
+ *
+ * @exception IOException if an IO error occurs when reading the entry
+ */
+ public ZipEntry getNextEntry() throws IOException
+ {
+ ZipEntry entry;
+ if (firstEntry != null)
+ {
+ entry = firstEntry;
+ firstEntry = null;
+ }
+ else
+ {
+ entry = super.getNextEntry();
+ }
+ return entry;
+ }
+
+ /**
+ * Returns the next jar entry or null when there are no more entries.
+ *
+ * @exception IOException if an IO error occurs when reading the entry
+ */
+ public JarEntry getNextJarEntry() throws IOException
+ {
+ return (JarEntry) getNextEntry();
+ }
+
+ /**
+ * XXX
+ *
+ * @param buf XXX
+ * @param off XXX
+ * @param len XXX
+ * @return XXX
+ * @exception IOException XXX
+ */
+ public int read(byte[]buf, int off, int len) throws IOException
+ {
+ // XXX if (verify) {}
+ return super.read(buf, off, len);
+ }
+}
diff --git a/libjava/classpath/java/util/jar/JarOutputStream.java b/libjava/classpath/java/util/jar/JarOutputStream.java
new file mode 100644
index 000000000..0ba6002cb
--- /dev/null
+++ b/libjava/classpath/java/util/jar/JarOutputStream.java
@@ -0,0 +1,113 @@
+/* JarOutputStream.java - OutputStream for writing jar files
+ Copyright (C) 2000, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.jar;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * OutputStream for writing jar files.
+ * A special ZipOutputStream that can take JarEntries and can have a optional
+ * Manifest as first entry.
+ *
+ * @author Mark Wielaard (mark@klomp.org)
+ */
+
+public class JarOutputStream extends ZipOutputStream
+{
+ // Constructors
+
+ /**
+ * Creates a new JarOutputStream without a manifest entry.
+ *
+ * @param out the stream to create the new jar on
+ * @exception IOException if something unexpected happend
+ */
+ public JarOutputStream(OutputStream out) throws IOException
+ {
+ this(out, null);
+ }
+
+ /**
+ * Creates a new JarOutputStream with a manifest entry.
+ * The manifest will be the first entry in the jar.
+ *
+ * @param out the stream to create the new jar on
+ * @param man the manifest that should be put in the jar file or null
+ * for no manifest entry
+ * @exception IOException if something unexpected happend
+ */
+ public JarOutputStream(OutputStream out, Manifest man) throws IOException
+ {
+ super(out);
+ if (man != null)
+ writeManifest(man);
+ }
+
+ // Methods
+
+ /**
+ * Writes the manifest to a new JarEntry in this JarOutputStream with as
+ * name JarFile.MANIFEST_NAME.
+ *
+ * @param manifest the non null manifest to be written
+ * @exception IOException if something unexpected happend
+ */
+ private void writeManifest(Manifest manifest) throws IOException
+ {
+ // Create a new Jar Entry for the Manifest
+ JarEntry entry = new JarEntry(JarFile.MANIFEST_NAME);
+ putNextEntry(entry);
+ manifest.write(this);
+ closeEntry();
+ }
+
+ /**
+ * Prepares the JarOutputStream for writing the next entry.
+ * This implementation just calls <code>super.putNextEntry()</code>.
+ *
+ * @param entry The information for the next entry
+ * @exception IOException when some unexpected I/O exception occurred
+ */
+ public void putNextEntry(ZipEntry entry) throws IOException
+ {
+ super.putNextEntry(entry); // XXX
+ }
+}
diff --git a/libjava/classpath/java/util/jar/Manifest.java b/libjava/classpath/java/util/jar/Manifest.java
new file mode 100644
index 000000000..f266d823e
--- /dev/null
+++ b/libjava/classpath/java/util/jar/Manifest.java
@@ -0,0 +1,213 @@
+/* Manifest.java -- Reads, writes and manipulates jar manifest files
+ Copyright (C) 2000, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.jar;
+
+import gnu.java.util.jar.JarUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Hashtable;
+import java.util.Map;
+
+/**
+ * Reads, writes and manipulaties jar manifest files.
+ * XXX
+ *
+ * @since 1.2
+ * @author Mark Wielaard (mark@klomp.org)
+ */
+public class Manifest implements Cloneable
+{
+ // Fields
+
+ /** The main attributes of the manifest (jar file). */
+ private final Attributes mainAttr;
+
+ /** A map of atrributes for all entries described in this Manifest. */
+ private final Map<String, Attributes> entries;
+
+ // Constructors
+
+ /**
+ * Creates a new empty Manifest.
+ */
+ public Manifest()
+ {
+ mainAttr = new Attributes();
+ entries = new Hashtable<String, Attributes>();
+ }
+
+ /**
+ * Creates a Manifest from the supplied input stream.
+ *
+ * @see #read(InputStream)
+ * @see #write(OutputStream)
+ *
+ * @param in the input stream to read the manifest from
+ * @exception IOException when an i/o exception occurs or the input stream
+ * does not describe a valid manifest
+ */
+ public Manifest(InputStream in) throws IOException
+ {
+ this();
+ read(in);
+ }
+
+ /**
+ * Creates a Manifest from another Manifest.
+ * Makes a deep copy of the main attributes, but a shallow copy of
+ * the other entries. This means that you can freely add, change or remove
+ * the main attributes or the entries of the new manifest without effecting
+ * the original manifest, but adding, changing or removing attributes from
+ * a particular entry also changes the attributes of that entry in the
+ * original manifest.
+ *
+ * @see #clone()
+ * @param man the Manifest to copy from
+ */
+ public Manifest(Manifest man)
+ {
+ mainAttr = new Attributes(man.getMainAttributes());
+ entries = new Hashtable<String, Attributes>(man.getEntries());
+ }
+
+ // Methods
+
+ /**
+ * Gets the main attributes of this Manifest.
+ */
+ public Attributes getMainAttributes()
+ {
+ return mainAttr;
+ }
+
+ /**
+ * Gets a map of entry Strings to Attributes for all the entries described
+ * in this manifest. Adding, changing or removing from this entries map
+ * changes the entries of this manifest.
+ */
+ public Map<String, Attributes> getEntries()
+ {
+ return entries;
+ }
+
+ /**
+ * Returns the Attributes associated with the Entry.
+ * <p>
+ * Implemented as:
+ * <code>return (Attributes)getEntries().get(entryName)</code>
+ *
+ * @param entryName the name of the entry to look up
+ * @return the attributes associated with the entry or null when none
+ */
+ public Attributes getAttributes(String entryName)
+ {
+ return getEntries().get(entryName);
+ }
+
+ /**
+ * Clears the main attributes and removes all the entries from the
+ * manifest.
+ */
+ public void clear()
+ {
+ mainAttr.clear();
+ entries.clear();
+ }
+
+ /**
+ * Read and merge a <code>Manifest</code> from the designated input stream.
+ *
+ * @param in the input stream to read from.
+ * @throws IOException if an I/O related exception occurs during the process.
+ */
+ public void read(InputStream in) throws IOException
+ {
+ JarUtils.readMFManifest(getMainAttributes(), getEntries(), in);
+ }
+
+ /**
+ * Writes the contents of this <code>Manifest</code> to the designated
+ * output stream. Line-endings are platform-independent and consist of the
+ * 2-codepoint sequence <code>0x0D</code> and <code>0x0A</code>.
+ *
+ * @param out the output stream to write this <code>Manifest</code> to.
+ * @throws IOException if an I/O related exception occurs during the process.
+ */
+ public void write(OutputStream out) throws IOException
+ {
+ JarUtils.writeMFManifest(getMainAttributes(), getEntries(), out);
+ }
+
+ /**
+ * Makes a deep copy of the main attributes, but a shallow copy of
+ * the other entries. This means that you can freely add, change or remove
+ * the main attributes or the entries of the new manifest without effecting
+ * the original manifest, but adding, changing or removing attributes from
+ * a particular entry also changes the attributes of that entry in the
+ * original manifest. Calls <CODE>new Manifest(this)</CODE>.
+ */
+ public Object clone()
+ {
+ return new Manifest(this);
+ }
+
+ /**
+ * Checks if another object is equal to this Manifest object.
+ * Another Object is equal to this Manifest object if it is an instance of
+ * Manifest and the main attributes and the entries of the other manifest
+ * are equal to this one.
+ */
+ public boolean equals(Object o)
+ {
+ return (o instanceof Manifest) &&
+ (mainAttr.equals(((Manifest) o).mainAttr)) &&
+ (entries.equals(((Manifest) o).entries));
+ }
+
+ /**
+ * Calculates the hash code of the manifest. Implemented by a xor of the
+ * hash code of the main attributes with the hash code of the entries map.
+ */
+ public int hashCode()
+ {
+ return mainAttr.hashCode() ^ entries.hashCode();
+ }
+
+}
diff --git a/libjava/classpath/java/util/jar/package.html b/libjava/classpath/java/util/jar/package.html
new file mode 100644
index 000000000..7fd87878d
--- /dev/null
+++ b/libjava/classpath/java/util/jar/package.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in java.util.jar package.
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - java.util.jar</title></head>
+
+<body>
+<p>Utility classes for manipulating java archives
+(zip files with a manifest file with attributes).</p>
+
+</body>
+</html>
diff --git a/libjava/classpath/java/util/logging/ConsoleHandler.java b/libjava/classpath/java/util/logging/ConsoleHandler.java
new file mode 100644
index 000000000..9f6bb7240
--- /dev/null
+++ b/libjava/classpath/java/util/logging/ConsoleHandler.java
@@ -0,0 +1,125 @@
+/* ConsoleHandler.java -- a class for publishing log messages to System.err
+ Copyright (C) 2002, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.logging;
+
+/**
+ * A <code>ConsoleHandler</code> publishes log records to
+ * <code>System.err</code>.
+ *
+ * <p><strong>Configuration:</strong> Values of the subsequent
+ * <code>LogManager</code> properties are taken into consideration
+ * when a <code>ConsoleHandler</code> is initialized.
+ * If a property is not defined, or if it has an invalid
+ * value, a default is taken without an exception being thrown.
+ *
+ * <ul>
+ *
+ * <li><code>java.util.logging.ConsoleHandler.level</code> - specifies
+ * the initial severity level threshold. Default value:
+ * <code>Level.INFO</code>.</li>
+ *
+ * <li><code>java.util.logging.ConsoleHandler.filter</code> - specifies
+ * the name of a Filter class. Default value: No Filter.</li>
+ *
+ * <li><code>java.util.logging.ConsoleHandler.formatter</code> - specifies
+ * the name of a Formatter class. Default value:
+ * <code>java.util.logging.SimpleFormatter</code>.</li>
+ *
+ * <li><code>java.util.logging.ConsoleHandler.encoding</code> - specifies
+ * the name of the character encoding. Default value:
+ * the default platform encoding.</li>
+ *
+ * </ul>
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class ConsoleHandler
+ extends StreamHandler
+{
+ /**
+ * Constructs a <code>StreamHandler</code> that publishes
+ * log records to <code>System.err</code>. The initial
+ * configuration is determined by the <code>LogManager</code>
+ * properties described above.
+ */
+ public ConsoleHandler()
+ {
+ super(System.err, "java.util.logging.ConsoleHandler", Level.INFO,
+ /* formatter */ null, SimpleFormatter.class);
+ }
+
+
+ /**
+ * Forces any data that may have been buffered to the underlying
+ * output device, but does <i>not</i> close <code>System.err</code>.
+ *
+ * <p>In case of an I/O failure, the <code>ErrorManager</code>
+ * of this <code>ConsoleHandler</code> will be informed, but the caller
+ * of this method will not receive an exception.
+ */
+ public void close()
+ {
+ flush();
+ }
+
+
+ /**
+ * Publishes a <code>LogRecord</code> to the console, provided the
+ * record passes all tests for being loggable.
+ *
+ * <p>Most applications do not need to call this method directly.
+ * Instead, they will use use a <code>Logger</code>, which will
+ * create LogRecords and distribute them to registered handlers.
+ *
+ * <p>In case of an I/O failure, the <code>ErrorManager</code>
+ * of this <code>SocketHandler</code> will be informed, but the caller
+ * of this method will not receive an exception.
+ *
+ * <p>The GNU implementation of <code>ConsoleHandler.publish</code>
+ * calls flush() for every request to publish a record, so
+ * they appear immediately on the console.
+ *
+ * @param record the log event to be published.
+ */
+ public void publish(LogRecord record)
+ {
+ super.publish(record);
+ flush();
+ }
+}
diff --git a/libjava/classpath/java/util/logging/ErrorManager.java b/libjava/classpath/java/util/logging/ErrorManager.java
new file mode 100644
index 000000000..291efb2f1
--- /dev/null
+++ b/libjava/classpath/java/util/logging/ErrorManager.java
@@ -0,0 +1,193 @@
+/* ErrorManager.java --
+ A class for dealing with errors that a Handler encounters
+ during logging
+ Copyright (C) 2002, 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.logging;
+
+/**
+ * An <code>ErrorManager</code> deals with errors that a <code>Handler</code>
+ * encounters while logging.
+ *
+ * @see Handler#setErrorManager(ErrorManager)
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class ErrorManager
+{
+ /* The values have been taken from Sun's public J2SE 1.4 API
+ * documentation.
+ * See http://java.sun.com/j2se/1.4/docs/api/constant-values.html
+ */
+
+ /**
+ * Indicates that there was a failure that does not readily
+ * fall into any of the other categories.
+ */
+ public static final int GENERIC_FAILURE = 0;
+
+
+ /**
+ * Indicates that there was a problem upon writing to
+ * an output stream.
+ */
+ public static final int WRITE_FAILURE = 1;
+
+
+ /**
+ * Indicates that there was a problem upon flushing
+ * an output stream.
+ */
+ public static final int FLUSH_FAILURE = 2;
+
+
+ /**
+ * Indicates that there was a problem upon closing
+ * an output stream.
+ */
+ public static final int CLOSE_FAILURE = 3;
+
+
+ /**
+ * Indicates that there was a problem upon opening
+ * an output stream.
+ */
+ public static final int OPEN_FAILURE = 4;
+
+
+ /**
+ * Indicates that there was a problem upon formatting
+ * the message of a log record.
+ */
+ public static final int FORMAT_FAILURE = 5;
+
+
+ /**
+ * Indicates whether the {@link #error} method of this ErrorManager
+ * has ever been used.
+ *
+ * Declared volatile in order to correctly support the
+ * double-checked locking idiom (once the revised Java Memory Model
+ * gets adopted); see Classpath bug #2944.
+ */
+ private volatile boolean everUsed = false;
+
+
+ public ErrorManager()
+ {
+ }
+
+
+ /**
+ * Reports an error that occured upon logging. The default implementation
+ * emits the very first error to System.err, ignoring subsequent errors.
+ *
+ * @param message a message describing the error, or <code>null</code> if
+ * there is no suitable description.
+ *
+ * @param ex an exception, or <code>null</code> if the error is not
+ * related to an exception.
+ *
+ * @param errorCode one of the defined error codes, for example
+ * <code>ErrorManager.CLOSE_FAILURE</code>.
+ */
+ public void error(String message, Exception ex, int errorCode)
+ {
+ if (everUsed)
+ return;
+
+ synchronized (this)
+ {
+ /* The double check is intentional. If the first check was
+ * omitted, the monitor would have to be entered every time
+ * error() method was called. If the second check was
+ * omitted, the code below could be executed by multiple
+ * threads simultaneously.
+ *
+ * This is the 'double-checked locking' idiom, which is broken
+ * with the current version of the Java memory model. However,
+ * we assume that JVMs will have adopted a revised version of
+ * the Java Memory Model by the time GNU Classpath gains
+ * widespread acceptance. See Classpath bug #2944.
+ */
+ if (everUsed)
+ return;
+
+ everUsed = true;
+ }
+
+ String codeMsg;
+ switch (errorCode)
+ {
+ case GENERIC_FAILURE:
+ codeMsg = "GENERIC_FAILURE";
+ break;
+
+ case WRITE_FAILURE:
+ codeMsg = "WRITE_FAILURE";
+ break;
+
+ case FLUSH_FAILURE:
+ codeMsg = "FLUSH_FAILURE";
+ break;
+
+ case CLOSE_FAILURE:
+ codeMsg = "CLOSE_FAILURE";
+ break;
+
+ case OPEN_FAILURE:
+ codeMsg = "OPEN_FAILURE";
+ break;
+
+ case FORMAT_FAILURE:
+ codeMsg = "FORMAT_FAILURE";
+ break;
+
+ default:
+ codeMsg = String.valueOf(errorCode);
+ break;
+ }
+
+ System.err.println("Error upon logging: " + codeMsg);
+ if ((message != null) && (message.length() > 0))
+ System.err.println(message);
+
+ if (ex != null)
+ ex.printStackTrace();
+ }
+}
diff --git a/libjava/classpath/java/util/logging/FileHandler.java b/libjava/classpath/java/util/logging/FileHandler.java
new file mode 100644
index 000000000..00d967f57
--- /dev/null
+++ b/libjava/classpath/java/util/logging/FileHandler.java
@@ -0,0 +1,665 @@
+/* FileHandler.java -- a class for publishing log messages to log files
+ Copyright (C) 2002, 2003, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.logging;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.LinkedList;
+import java.util.ListIterator;
+
+/**
+ * A <code>FileHandler</code> publishes log records to a set of log
+ * files. A maximum file size can be specified; as soon as a log file
+ * reaches the size limit, it is closed and the next file in the set
+ * is taken.
+ *
+ * <p><strong>Configuration:</strong> Values of the subsequent
+ * <code>LogManager</code> properties are taken into consideration
+ * when a <code>FileHandler</code> is initialized. If a property is
+ * not defined, or if it has an invalid value, a default is taken
+ * without an exception being thrown.
+ *
+ * <ul>
+ *
+ * <li><code>java.util.FileHandler.level</code> - specifies
+ * the initial severity level threshold. Default value:
+ * <code>Level.ALL</code>.</li>
+ *
+ * <li><code>java.util.FileHandler.filter</code> - specifies
+ * the name of a Filter class. Default value: No Filter.</li>
+ *
+ * <li><code>java.util.FileHandler.formatter</code> - specifies
+ * the name of a Formatter class. Default value:
+ * <code>java.util.logging.XMLFormatter</code>.</li>
+ *
+ * <li><code>java.util.FileHandler.encoding</code> - specifies
+ * the name of the character encoding. Default value:
+ * the default platform encoding.</li>
+ *
+ * <li><code>java.util.FileHandler.limit</code> - specifies the number
+ * of bytes a log file is approximately allowed to reach before it
+ * is closed and the handler switches to the next file in the
+ * rotating set. A value of zero means that files can grow
+ * without limit. Default value: 0 (unlimited growth).</li>
+ *
+ * <li><code>java.util.FileHandler.count</code> - specifies the number
+ * of log files through which this handler cycles. Default value:
+ * 1.</li>
+ *
+ * <li><code>java.util.FileHandler.pattern</code> - specifies a
+ * pattern for the location and name of the produced log files.
+ * See the section on <a href="#filePatterns">file name
+ * patterns</a> for details. Default value:
+ * <code>"%h/java%u.log"</code>.</li>
+ *
+ * <li><code>java.util.FileHandler.append</code> - specifies
+ * whether the handler will append log records to existing
+ * files, or whether the handler will clear log files
+ * upon switching to them. Default value: <code>false</code>,
+ * indicating that files will be cleared.</li>
+ *
+ * </ul>
+ *
+ * <p><a name="filePatterns"><strong>File Name Patterns:</strong></a>
+ * The name and location and log files are specified with pattern
+ * strings. The handler will replace the following character sequences
+ * when opening log files:
+ *
+ * <p><ul>
+ * <li><code>/</code> - replaced by the platform-specific path name
+ * separator. This value is taken from the system property
+ * <code>file.separator</code>.</li>
+ *
+ * <li><code>%t</code> - replaced by the platform-specific location of
+ * the directory intended for temporary files. This value is
+ * taken from the system property <code>java.io.tmpdir</code>.</li>
+ *
+ * <li><code>%h</code> - replaced by the location of the home
+ * directory of the current user. This value is taken from the
+ * system property <code>user.home</code>.</li>
+ *
+ * <li><code>%g</code> - replaced by a generation number for
+ * distinguisthing the individual items in the rotating set
+ * of log files. The generation number cycles through the
+ * sequence 0, 1, ..., <code>count</code> - 1.</li>
+ *
+ * <li><code>%u</code> - replaced by a unique number for
+ * distinguisthing the output files of several concurrently
+ * running processes. The <code>FileHandler</code> starts
+ * with 0 when it tries to open a log file. If the file
+ * cannot be opened because it is currently in use,
+ * the unique number is incremented by one and opening
+ * is tried again. These steps are repeated until the
+ * opening operation succeeds.
+ *
+ * <p>FIXME: Is the following correct? Please review. The unique
+ * number is determined for each log file individually when it is
+ * opened upon switching to the next file. Therefore, it is not
+ * correct to assume that all log files in a rotating set bear the
+ * same unique number.
+ *
+ * <p>FIXME: The Javadoc for the Sun reference implementation
+ * says: "Note that the use of unique ids to avoid conflicts is
+ * only guaranteed to work reliably when using a local disk file
+ * system." Why? This needs to be mentioned as well, in case
+ * the reviewers decide the statement is true. Otherwise,
+ * file a bug report with Sun.</li>
+ *
+ * <li><code>%%</code> - replaced by a single percent sign.</li>
+ * </ul>
+ *
+ * <p>If the pattern string does not contain <code>%g</code> and
+ * <code>count</code> is greater than one, the handler will append
+ * the string <code>.%g</code> to the specified pattern.
+ *
+ * <p>If the handler attempts to open a log file, this log file
+ * is being used at the time of the attempt, and the pattern string
+ * does not contain <code>%u</code>, the handler will append
+ * the string <code>.%u</code> to the specified pattern. This
+ * step is performed after any generation number has been
+ * appended.
+ *
+ * <p><em>Examples for the GNU platform:</em>
+ *
+ * <p><ul>
+ *
+ * <li><code>%h/java%u.log</code> will lead to a single log file
+ * <code>/home/janet/java0.log</code>, assuming <code>count</code>
+ * equals 1, the user's home directory is
+ * <code>/home/janet</code>, and the attempt to open the file
+ * succeeds.</li>
+ *
+ * <li><code>%h/java%u.log</code> will lead to three log files
+ * <code>/home/janet/java0.log.0</code>,
+ * <code>/home/janet/java0.log.1</code>, and
+ * <code>/home/janet/java0.log.2</code>,
+ * assuming <code>count</code> equals 3, the user's home
+ * directory is <code>/home/janet</code>, and all attempts
+ * to open files succeed.</li>
+ *
+ * <li><code>%h/java%u.log</code> will lead to three log files
+ * <code>/home/janet/java0.log.0</code>,
+ * <code>/home/janet/java1.log.1</code>, and
+ * <code>/home/janet/java0.log.2</code>,
+ * assuming <code>count</code> equals 3, the user's home
+ * directory is <code>/home/janet</code>, and the attempt
+ * to open <code>/home/janet/java0.log.1</code> fails.</li>
+ *
+ * </ul>
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class FileHandler
+ extends StreamHandler
+{
+ /**
+ * A literal that prefixes all file-handler related properties in the
+ * logging.properties file.
+ */
+ private static final String PROPERTY_PREFIX = "java.util.logging.FileHandler";
+ /**
+ * The name of the property to set for specifying a file naming (incl. path)
+ * pattern to use with rotating log files.
+ */
+ private static final String PATTERN_KEY = PROPERTY_PREFIX + ".pattern";
+ /**
+ * The default pattern to use when the <code>PATTERN_KEY</code> property was
+ * not specified in the logging.properties file.
+ */
+ private static final String DEFAULT_PATTERN = "%h/java%u.log";
+ /**
+ * The name of the property to set for specifying an approximate maximum
+ * amount, in bytes, to write to any one log output file. A value of zero
+ * (which is the default) implies a no limit.
+ */
+ private static final String LIMIT_KEY = PROPERTY_PREFIX + ".limit";
+ private static final int DEFAULT_LIMIT = 0;
+ /**
+ * The name of the property to set for specifying how many output files to
+ * cycle through. The default value is 1.
+ */
+ private static final String COUNT_KEY = PROPERTY_PREFIX + ".count";
+ private static final int DEFAULT_COUNT = 1;
+ /**
+ * The name of the property to set for specifying whether this handler should
+ * append, or not, its output to existing files. The default value is
+ * <code>false</code> meaning NOT to append.
+ */
+ private static final String APPEND_KEY = PROPERTY_PREFIX + ".append";
+ private static final boolean DEFAULT_APPEND = false;
+
+ /**
+ * The number of bytes a log file is approximately allowed to reach
+ * before it is closed and the handler switches to the next file in
+ * the rotating set. A value of zero means that files can grow
+ * without limit.
+ */
+ private final int limit;
+
+
+ /**
+ * The number of log files through which this handler cycles.
+ */
+ private final int count;
+
+
+ /**
+ * The pattern for the location and name of the produced log files.
+ * See the section on <a href="#filePatterns">file name patterns</a>
+ * for details.
+ */
+ private final String pattern;
+
+
+ /**
+ * Indicates whether the handler will append log records to existing
+ * files (<code>true</code>), or whether the handler will clear log files
+ * upon switching to them (<code>false</code>).
+ */
+ private final boolean append;
+
+
+ /**
+ * The number of bytes that have currently been written to the stream.
+ * Package private for use in inner classes.
+ */
+ long written;
+
+
+ /**
+ * A linked list of files we are, or have written to. The entries
+ * are file path strings, kept in the order
+ */
+ private LinkedList logFiles;
+
+
+ /**
+ * Constructs a <code>FileHandler</code>, taking all property values
+ * from the current {@link LogManager LogManager} configuration.
+ *
+ * @throws java.io.IOException FIXME: The Sun Javadoc says: "if
+ * there are IO problems opening the files." This conflicts
+ * with the general principle that configuration errors do
+ * not prohibit construction. Needs review.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ */
+ public FileHandler()
+ throws IOException, SecurityException
+ {
+ this(LogManager.getLogManager().getProperty(PATTERN_KEY),
+ LogManager.getIntProperty(LIMIT_KEY, DEFAULT_LIMIT),
+ LogManager.getIntProperty(COUNT_KEY, DEFAULT_COUNT),
+ LogManager.getBooleanProperty(APPEND_KEY, DEFAULT_APPEND));
+ }
+
+
+ /* FIXME: Javadoc missing. */
+ public FileHandler(String pattern)
+ throws IOException, SecurityException
+ {
+ this(pattern, DEFAULT_LIMIT, DEFAULT_COUNT, DEFAULT_APPEND);
+ }
+
+
+ /* FIXME: Javadoc missing. */
+ public FileHandler(String pattern, boolean append)
+ throws IOException, SecurityException
+ {
+ this(pattern, DEFAULT_LIMIT, DEFAULT_COUNT, append);
+ }
+
+
+ /* FIXME: Javadoc missing. */
+ public FileHandler(String pattern, int limit, int count)
+ throws IOException, SecurityException
+ {
+ this(pattern, limit, count,
+ LogManager.getBooleanProperty(APPEND_KEY, DEFAULT_APPEND));
+ }
+
+
+ /**
+ * Constructs a <code>FileHandler</code> given the pattern for the
+ * location and name of the produced log files, the size limit, the
+ * number of log files thorough which the handler will rotate, and
+ * the <code>append</code> property. All other property values are
+ * taken from the current {@link LogManager LogManager}
+ * configuration.
+ *
+ * @param pattern The pattern for the location and name of the
+ * produced log files. See the section on <a
+ * href="#filePatterns">file name patterns</a> for details.
+ * If <code>pattern</code> is <code>null</code>, the value is
+ * taken from the {@link LogManager LogManager} configuration
+ * property
+ * <code>java.util.logging.FileHandler.pattern</code>.
+ * However, this is a pecularity of the GNU implementation,
+ * and Sun's API specification does not mention what behavior
+ * is to be expected for <code>null</code>. Therefore,
+ * applications should not rely on this feature.
+ *
+ * @param limit specifies the number of bytes a log file is
+ * approximately allowed to reach before it is closed and the
+ * handler switches to the next file in the rotating set. A
+ * value of zero means that files can grow without limit.
+ *
+ * @param count specifies the number of log files through which this
+ * handler cycles.
+ *
+ * @param append specifies whether the handler will append log
+ * records to existing files (<code>true</code>), or whether the
+ * handler will clear log files upon switching to them
+ * (<code>false</code>).
+ *
+ * @throws java.io.IOException FIXME: The Sun Javadoc says: "if
+ * there are IO problems opening the files." This conflicts
+ * with the general principle that configuration errors do
+ * not prohibit construction. Needs review.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ * <p>FIXME: This seems in contrast to all other handler
+ * constructors -- verify this by running tests against
+ * the Sun reference implementation.
+ */
+ public FileHandler(String pattern,
+ int limit,
+ int count,
+ boolean append)
+ throws IOException, SecurityException
+ {
+ super(/* output stream, created below */ null,
+ PROPERTY_PREFIX,
+ /* default level */ Level.ALL,
+ /* formatter */ null,
+ /* default formatter */ XMLFormatter.class);
+
+ if ((limit <0) || (count < 1))
+ throw new IllegalArgumentException();
+
+ this.pattern = pattern != null ? pattern : DEFAULT_PATTERN;
+ this.limit = limit;
+ this.count = count;
+ this.append = append;
+ this.written = 0;
+ this.logFiles = new LinkedList ();
+
+ setOutputStream (createFileStream (this.pattern, limit, count, append,
+ /* generation */ 0));
+ }
+
+
+ /* FIXME: Javadoc missing. */
+ private OutputStream createFileStream(String pattern,
+ int limit,
+ int count,
+ boolean append,
+ int generation)
+ {
+ String path;
+ int unique = 0;
+
+ /* Throws a SecurityException if the caller does not have
+ * LoggingPermission("control").
+ */
+ LogManager.getLogManager().checkAccess();
+
+ /* Default value from the java.util.logging.FileHandler.pattern
+ * LogManager configuration property.
+ */
+ if (pattern == null)
+ pattern = LogManager.getLogManager().getProperty(PATTERN_KEY);
+ if (pattern == null)
+ pattern = DEFAULT_PATTERN;
+
+ if (count > 1 && !has (pattern, 'g'))
+ pattern = pattern + ".%g";
+
+ do
+ {
+ path = replaceFileNameEscapes(pattern, generation, unique, count);
+
+ try
+ {
+ File file = new File(path);
+ if (!file.exists () || append)
+ {
+ FileOutputStream fout = new FileOutputStream (file, append);
+ // FIXME we need file locks for this to work properly, but they
+ // are not implemented yet in Classpath! Madness!
+// FileChannel channel = fout.getChannel ();
+// FileLock lock = channel.tryLock ();
+// if (lock != null) // We've locked the file.
+// {
+ if (logFiles.isEmpty ())
+ logFiles.addFirst (path);
+ return new ostr (fout);
+// }
+ }
+ }
+ catch (Exception ex)
+ {
+ reportError (null, ex, ErrorManager.OPEN_FAILURE);
+ }
+
+ unique = unique + 1;
+ if (!has (pattern, 'u'))
+ pattern = pattern + ".%u";
+ }
+ while (true);
+ }
+
+
+ /**
+ * Replaces the substrings <code>"/"</code> by the value of the
+ * system property <code>"file.separator"</code>, <code>"%t"</code>
+ * by the value of the system property
+ * <code>"java.io.tmpdir"</code>, <code>"%h"</code> by the value of
+ * the system property <code>"user.home"</code>, <code>"%g"</code>
+ * by the value of <code>generation</code>, <code>"%u"</code> by the
+ * value of <code>uniqueNumber</code>, and <code>"%%"</code> by a
+ * single percent character. If <code>pattern</code> does
+ * <em>not</em> contain the sequence <code>"%g"</code>,
+ * the value of <code>generation</code> will be appended to
+ * the result.
+ *
+ * @throws NullPointerException if one of the system properties
+ * <code>"file.separator"</code>,
+ * <code>"java.io.tmpdir"</code>, or
+ * <code>"user.home"</code> has no value and the
+ * corresponding escape sequence appears in
+ * <code>pattern</code>.
+ */
+ private static String replaceFileNameEscapes(String pattern,
+ int generation,
+ int uniqueNumber,
+ int count)
+ {
+ CPStringBuilder buf = new CPStringBuilder(pattern);
+ String replaceWith;
+ boolean foundGeneration = false;
+
+ int pos = 0;
+ do
+ {
+ // Uncomment the next line for finding bugs.
+ // System.out.println(buf.substring(0,pos) + '|' + buf.substring(pos));
+
+ if (buf.charAt(pos) == '/')
+ {
+ /* The same value is also provided by java.io.File.separator. */
+ replaceWith = System.getProperty("file.separator");
+ buf.replace(pos, pos + 1, replaceWith);
+ pos = pos + replaceWith.length() - 1;
+ continue;
+ }
+
+ if (buf.charAt(pos) == '%')
+ {
+ switch (buf.charAt(pos + 1))
+ {
+ case 't':
+ replaceWith = System.getProperty("java.io.tmpdir");
+ break;
+
+ case 'h':
+ replaceWith = System.getProperty("user.home");
+ break;
+
+ case 'g':
+ replaceWith = Integer.toString(generation);
+ foundGeneration = true;
+ break;
+
+ case 'u':
+ replaceWith = Integer.toString(uniqueNumber);
+ break;
+
+ case '%':
+ replaceWith = "%";
+ break;
+
+ default:
+ replaceWith = "??";
+ break; // FIXME: Throw exception?
+ }
+
+ buf.replace(pos, pos + 2, replaceWith);
+ pos = pos + replaceWith.length() - 1;
+ continue;
+ }
+ }
+ while (++pos < buf.length() - 1);
+
+ if (!foundGeneration && (count > 1))
+ {
+ buf.append('.');
+ buf.append(generation);
+ }
+
+ return buf.toString();
+ }
+
+
+ /* FIXME: Javadoc missing. */
+ public void publish(LogRecord record)
+ {
+ if (limit > 0 && written >= limit)
+ rotate ();
+ super.publish(record);
+ flush ();
+ }
+
+ /**
+ * Rotates the current log files, possibly removing one if we
+ * exceed the file count.
+ */
+ private synchronized void rotate ()
+ {
+ if (logFiles.size () > 0)
+ {
+ File f1 = null;
+ ListIterator lit = null;
+
+ // If we reach the file count, ditch the oldest file.
+ if (logFiles.size () == count)
+ {
+ f1 = new File ((String) logFiles.getLast ());
+ f1.delete ();
+ lit = logFiles.listIterator (logFiles.size () - 1);
+ }
+ // Otherwise, move the oldest to a new location.
+ else
+ {
+ String path = replaceFileNameEscapes (pattern, logFiles.size (),
+ /* unique */ 0, count);
+ f1 = new File (path);
+ logFiles.addLast (path);
+ lit = logFiles.listIterator (logFiles.size () - 1);
+ }
+
+ // Now rotate the files.
+ while (lit.hasPrevious ())
+ {
+ String s = (String) lit.previous ();
+ File f2 = new File (s);
+ f2.renameTo (f1);
+ f1 = f2;
+ }
+ }
+
+ setOutputStream (createFileStream (pattern, limit, count, append,
+ /* generation */ 0));
+
+ // Reset written count.
+ written = 0;
+ }
+
+ /**
+ * Tell if <code>pattern</code> contains the pattern sequence
+ * with character <code>escape</code>. That is, if <code>escape</code>
+ * is 'g', this method returns true if the given pattern contains
+ * "%g", and not just the substring "%g" (for example, in the case of
+ * "%%g").
+ *
+ * @param pattern The pattern to test.
+ * @param escape The escape character to search for.
+ * @return True iff the pattern contains the escape sequence with the
+ * given character.
+ */
+ private static boolean has (final String pattern, final char escape)
+ {
+ final int len = pattern.length ();
+ boolean sawPercent = false;
+ for (int i = 0; i < len; i++)
+ {
+ char c = pattern.charAt (i);
+ if (sawPercent)
+ {
+ if (c == escape)
+ return true;
+ if (c == '%') // Double percent
+ {
+ sawPercent = false;
+ continue;
+ }
+ }
+ sawPercent = (c == '%');
+ }
+ return false;
+ }
+
+ /**
+ * An output stream that tracks the number of bytes written to it.
+ */
+ private final class ostr extends FilterOutputStream
+ {
+ private ostr (OutputStream out)
+ {
+ super (out);
+ }
+
+ public void write (final int b) throws IOException
+ {
+ out.write (b);
+ FileHandler.this.written++; // FIXME: synchronize?
+ }
+
+ public void write (final byte[] b) throws IOException
+ {
+ write (b, 0, b.length);
+ }
+
+ public void write (final byte[] b, final int offset, final int length)
+ throws IOException
+ {
+ out.write (b, offset, length);
+ FileHandler.this.written += length; // FIXME: synchronize?
+ }
+ }
+}
diff --git a/libjava/classpath/java/util/logging/Filter.java b/libjava/classpath/java/util/logging/Filter.java
new file mode 100644
index 000000000..ec4597670
--- /dev/null
+++ b/libjava/classpath/java/util/logging/Filter.java
@@ -0,0 +1,64 @@
+/* Filter.java -- an interface for filters that decide whether a
+ LogRecord should be published or discarded
+ Copyright (C) 2002, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.logging;
+
+/**
+ * By implementing the <code>Filter</code> interface, applications
+ * can control what is being logged based on arbitrary properties,
+ * not just the severity level. Both <code>Handler</code> and
+ * <code>Logger</code> allow to register Filters whose
+ * <code>isLoggable</code> method will be called when a
+ * <code>LogRecord</code> has passed the test based on the
+ * severity level.
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public interface Filter
+{
+ /**
+ * Determines whether a LogRecord should be published or discarded.
+ *
+ * @param record the <code>LogRecord</code> to be inspected.
+ *
+ * @return <code>true</code> if the record should be published,
+ * <code>false</code> if it should be discarded.
+ */
+ boolean isLoggable(LogRecord record);
+}
diff --git a/libjava/classpath/java/util/logging/Formatter.java b/libjava/classpath/java/util/logging/Formatter.java
new file mode 100644
index 000000000..feaf55315
--- /dev/null
+++ b/libjava/classpath/java/util/logging/Formatter.java
@@ -0,0 +1,171 @@
+/* Formatter.java --
+ A class for formatting log messages by localizing message texts
+ and performing substitution of parameters
+ Copyright (C) 2002, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.logging;
+
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+
+/**
+ * A <code>Formatter</code> supports handlers by localizing
+ * message texts and by subsituting parameter values for their
+ * placeholders.
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public abstract class Formatter
+{
+ /**
+ * Constructs a new Formatter.
+ */
+ protected Formatter()
+ {
+ }
+
+
+ /**
+ * Formats a LogRecord into a string. Usually called by handlers
+ * which need a string for a log record, for example to append
+ * a record to a log file or to transmit a record over the network.
+ *
+ * @param record the log record for which a string form is requested.
+ */
+ public abstract String format(LogRecord record);
+
+
+ /**
+ * Returns a string that handlers are supposed to emit before
+ * the first log record. The base implementation returns an
+ * empty string, but subclasses such as {@link XMLFormatter}
+ * override this method in order to provide a suitable header.
+ *
+ * @return a string for the header.
+ *
+ * @param handler the handler which will prepend the returned
+ * string in front of the first log record. This method
+ * may inspect certain properties of the handler, for
+ * example its encoding, in order to construct the header.
+ */
+ public String getHead(Handler handler)
+ {
+ return "";
+ }
+
+
+ /**
+ * Returns a string that handlers are supposed to emit after
+ * the last log record. The base implementation returns an
+ * empty string, but subclasses such as {@link XMLFormatter}
+ * override this method in order to provide a suitable tail.
+ *
+ * @return a string for the header.
+ *
+ * @param handler the handler which will append the returned
+ * string after the last log record. This method
+ * may inspect certain properties of the handler
+ * in order to construct the tail.
+ */
+ public String getTail(Handler handler)
+ {
+ return "";
+ }
+
+
+ /**
+ * Formats the message part of a log record.
+ *
+ * <p>First, the Formatter localizes the record message to the
+ * default locale by looking up the message in the record's
+ * localization resource bundle. If this step fails because there
+ * is no resource bundle associated with the record, or because the
+ * record message is not a key in the bundle, the raw message is
+ * used instead.
+ *
+ * <p>Second, the Formatter substitutes appropriate strings for
+ * the message parameters. If the record returns a non-empty
+ * array for <code>getParameters()</code> and the localized
+ * message string contains the character sequence "{0", the
+ * formatter uses <code>java.text.MessageFormat</code> to format
+ * the message. Otherwise, no parameter substitution is performed.
+ *
+ * @param record the log record to be localized and formatted.
+ *
+ * @return the localized message text where parameters have been
+ * substituted by suitable strings.
+ *
+ * @throws NullPointerException if <code>record</code>
+ * is <code>null</code>.
+ */
+ public String formatMessage(LogRecord record)
+ {
+ String msg;
+ ResourceBundle bundle;
+ Object[] params;
+
+ /* This will throw a NullPointerExceptionif record is null. */
+ msg = record.getMessage();
+ if (msg == null)
+ msg = "";
+
+ /* Try to localize the message. */
+ bundle = record.getResourceBundle();
+ if (bundle != null)
+ {
+ try
+ {
+ msg = bundle.getString(msg);
+ }
+ catch (java.util.MissingResourceException _)
+ {
+ }
+ }
+
+ /* Format the message if there are parameters. */
+ params = record.getParameters();
+ if ((params != null)
+ && (params.length > 0)
+ && (msg.indexOf("{0") >= 0))
+ {
+ msg = MessageFormat.format(msg, params);
+ }
+
+ return msg;
+ }
+}
diff --git a/libjava/classpath/java/util/logging/Handler.java b/libjava/classpath/java/util/logging/Handler.java
new file mode 100644
index 000000000..e254b4d35
--- /dev/null
+++ b/libjava/classpath/java/util/logging/Handler.java
@@ -0,0 +1,386 @@
+/* Handler.java -- a class for publishing log messages
+ Copyright (C) 2002, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.logging;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * A <code>Handler</code> publishes <code>LogRecords</code> to
+ * a sink, for example a file, the console or a network socket.
+ * There are different subclasses of <code>Handler</code>
+ * to deal with different kinds of sinks.
+ *
+ * <p>FIXME: Are handlers thread-safe, or is the assumption that only
+ * loggers are, and a handler can belong only to one single logger? If
+ * the latter, should we enforce it? (Spec not clear). In any
+ * case, it needs documentation.
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public abstract class Handler
+{
+ Formatter formatter;
+ Filter filter;
+ Level level;
+ ErrorManager errorManager;
+ String encoding;
+
+ /**
+ * Constructs a Handler with a logging severity level of
+ * <code>Level.ALL</code>, no formatter, no filter, and
+ * an instance of <code>ErrorManager</code> managing errors.
+ *
+ * <p><strong>Specification Note:</strong> The specification of the
+ * Java<sup>TM</sup> Logging API does not mention which character
+ * encoding is to be used by freshly constructed Handlers. The GNU
+ * implementation uses the default platform encoding, but other
+ * Java implementations might behave differently.
+ *
+ * <p><strong>Specification Note:</strong> While a freshly constructed
+ * Handler is required to have <em>no filter</em> according to the
+ * specification, <code>null</code> is not a valid parameter for
+ * <code>Handler.setFormatter</code>. Therefore, the following
+ * code will throw a <code>java.lang.NullPointerException</code>:
+ *
+ * <p><pre>Handler h = new MyConcreteSubclassOfHandler();
+h.setFormatter(h.getFormatter());</pre>
+ *
+ * It seems strange that a freshly constructed Handler is not
+ * supposed to provide a Formatter, but this is what the specification
+ * says.
+ */
+ protected Handler()
+ {
+ level = Level.ALL;
+ }
+
+
+ /**
+ * Publishes a <code>LogRecord</code> to an appropriate sink,
+ * provided the record passes all tests for being loggable. The
+ * <code>Handler</code> will localize the message of the log
+ * record and substitute any message parameters.
+ *
+ * <p>Most applications do not need to call this method directly.
+ * Instead, they will use use a {@link Logger}, which will
+ * create LogRecords and distribute them to registered handlers.
+ *
+ * <p>In case of an I/O failure, the <code>ErrorManager</code>
+ * of this <code>Handler</code> will be informed, but the caller
+ * of this method will not receive an exception.
+ *
+ * @param record the log event to be published.
+ */
+ public abstract void publish(LogRecord record);
+
+
+ /**
+ * Forces any data that may have been buffered to the underlying
+ * output device.
+ *
+ * <p>In case of an I/O failure, the <code>ErrorManager</code>
+ * of this <code>Handler</code> will be informed, but the caller
+ * of this method will not receive an exception.
+ */
+ public abstract void flush();
+
+
+ /**
+ * Closes this <code>Handler</code> after having flushed
+ * the buffers. As soon as <code>close</code> has been called,
+ * a <code>Handler</code> should not be used anymore. Attempts
+ * to publish log records, to flush buffers, or to modify the
+ * <code>Handler</code> in any other way may throw runtime
+ * exceptions after calling <code>close</code>.
+ *
+ * <p>In case of an I/O failure, the <code>ErrorManager</code>
+ * of this <code>Handler</code> will be informed, but the caller
+ * of this method will not receive an exception.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ */
+ public abstract void close()
+ throws SecurityException;
+
+
+ /**
+ * Returns the <code>Formatter</code> which will be used to
+ * localize the text of log messages and to substitute
+ * message parameters. A <code>Handler</code> is encouraged,
+ * but not required to actually use an assigned
+ * <code>Formatter</code>.
+ *
+ * @return the <code>Formatter</code> being used, or
+ * <code>null</code> if this <code>Handler</code>
+ * does not use formatters and no formatter has
+ * ever been set by calling <code>setFormatter</code>.
+ */
+ public Formatter getFormatter()
+ {
+ return formatter;
+ }
+
+
+ /**
+ * Sets the <code>Formatter</code> which will be used to
+ * localize the text of log messages and to substitute
+ * message parameters. A <code>Handler</code> is encouraged,
+ * but not required to actually use an assigned
+ * <code>Formatter</code>.
+ *
+ * @param formatter the new <code>Formatter</code> to use.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ *
+ * @throws NullPointerException if <code>formatter</code> is
+ * <code>null</code>.
+ */
+ public void setFormatter(Formatter formatter)
+ throws SecurityException
+ {
+ LogManager.getLogManager().checkAccess();
+
+ /* Throws a NullPointerException if formatter is null. */
+ formatter.getClass();
+
+ this.formatter = formatter;
+ }
+
+
+ /**
+ * Returns the character encoding which this handler uses for publishing
+ * log records.
+ *
+ * @return the name of a character encoding, or <code>null</code>
+ * for the default platform encoding.
+ */
+ public String getEncoding()
+ {
+ return encoding;
+ }
+
+
+ /**
+ * Sets the character encoding which this handler uses for publishing
+ * log records. The encoding of a <code>Handler</code> must be
+ * set before any log records have been published.
+ *
+ * @param encoding the name of a character encoding, or <code>null</code>
+ * for the default encoding.
+ *
+ * @exception SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ *
+ */
+ public void setEncoding(String encoding)
+ throws SecurityException, UnsupportedEncodingException
+ {
+ /* Should any developer ever change this implementation, they are
+ * advised to have a look at StreamHandler.setEncoding(String),
+ * which overrides this method without calling super.setEncoding.
+ */
+ LogManager.getLogManager().checkAccess();
+
+ /* Simple check for supported encodings. This is more expensive
+ * than it could be, but this method is overwritten by StreamHandler
+ * anyway.
+ */
+ if (encoding != null)
+ new String(new byte[0], encoding);
+
+ this.encoding = encoding;
+ }
+
+
+ /**
+ * Returns the <code>Filter</code> that currently controls which
+ * log records are being published by this <code>Handler</code>.
+ *
+ * @return the currently active <code>Filter</code>, or
+ * <code>null</code> if no filter has been associated.
+ * In the latter case, log records are filtered purely
+ * based on their severity level.
+ */
+ public Filter getFilter()
+ {
+ return filter;
+ }
+
+
+ /**
+ * Sets the <code>Filter</code> for controlling which
+ * log records will be published by this <code>Handler</code>.
+ *
+ * @param filter the <code>Filter</code> to use, or
+ * <code>null</code> to filter log records purely based
+ * on their severity level.
+ */
+ public void setFilter(Filter filter)
+ throws SecurityException
+ {
+ LogManager.getLogManager().checkAccess();
+ this.filter = filter;
+ }
+
+
+ /**
+ * Returns the <code>ErrorManager</code> that currently deals
+ * with errors originating from this Handler.
+ *
+ * @exception SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ */
+ public ErrorManager getErrorManager()
+ {
+ LogManager.getLogManager().checkAccess();
+
+ /* Developers wanting to change the subsequent code should
+ * have a look at Handler.reportError -- it also can create
+ * an ErrorManager, but does so without checking permissions
+ * to control the logging infrastructure.
+ */
+ if (errorManager == null)
+ errorManager = new ErrorManager();
+
+ return errorManager;
+ }
+
+
+ public void setErrorManager(ErrorManager manager)
+ {
+ LogManager.getLogManager().checkAccess();
+
+ /* Make sure manager is not null. */
+ manager.getClass();
+
+ this.errorManager = manager;
+ }
+
+
+ protected void reportError(String message, Exception ex, int code)
+ {
+ if (errorManager == null)
+ errorManager = new ErrorManager();
+
+ errorManager.error(message, ex, code);
+ }
+
+
+ /**
+ * Returns the severity level threshold for this <code>Handler</code>
+ * All log records with a lower severity level will be discarded;
+ * a log record of the same or a higher level will be published
+ * unless an installed <code>Filter</code> decides to discard it.
+ *
+ * @return the severity level below which all log messages
+ * will be discarded.
+ */
+ public Level getLevel()
+ {
+ return level;
+ }
+
+
+ /**
+ * Sets the severity level threshold for this <code>Handler</code>.
+ * All log records with a lower severity level will be discarded;
+ * a log record of the same or a higher level will be published
+ * unless an installed <code>Filter</code> decides to discard it.
+ *
+ * @param level the severity level below which all log messages
+ * will be discarded.
+ *
+ * @exception SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ *
+ * @exception NullPointerException if <code>level</code> is
+ * <code>null</code>.
+ */
+ public void setLevel(Level level)
+ {
+ LogManager.getLogManager().checkAccess();
+
+ /* Throw NullPointerException if level is null. */
+ level.getClass();
+ this.level = level;
+ }
+
+
+ /**
+ * Checks whether a <code>LogRecord</code> would be logged
+ * if it was passed to this <code>Handler</code> for publication.
+ *
+ * <p>The <code>Handler</code> implementation considers a record as
+ * loggable if its level is greater than or equal to the severity
+ * level threshold. In a second step, if a {@link Filter} has
+ * been installed, its {@link Filter#isLoggable(LogRecord) isLoggable}
+ * method is invoked. Subclasses of <code>Handler</code> can override
+ * this method to impose their own constraints.
+ *
+ * @param record the <code>LogRecord</code> to be checked.
+ *
+ * @return <code>true</code> if <code>record</code> would
+ * be published by {@link #publish(LogRecord) publish},
+ * <code>false</code> if it would be discarded.
+ *
+ * @see #setLevel(Level)
+ * @see #setFilter(Filter)
+ * @see Filter#isLoggable(LogRecord)
+ *
+ * @throws NullPointerException if <code>record</code>
+ * is <code>null</code>.
+ */
+ public boolean isLoggable(LogRecord record)
+ {
+ if (record.getLevel().intValue() < level.intValue())
+ return false;
+
+ if (filter != null)
+ return filter.isLoggable(record);
+ else
+ return true;
+ }
+}
diff --git a/libjava/classpath/java/util/logging/Level.java b/libjava/classpath/java/util/logging/Level.java
new file mode 100644
index 000000000..ea2c83fb2
--- /dev/null
+++ b/libjava/classpath/java/util/logging/Level.java
@@ -0,0 +1,416 @@
+/* Level.java -- a class for indicating logging levels
+ Copyright (C) 2002, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.logging;
+
+import java.io.Serializable;
+import java.util.ResourceBundle;
+
+/**
+ * A class for indicating logging levels. A number of commonly used
+ * levels is pre-defined (such as <code>java.util.logging.Level.INFO</code>),
+ * and applications should utilize those whenever possible. For specialized
+ * purposes, however, applications can sub-class Level in order to define
+ * custom logging levels.
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class Level implements Serializable
+{
+ /* The integer values are the same as in the Sun J2SE 1.4.
+ * They have been obtained with a test program. In J2SE 1.4.1,
+ * Sun has amended the API documentation; these values are now
+ * publicly documented.
+ */
+
+ /**
+ * The <code>OFF</code> level is used as a threshold for filtering
+ * log records, meaning that no message should be logged.
+ *
+ * @see Logger#setLevel(java.util.logging.Level)
+ */
+ public static final Level OFF = new Level ("OFF", Integer.MAX_VALUE);
+
+ /**
+ * Log records whose level is <code>SEVERE</code> indicate a serious
+ * failure that prevents normal program execution. Messages at this
+ * level should be understandable to an inexperienced, non-technical
+ * end user. Ideally, they explain in simple words what actions the
+ * user can take in order to resolve the problem.
+ */
+ public static final Level SEVERE = new Level ("SEVERE", 1000);
+
+
+ /**
+ * Log records whose level is <code>WARNING</code> indicate a
+ * potential problem that does not prevent normal program execution.
+ * Messages at this level should be understandable to an
+ * inexperienced, non-technical end user. Ideally, they explain in
+ * simple words what actions the user can take in order to resolve
+ * the problem.
+ */
+ public static final Level WARNING = new Level ("WARNING", 900);
+
+
+ /**
+ * Log records whose level is <code>INFO</code> are used in purely
+ * informational situations that do not constitute serious errors or
+ * potential problems. In the default logging configuration, INFO
+ * messages will be written to the system console. For this reason,
+ * the INFO level should be used only for messages that are
+ * important to end users and system administrators. Messages at
+ * this level should be understandable to an inexperienced,
+ * non-technical user.
+ */
+ public static final Level INFO = new Level ("INFO", 800);
+
+
+ /**
+ * Log records whose level is <code>CONFIG</code> are used for
+ * describing the static configuration, for example the windowing
+ * environment, the operating system version, etc.
+ */
+ public static final Level CONFIG = new Level ("CONFIG", 700);
+
+
+ /**
+ * Log records whose level is <code>FINE</code> are typically used
+ * for messages that are relevant for developers using
+ * the component generating log messages. Examples include minor,
+ * recoverable failures, or possible inefficiencies.
+ */
+ public static final Level FINE = new Level ("FINE", 500);
+
+
+ /**
+ * Log records whose level is <code>FINER</code> are intended for
+ * rather detailed tracing, for example entering a method, returning
+ * from a method, or throwing an exception.
+ */
+ public static final Level FINER = new Level ("FINER", 400);
+
+
+ /**
+ * Log records whose level is <code>FINEST</code> are used for
+ * highly detailed tracing, for example to indicate that a certain
+ * point inside the body of a method has been reached.
+ */
+ public static final Level FINEST = new Level ("FINEST", 300);
+
+
+ /**
+ * The <code>ALL</code> level is used as a threshold for filtering
+ * log records, meaning that every message should be logged.
+ *
+ * @see Logger#setLevel(java.util.logging.Level)
+ */
+ public static final Level ALL = new Level ("ALL", Integer.MIN_VALUE);
+
+
+ private static final Level[] knownLevels = {
+ ALL, FINEST, FINER, FINE, CONFIG, INFO, WARNING, SEVERE, OFF
+ };
+
+
+ /**
+ * The name of the Level without localizing it, for example
+ * "WARNING".
+ */
+ private String name;
+
+
+ /**
+ * The integer value of this <code>Level</code>.
+ */
+ private int value;
+
+
+ /**
+ * The name of the resource bundle used for localizing the level
+ * name, or <code>null</code> if the name does not undergo
+ * localization.
+ */
+ private String resourceBundleName;
+
+
+ /**
+ * Creates a logging level given a name and an integer value.
+ * It rarely is necessary to create custom levels,
+ * as most applications should be well served with one of the
+ * standard levels such as <code>Level.CONFIG</code>,
+ * <code>Level.INFO</code>, or <code>Level.FINE</code>.
+ *
+ * @param name the name of the level.
+ *
+ * @param value the integer value of the level. Please note
+ * that the Java<small><sup>TM</sup></small>
+ * Logging API does not specify integer
+ * values for standard levels (such as
+ * Level.FINE). Therefore, a custom
+ * level should pass an integer value that
+ * is calculated at run-time, e.g.
+ * <code>(Level.FINE.intValue() + Level.CONFIG.intValue())
+ * / 2</code> for a level between FINE and CONFIG.
+ */
+ protected Level(String name, int value)
+ {
+ this(name, value, null);
+ }
+
+
+ /**
+ * Create a logging level given a name, an integer value and a name
+ * of a resource bundle for localizing the level name. It rarely
+ * is necessary to create custom levels, as most applications
+ * should be well served with one of the standard levels such as
+ * <code>Level.CONFIG</code>, <code>Level.INFO</code>, or
+ * <code>Level.FINE</code>.
+ *
+ * @param name the name of the level.
+ *
+ * @param value the integer value of the level. Please note
+ * that the Java<small><sup>TM</sup></small>
+ * Logging API does not specify integer
+ * values for standard levels (such as
+ * Level.FINE). Therefore, a custom
+ * level should pass an integer value that
+ * is calculated at run-time, e.g.
+ * <code>(Level.FINE.intValue() + Level.CONFIG.intValue())
+ * / 2</code> for a level between FINE and CONFIG.
+ *
+ * @param resourceBundleName the name of a resource bundle
+ * for localizing the level name, or <code>null</code>
+ * if the name does not need to be localized.
+ */
+ protected Level(String name, int value, String resourceBundleName)
+ {
+ this.name = name;
+ this.value = value;
+ this.resourceBundleName = resourceBundleName;
+ }
+
+
+ static final long serialVersionUID = -8176160795706313070L;
+
+
+ /**
+ * Checks whether the Level has the same intValue as one of the
+ * pre-defined levels. If so, the pre-defined level object is
+ * returned.
+ *
+ * <br/>Since the resource bundle name is not taken into
+ * consideration, it is possible to resolve Level objects that have
+ * been de-serialized by another implementation, even if the other
+ * implementation uses a different resource bundle for localizing
+ * the names of pre-defined levels.
+ */
+ private Object readResolve()
+ {
+ for (int i = 0; i < knownLevels.length; i++)
+ if (value == knownLevels[i].intValue())
+ return knownLevels[i];
+
+ return this;
+ }
+
+
+ /**
+ * Returns the name of the resource bundle used for localizing the
+ * level name.
+ *
+ * @return the name of the resource bundle used for localizing the
+ * level name, or <code>null</code> if the name does not undergo
+ * localization.
+ */
+ public String getResourceBundleName()
+ {
+ return resourceBundleName;
+ }
+
+
+ /**
+ * Returns the name of the Level without localizing it, for example
+ * "WARNING".
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+
+ /**
+ * Returns the name of the Level after localizing it, for example
+ * "WARNUNG".
+ */
+ public String getLocalizedName()
+ {
+ String localizedName = null;
+
+ if (resourceBundleName != null)
+ {
+ try
+ {
+ ResourceBundle b = ResourceBundle.getBundle(resourceBundleName);
+ localizedName = b.getString(name);
+ }
+ catch (Exception _)
+ {
+ }
+ }
+
+ if (localizedName != null)
+ return localizedName;
+ else
+ return name;
+ }
+
+
+ /**
+ * Returns the name of the Level without localizing it, for example
+ * "WARNING".
+ */
+ public final String toString()
+ {
+ return getName();
+ }
+
+
+ /**
+ * Returns the integer value of the Level.
+ */
+ public final int intValue()
+ {
+ return value;
+ }
+
+
+ /**
+ * Returns one of the standard Levels given either its name or its
+ * integer value. Custom subclasses of Level will not be returned
+ * by this method.
+ *
+ * @throws IllegalArgumentException if <code>name</code> is neither
+ * the name nor the integer value of one of the pre-defined standard
+ * logging levels.
+ *
+ * @throws NullPointerException if <code>name</code> is null.
+ *
+ */
+ public static Level parse(String name)
+ throws IllegalArgumentException
+ {
+ /* This will throw a NullPointerException if name is null,
+ * as required by the API specification.
+ */
+ name = name.intern();
+
+ for (int i = 0; i < knownLevels.length; i++)
+ {
+ // It's safe to use == instead of .equals here because only the
+ // standard logging levels will be returned by this method, and
+ // they are all created using string literals.
+ if (name == knownLevels[i].name)
+ return knownLevels[i];
+ }
+
+ try
+ {
+ int num = Integer.parseInt(name);
+ for (int i = 0; i < knownLevels.length; i++)
+ if (num == knownLevels[i].value)
+ return knownLevels[i];
+ }
+ catch (NumberFormatException _)
+ {
+ }
+
+ String msg = "Not the name of a standard logging level: \"" + name + "\"";
+ throw new IllegalArgumentException(msg);
+ }
+
+
+ /**
+ * Checks whether this Level's integer value is equal to that of
+ * another object.
+ *
+ * @return <code>true</code> if <code>other</code> is an instance of
+ * <code>java.util.logging.Level</code> and has the same integer
+ * value, <code>false</code> otherwise.
+ */
+ public boolean equals(Object other)
+ {
+ if (!(other instanceof Level))
+ return false;
+
+ return value == ((Level) other).value;
+ }
+
+
+ /**
+ * Returns a hash code for this Level which is based on its numeric
+ * value.
+ */
+ public int hashCode()
+ {
+ return value;
+ }
+
+
+ /**
+ * Determines whether or not this Level is one of the standard
+ * levels specified in the Logging API.
+ *
+ * <p>This method is package-private because it is not part
+ * of the logging API specification. However, an XMLFormatter
+ * is supposed to emit the numeric value for a custom log
+ * level, but the name for a pre-defined level. It seems
+ * cleaner to put this method to Level than to write some
+ * procedural code for XMLFormatter.
+ *
+ * @return <code>true</code> if this Level is a standard level,
+ * <code>false</code> otherwise.
+ */
+ final boolean isStandardLevel()
+ {
+ for (int i = 0; i < knownLevels.length; i++)
+ if (knownLevels[i] == this)
+ return true;
+
+ return false;
+ }
+}
diff --git a/libjava/classpath/java/util/logging/LogManager.java b/libjava/classpath/java/util/logging/LogManager.java
new file mode 100644
index 000000000..dffa44d9c
--- /dev/null
+++ b/libjava/classpath/java/util/logging/LogManager.java
@@ -0,0 +1,986 @@
+/* LogManager.java -- a class for maintaining Loggers and managing
+ configuration properties
+ Copyright (C) 2002, 2005, 2006, 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.logging;
+
+import gnu.classpath.SystemProperties;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.ref.WeakReference;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+/**
+ * The <code>LogManager</code> maintains a hierarchical namespace
+ * of Logger objects and manages properties for configuring the logging
+ * framework. There exists only one single <code>LogManager</code>
+ * per virtual machine. This instance can be retrieved using the
+ * static method {@link #getLogManager()}.
+ *
+ * <p><strong>Configuration Process:</strong> The global LogManager
+ * object is created and configured when the class
+ * <code>java.util.logging.LogManager</code> is initialized.
+ * The configuration process includes the subsequent steps:
+ *
+ * <ul>
+ * <li>If the system property <code>java.util.logging.manager</code>
+ * is set to the name of a subclass of
+ * <code>java.util.logging.LogManager</code>, an instance of
+ * that subclass is created and becomes the global LogManager.
+ * Otherwise, a new instance of LogManager is created.</li>
+ * <li>The <code>LogManager</code> constructor tries to create
+ * a new instance of the class specified by the system
+ * property <code>java.util.logging.config.class</code>.
+ * Typically, the constructor of this class will call
+ * <code>LogManager.getLogManager().readConfiguration(java.io.InputStream)</code>
+ * for configuring the logging framework.
+ * The configuration process stops at this point if
+ * the system property <code>java.util.logging.config.class</code>
+ * is set (irrespective of whether the class constructor
+ * could be called or an exception was thrown).</li>
+ *
+ * <li>If the system property <code>java.util.logging.config.class</code>
+ * is <em>not</em> set, the configuration parameters are read in from
+ * a file and passed to
+ * {@link #readConfiguration(java.io.InputStream)}.
+ * The name and location of this file are specified by the system
+ * property <code>java.util.logging.config.file</code>.</li>
+ * <li>If the system property <code>java.util.logging.config.file</code>
+ * is not set, however, the contents of the URL
+ * "{gnu.classpath.home.url}/logging.properties" are passed to
+ * {@link #readConfiguration(java.io.InputStream)}.
+ * Here, "{gnu.classpath.home.url}" stands for the value of
+ * the system property <code>gnu.classpath.home.url</code>.</li>
+ * </ul>
+ *
+ * <p>The <code>LogManager</code> has a level of <code>INFO</code> by
+ * default, and this will be inherited by <code>Logger</code>s unless they
+ * override it either by properties or programmatically.
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class LogManager
+{
+ /**
+ * The object name for the logging management bean.
+ * @since 1.5
+ */
+ public static final String LOGGING_MXBEAN_NAME
+ = "java.util.logging:type=Logging";
+
+ /**
+ * The singleton LogManager instance.
+ */
+ private static LogManager logManager;
+
+ /**
+ * The singleton logging bean.
+ */
+ private static LoggingMXBean loggingBean;
+
+ /**
+ * The registered named loggers; maps the name of a Logger to
+ * a WeakReference to it.
+ */
+ private Map<String, WeakReference<Logger>> loggers;
+
+ /**
+ * The properties for the logging framework which have been
+ * read in last.
+ */
+ private Properties properties;
+
+ /**
+ * A delegate object that provides support for handling
+ * PropertyChangeEvents. The API specification does not
+ * mention which bean should be the source in the distributed
+ * PropertyChangeEvents, but Mauve test code has determined that
+ * the Sun J2SE 1.4 reference implementation uses the LogManager
+ * class object. This is somewhat strange, as the class object
+ * is not the bean with which listeners have to register, but
+ * there is no reason for the GNU Classpath implementation to
+ * behave differently from the reference implementation in
+ * this case.
+ */
+ private final PropertyChangeSupport pcs = new PropertyChangeSupport( /* source bean */
+ LogManager.class);
+
+ protected LogManager()
+ {
+ loggers = new HashMap();
+ }
+
+ /**
+ * Returns the globally shared LogManager instance.
+ */
+ public static synchronized LogManager getLogManager()
+ {
+ if (logManager == null)
+ {
+ logManager = makeLogManager();
+ initLogManager();
+ }
+ return logManager;
+ }
+
+ private static final String MANAGER_PROPERTY = "java.util.logging.manager";
+
+ private static LogManager makeLogManager()
+ {
+ String managerClassName = SystemProperties.getProperty(MANAGER_PROPERTY);
+ LogManager manager = (LogManager) createInstance
+ (managerClassName, LogManager.class, MANAGER_PROPERTY);
+ if (manager == null)
+ manager = new LogManager();
+ return manager;
+ }
+
+ private static final String CONFIG_PROPERTY = "java.util.logging.config.class";
+
+ private static void initLogManager()
+ {
+ LogManager manager = getLogManager();
+ Logger.root.setLevel(Level.INFO);
+ manager.addLogger(Logger.root);
+
+ /* The Javadoc description of the class explains
+ * what is going on here.
+ */
+ Object configurator = createInstance(System.getProperty(CONFIG_PROPERTY),
+ /* must be instance of */ Object.class,
+ CONFIG_PROPERTY);
+
+ try
+ {
+ if (configurator == null)
+ manager.readConfiguration();
+ }
+ catch (IOException ex)
+ {
+ /* FIXME: Is it ok to ignore exceptions here? */
+ }
+ }
+
+ /**
+ * Registers a listener which will be notified when the
+ * logging properties are re-read.
+ */
+ public synchronized void addPropertyChangeListener(PropertyChangeListener listener)
+ {
+ /* do not register null. */
+ listener.getClass();
+
+ pcs.addPropertyChangeListener(listener);
+ }
+
+ /**
+ * Unregisters a listener.
+ *
+ * If <code>listener</code> has not been registered previously,
+ * nothing happens. Also, no exception is thrown if
+ * <code>listener</code> is <code>null</code>.
+ */
+ public synchronized void removePropertyChangeListener(PropertyChangeListener listener)
+ {
+ if (listener != null)
+ pcs.removePropertyChangeListener(listener);
+ }
+
+ /**
+ * Adds a named logger. If a logger with the same name has
+ * already been registered, the method returns <code>false</code>
+ * without adding the logger.
+ *
+ * <p>The <code>LogManager</code> only keeps weak references
+ * to registered loggers. Therefore, names can become available
+ * after automatic garbage collection.
+ *
+ * @param logger the logger to be added.
+ *
+ * @return <code>true</code>if <code>logger</code> was added,
+ * <code>false</code> otherwise.
+ *
+ * @throws NullPointerException if <code>name</code> is
+ * <code>null</code>.
+ */
+ public synchronized boolean addLogger(Logger logger)
+ {
+ /* To developers thinking about to remove the 'synchronized'
+ * declaration from this method: Please read the comment
+ * in java.util.logging.Logger.getLogger(String, String)
+ * and make sure that whatever you change wrt. synchronization
+ * does not endanger thread-safety of Logger.getLogger.
+ * The current implementation of Logger.getLogger assumes
+ * that LogManager does its synchronization on the globally
+ * shared instance of LogManager.
+ */
+ String name;
+ WeakReference ref;
+
+ /* This will throw a NullPointerException if logger is null,
+ * as required by the API specification.
+ */
+ name = logger.getName();
+
+ ref = loggers.get(name);
+ if (ref != null)
+ {
+ if (ref.get() != null)
+ return false;
+
+ /* There has been a logger under this name in the past,
+ * but it has been garbage collected.
+ */
+ loggers.remove(ref);
+ }
+
+ /* Adding a named logger requires a security permission. */
+ if ((name != null) && ! name.equals(""))
+ checkAccess();
+
+ Logger parent = findAncestor(logger);
+ loggers.put(name, new WeakReference<Logger>(logger));
+ if (parent != logger.getParent())
+ logger.setParent(parent);
+
+ // The level of the newly added logger must be specified.
+ // The easiest case is if there is a level for exactly this logger
+ // in the properties. If no such level exists the level needs to be
+ // searched along the hirachy. So if there is a new logger 'foo.blah.blub'
+ // and an existing parent logger 'foo' the properties 'foo.blah.blub.level'
+ // and 'foo.blah.level' need to be checked. If both do not exist in the
+ // properties the level of the new logger is set to 'null' (i.e. it uses the
+ // level of its parent 'foo').
+ Level logLevel = logger.getLevel();
+ String searchName = name;
+ String parentName = parent != null ? parent.getName() : "";
+ while (logLevel == null && ! searchName.equals(parentName))
+ {
+ logLevel = getLevelProperty(searchName + ".level", logLevel);
+ int index = searchName.lastIndexOf('.');
+ if(index > -1)
+ searchName = searchName.substring(0,index);
+ else
+ searchName = "";
+ }
+ logger.setLevel(logLevel);
+
+ /* It can happen that existing loggers should be children of
+ * the newly added logger. For example, assume that there
+ * already exist loggers under the names "", "foo", and "foo.bar.baz".
+ * When adding "foo.bar", the logger "foo.bar.baz" should change
+ * its parent to "foo.bar".
+ */
+ for (Iterator iter = loggers.keySet().iterator(); iter.hasNext();)
+ {
+ Logger possChild = (Logger) ((WeakReference) loggers.get(iter.next()))
+ .get();
+ if ((possChild == null) || (possChild == logger)
+ || (possChild.getParent() != parent))
+ continue;
+
+ if (! possChild.getName().startsWith(name))
+ continue;
+
+ if (possChild.getName().charAt(name.length()) != '.')
+ continue;
+
+ possChild.setParent(logger);
+ }
+
+ return true;
+ }
+
+ /**
+ * Finds the closest ancestor for a logger among the currently
+ * registered ones. For example, if the currently registered
+ * loggers have the names "", "foo", and "foo.bar", the result for
+ * "foo.bar.baz" will be the logger whose name is "foo.bar".
+ *
+ * @param child a logger for whose name no logger has been
+ * registered.
+ *
+ * @return the closest ancestor for <code>child</code>,
+ * or <code>null</code> if <code>child</code>
+ * is the root logger.
+ *
+ * @throws NullPointerException if <code>child</code>
+ * is <code>null</code>.
+ */
+ private synchronized Logger findAncestor(Logger child)
+ {
+ String childName = child.getName();
+ int childNameLength = childName.length();
+ Logger best = Logger.root;
+ int bestNameLength = 0;
+
+ Logger cand;
+ int candNameLength;
+
+ if (child == Logger.root)
+ return null;
+
+ for (String candName : loggers.keySet())
+ {
+ candNameLength = candName.length();
+
+ if (candNameLength > bestNameLength
+ && childNameLength > candNameLength
+ && childName.startsWith(candName)
+ && childName.charAt(candNameLength) == '.')
+ {
+ cand = loggers.get(candName).get();
+ if ((cand == null) || (cand == child))
+ continue;
+
+ bestNameLength = candName.length();
+ best = cand;
+ }
+ }
+
+ return best;
+ }
+
+ /**
+ * Returns a Logger given its name.
+ *
+ * @param name the name of the logger.
+ *
+ * @return a named Logger, or <code>null</code> if there is no
+ * logger with that name.
+ *
+ * @throw java.lang.NullPointerException if <code>name</code>
+ * is <code>null</code>.
+ */
+ public synchronized Logger getLogger(String name)
+ {
+ WeakReference<Logger> ref;
+
+ /* Throw a NullPointerException if name is null. */
+ name.getClass();
+
+ ref = loggers.get(name);
+ if (ref != null)
+ return ref.get();
+ else
+ return null;
+ }
+
+ /**
+ * Returns an Enumeration of currently registered Logger names.
+ * Since other threads can register loggers at any time, the
+ * result could be different any time this method is called.
+ *
+ * @return an Enumeration with the names of the currently
+ * registered Loggers.
+ */
+ public synchronized Enumeration<String> getLoggerNames()
+ {
+ return Collections.enumeration(loggers.keySet());
+ }
+
+ /**
+ * Resets the logging configuration by removing all handlers for
+ * registered named loggers and setting their level to <code>null</code>.
+ * The level of the root logger will be set to <code>Level.INFO</code>.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ */
+ public synchronized void reset() throws SecurityException
+ {
+ /* Throw a SecurityException if the caller does not have the
+ * permission to control the logging infrastructure.
+ */
+ checkAccess();
+
+ properties = new Properties();
+
+ Iterator<WeakReference<Logger>> iter = loggers.values().iterator();
+ while (iter.hasNext())
+ {
+ WeakReference<Logger> ref;
+ Logger logger;
+
+ ref = iter.next();
+ if (ref != null)
+ {
+ logger = ref.get();
+
+ if (logger == null)
+ iter.remove();
+ else if (logger != Logger.root)
+ {
+ logger.resetLogger();
+ logger.setLevel(null);
+ }
+ }
+ }
+
+ Logger.root.setLevel(Level.INFO);
+ Logger.root.resetLogger();
+ }
+
+ /**
+ * Configures the logging framework by reading a configuration file.
+ * The name and location of this file are specified by the system
+ * property <code>java.util.logging.config.file</code>. If this
+ * property is not set, the URL
+ * "{gnu.classpath.home.url}/logging.properties" is taken, where
+ * "{gnu.classpath.home.url}" stands for the value of the system
+ * property <code>gnu.classpath.home.url</code>.
+ *
+ * <p>The task of configuring the framework is then delegated to
+ * {@link #readConfiguration(java.io.InputStream)}, which will
+ * notify registered listeners after having read the properties.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure, or if the caller is
+ * not granted the permission to read the configuration
+ * file.
+ *
+ * @throws IOException if there is a problem reading in the
+ * configuration file.
+ */
+ public synchronized void readConfiguration()
+ throws IOException, SecurityException
+ {
+ String path;
+ InputStream inputStream;
+
+ path = System.getProperty("java.util.logging.config.file");
+ if ((path == null) || (path.length() == 0))
+ {
+ String url = (System.getProperty("gnu.classpath.home.url")
+ + "/logging.properties");
+ try
+ {
+ inputStream = new URL(url).openStream();
+ }
+ catch (Exception e)
+ {
+ inputStream=null;
+ }
+
+ // If no config file could be found use a default configuration.
+ if(inputStream == null)
+ {
+ String defaultConfig = "handlers = java.util.logging.ConsoleHandler \n"
+ + ".level=INFO \n";
+ inputStream = new ByteArrayInputStream(defaultConfig.getBytes());
+ }
+ }
+ else
+ inputStream = new java.io.FileInputStream(path);
+
+ try
+ {
+ readConfiguration(inputStream);
+ }
+ finally
+ {
+ // Close the stream in order to save
+ // resources such as file descriptors.
+ inputStream.close();
+ }
+ }
+
+ public synchronized void readConfiguration(InputStream inputStream)
+ throws IOException, SecurityException
+ {
+ Properties newProperties;
+ Enumeration keys;
+
+ checkAccess();
+ newProperties = new Properties();
+ newProperties.load(inputStream);
+ reset();
+ this.properties = newProperties;
+ keys = newProperties.propertyNames();
+
+ while (keys.hasMoreElements())
+ {
+ String key = ((String) keys.nextElement()).trim();
+ String value = newProperties.getProperty(key);
+
+ if (value == null)
+ continue;
+
+ value = value.trim();
+
+ if ("handlers".equals(key))
+ {
+ // In Java 5 and earlier this was specified to be
+ // whitespace-separated, but in reality it also accepted
+ // commas (tomcat relied on this), and in Java 6 the
+ // documentation was updated to fit the implementation.
+ StringTokenizer tokenizer = new StringTokenizer(value,
+ " \t\n\r\f,");
+ while (tokenizer.hasMoreTokens())
+ {
+ String handlerName = tokenizer.nextToken();
+ Handler handler = (Handler)
+ createInstance(handlerName, Handler.class, key);
+ // Tomcat also relies on the implementation ignoring
+ // items in 'handlers' which are not class names.
+ if (handler != null)
+ Logger.root.addHandler(handler);
+ }
+ }
+
+ if (key.endsWith(".level"))
+ {
+ String loggerName = key.substring(0, key.length() - 6);
+ Logger logger = getLogger(loggerName);
+
+ if (logger == null)
+ {
+ logger = Logger.getLogger(loggerName);
+ addLogger(logger);
+ }
+ Level level = null;
+ try
+ {
+ level = Level.parse(value);
+ }
+ catch (IllegalArgumentException e)
+ {
+ warn("bad level \'" + value + "\'", e);
+ }
+ if (level != null)
+ {
+ logger.setLevel(level);
+ }
+ continue;
+ }
+ }
+
+ /* The API specification does not talk about the
+ * property name that is distributed with the
+ * PropertyChangeEvent. With test code, it could
+ * be determined that the Sun J2SE 1.4 reference
+ * implementation uses null for the property name.
+ */
+ pcs.firePropertyChange(null, null, null);
+ }
+
+ /**
+ * Returns the value of a configuration property as a String.
+ */
+ public synchronized String getProperty(String name)
+ {
+ if (properties != null)
+ return properties.getProperty(name);
+ else
+ return null;
+ }
+
+ /**
+ * Returns the value of a configuration property as an integer.
+ * This function is a helper used by the Classpath implementation
+ * of java.util.logging, it is <em>not</em> specified in the
+ * logging API.
+ *
+ * @param name the name of the configuration property.
+ *
+ * @param defaultValue the value that will be returned if the
+ * property is not defined, or if its value is not an integer
+ * number.
+ */
+ static int getIntProperty(String name, int defaultValue)
+ {
+ try
+ {
+ return Integer.parseInt(getLogManager().getProperty(name));
+ }
+ catch (Exception ex)
+ {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns the value of a configuration property as an integer,
+ * provided it is inside the acceptable range.
+ * This function is a helper used by the Classpath implementation
+ * of java.util.logging, it is <em>not</em> specified in the
+ * logging API.
+ *
+ * @param name the name of the configuration property.
+ *
+ * @param minValue the lowest acceptable value.
+ *
+ * @param maxValue the highest acceptable value.
+ *
+ * @param defaultValue the value that will be returned if the
+ * property is not defined, or if its value is not an integer
+ * number, or if it is less than the minimum value,
+ * or if it is greater than the maximum value.
+ */
+ static int getIntPropertyClamped(String name, int defaultValue,
+ int minValue, int maxValue)
+ {
+ int val = getIntProperty(name, defaultValue);
+ if ((val < minValue) || (val > maxValue))
+ val = defaultValue;
+ return val;
+ }
+
+ /**
+ * Returns the value of a configuration property as a boolean.
+ * This function is a helper used by the Classpath implementation
+ * of java.util.logging, it is <em>not</em> specified in the
+ * logging API.
+ *
+ * @param name the name of the configuration property.
+ *
+ * @param defaultValue the value that will be returned if the
+ * property is not defined, or if its value is neither
+ * <code>"true"</code> nor <code>"false"</code>.
+ */
+ static boolean getBooleanProperty(String name, boolean defaultValue)
+ {
+ try
+ {
+ return (Boolean.valueOf(getLogManager().getProperty(name))).booleanValue();
+ }
+ catch (Exception ex)
+ {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns the value of a configuration property as a Level.
+ * This function is a helper used by the Classpath implementation
+ * of java.util.logging, it is <em>not</em> specified in the
+ * logging API.
+ *
+ * @param propertyName the name of the configuration property.
+ *
+ * @param defaultValue the value that will be returned if the
+ * property is not defined, or if
+ * {@link Level#parse(java.lang.String)} does not like
+ * the property value.
+ */
+ static Level getLevelProperty(String propertyName, Level defaultValue)
+ {
+ try
+ {
+ String value = getLogManager().getProperty(propertyName);
+ if (value != null)
+ return Level.parse(getLogManager().getProperty(propertyName));
+ else
+ return defaultValue;
+ }
+ catch (Exception ex)
+ {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns the value of a configuration property as a Class.
+ * This function is a helper used by the Classpath implementation
+ * of java.util.logging, it is <em>not</em> specified in the
+ * logging API.
+ *
+ * @param propertyName the name of the configuration property.
+ *
+ * @param defaultValue the value that will be returned if the
+ * property is not defined, or if it does not specify
+ * the name of a loadable class.
+ */
+ static final Class getClassProperty(String propertyName, Class defaultValue)
+ {
+ String propertyValue = logManager.getProperty(propertyName);
+
+ if (propertyValue != null)
+ try
+ {
+ return locateClass(propertyValue);
+ }
+ catch (ClassNotFoundException e)
+ {
+ warn(propertyName + " = " + propertyValue, e);
+ }
+
+ return defaultValue;
+ }
+
+ static final Object getInstanceProperty(String propertyName, Class ofClass,
+ Class defaultClass)
+ {
+ Class klass = getClassProperty(propertyName, defaultClass);
+ if (klass == null)
+ return null;
+
+ try
+ {
+ Object obj = klass.newInstance();
+ if (ofClass.isInstance(obj))
+ return obj;
+ }
+ catch (InstantiationException e)
+ {
+ warn(propertyName + " = " + klass.getName(), e);
+ }
+ catch (IllegalAccessException e)
+ {
+ warn(propertyName + " = " + klass.getName(), e);
+ }
+
+ if (defaultClass == null)
+ return null;
+
+ try
+ {
+ return defaultClass.newInstance();
+ }
+ catch (java.lang.InstantiationException ex)
+ {
+ throw new RuntimeException(ex.getMessage());
+ }
+ catch (java.lang.IllegalAccessException ex)
+ {
+ throw new RuntimeException(ex.getMessage());
+ }
+ }
+
+ /**
+ * An instance of <code>LoggingPermission("control")</code>
+ * that is shared between calls to <code>checkAccess()</code>.
+ */
+ private static final LoggingPermission controlPermission = new LoggingPermission("control",
+ null);
+
+ /**
+ * Checks whether the current security context allows changing
+ * the configuration of the logging framework. For the security
+ * context to be trusted, it has to be granted
+ * a LoggingPermission("control").
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ */
+ public void checkAccess() throws SecurityException
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkPermission(controlPermission);
+ }
+
+ /**
+ * Creates a new instance of a class specified by name and verifies
+ * that it is an instance (or subclass of) a given type.
+ *
+ * @param className the name of the class of which a new instance
+ * should be created.
+ *
+ * @param type the object created must be an instance of
+ * <code>type</code> or any subclass of <code>type</code>
+ *
+ * @param property the system property to reference in error
+ * messages
+ *
+ * @return the new instance, or <code>null</code> if
+ * <code>className</code> is <code>null</code>, if no class
+ * with that name could be found, if there was an error
+ * loading that class, or if the constructor of the class
+ * has thrown an exception.
+ */
+ private static final Object createInstance(String className, Class type,
+ String property)
+ {
+ Class klass = null;
+
+ if ((className == null) || (className.length() == 0))
+ return null;
+
+ try
+ {
+ klass = locateClass(className);
+ if (type.isAssignableFrom(klass))
+ return klass.newInstance();
+ warn(property, className, "not an instance of " + type.getName());
+ }
+ catch (ClassNotFoundException e)
+ {
+ warn(property, className, "class not found", e);
+ }
+ catch (IllegalAccessException e)
+ {
+ warn(property, className, "illegal access", e);
+ }
+ catch (InstantiationException e)
+ {
+ warn(property, className, e);
+ }
+ catch (java.lang.LinkageError e)
+ {
+ warn(property, className, "linkage error", e);
+ }
+
+ return null;
+ }
+
+ private static final void warn(String property, String klass, Throwable t)
+ {
+ warn(property, klass, null, t);
+ }
+
+ private static final void warn(String property, String klass, String msg)
+ {
+ warn(property, klass, msg, null);
+ }
+
+ private static final void warn(String property, String klass, String msg,
+ Throwable t)
+ {
+ warn("error instantiating '" + klass + "' referenced by " + property +
+ (msg == null ? "" : ", " + msg), t);
+ }
+
+ /**
+ * All debug warnings go through this method.
+ */
+
+ private static final void warn(String msg, Throwable t)
+ {
+ System.err.println("WARNING: " + msg);
+ if (t != null)
+ t.printStackTrace(System.err);
+ }
+
+ /**
+ * Locates a class by first checking the system class loader and
+ * then checking the context class loader.
+ *
+ * @param name the fully qualified name of the Class to locate
+ * @return Class the located Class
+ */
+
+ private static Class locateClass(String name) throws ClassNotFoundException
+ {
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ try
+ {
+ return Class.forName(name, true, loader);
+ }
+ catch (ClassNotFoundException e)
+ {
+ loader = ClassLoader.getSystemClassLoader();
+ return Class.forName(name, true, loader);
+ }
+ }
+
+ /**
+ * Return the logging bean. There is a single logging bean per
+ * VM instance.
+ * @since 1.5
+ */
+ public static synchronized LoggingMXBean getLoggingMXBean()
+ {
+ if (loggingBean == null)
+ {
+ loggingBean = new LoggingMXBean()
+ {
+ public String getLoggerLevel(String logger)
+ {
+ LogManager mgr = getLogManager();
+ Logger l = mgr.getLogger(logger);
+ if (l == null)
+ return null;
+ Level lev = l.getLevel();
+ if (lev == null)
+ return "";
+ return lev.getName();
+ }
+
+ public List getLoggerNames()
+ {
+ LogManager mgr = getLogManager();
+ // This is inefficient, but perhaps better for maintenance.
+ return Collections.list(mgr.getLoggerNames());
+ }
+
+ public String getParentLoggerName(String logger)
+ {
+ LogManager mgr = getLogManager();
+ Logger l = mgr.getLogger(logger);
+ if (l == null)
+ return null;
+ l = l.getParent();
+ if (l == null)
+ return "";
+ return l.getName();
+ }
+
+ public void setLoggerLevel(String logger, String level)
+ {
+ LogManager mgr = getLogManager();
+ Logger l = mgr.getLogger(logger);
+ if (l == null)
+ throw new IllegalArgumentException("no logger named " + logger);
+ Level newLevel;
+ if (level == null)
+ newLevel = null;
+ else
+ newLevel = Level.parse(level);
+ l.setLevel(newLevel);
+ }
+ };
+ }
+ return loggingBean;
+ }
+}
diff --git a/libjava/classpath/java/util/logging/LogRecord.java b/libjava/classpath/java/util/logging/LogRecord.java
new file mode 100644
index 000000000..ee99ee69a
--- /dev/null
+++ b/libjava/classpath/java/util/logging/LogRecord.java
@@ -0,0 +1,672 @@
+/* LogRecord.java --
+ A class for the state associated with individual logging events
+ Copyright (C) 2002, 2003, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.logging;
+
+import java.util.ResourceBundle;
+
+
+/**
+ * A <code>LogRecord</code> contains the state for an individual
+ * event to be logged.
+ *
+ * <p>As soon as a LogRecord instance has been handed over to the
+ * logging framework, applications should not manipulate it anymore.
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class LogRecord
+ implements java.io.Serializable
+{
+ /**
+ * The severity level of this <code>LogRecord</code>.
+ */
+ private Level level;
+
+
+ /**
+ * The sequence number of this <code>LogRecord</code>.
+ */
+ private long sequenceNumber;
+
+
+ /**
+ * The name of the class that issued the logging request, or
+ * <code>null</code> if this information could not be obtained.
+ */
+ private String sourceClassName;
+
+
+ /**
+ * The name of the method that issued the logging request, or
+ * <code>null</code> if this information could not be obtained.
+ */
+ private String sourceMethodName;
+
+
+ /**
+ * The message for this <code>LogRecord</code> before
+ * any localization or formatting.
+ */
+ private String message;
+
+
+ /**
+ * An identifier for the thread in which this <code>LogRecord</code>
+ * was created. The identifier is not necessarily related to any
+ * thread identifiers used by the operating system.
+ */
+ private int threadID;
+
+
+ /**
+ * The time when this <code>LogRecord</code> was created,
+ * in milliseconds since the beginning of January 1, 1970.
+ */
+ private long millis;
+
+
+ /**
+ * The Throwable associated with this <code>LogRecord</code>, or
+ * <code>null</code> if the logged event is not related to an
+ * exception or error.
+ */
+ private Throwable thrown;
+
+
+ /**
+ * The name of the logger where this <code>LogRecord</code> has
+ * originated, or <code>null</code> if this <code>LogRecord</code>
+ * does not originate from a <code>Logger</code>.
+ */
+ private String loggerName;
+
+
+ /**
+ * The name of the resource bundle used for localizing log messages,
+ * or <code>null</code> if no bundle has been specified.
+ */
+ private String resourceBundleName;
+
+ private transient Object[] parameters;
+
+ private transient ResourceBundle bundle;
+
+
+ /**
+ * Constructs a <code>LogRecord</code> given a severity level and
+ * an unlocalized message text. In addition, the sequence number,
+ * creation time (as returned by <code>getMillis()</code>) and
+ * thread ID are assigned. All other properties are set to
+ * <code>null</code>.
+ *
+ * @param level the severity level, for example <code>Level.WARNING</code>.
+ *
+ * @param message the message text (which will be used as key
+ * for looking up the localized message text
+ * if a resource bundle has been associated).
+ */
+ public LogRecord(Level level, String message)
+ {
+ this.level = level;
+ this.message = message;
+ this.millis = System.currentTimeMillis();
+
+ /* A subclass of java.lang.Thread could override hashCode(),
+ * in which case the result would not be guaranteed anymore
+ * to be unique among all threads. While System.identityHashCode
+ * is not necessarily unique either, it at least cannot be
+ * overridden by user code. However, is might be a good idea
+ * to use something better for generating thread IDs.
+ */
+ this.threadID = System.identityHashCode(Thread.currentThread());
+
+ sequenceNumber = allocateSeqNum();
+ }
+
+
+ /**
+ * Determined with the serialver tool of the Sun J2SE 1.4.
+ */
+ static final long serialVersionUID = 5372048053134512534L;
+
+ private void readObject(java.io.ObjectInputStream in)
+ throws java.io.IOException, java.lang.ClassNotFoundException
+ {
+ in.defaultReadObject();
+
+ /* We assume that future versions will be downwards compatible,
+ * so we can ignore the versions.
+ */
+ byte majorVersion = in.readByte();
+ byte minorVersion = in.readByte();
+
+ int numParams = in.readInt();
+ if (numParams >= 0)
+ {
+ parameters = new Object[numParams];
+ for (int i = 0; i < numParams; i++)
+ parameters[i] = in.readObject();
+ }
+ }
+
+
+ /**
+ * @serialData The default fields, followed by a major byte version
+ * number, followed by a minor byte version number, followed by
+ * information about the log record parameters. If
+ * <code>parameters</code> is <code>null</code>, the integer -1 is
+ * written, otherwise the length of the <code>parameters</code>
+ * array (which can be zero), followed by the result of calling
+ * {@link Object#toString() toString()} on the parameter (or
+ * <code>null</code> if the parameter is <code>null</code>).
+ *
+ * <p><strong>Specification Note:</strong> The Javadoc for the
+ * Sun reference implementation does not specify the version
+ * number. FIXME: Reverse-engineer the JDK and file a bug
+ * report with Sun, asking for amendment of the specification.
+ */
+ private void writeObject(java.io.ObjectOutputStream out)
+ throws java.io.IOException
+ {
+ out.defaultWriteObject();
+
+ /* Major, minor version number: The Javadoc for J2SE1.4 does not
+ * specify the values.
+ */
+ out.writeByte(0);
+ out.writeByte(0);
+
+ if (parameters == null)
+ out.writeInt(-1);
+ else
+ {
+ out.writeInt(parameters.length);
+ for (int i = 0; i < parameters.length; i++)
+ {
+ if (parameters[i] == null)
+ out.writeObject(null);
+ else
+ out.writeObject(parameters[i].toString());
+ }
+ }
+ }
+
+
+ /**
+ * Returns the name of the logger where this <code>LogRecord</code>
+ * has originated.
+ *
+ * @return the name of the source {@link Logger}, or
+ * <code>null</code> if this <code>LogRecord</code>
+ * does not originate from a <code>Logger</code>.
+ */
+ public String getLoggerName()
+ {
+ return loggerName;
+ }
+
+
+ /**
+ * Sets the name of the logger where this <code>LogRecord</code>
+ * has originated.
+ *
+ * <p>As soon as a <code>LogRecord</code> has been handed over
+ * to the logging framework, applications should not modify it
+ * anymore. Therefore, this method should only be called on
+ * freshly constructed LogRecords.
+ *
+ * @param name the name of the source logger, or <code>null</code> to
+ * indicate that this <code>LogRecord</code> does not
+ * originate from a <code>Logger</code>.
+ */
+ public void setLoggerName(String name)
+ {
+ loggerName = name;
+ }
+
+
+ /**
+ * Returns the resource bundle that is used when the message
+ * of this <code>LogRecord</code> needs to be localized.
+ *
+ * @return the resource bundle used for localization,
+ * or <code>null</code> if this message does not need
+ * to be localized.
+ */
+ public ResourceBundle getResourceBundle()
+ {
+ return bundle;
+ }
+
+
+ /**
+ * Sets the resource bundle that is used when the message
+ * of this <code>LogRecord</code> needs to be localized.
+ *
+ * <p>As soon as a <code>LogRecord</code> has been handed over
+ * to the logging framework, applications should not modify it
+ * anymore. Therefore, this method should only be called on
+ * freshly constructed LogRecords.
+ *
+ * @param bundle the resource bundle to be used, or
+ * <code>null</code> to indicate that this
+ * message does not need to be localized.
+ */
+ public void setResourceBundle(ResourceBundle bundle)
+ {
+ this.bundle = bundle;
+
+ /* FIXME: Is there a way to infer the name
+ * of a resource bundle from a ResourceBundle object?
+ */
+ this.resourceBundleName = null;
+ }
+
+
+ /**
+ * Returns the name of the resource bundle that is used when the
+ * message of this <code>LogRecord</code> needs to be localized.
+ *
+ * @return the name of the resource bundle used for localization,
+ * or <code>null</code> if this message does not need
+ * to be localized.
+ */
+ public String getResourceBundleName()
+ {
+ return resourceBundleName;
+ }
+
+
+ /**
+ * Sets the name of the resource bundle that is used when the
+ * message of this <code>LogRecord</code> needs to be localized.
+ *
+ * <p>As soon as a <code>LogRecord</code> has been handed over
+ * to the logging framework, applications should not modify it
+ * anymore. Therefore, this method should only be called on
+ * freshly constructed LogRecords.
+ *
+ * @param name the name of the resource bundle to be used, or
+ * <code>null</code> to indicate that this message
+ * does not need to be localized.
+ */
+ public void setResourceBundleName(String name)
+ {
+ resourceBundleName = name;
+ bundle = null;
+
+ try
+ {
+ if (resourceBundleName != null)
+ bundle = ResourceBundle.getBundle(resourceBundleName);
+ }
+ catch (java.util.MissingResourceException _)
+ {
+ }
+ }
+
+
+ /**
+ * Returns the level of the LogRecord.
+ *
+ * <p>Applications should be aware of the possibility that the
+ * result is not necessarily one of the standard logging levels,
+ * since the logging framework allows to create custom subclasses
+ * of <code>java.util.logging.Level</code>. Therefore, filters
+ * should perform checks like <code>theRecord.getLevel().intValue()
+ * == Level.INFO.intValue()</code> instead of <code>theRecord.getLevel()
+ * == Level.INFO</code>.
+ */
+ public Level getLevel()
+ {
+ return level;
+ }
+
+
+ /**
+ * Sets the severity level of this <code>LogRecord</code> to a new
+ * value.
+ *
+ * <p>As soon as a <code>LogRecord</code> has been handed over
+ * to the logging framework, applications should not modify it
+ * anymore. Therefore, this method should only be called on
+ * freshly constructed LogRecords.
+ *
+ * @param level the new severity level, for example
+ * <code>Level.WARNING</code>.
+ */
+ public void setLevel(Level level)
+ {
+ this.level = level;
+ }
+
+
+ /**
+ * The last used sequence number for any LogRecord.
+ */
+ private static long lastSeqNum;
+
+
+ /**
+ * Allocates a sequence number for a new LogRecord. This class
+ * method is only called by the LogRecord constructor.
+ */
+ private static synchronized long allocateSeqNum()
+ {
+ lastSeqNum += 1;
+ return lastSeqNum;
+ }
+
+
+ /**
+ * Returns the sequence number of this <code>LogRecord</code>.
+ */
+ public long getSequenceNumber()
+ {
+ return sequenceNumber;
+ }
+
+
+ /**
+ * Sets the sequence number of this <code>LogRecord</code> to a new
+ * value.
+ *
+ * <p>As soon as a <code>LogRecord</code> has been handed over
+ * to the logging framework, applications should not modify it
+ * anymore. Therefore, this method should only be called on
+ * freshly constructed LogRecords.
+ *
+ * @param seqNum the new sequence number.
+ */
+ public void setSequenceNumber(long seqNum)
+ {
+ this.sequenceNumber = seqNum;
+ }
+
+
+ /**
+ * Returns the name of the class where the event being logged
+ * has had its origin. This information can be passed as
+ * parameter to some logging calls, and in certain cases, the
+ * logging framework tries to determine an approximation
+ * (which may or may not be accurate).
+ *
+ * @return the name of the class that issued the logging request,
+ * or <code>null</code> if this information could not
+ * be obtained.
+ */
+ public String getSourceClassName()
+ {
+ if (sourceClassName != null)
+ return sourceClassName;
+
+ /* FIXME: Should infer this information from the call stack. */
+ return null;
+ }
+
+
+ /**
+ * Sets the name of the class where the event being logged
+ * has had its origin.
+ *
+ * <p>As soon as a <code>LogRecord</code> has been handed over
+ * to the logging framework, applications should not modify it
+ * anymore. Therefore, this method should only be called on
+ * freshly constructed LogRecords.
+ *
+ * @param sourceClassName the name of the class that issued the
+ * logging request, or <code>null</code> to indicate that
+ * this information could not be obtained.
+ */
+ public void setSourceClassName(String sourceClassName)
+ {
+ this.sourceClassName = sourceClassName;
+ }
+
+
+ /**
+ * Returns the name of the method where the event being logged
+ * has had its origin. This information can be passed as
+ * parameter to some logging calls, and in certain cases, the
+ * logging framework tries to determine an approximation
+ * (which may or may not be accurate).
+ *
+ * @return the name of the method that issued the logging request,
+ * or <code>null</code> if this information could not
+ * be obtained.
+ */
+ public String getSourceMethodName()
+ {
+ if (sourceMethodName != null)
+ return sourceMethodName;
+
+ /* FIXME: Should infer this information from the call stack. */
+ return null;
+ }
+
+
+ /**
+ * Sets the name of the method where the event being logged
+ * has had its origin.
+ *
+ * <p>As soon as a <code>LogRecord</code> has been handed over
+ * to the logging framework, applications should not modify it
+ * anymore. Therefore, this method should only be called on
+ * freshly constructed LogRecords.
+ *
+ * @param sourceMethodName the name of the method that issued the
+ * logging request, or <code>null</code> to indicate that
+ * this information could not be obtained.
+ */
+ public void setSourceMethodName(String sourceMethodName)
+ {
+ this.sourceMethodName = sourceMethodName;
+ }
+
+
+ /**
+ * Returns the message for this <code>LogRecord</code> before
+ * any localization or parameter substitution.
+ *
+ * <p>A {@link Logger} will try to localize the message
+ * if a resource bundle has been associated with this
+ * <code>LogRecord</code>. In this case, the logger will call
+ * <code>getMessage()</code> and use the result as the key
+ * for looking up the localized message in the bundle.
+ * If no bundle has been associated, or if the result of
+ * <code>getMessage()</code> is not a valid key in the
+ * bundle, the logger will use the raw message text as
+ * returned by this method.
+ *
+ * @return the message text, or <code>null</code> if there
+ * is no message text.
+ */
+ public String getMessage()
+ {
+ return message;
+ }
+
+
+ /**
+ * Sets the message for this <code>LogRecord</code>.
+ *
+ * <p>A <code>Logger</code> will try to localize the message
+ * if a resource bundle has been associated with this
+ * <code>LogRecord</code>. In this case, the logger will call
+ * <code>getMessage()</code> and use the result as the key
+ * for looking up the localized message in the bundle.
+ * If no bundle has been associated, or if the result of
+ * <code>getMessage()</code> is not a valid key in the
+ * bundle, the logger will use the raw message text as
+ * returned by this method.
+ *
+ * <p>It is possible to set the message to either an empty String or
+ * <code>null</code>, although this does not make the the message
+ * very helpful to human users.
+ *
+ * @param message the message text (which will be used as key
+ * for looking up the localized message text
+ * if a resource bundle has been associated).
+ */
+ public void setMessage(String message)
+ {
+ this.message = message;
+ }
+
+
+ /**
+ * Returns the parameters to the log message.
+ *
+ * @return the parameters to the message, or <code>null</code> if
+ * the message has no parameters.
+ */
+ public Object[] getParameters()
+ {
+ return parameters;
+ }
+
+
+ /**
+ * Sets the parameters to the log message.
+ *
+ * <p>As soon as a <code>LogRecord</code> has been handed over
+ * to the logging framework, applications should not modify it
+ * anymore. Therefore, this method should only be called on
+ * freshly constructed LogRecords.
+ *
+ * @param parameters the parameters to the message, or <code>null</code>
+ * to indicate that the message has no parameters.
+ */
+ public void setParameters(Object[] parameters)
+ {
+ this.parameters = parameters;
+ }
+
+
+ /**
+ * Returns an identifier for the thread in which this
+ * <code>LogRecord</code> was created. The identifier is not
+ * necessarily related to any thread identifiers used by the
+ * operating system.
+ *
+ * @return an identifier for the source thread.
+ */
+ public int getThreadID()
+ {
+ return threadID;
+ }
+
+
+ /**
+ * Sets the identifier indicating in which thread this
+ * <code>LogRecord</code> was created. The identifier is not
+ * necessarily related to any thread identifiers used by the
+ * operating system.
+ *
+ * <p>As soon as a <code>LogRecord</code> has been handed over
+ * to the logging framework, applications should not modify it
+ * anymore. Therefore, this method should only be called on
+ * freshly constructed LogRecords.
+ *
+ * @param threadID the identifier for the source thread.
+ */
+ public void setThreadID(int threadID)
+ {
+ this.threadID = threadID;
+ }
+
+
+ /**
+ * Returns the time when this <code>LogRecord</code> was created.
+ *
+ * @return the time of creation in milliseconds since the beginning
+ * of January 1, 1970.
+ */
+ public long getMillis()
+ {
+ return millis;
+ }
+
+
+ /**
+ * Sets the time when this <code>LogRecord</code> was created.
+ *
+ * <p>As soon as a <code>LogRecord</code> has been handed over
+ * to the logging framework, applications should not modify it
+ * anymore. Therefore, this method should only be called on
+ * freshly constructed LogRecords.
+ *
+ * @param millis the time of creation in milliseconds since the
+ * beginning of January 1, 1970.
+ */
+ public void setMillis(long millis)
+ {
+ this.millis = millis;
+ }
+
+
+ /**
+ * Returns the Throwable associated with this <code>LogRecord</code>,
+ * or <code>null</code> if the logged event is not related to an exception
+ * or error.
+ */
+ public Throwable getThrown()
+ {
+ return thrown;
+ }
+
+
+ /**
+ * Associates this <code>LogRecord</code> with an exception or error.
+ *
+ * <p>As soon as a <code>LogRecord</code> has been handed over
+ * to the logging framework, applications should not modify it
+ * anymore. Therefore, this method should only be called on
+ * freshly constructed LogRecords.
+ *
+ * @param thrown the exception or error to associate with, or
+ * <code>null</code> if this <code>LogRecord</code>
+ * should be made unrelated to an exception or error.
+ */
+ public void setThrown(Throwable thrown)
+ {
+ this.thrown = thrown;
+ }
+}
diff --git a/libjava/classpath/java/util/logging/Logger.java b/libjava/classpath/java/util/logging/Logger.java
new file mode 100644
index 000000000..c55e133e5
--- /dev/null
+++ b/libjava/classpath/java/util/logging/Logger.java
@@ -0,0 +1,1193 @@
+/* Logger.java -- a class for logging messages
+ Copyright (C) 2002, 2004, 2006, 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.logging;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.util.List;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * A Logger is used for logging information about events. Usually, there is a
+ * seprate logger for each subsystem or component, although there is a shared
+ * instance for components that make only occasional use of the logging
+ * framework.
+ * <p>
+ * It is common to name a logger after the name of a corresponding Java package.
+ * Loggers are organized into a hierarchical namespace; for example, the logger
+ * <code>"org.gnu.foo"</code> is the <em>parent</em> of logger
+ * <code>"org.gnu.foo.bar"</code>.
+ * <p>
+ * A logger for a named subsystem can be obtained through {@link
+ * java.util.logging.Logger#getLogger(java.lang.String)}. However, only code
+ * which has been granted the permission to control the logging infrastructure
+ * will be allowed to customize that logger. Untrusted code can obtain a
+ * private, anonymous logger through {@link #getAnonymousLogger()} if it wants
+ * to perform any modifications to the logger.
+ * <p>
+ * FIXME: Write more documentation.
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class Logger
+{
+ static final Logger root = new Logger("", null);
+
+ /**
+ * A logger provided to applications that make only occasional use of the
+ * logging framework, typically early prototypes. Serious products are
+ * supposed to create and use their own Loggers, so they can be controlled
+ * individually.
+ */
+ public static final Logger global;
+
+ /**
+ * Use to lock methods on this class instead of calling synchronize on methods
+ * to avoid deadlocks. Yeah, no kidding, we got them :)
+ */
+ private static final Object[] lock = new Object[0];
+
+ static
+ {
+ // Our class might be initialized from an unprivileged context
+ global = (Logger) AccessController.doPrivileged(new PrivilegedAction()
+ {
+ public Object run()
+ {
+ return getLogger("global");
+ }
+ });
+ }
+
+ /**
+ * The name of the Logger, or <code>null</code> if the logger is anonymous.
+ * <p>
+ * A previous version of the GNU Classpath implementation granted untrusted
+ * code the permission to control any logger whose name was null. However,
+ * test code revealed that the Sun J2SE 1.4 reference implementation enforces
+ * the security control for any logger that was not created through
+ * getAnonymousLogger, even if it has a null name. Therefore, a separate flag
+ * {@link Logger#anonymous} was introduced.
+ */
+ private final String name;
+
+ /**
+ * The name of the resource bundle used for localization.
+ * <p>
+ * This variable cannot be declared as <code>final</code> because its value
+ * can change as a result of calling getLogger(String,String).
+ */
+ private String resourceBundleName;
+
+ /**
+ * The resource bundle used for localization.
+ * <p>
+ * This variable cannot be declared as <code>final</code> because its value
+ * can change as a result of calling getLogger(String,String).
+ */
+ private ResourceBundle resourceBundle;
+
+ private Filter filter;
+
+ private final List handlerList = new java.util.ArrayList(4);
+
+ private Handler[] handlers = new Handler[0];
+
+ /**
+ * Indicates whether or not this logger is anonymous. While a
+ * LoggingPermission is required for any modifications to a normal logger,
+ * untrusted code can obtain an anonymous logger and modify it according to
+ * its needs.
+ * <p>
+ * A previous version of the GNU Classpath implementation granted access to
+ * every logger whose name was null. However, test code revealed that the Sun
+ * J2SE 1.4 reference implementation enforces the security control for any
+ * logger that was not created through getAnonymousLogger, even if it has a
+ * null name.
+ */
+ private boolean anonymous;
+
+ private boolean useParentHandlers;
+
+ private Level level;
+
+ private Logger parent;
+
+ /**
+ * Constructs a Logger for a subsystem. Most applications do not need to
+ * create new Loggers explicitly; instead, they should call the static factory
+ * methods {@link #getLogger(java.lang.String,java.lang.String) getLogger}
+ * (with ResourceBundle for localization) or
+ * {@link #getLogger(java.lang.String) getLogger} (without ResourceBundle),
+ * respectively.
+ *
+ * @param name the name for the logger, for example "java.awt" or
+ * "com.foo.bar". The name should be based on the name of the
+ * package issuing log records and consist of dot-separated Java
+ * identifiers.
+ * @param resourceBundleName the name of a resource bundle for localizing
+ * messages, or <code>null</code> to indicate that messages do
+ * not need to be localized.
+ * @throws java.util.MissingResourceException if
+ * <code>resourceBundleName</code> is not <code>null</code>
+ * and no such bundle could be located.
+ */
+ protected Logger(String name, String resourceBundleName)
+ throws MissingResourceException
+ {
+ this.name = name;
+ this.resourceBundleName = resourceBundleName;
+
+ if (resourceBundleName == null)
+ resourceBundle = null;
+ else
+ resourceBundle = ResourceBundle.getBundle(resourceBundleName);
+
+ level = null;
+
+ /*
+ * This is null when the root logger is being constructed, and the root
+ * logger afterwards.
+ */
+ parent = root;
+
+ useParentHandlers = (parent != null);
+ }
+
+ /**
+ * Finds a registered logger for a subsystem, or creates one in case no logger
+ * has been registered yet.
+ *
+ * @param name the name for the logger, for example "java.awt" or
+ * "com.foo.bar". The name should be based on the name of the
+ * package issuing log records and consist of dot-separated Java
+ * identifiers.
+ * @throws IllegalArgumentException if a logger for the subsystem identified
+ * by <code>name</code> has already been created, but uses a a
+ * resource bundle for localizing messages.
+ * @throws NullPointerException if <code>name</code> is <code>null</code>.
+ * @return a logger for the subsystem specified by <code>name</code> that
+ * does not localize messages.
+ */
+ public static Logger getLogger(String name)
+ {
+ return getLogger(name, null);
+ }
+
+ /**
+ * Finds a registered logger for a subsystem, or creates one in case no logger
+ * has been registered yet.
+ * <p>
+ * If a logger with the specified name has already been registered, the
+ * behavior depends on the resource bundle that is currently associated with
+ * the existing logger.
+ * <ul>
+ * <li>If the existing logger uses the same resource bundle as specified by
+ * <code>resourceBundleName</code>, the existing logger is returned.</li>
+ * <li>If the existing logger currently does not localize messages, the
+ * existing logger is modified to use the bundle specified by
+ * <code>resourceBundleName</code>. The existing logger is then returned.
+ * Therefore, all subsystems currently using this logger will produce
+ * localized messages from now on.</li>
+ * <li>If the existing logger already has an associated resource bundle, but
+ * a different one than specified by <code>resourceBundleName</code>, an
+ * <code>IllegalArgumentException</code> is thrown.</li>
+ * </ul>
+ *
+ * @param name the name for the logger, for example "java.awt" or
+ * "org.gnu.foo". The name should be based on the name of the
+ * package issuing log records and consist of dot-separated Java
+ * identifiers.
+ * @param resourceBundleName the name of a resource bundle for localizing
+ * messages, or <code>null</code> to indicate that messages do
+ * not need to be localized.
+ * @return a logger for the subsystem specified by <code>name</code>.
+ * @throws java.util.MissingResourceException if
+ * <code>resourceBundleName</code> is not <code>null</code>
+ * and no such bundle could be located.
+ * @throws IllegalArgumentException if a logger for the subsystem identified
+ * by <code>name</code> has already been created, but uses a
+ * different resource bundle for localizing messages.
+ * @throws NullPointerException if <code>name</code> is <code>null</code>.
+ */
+ public static Logger getLogger(String name, String resourceBundleName)
+ {
+ LogManager lm = LogManager.getLogManager();
+ Logger result;
+
+ if (name == null)
+ throw new NullPointerException();
+
+ /*
+ * Without synchronized(lm), it could happen that another thread would
+ * create a logger between our calls to getLogger and addLogger. While
+ * addLogger would indicate this by returning false, we could not be sure
+ * that this other logger was still existing when we called getLogger a
+ * second time in order to retrieve it -- note that LogManager is only
+ * allowed to keep weak references to registered loggers, so Loggers can be
+ * garbage collected at any time in general, and between our call to
+ * addLogger and our second call go getLogger in particular. Of course, we
+ * assume here that LogManager.addLogger etc. are synchronizing on the
+ * global LogManager object. There is a comment in the implementation of
+ * LogManager.addLogger referring to this comment here, so that any change
+ * in the synchronization of LogManager will be reflected here.
+ */
+ synchronized (lock)
+ {
+ synchronized (lm)
+ {
+ result = lm.getLogger(name);
+ if (result == null)
+ {
+ boolean couldBeAdded;
+
+ result = new Logger(name, resourceBundleName);
+ couldBeAdded = lm.addLogger(result);
+ if (! couldBeAdded)
+ throw new IllegalStateException("cannot register new logger");
+ }
+ else
+ {
+ /*
+ * The logger already exists. Make sure it uses the same
+ * resource bundle for localizing messages.
+ */
+ String existingBundleName = result.getResourceBundleName();
+
+ /*
+ * The Sun J2SE 1.4 reference implementation will return the
+ * registered logger object, even if it does not have a resource
+ * bundle associated with it. However, it seems to change the
+ * resourceBundle of the registered logger to the bundle whose
+ * name was passed to getLogger.
+ */
+ if ((existingBundleName == null) &&
+ (resourceBundleName != null))
+ {
+ /*
+ * If ResourceBundle.getBundle throws an exception, the
+ * existing logger will be unchanged. This would be
+ * different if the assignment to resourceBundleName came
+ * first.
+ */
+ result.resourceBundle =
+ ResourceBundle.getBundle(resourceBundleName);
+
+ result.resourceBundleName = resourceBundleName;
+ return result;
+ }
+
+ if ((existingBundleName != resourceBundleName)
+ && ((existingBundleName == null)
+ || !existingBundleName.equals(resourceBundleName)))
+ {
+ throw new IllegalArgumentException();
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Creates a new, unnamed logger. Unnamed loggers are not registered in the
+ * namespace of the LogManager, and no special security permission is required
+ * for changing their state. Therefore, untrusted applets are able to modify
+ * their private logger instance obtained through this method.
+ * <p>
+ * The parent of the newly created logger will the the root logger, from which
+ * the level threshold and the handlers are inherited.
+ */
+ public static Logger getAnonymousLogger()
+ {
+ return getAnonymousLogger(null);
+ }
+
+ /**
+ * Creates a new, unnamed logger. Unnamed loggers are not registered in the
+ * namespace of the LogManager, and no special security permission is required
+ * for changing their state. Therefore, untrusted applets are able to modify
+ * their private logger instance obtained through this method.
+ * <p>
+ * The parent of the newly created logger will the the root logger, from which
+ * the level threshold and the handlers are inherited.
+ *
+ * @param resourceBundleName the name of a resource bundle for localizing
+ * messages, or <code>null</code> to indicate that messages do
+ * not need to be localized.
+ * @throws java.util.MissingResourceException if
+ * <code>resourceBundleName</code> is not <code>null</code>
+ * and no such bundle could be located.
+ */
+ public static Logger getAnonymousLogger(String resourceBundleName)
+ throws MissingResourceException
+ {
+ Logger result;
+
+ result = new Logger(null, resourceBundleName);
+ result.anonymous = true;
+ return result;
+ }
+
+ /**
+ * Returns the name of the resource bundle that is being used for localizing
+ * messages.
+ *
+ * @return the name of the resource bundle used for localizing messages, or
+ * <code>null</code> if the parent's resource bundle is used for
+ * this purpose.
+ */
+ public String getResourceBundleName()
+ {
+ synchronized (lock)
+ {
+ return resourceBundleName;
+ }
+ }
+
+ /**
+ * Returns the resource bundle that is being used for localizing messages.
+ *
+ * @return the resource bundle used for localizing messages, or
+ * <code>null</code> if the parent's resource bundle is used for
+ * this purpose.
+ */
+ public ResourceBundle getResourceBundle()
+ {
+ synchronized (lock)
+ {
+ return resourceBundle;
+ }
+ }
+
+ /**
+ * Returns the severity level threshold for this <code>Handler</code>. All
+ * log records with a lower severity level will be discarded; a log record of
+ * the same or a higher level will be published unless an installed
+ * <code>Filter</code> decides to discard it.
+ *
+ * @return the severity level below which all log messages will be discarded,
+ * or <code>null</code> if the logger inherits the threshold from
+ * its parent.
+ */
+ public Level getLevel()
+ {
+ synchronized (lock)
+ {
+ return level;
+ }
+ }
+
+ /**
+ * Returns whether or not a message of the specified level would be logged by
+ * this logger.
+ *
+ * @throws NullPointerException if <code>level</code> is <code>null</code>.
+ */
+ public boolean isLoggable(Level level)
+ {
+ synchronized (lock)
+ {
+ if (this.level != null)
+ return this.level.intValue() <= level.intValue();
+
+ if (parent != null)
+ return parent.isLoggable(level);
+ else
+ return false;
+ }
+ }
+
+ /**
+ * Sets the severity level threshold for this <code>Handler</code>. All log
+ * records with a lower severity level will be discarded immediately. A log
+ * record of the same or a higher level will be published unless an installed
+ * <code>Filter</code> decides to discard it.
+ *
+ * @param level the severity level below which all log messages will be
+ * discarded, or <code>null</code> to indicate that the logger
+ * should inherit the threshold from its parent.
+ * @throws SecurityException if this logger is not anonymous, a security
+ * manager exists, and the caller is not granted the permission to
+ * control the logging infrastructure by having
+ * LoggingPermission("control"). Untrusted code can obtain an
+ * anonymous logger through the static factory method
+ * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}.
+ */
+ public void setLevel(Level level)
+ {
+ synchronized (lock)
+ {
+ /*
+ * An application is allowed to control an anonymous logger without
+ * having the permission to control the logging infrastructure.
+ */
+ if (! anonymous)
+ LogManager.getLogManager().checkAccess();
+
+ this.level = level;
+ }
+ }
+
+ public Filter getFilter()
+ {
+ synchronized (lock)
+ {
+ return filter;
+ }
+ }
+
+ /**
+ * @throws SecurityException if this logger is not anonymous, a security
+ * manager exists, and the caller is not granted the permission to
+ * control the logging infrastructure by having
+ * LoggingPermission("control"). Untrusted code can obtain an
+ * anonymous logger through the static factory method
+ * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}.
+ */
+ public void setFilter(Filter filter) throws SecurityException
+ {
+ synchronized (lock)
+ {
+ /*
+ * An application is allowed to control an anonymous logger without
+ * having the permission to control the logging infrastructure.
+ */
+ if (! anonymous)
+ LogManager.getLogManager().checkAccess();
+
+ this.filter = filter;
+ }
+ }
+
+ /**
+ * Returns the name of this logger.
+ *
+ * @return the name of this logger, or <code>null</code> if the logger is
+ * anonymous.
+ */
+ public String getName()
+ {
+ /*
+ * Note that the name of a logger cannot be changed during its lifetime, so
+ * no synchronization is needed.
+ */
+ return name;
+ }
+
+ /**
+ * Passes a record to registered handlers, provided the record is considered
+ * as loggable both by {@link #isLoggable(Level)} and a possibly installed
+ * custom {@link #setFilter(Filter) filter}.
+ * <p>
+ * If the logger has been configured to use parent handlers, the record will
+ * be forwarded to the parent of this logger in addition to being processed by
+ * the handlers registered with this logger.
+ * <p>
+ * The other logging methods in this class are convenience methods that merely
+ * create a new LogRecord and pass it to this method. Therefore, subclasses
+ * usually just need to override this single method for customizing the
+ * logging behavior.
+ *
+ * @param record the log record to be inspected and possibly forwarded.
+ */
+ public void log(LogRecord record)
+ {
+ synchronized (lock)
+ {
+ if (!isLoggable(record.getLevel()))
+ return;
+
+ if ((filter != null) && ! filter.isLoggable(record))
+ return;
+
+ /*
+ * If no logger name has been set for the log record, use the name of
+ * this logger.
+ */
+ if (record.getLoggerName() == null)
+ record.setLoggerName(name);
+
+ /*
+ * Avoid that some other thread is changing the logger hierarchy while
+ * we are traversing it.
+ */
+ synchronized (LogManager.getLogManager())
+ {
+ Logger curLogger = this;
+
+ do
+ {
+ /*
+ * The Sun J2SE 1.4 reference implementation seems to call the
+ * filter only for the logger whose log method is called, never
+ * for any of its parents. Also, parent loggers publish log
+ * record whatever their level might be. This is pretty weird,
+ * but GNU Classpath tries to be as compatible as possible to
+ * the reference implementation.
+ */
+ for (int i = 0; i < curLogger.handlers.length; i++)
+ curLogger.handlers[i].publish(record);
+
+ if (curLogger.getUseParentHandlers() == false)
+ break;
+
+ curLogger = curLogger.getParent();
+ }
+ while (parent != null);
+ }
+ }
+ }
+
+ public void log(Level level, String message)
+ {
+ if (isLoggable(level))
+ log(level, message, (Object[]) null);
+ }
+
+ public void log(Level level, String message, Object param)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(level))
+ {
+ StackTraceElement caller = getCallerStackFrame();
+ logp(level, caller != null ? caller.getClassName() : "<unknown>",
+ caller != null ? caller.getMethodName() : "<unknown>",
+ message, param);
+ }
+ }
+ }
+
+ public void log(Level level, String message, Object[] params)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(level))
+ {
+ StackTraceElement caller = getCallerStackFrame();
+ logp(level, caller != null ? caller.getClassName() : "<unknown>",
+ caller != null ? caller.getMethodName() : "<unknown>",
+ message, params);
+
+ }
+ }
+ }
+
+ public void log(Level level, String message, Throwable thrown)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(level))
+ {
+ StackTraceElement caller = getCallerStackFrame();
+ logp(level, caller != null ? caller.getClassName() : "<unknown>",
+ caller != null ? caller.getMethodName() : "<unknown>",
+ message, thrown);
+ }
+ }
+ }
+
+ public void logp(Level level, String sourceClass, String sourceMethod,
+ String message)
+ {
+ synchronized (lock)
+ {
+ logp(level, sourceClass, sourceMethod, message, (Object[]) null);
+ }
+ }
+
+ public void logp(Level level, String sourceClass, String sourceMethod,
+ String message, Object param)
+ {
+ synchronized (lock)
+ {
+ logp(level, sourceClass, sourceMethod, message, new Object[] { param });
+ }
+
+ }
+
+ private ResourceBundle findResourceBundle()
+ {
+ synchronized (lock)
+ {
+ if (resourceBundle != null)
+ return resourceBundle;
+
+ if (parent != null)
+ return parent.findResourceBundle();
+
+ return null;
+ }
+ }
+
+ private void logImpl(Level level, String sourceClass, String sourceMethod,
+ String message, Object[] params)
+ {
+ synchronized (lock)
+ {
+ LogRecord rec = new LogRecord(level, message);
+
+ rec.setResourceBundle(findResourceBundle());
+ rec.setSourceClassName(sourceClass);
+ rec.setSourceMethodName(sourceMethod);
+ rec.setParameters(params);
+
+ log(rec);
+ }
+ }
+
+ public void logp(Level level, String sourceClass, String sourceMethod,
+ String message, Object[] params)
+ {
+ synchronized (lock)
+ {
+ logImpl(level, sourceClass, sourceMethod, message, params);
+ }
+ }
+
+ public void logp(Level level, String sourceClass, String sourceMethod,
+ String message, Throwable thrown)
+ {
+ synchronized (lock)
+ {
+ LogRecord rec = new LogRecord(level, message);
+
+ rec.setResourceBundle(resourceBundle);
+ rec.setSourceClassName(sourceClass);
+ rec.setSourceMethodName(sourceMethod);
+ rec.setThrown(thrown);
+
+ log(rec);
+ }
+ }
+
+ public void logrb(Level level, String sourceClass, String sourceMethod,
+ String bundleName, String message)
+ {
+ synchronized (lock)
+ {
+ logrb(level, sourceClass, sourceMethod, bundleName, message,
+ (Object[]) null);
+ }
+ }
+
+ public void logrb(Level level, String sourceClass, String sourceMethod,
+ String bundleName, String message, Object param)
+ {
+ synchronized (lock)
+ {
+ logrb(level, sourceClass, sourceMethod, bundleName, message,
+ new Object[] { param });
+ }
+ }
+
+ public void logrb(Level level, String sourceClass, String sourceMethod,
+ String bundleName, String message, Object[] params)
+ {
+ synchronized (lock)
+ {
+ LogRecord rec = new LogRecord(level, message);
+
+ rec.setResourceBundleName(bundleName);
+ rec.setSourceClassName(sourceClass);
+ rec.setSourceMethodName(sourceMethod);
+ rec.setParameters(params);
+
+ log(rec);
+ }
+ }
+
+ public void logrb(Level level, String sourceClass, String sourceMethod,
+ String bundleName, String message, Throwable thrown)
+ {
+ synchronized (lock)
+ {
+ LogRecord rec = new LogRecord(level, message);
+
+ rec.setResourceBundleName(bundleName);
+ rec.setSourceClassName(sourceClass);
+ rec.setSourceMethodName(sourceMethod);
+ rec.setThrown(thrown);
+
+ log(rec);
+ }
+ }
+
+ public void entering(String sourceClass, String sourceMethod)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.FINER))
+ logp(Level.FINER, sourceClass, sourceMethod, "ENTRY");
+ }
+ }
+
+ public void entering(String sourceClass, String sourceMethod, Object param)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.FINER))
+ logp(Level.FINER, sourceClass, sourceMethod, "ENTRY {0}", param);
+ }
+ }
+
+ public void entering(String sourceClass, String sourceMethod, Object[] params)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.FINER))
+ {
+ CPStringBuilder buf = new CPStringBuilder(80);
+ buf.append("ENTRY");
+ for (int i = 0; i < params.length; i++)
+ {
+ buf.append(" {");
+ buf.append(i);
+ buf.append('}');
+ }
+
+ logp(Level.FINER, sourceClass, sourceMethod, buf.toString(), params);
+ }
+ }
+ }
+
+ public void exiting(String sourceClass, String sourceMethod)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.FINER))
+ logp(Level.FINER, sourceClass, sourceMethod, "RETURN");
+ }
+ }
+
+ public void exiting(String sourceClass, String sourceMethod, Object result)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.FINER))
+ logp(Level.FINER, sourceClass, sourceMethod, "RETURN {0}", result);
+ }
+ }
+
+ public void throwing(String sourceClass, String sourceMethod, Throwable thrown)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.FINER))
+ logp(Level.FINER, sourceClass, sourceMethod, "THROW", thrown);
+ }
+ }
+
+ /**
+ * Logs a message with severity level SEVERE, indicating a serious failure
+ * that prevents normal program execution. Messages at this level should be
+ * understandable to an inexperienced, non-technical end user. Ideally, they
+ * explain in simple words what actions the user can take in order to resolve
+ * the problem.
+ *
+ * @see Level#SEVERE
+ * @param message the message text, also used as look-up key if the logger is
+ * localizing messages with a resource bundle. While it is possible
+ * to pass <code>null</code>, this is not recommended, since a
+ * logging message without text is unlikely to be helpful.
+ */
+ public void severe(String message)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.SEVERE))
+ log(Level.SEVERE, message);
+ }
+ }
+
+ /**
+ * Logs a message with severity level WARNING, indicating a potential problem
+ * that does not prevent normal program execution. Messages at this level
+ * should be understandable to an inexperienced, non-technical end user.
+ * Ideally, they explain in simple words what actions the user can take in
+ * order to resolve the problem.
+ *
+ * @see Level#WARNING
+ * @param message the message text, also used as look-up key if the logger is
+ * localizing messages with a resource bundle. While it is possible
+ * to pass <code>null</code>, this is not recommended, since a
+ * logging message without text is unlikely to be helpful.
+ */
+ public void warning(String message)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.WARNING))
+ log(Level.WARNING, message);
+ }
+ }
+
+ /**
+ * Logs a message with severity level INFO. {@link Level#INFO} is intended for
+ * purely informational messages that do not indicate error or warning
+ * situations. In the default logging configuration, INFO messages will be
+ * written to the system console. For this reason, the INFO level should be
+ * used only for messages that are important to end users and system
+ * administrators. Messages at this level should be understandable to an
+ * inexperienced, non-technical user.
+ *
+ * @param message the message text, also used as look-up key if the logger is
+ * localizing messages with a resource bundle. While it is possible
+ * to pass <code>null</code>, this is not recommended, since a
+ * logging message without text is unlikely to be helpful.
+ */
+ public void info(String message)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.INFO))
+ log(Level.INFO, message);
+ }
+ }
+
+ /**
+ * Logs a message with severity level CONFIG. {@link Level#CONFIG} is intended
+ * for static configuration messages, for example about the windowing
+ * environment, the operating system version, etc.
+ *
+ * @param message the message text, also used as look-up key if the logger is
+ * localizing messages with a resource bundle. While it is possible
+ * to pass <code>null</code>, this is not recommended, since a
+ * logging message without text is unlikely to be helpful.
+ */
+ public void config(String message)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.CONFIG))
+ log(Level.CONFIG, message);
+ }
+ }
+
+ /**
+ * Logs a message with severity level FINE. {@link Level#FINE} is intended for
+ * messages that are relevant for developers using the component generating
+ * log messages. Examples include minor, recoverable failures, or possible
+ * inefficiencies.
+ *
+ * @param message the message text, also used as look-up key if the logger is
+ * localizing messages with a resource bundle. While it is possible
+ * to pass <code>null</code>, this is not recommended, since a
+ * logging message without text is unlikely to be helpful.
+ */
+ public void fine(String message)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.FINE))
+ log(Level.FINE, message);
+ }
+ }
+
+ /**
+ * Logs a message with severity level FINER. {@link Level#FINER} is intended
+ * for rather detailed tracing, for example entering a method, returning from
+ * a method, or throwing an exception.
+ *
+ * @param message the message text, also used as look-up key if the logger is
+ * localizing messages with a resource bundle. While it is possible
+ * to pass <code>null</code>, this is not recommended, since a
+ * logging message without text is unlikely to be helpful.
+ */
+ public void finer(String message)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.FINER))
+ log(Level.FINER, message);
+ }
+ }
+
+ /**
+ * Logs a message with severity level FINEST. {@link Level#FINEST} is intended
+ * for highly detailed tracing, for example reaching a certain point inside
+ * the body of a method.
+ *
+ * @param message the message text, also used as look-up key if the logger is
+ * localizing messages with a resource bundle. While it is possible
+ * to pass <code>null</code>, this is not recommended, since a
+ * logging message without text is unlikely to be helpful.
+ */
+ public void finest(String message)
+ {
+ synchronized (lock)
+ {
+ if (isLoggable(Level.FINEST))
+ log(Level.FINEST, message);
+ }
+ }
+
+ /**
+ * Adds a handler to the set of handlers that get notified when a log record
+ * is to be published.
+ *
+ * @param handler the handler to be added.
+ * @throws NullPointerException if <code>handler</code> is <code>null</code>.
+ * @throws SecurityException if this logger is not anonymous, a security
+ * manager exists, and the caller is not granted the permission to
+ * control the logging infrastructure by having
+ * LoggingPermission("control"). Untrusted code can obtain an
+ * anonymous logger through the static factory method
+ * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}.
+ */
+ public void addHandler(Handler handler) throws SecurityException
+ {
+ synchronized (lock)
+ {
+ if (handler == null)
+ throw new NullPointerException();
+
+ /*
+ * An application is allowed to control an anonymous logger without
+ * having the permission to control the logging infrastructure.
+ */
+ if (! anonymous)
+ LogManager.getLogManager().checkAccess();
+
+ if (! handlerList.contains(handler))
+ {
+ handlerList.add(handler);
+ handlers = getHandlers();
+ }
+ }
+ }
+
+ /**
+ * Removes a handler from the set of handlers that get notified when a log
+ * record is to be published.
+ *
+ * @param handler the handler to be removed.
+ * @throws SecurityException if this logger is not anonymous, a security
+ * manager exists, and the caller is not granted the permission to
+ * control the logging infrastructure by having
+ * LoggingPermission("control"). Untrusted code can obtain an
+ * anonymous logger through the static factory method {@link
+ * #getAnonymousLogger(java.lang.String) getAnonymousLogger}.
+ * @throws NullPointerException if <code>handler</code> is <code>null</code>.
+ */
+ public void removeHandler(Handler handler) throws SecurityException
+ {
+ synchronized (lock)
+ {
+ /*
+ * An application is allowed to control an anonymous logger without
+ * having the permission to control the logging infrastructure.
+ */
+ if (! anonymous)
+ LogManager.getLogManager().checkAccess();
+
+ if (handler == null)
+ throw new NullPointerException();
+
+ handlerList.remove(handler);
+ handlers = getHandlers();
+ }
+ }
+
+ /**
+ * Returns the handlers currently registered for this Logger. When a log
+ * record has been deemed as being loggable, it will be passed to all
+ * registered handlers for publication. In addition, if the logger uses parent
+ * handlers (see {@link #getUseParentHandlers() getUseParentHandlers} and
+ * {@link #setUseParentHandlers(boolean) setUseParentHandlers}, the log
+ * record will be passed to the parent's handlers.
+ */
+ public Handler[] getHandlers()
+ {
+ synchronized (lock)
+ {
+ /*
+ * We cannot return our internal handlers array because we do not have
+ * any guarantee that the caller would not change the array entries.
+ */
+ return (Handler[]) handlerList.toArray(new Handler[handlerList.size()]);
+ }
+ }
+
+ /**
+ * Returns whether or not this Logger forwards log records to handlers
+ * registered for its parent loggers.
+ *
+ * @return <code>false</code> if this Logger sends log records merely to
+ * Handlers registered with itself; <code>true</code> if this Logger
+ * sends log records not only to Handlers registered with itself, but
+ * also to those Handlers registered with parent loggers.
+ */
+ public boolean getUseParentHandlers()
+ {
+ synchronized (lock)
+ {
+ return useParentHandlers;
+ }
+ }
+
+ /**
+ * Sets whether or not this Logger forwards log records to handlers registered
+ * for its parent loggers.
+ *
+ * @param useParentHandlers <code>false</code> to let this Logger send log
+ * records merely to Handlers registered with itself;
+ * <code>true</code> to let this Logger send log records not only
+ * to Handlers registered with itself, but also to those Handlers
+ * registered with parent loggers.
+ * @throws SecurityException if this logger is not anonymous, a security
+ * manager exists, and the caller is not granted the permission to
+ * control the logging infrastructure by having
+ * LoggingPermission("control"). Untrusted code can obtain an
+ * anonymous logger through the static factory method
+ * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}.
+ */
+ public void setUseParentHandlers(boolean useParentHandlers)
+ {
+ synchronized (lock)
+ {
+ /*
+ * An application is allowed to control an anonymous logger without
+ * having the permission to control the logging infrastructure.
+ */
+ if (! anonymous)
+ LogManager.getLogManager().checkAccess();
+
+ this.useParentHandlers = useParentHandlers;
+ }
+ }
+
+ /**
+ * Returns the parent of this logger. By default, the parent is assigned by
+ * the LogManager by inspecting the logger's name.
+ *
+ * @return the parent of this logger (as detemined by the LogManager by
+ * inspecting logger names), the root logger if no other logger has a
+ * name which is a prefix of this logger's name, or <code>null</code>
+ * for the root logger.
+ */
+ public Logger getParent()
+ {
+ synchronized (lock)
+ {
+ return parent;
+ }
+ }
+
+ /**
+ * Sets the parent of this logger. Usually, applications do not call this
+ * method directly. Instead, the LogManager will ensure that the tree of
+ * loggers reflects the hierarchical logger namespace. Basically, this method
+ * should not be public at all, but the GNU implementation follows the API
+ * specification.
+ *
+ * @throws NullPointerException if <code>parent</code> is <code>null</code>.
+ * @throws SecurityException if this logger is not anonymous, a security
+ * manager exists, and the caller is not granted the permission to
+ * control the logging infrastructure by having
+ * LoggingPermission("control"). Untrusted code can obtain an
+ * anonymous logger through the static factory method
+ * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}.
+ */
+ public void setParent(Logger parent)
+ {
+ synchronized (lock)
+ {
+ if (parent == null)
+ throw new NullPointerException();
+
+ if (this == root)
+ throw new IllegalArgumentException(
+ "the root logger can only have a null parent");
+
+ /*
+ * An application is allowed to control an anonymous logger without
+ * having the permission to control the logging infrastructure.
+ */
+ if (! anonymous)
+ LogManager.getLogManager().checkAccess();
+
+ this.parent = parent;
+ }
+ }
+
+ /**
+ * Gets the StackTraceElement of the first class that is not this class. That
+ * should be the initial caller of a logging method.
+ *
+ * @return caller of the initial logging method or null if unknown.
+ */
+ private StackTraceElement getCallerStackFrame()
+ {
+ Throwable t = new Throwable();
+ StackTraceElement[] stackTrace = t.getStackTrace();
+ int index = 0;
+
+ // skip to stackentries until this class
+ while (index < stackTrace.length
+ && ! stackTrace[index].getClassName().equals(getClass().getName()))
+ index++;
+
+ // skip the stackentries of this class
+ while (index < stackTrace.length
+ && stackTrace[index].getClassName().equals(getClass().getName()))
+ index++;
+
+ return index < stackTrace.length ? stackTrace[index] : null;
+ }
+
+ /**
+ * Reset and close handlers attached to this logger. This function is package
+ * private because it must only be available to the LogManager.
+ */
+ void resetLogger()
+ {
+ for (int i = 0; i < handlers.length; i++)
+ {
+ handlers[i].close();
+ handlerList.remove(handlers[i]);
+ }
+ handlers = getHandlers();
+ }
+}
diff --git a/libjava/classpath/java/util/logging/LoggingMXBean.java b/libjava/classpath/java/util/logging/LoggingMXBean.java
new file mode 100644
index 000000000..24d8834c7
--- /dev/null
+++ b/libjava/classpath/java/util/logging/LoggingMXBean.java
@@ -0,0 +1,85 @@
+/* LoggingMxBean.java -- Management interface for logging
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.logging;
+
+import java.util.List;
+
+/**
+ * This interface represents the management interface for logging.
+ * There is a single logging bean per VM instance, which can be
+ * retrieved via {@link LogManager#getLoggingMXBean()}.
+ *
+ * @since 1.5
+ */
+public interface LoggingMXBean
+{
+ /**
+ * Return the name of the logging level given the name of
+ * a logger. Returns null if no such logger exists.
+ * @param logger the logger's name
+ * @return the logging level's name, or null
+ */
+ String getLoggerLevel(String logger);
+
+ /**
+ * Return a list of all logger names.
+ */
+ List<String> getLoggerNames();
+
+ /**
+ * Return the name of the parent of the indicated logger.
+ * If no such logger exists, returns null. If the logger
+ * is the root logger, returns the empty string.
+ * @param logger the logger's name
+ * @return the name of the logger's parent, or null
+ */
+ String getParentLoggerName(String logger);
+
+ /**
+ * Sets the logging level for a particular logger.
+ *
+ * @param logger the name of the logger
+ * @param level the name of the new logging level, or null
+ * @throws IllegalArgumentException if the level is not
+ * recognized, or if the logger does not exist
+ * @throws SecurityException if access is denied;
+ * see {@link Logger#setLevel(Level)}
+ */
+ void setLoggerLevel(String logger, String level);
+}
diff --git a/libjava/classpath/java/util/logging/LoggingPermission.java b/libjava/classpath/java/util/logging/LoggingPermission.java
new file mode 100644
index 000000000..804fb9401
--- /dev/null
+++ b/libjava/classpath/java/util/logging/LoggingPermission.java
@@ -0,0 +1,75 @@
+/* LoggingPermission.java -- a class for logging permissions.
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.logging;
+
+public final class LoggingPermission
+ extends java.security.BasicPermission
+{
+ private static final long serialVersionUID = 63564341580231582L;
+
+ /**
+ * Creates a new LoggingPermission.
+ *
+ * @param name the name of the permission, which must be "control".
+ *
+ * @param actions the list of actions for the permission, which
+ * must be either <code>null</code> or an empty
+ * string.
+ *
+ * @exception IllegalArgumentException if <code>name</code>
+ * is not "control", or <code>actions</code> is
+ * neither <code>null</code> nor empty.
+ */
+ public LoggingPermission(String name, String actions)
+ {
+ super("control", "");
+
+ if (!"control".equals(name))
+ {
+ throw new IllegalArgumentException(
+ "name of LoggingPermission must be \"control\"");
+ }
+
+ if ((actions != null) && (actions.length() != 0))
+ {
+ throw new IllegalArgumentException(
+ "actions of LoggingPermissions must be null or empty");
+ }
+ }
+}
diff --git a/libjava/classpath/java/util/logging/MemoryHandler.java b/libjava/classpath/java/util/logging/MemoryHandler.java
new file mode 100644
index 000000000..e5c258bbf
--- /dev/null
+++ b/libjava/classpath/java/util/logging/MemoryHandler.java
@@ -0,0 +1,345 @@
+/* MemoryHandler.java -- a class for buffering log messages in a memory buffer
+ Copyright (C) 2002, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.logging;
+
+/**
+ * A <code>MemoryHandler</code> maintains a circular buffer of
+ * log records.
+ *
+ * <p><strong>Configuration:</strong> Values of the subsequent
+ * <code>LogManager</code> properties are taken into consideration
+ * when a <code>MemoryHandler</code> is initialized.
+ * If a property is not defined, or if it has an invalid
+ * value, a default is taken without an exception being thrown.
+ *
+ * <ul>
+ * <li><code>java.util.MemoryHandler.level</code> - specifies
+ * the initial severity level threshold. Default value:
+ * <code>Level.ALL</code>.</li>
+ * <li><code>java.util.MemoryHandler.filter</code> - specifies
+ * the name of a Filter class. Default value: No Filter.</li>
+ * <li><code>java.util.MemoryHandler.size</code> - specifies the
+ * maximum number of log records that are kept in the circular
+ * buffer. Default value: 1000.</li>
+ * <li><code>java.util.MemoryHandler.push</code> - specifies the
+ * <code>pushLevel</code>. Default value:
+ * <code>Level.SEVERE</code>.</li>
+ * <li><code>java.util.MemoryHandler.target</code> - specifies the
+ * name of a subclass of {@link Handler} that will be used as the
+ * target handler. There is no default value for this property;
+ * if it is not set, the no-argument MemoryHandler constructor
+ * will throw an exception.</li>
+ * </ul>
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class MemoryHandler
+ extends Handler
+{
+ /**
+ * The storage area used for buffering the unpushed log records in
+ * memory.
+ */
+ private final LogRecord[] buffer;
+
+
+ /**
+ * The current position in the circular buffer. For a new
+ * MemoryHandler, or immediately after {@link #push()} was called,
+ * the value of this variable is zero. Each call to {@link
+ * #publish(LogRecord)} will store the published LogRecord into
+ * <code>buffer[position]</code> before position is incremented by
+ * one. If position becomes greater than the size of the buffer, it
+ * is reset to zero.
+ */
+ private int position;
+
+
+ /**
+ * The number of log records which have been published, but not
+ * pushed yet to the target handler.
+ */
+ private int numPublished;
+
+
+ /**
+ * The push level threshold for this <code>Handler</code>. When a
+ * record is published whose severity level is greater than or equal
+ * to the <code>pushLevel</code> of this <code>MemoryHandler</code>,
+ * the {@link #push()} method will be invoked for pushing the buffer
+ * contents to the target <code>Handler</code>.
+ */
+ private Level pushLevel;
+
+
+ /**
+ * The Handler to which log records are forwarded for actual
+ * publication.
+ */
+ private final Handler target;
+
+
+ /**
+ * Constructs a <code>MemoryHandler</code> for keeping a circular
+ * buffer of LogRecords; the initial configuration is determined by
+ * the <code>LogManager</code> properties described above.
+ */
+ public MemoryHandler()
+ {
+ this((Handler) LogManager.getInstanceProperty(
+ "java.util.logging.MemoryHandler.target",
+ Handler.class, /* default */ null),
+ LogManager.getIntPropertyClamped(
+ "java.util.logging.MemoryHandler.size",
+ /* default */ 1000,
+ /* minimum value */ 1,
+ /* maximum value */ Integer.MAX_VALUE),
+ LogManager.getLevelProperty(
+ "java.util.logging.MemoryHandler.push",
+ /* default push level */ Level.SEVERE));
+ }
+
+
+ /**
+ * Constructs a <code>MemoryHandler</code> for keeping a circular
+ * buffer of LogRecords, given some parameters. The values of the
+ * other parameters are taken from LogManager properties, as
+ * described above.
+ *
+ * @param target the target handler that will receive those
+ * log records that are passed on for publication.
+ *
+ * @param size the number of log records that are kept in the buffer.
+ * The value must be a at least one.
+ *
+ * @param pushLevel the push level threshold for this
+ * <code>MemoryHandler</code>. When a record is published whose
+ * severity level is greater than or equal to
+ * <code>pushLevel</code>, the {@link #push()} method will be
+ * invoked in order to push the bufffer contents to
+ * <code>target</code>.
+ *
+ * @throws java.lang.IllegalArgumentException if <code>size</code>
+ * is negative or zero. The GNU implementation also throws
+ * an IllegalArgumentException if <code>target</code> or
+ * <code>pushLevel</code> are <code>null</code>, but the
+ * API specification does not prescribe what should happen
+ * in those cases.
+ */
+ public MemoryHandler(Handler target, int size, Level pushLevel)
+ {
+ if ((target == null) || (size <= 0) || (pushLevel == null))
+ throw new IllegalArgumentException();
+
+ buffer = new LogRecord[size];
+ this.pushLevel = pushLevel;
+ this.target = target;
+
+ setLevel(LogManager.getLevelProperty(
+ "java.util.logging.MemoryHandler.level",
+ /* default value */ Level.ALL));
+
+ setFilter((Filter) LogManager.getInstanceProperty(
+ "java.util.logging.MemoryHandler.filter",
+ /* must be instance of */ Filter.class,
+ /* default value */ null));
+ }
+
+
+ /**
+ * Stores a <code>LogRecord</code> in a fixed-size circular buffer,
+ * provided the record passes all tests for being loggable. If the
+ * buffer is full, the oldest record will be discarded.
+ *
+ * <p>If the record has a severity level which is greater than or
+ * equal to the <code>pushLevel</code> of this
+ * <code>MemoryHandler</code>, the {@link #push()} method will be
+ * invoked for pushing the buffer contents to the target
+ * <code>Handler</code>.
+ *
+ * <p>Most applications do not need to call this method directly.
+ * Instead, they will use use a {@link Logger}, which will create
+ * LogRecords and distribute them to registered handlers.
+ *
+ * @param record the log event to be published.
+ */
+ public void publish(LogRecord record)
+ {
+ if (!isLoggable(record))
+ return;
+
+ buffer[position] = record;
+ position = (position + 1) % buffer.length;
+ numPublished = numPublished + 1;
+
+ if (record.getLevel().intValue() >= pushLevel.intValue())
+ push();
+ }
+
+
+ /**
+ * Pushes the contents of the memory buffer to the target
+ * <code>Handler</code> and clears the buffer. Note that
+ * the target handler will discard those records that do
+ * not satisfy its own severity level threshold, or that are
+ * not considered loggable by an installed {@link Filter}.
+ *
+ * <p>In case of an I/O failure, the {@link ErrorManager} of the
+ * target <code>Handler</code> will be notified, but the caller of
+ * this method will not receive an exception.
+ */
+ public void push()
+ {
+ int i;
+
+ if (numPublished < buffer.length)
+ {
+ for (i = 0; i < position; i++)
+ target.publish(buffer[i]);
+ }
+ else
+ {
+ for (i = position; i < buffer.length; i++)
+ target.publish(buffer[i]);
+ for (i = 0; i < position; i++)
+ target.publish(buffer[i]);
+ }
+
+ numPublished = 0;
+ position = 0;
+ }
+
+
+ /**
+ * Forces any data that may have been buffered by the target
+ * <code>Handler</code> to the underlying output device, but
+ * does <em>not</em> push the contents of the circular memory
+ * buffer to the target handler.
+ *
+ * <p>In case of an I/O failure, the {@link ErrorManager} of the
+ * target <code>Handler</code> will be notified, but the caller of
+ * this method will not receive an exception.
+ *
+ * @see #push()
+ */
+ public void flush()
+ {
+ target.flush();
+ }
+
+
+ /**
+ * Closes this <code>MemoryHandler</code> and its associated target
+ * handler, discarding the contents of the memory buffer. However,
+ * any data that may have been buffered by the target
+ * <code>Handler</code> is forced to the underlying output device.
+ *
+ * <p>As soon as <code>close</code> has been called,
+ * a <code>Handler</code> should not be used anymore. Attempts
+ * to publish log records, to flush buffers, or to modify the
+ * <code>Handler</code> in any other way may throw runtime
+ * exceptions after calling <code>close</code>.</p>
+ *
+ * <p>In case of an I/O failure, the <code>ErrorManager</code> of
+ * the associated target <code>Handler</code> will be informed, but
+ * the caller of this method will not receive an exception.</p>
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ *
+ * @see #push()
+ */
+ public void close()
+ throws SecurityException
+ {
+ push();
+
+ /* This will check for LoggingPermission("control"). If the
+ * current security context does not grant this permission,
+ * push() has been executed, but this does not impose a
+ * security risk.
+ */
+ target.close();
+ }
+
+
+
+ /**
+ * Returns the push level threshold for this <code>Handler</code>.
+ * When a record is published whose severity level is greater
+ * than or equal to the <code>pushLevel</code> of this
+ * <code>MemoryHandler</code>, the {@link #push()} method will be
+ * invoked for pushing the buffer contents to the target
+ * <code>Handler</code>.
+ *
+ * @return the push level threshold for automatic pushing.
+ */
+ public Level getPushLevel()
+ {
+ return pushLevel;
+ }
+
+
+ /**
+ * Sets the push level threshold for this <code>Handler</code>.
+ * When a record is published whose severity level is greater
+ * than or equal to the <code>pushLevel</code> of this
+ * <code>MemoryHandler</code>, the {@link #push()} method will be
+ * invoked for pushing the buffer contents to the target
+ * <code>Handler</code>.
+ *
+ * @param pushLevel the push level threshold for automatic pushing.
+ *
+ * @exception SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ *
+ * @exception NullPointerException if <code>pushLevel</code> is
+ * <code>null</code>.
+ */
+ public void setPushLevel(Level pushLevel)
+ {
+ LogManager.getLogManager().checkAccess();
+
+ /* Throws a NullPointerException if pushLevel is null. */
+ pushLevel.getClass();
+
+ this.pushLevel = pushLevel;
+ }
+}
diff --git a/libjava/classpath/java/util/logging/SimpleFormatter.java b/libjava/classpath/java/util/logging/SimpleFormatter.java
new file mode 100644
index 000000000..da731f2b1
--- /dev/null
+++ b/libjava/classpath/java/util/logging/SimpleFormatter.java
@@ -0,0 +1,131 @@
+/* SimpleFormatter.java --
+ A class for formatting log records into short human-readable messages
+ Copyright (C) 2002, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.logging;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.DateFormat;
+import java.util.Date;
+
+/**
+ * A <code>SimpleFormatter</code> formats log records into
+ * short human-readable messages, typically one or two lines.
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class SimpleFormatter
+ extends Formatter
+{
+ /**
+ * Constructs a SimpleFormatter.
+ */
+ public SimpleFormatter()
+ {
+ }
+
+
+ /**
+ * An instance of a DateFormatter that is used for formatting
+ * the time of a log record into a human-readable string,
+ * according to the rules of the current locale. The value
+ * is set after the first invocation of format, since it is
+ * common that a JVM will instantiate a SimpleFormatter without
+ * ever using it.
+ */
+ private DateFormat dateFormat;
+
+ /**
+ * The character sequence that is used to separate lines in the
+ * generated stream. Somewhat surprisingly, the Sun J2SE 1.4
+ * reference implementation always uses UNIX line endings, even on
+ * platforms that have different line ending conventions (i.e.,
+ * DOS). The GNU implementation does not replicate this bug.
+ *
+ * @see Sun bug parade, bug #4462871,
+ * "java.util.logging.SimpleFormatter uses hard-coded line separator".
+ */
+ static final String lineSep = System.getProperty("line.separator");
+
+
+ /**
+ * Formats a log record into a String.
+ *
+ * @param record the log record to be formatted.
+ *
+ * @return a short human-readable message, typically one or two
+ * lines. Lines are separated using the default platform line
+ * separator.
+ *
+ * @throws NullPointerException if <code>record</code>
+ * is <code>null</code>.
+ */
+ public String format(LogRecord record)
+ {
+ CPStringBuilder buf = new CPStringBuilder(180);
+
+ if (dateFormat == null)
+ dateFormat = DateFormat.getDateTimeInstance();
+
+ buf.append(dateFormat.format(new Date(record.getMillis())));
+ buf.append(' ');
+ buf.append(record.getSourceClassName());
+ buf.append(' ');
+ buf.append(record.getSourceMethodName());
+ buf.append(lineSep);
+
+ buf.append(record.getLevel());
+ buf.append(": ");
+ buf.append(formatMessage(record));
+
+ buf.append(lineSep);
+
+ Throwable throwable = record.getThrown();
+ if (throwable != null)
+ {
+ StringWriter sink = new StringWriter();
+ throwable.printStackTrace(new PrintWriter(sink, true));
+ buf.append(sink.toString());
+ }
+
+ return buf.toString();
+ }
+}
diff --git a/libjava/classpath/java/util/logging/SocketHandler.java b/libjava/classpath/java/util/logging/SocketHandler.java
new file mode 100644
index 000000000..3c17b9bbc
--- /dev/null
+++ b/libjava/classpath/java/util/logging/SocketHandler.java
@@ -0,0 +1,220 @@
+/* SocketHandler.java -- a class for publishing log messages to network sockets
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.logging;
+
+
+/**
+ * A <code>SocketHandler</code> publishes log records to
+ * a TCP/IP socket.
+ *
+ * <p><strong>Configuration:</strong> Values of the subsequent
+ * <code>LogManager</code> properties are taken into consideration
+ * when a <code>SocketHandler</code> is initialized.
+ * If a property is not defined, or if it has an invalid
+ * value, a default is taken without an exception being thrown.
+ *
+ * <ul>
+ *
+ * <li><code>java.util.SocketHandler.level</code> - specifies
+ * the initial severity level threshold. Default value:
+ * <code>Level.ALL</code>.</li>
+ *
+ * <li><code>java.util.SocketHandler.filter</code> - specifies
+ * the name of a Filter class. Default value: No Filter.</li>
+ *
+ * <li><code>java.util.SocketHandler.formatter</code> - specifies
+ * the name of a Formatter class. Default value:
+ * <code>java.util.logging.XMLFormatter</code>.</li>
+ *
+ * <li><code>java.util.SocketHandler.encoding</code> - specifies
+ * the name of the character encoding. Default value:
+ * the default platform encoding.</li>
+ *
+ * <li><code>java.util.SocketHandler.host</code> - specifies
+ * the name of the host to which records are published.
+ * There is no default value for this property; if it is
+ * not set, the SocketHandler constructor will throw
+ * an exception.</li>
+ *
+ * <li><code>java.util.SocketHandler.port</code> - specifies
+ * the TCP/IP port to which records are published.
+ * There is no default value for this property; if it is
+ * not set, the SocketHandler constructor will throw
+ * an exception.</li>
+ *
+ * </ul>
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class SocketHandler
+ extends StreamHandler
+{
+ /**
+ * Constructs a <code>SocketHandler</code> that publishes log
+ * records to a TCP/IP socket. Tthe initial configuration is
+ * determined by the <code>LogManager</code> properties described
+ * above.
+ *
+ * @throws java.io.IOException if the connection to the specified
+ * network host and port cannot be established.
+ *
+ * @throws java.lang.IllegalArgumentException if either the
+ * <code>java.util.logging.SocketHandler.host</code>
+ * or <code>java.util.logging.SocketHandler.port</code>
+ * LogManager properties is not defined, or specifies
+ * an invalid value.
+ */
+ public SocketHandler()
+ throws java.io.IOException
+ {
+ this(LogManager.getLogManager().getProperty("java.util.logging.SocketHandler.host"),
+ getPortNumber());
+ }
+
+
+ /**
+ * Constructs a <code>SocketHandler</code> that publishes log
+ * records to a TCP/IP socket. With the exception of the internet
+ * host and port, the initial configuration is determined by the
+ * <code>LogManager</code> properties described above.
+ *
+ * @param host the Internet host to which log records will be
+ * forwarded.
+ *
+ * @param port the port at the host which will accept a request
+ * for a TCP/IP connection.
+ *
+ * @throws java.io.IOException if the connection to the specified
+ * network host and port cannot be established.
+ *
+ * @throws java.lang.IllegalArgumentException if either
+ * <code>host</code> or <code>port</code> specify
+ * an invalid value.
+ */
+ public SocketHandler(String host, int port)
+ throws java.io.IOException
+ {
+ super(createSocket(host, port),
+ "java.util.logging.SocketHandler",
+ /* default level */ Level.ALL,
+ /* formatter */ null,
+ /* default formatter */ XMLFormatter.class);
+ }
+
+
+ /**
+ * Retrieves the port number from the java.util.logging.SocketHandler.port
+ * LogManager property.
+ *
+ * @throws IllegalArgumentException if the property is not defined or
+ * does not specify an integer value.
+ */
+ private static int getPortNumber()
+ {
+ try {
+ return Integer.parseInt(LogManager.getLogManager().getProperty("java.util.logging.SocketHandler.port"));
+ } catch (Exception ex) {
+ throw new IllegalArgumentException();
+ }
+ }
+
+
+ /**
+ * Creates an OutputStream for publishing log records to an Internet
+ * host and port. This private method is a helper for use by the
+ * constructor of SocketHandler.
+ *
+ * @param host the Internet host to which log records will be
+ * forwarded.
+ *
+ * @param port the port at the host which will accept a request
+ * for a TCP/IP connection.
+ *
+ * @throws java.io.IOException if the connection to the specified
+ * network host and port cannot be established.
+ *
+ * @throws java.lang.IllegalArgumentException if either
+ * <code>host</code> or <code>port</code> specify
+ * an invalid value.
+ */
+ private static java.io.OutputStream createSocket(String host, int port)
+ throws java.io.IOException, java.lang.IllegalArgumentException
+ {
+ java.net.Socket socket;
+
+ if ((host == null) || (port < 1))
+ throw new IllegalArgumentException();
+
+ socket = new java.net.Socket(host, port);
+
+ socket.shutdownInput();
+
+ /* The architecture of the logging framework provides replaceable
+ * formatters. Because these formatters perform their task by
+ * returning one single String for each LogRecord to be formatted,
+ * there is no need to buffer.
+ */
+ socket.setTcpNoDelay(true);
+
+ return socket.getOutputStream();
+ }
+
+
+ /**
+ * Publishes a <code>LogRecord</code> to the network socket,
+ * provided the record passes all tests for being loggable.
+ * In addition, all data that may have been buffered will
+ * be forced to the network stream.
+ *
+ * <p>Most applications do not need to call this method directly.
+ * Instead, they will use a {@link Logger} instance, which will
+ * create LogRecords and distribute them to registered handlers.
+ *
+ * <p>In case of an I/O failure, the <code>ErrorManager</code>
+ * of this <code>SocketHandler</code> will be informed, but the caller
+ * of this method will not receive an exception.
+ *
+ * @param record the log event to be published.
+ */
+ public void publish(LogRecord record)
+ {
+ super.publish(record);
+ flush();
+ }
+}
diff --git a/libjava/classpath/java/util/logging/StreamHandler.java b/libjava/classpath/java/util/logging/StreamHandler.java
new file mode 100644
index 000000000..d74dfac0c
--- /dev/null
+++ b/libjava/classpath/java/util/logging/StreamHandler.java
@@ -0,0 +1,521 @@
+/* StreamHandler.java --
+ A class for publishing log messages to instances of java.io.OutputStream
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.logging;
+
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+
+/**
+ * A <code>StreamHandler</code> publishes <code>LogRecords</code> to
+ * a instances of <code>java.io.OutputStream</code>.
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class StreamHandler
+ extends Handler
+{
+ private OutputStream out;
+ private Writer writer;
+
+
+ /**
+ * Indicates the current state of this StreamHandler. The value
+ * should be one of STATE_FRESH, STATE_PUBLISHED, or STATE_CLOSED.
+ */
+ private int streamState = STATE_FRESH;
+
+
+ /**
+ * streamState having this value indicates that the StreamHandler
+ * has been created, but the publish(LogRecord) method has not been
+ * called yet. If the StreamHandler has been constructed without an
+ * OutputStream, writer will be null, otherwise it is set to a
+ * freshly created OutputStreamWriter.
+ */
+ private static final int STATE_FRESH = 0;
+
+
+ /**
+ * streamState having this value indicates that the publish(LocRecord)
+ * method has been called at least once.
+ */
+ private static final int STATE_PUBLISHED = 1;
+
+
+ /**
+ * streamState having this value indicates that the close() method
+ * has been called.
+ */
+ private static final int STATE_CLOSED = 2;
+
+
+ /**
+ * Creates a <code>StreamHandler</code> without an output stream.
+ * Subclasses can later use {@link
+ * #setOutputStream(java.io.OutputStream)} to associate an output
+ * stream with this StreamHandler.
+ */
+ public StreamHandler()
+ {
+ this(null, null);
+ }
+
+
+ /**
+ * Creates a <code>StreamHandler</code> that formats log messages
+ * with the specified Formatter and publishes them to the specified
+ * output stream.
+ *
+ * @param out the output stream to which the formatted log messages
+ * are published.
+ *
+ * @param formatter the <code>Formatter</code> that will be used
+ * to format log messages.
+ */
+ public StreamHandler(OutputStream out, Formatter formatter)
+ {
+ this(out, "java.util.logging.StreamHandler", Level.INFO,
+ formatter, SimpleFormatter.class);
+ }
+
+
+ StreamHandler(
+ OutputStream out,
+ String propertyPrefix,
+ Level defaultLevel,
+ Formatter formatter, Class defaultFormatterClass)
+ {
+ this.level = LogManager.getLevelProperty(propertyPrefix + ".level",
+ defaultLevel);
+
+ this.filter = (Filter) LogManager.getInstanceProperty(
+ propertyPrefix + ".filter",
+ /* must be instance of */ Filter.class,
+ /* default: new instance of */ null);
+
+ if (formatter != null)
+ this.formatter = formatter;
+ else
+ this.formatter = (Formatter) LogManager.getInstanceProperty(
+ propertyPrefix + ".formatter",
+ /* must be instance of */ Formatter.class,
+ /* default: new instance of */ defaultFormatterClass);
+
+ try
+ {
+ String enc = LogManager.getLogManager().getProperty(propertyPrefix
+ + ".encoding");
+
+ /* make sure enc actually is a valid encoding */
+ if ((enc != null) && (enc.length() > 0))
+ new String(new byte[0], enc);
+
+ this.encoding = enc;
+ }
+ catch (Exception _)
+ {
+ }
+
+ if (out != null)
+ {
+ try
+ {
+ changeWriter(out, getEncoding());
+ }
+ catch (UnsupportedEncodingException uex)
+ {
+ /* This should never happen, since the validity of the encoding
+ * name has been checked above.
+ */
+ throw new RuntimeException(uex.getMessage());
+ }
+ }
+ }
+
+
+ private void checkOpen()
+ {
+ if (streamState == STATE_CLOSED)
+ throw new IllegalStateException(this.toString() + " has been closed");
+ }
+
+ private void checkFresh()
+ {
+ checkOpen();
+ if (streamState != STATE_FRESH)
+ throw new IllegalStateException("some log records have been published to " + this);
+ }
+
+
+ private void changeWriter(OutputStream out, String encoding)
+ throws UnsupportedEncodingException
+ {
+ OutputStreamWriter writer;
+
+ /* The logging API says that a null encoding means the default
+ * platform encoding. However, java.io.OutputStreamWriter needs
+ * another constructor for the default platform encoding,
+ * passing null would throw an exception.
+ */
+ if (encoding == null)
+ writer = new OutputStreamWriter(out);
+ else
+ writer = new OutputStreamWriter(out, encoding);
+
+ /* Closing the stream has side effects -- do this only after
+ * creating a new writer has been successful.
+ */
+ if ((streamState != STATE_FRESH) || (this.writer != null))
+ close();
+
+ this.writer = writer;
+ this.out = out;
+ this.encoding = encoding;
+ streamState = STATE_FRESH;
+ }
+
+
+ /**
+ * Sets the character encoding which this handler uses for publishing
+ * log records. The encoding of a <code>StreamHandler</code> must be
+ * set before any log records have been published.
+ *
+ * @param encoding the name of a character encoding, or <code>null</code>
+ * for the default encoding.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control the
+ * the logging infrastructure.
+ *
+ * @exception IllegalStateException if any log records have been
+ * published to this <code>StreamHandler</code> before. Please
+ * be aware that this is a pecularity of the GNU implementation.
+ * While the API specification indicates that it is an error
+ * if the encoding is set after records have been published,
+ * it does not mandate any specific behavior for that case.
+ */
+ public void setEncoding(String encoding)
+ throws SecurityException, UnsupportedEncodingException
+ {
+ /* The inherited implementation first checks whether the invoking
+ * code indeed has the permission to control the logging infra-
+ * structure, and throws a SecurityException if this was not the
+ * case.
+ *
+ * Next, it verifies that the encoding is supported and throws
+ * an UnsupportedEncodingExcpetion otherwise. Finally, it remembers
+ * the name of the encoding.
+ */
+ super.setEncoding(encoding);
+
+ checkFresh();
+
+ /* If out is null, setEncoding is being called before an output
+ * stream has been set. In that case, we need to check that the
+ * encoding is valid, and remember it if this is the case. Since
+ * this is exactly what the inherited implementation of
+ * Handler.setEncoding does, we can delegate.
+ */
+ if (out != null)
+ {
+ /* The logging API says that a null encoding means the default
+ * platform encoding. However, java.io.OutputStreamWriter needs
+ * another constructor for the default platform encoding, passing
+ * null would throw an exception.
+ */
+ if (encoding == null)
+ writer = new OutputStreamWriter(out);
+ else
+ writer = new OutputStreamWriter(out, encoding);
+ }
+ }
+
+
+ /**
+ * Changes the output stream to which this handler publishes
+ * logging records.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ *
+ * @throws NullPointerException if <code>out</code>
+ * is <code>null</code>.
+ */
+ protected void setOutputStream(OutputStream out)
+ throws SecurityException
+ {
+ LogManager.getLogManager().checkAccess();
+
+ /* Throw a NullPointerException if out is null. */
+ out.getClass();
+
+ try
+ {
+ changeWriter(out, getEncoding());
+ }
+ catch (UnsupportedEncodingException ex)
+ {
+ /* This seems quite unlikely to happen, unless the underlying
+ * implementation of java.io.OutputStreamWriter changes its
+ * mind (at runtime) about the set of supported character
+ * encodings.
+ */
+ throw new RuntimeException(ex.getMessage());
+ }
+ }
+
+
+ /**
+ * Publishes a <code>LogRecord</code> to the associated output
+ * stream, provided the record passes all tests for being loggable.
+ * The <code>StreamHandler</code> will localize the message of the
+ * log record and substitute any message parameters.
+ *
+ * <p>Most applications do not need to call this method directly.
+ * Instead, they will use use a {@link Logger}, which will create
+ * LogRecords and distribute them to registered handlers.
+ *
+ * <p>In case of an I/O failure, the <code>ErrorManager</code>
+ * of this <code>Handler</code> will be informed, but the caller
+ * of this method will not receive an exception.
+ *
+ * <p>If a log record is being published to a
+ * <code>StreamHandler</code> that has been closed earlier, the Sun
+ * J2SE 1.4 reference can be observed to silently ignore the
+ * call. The GNU implementation, however, intentionally behaves
+ * differently by informing the <code>ErrorManager</code> associated
+ * with this <code>StreamHandler</code>. Since the condition
+ * indicates a programming error, the programmer should be
+ * informed. It also seems extremely unlikely that any application
+ * would depend on the exact behavior in this rather obscure,
+ * erroneous case -- especially since the API specification does not
+ * prescribe what is supposed to happen.
+ *
+ * @param record the log event to be published.
+ */
+ public void publish(LogRecord record)
+ {
+ String formattedMessage;
+
+ if (!isLoggable(record))
+ return;
+
+ if (streamState == STATE_FRESH)
+ {
+ try
+ {
+ writer.write(formatter.getHead(this));
+ }
+ catch (java.io.IOException ex)
+ {
+ reportError(null, ex, ErrorManager.WRITE_FAILURE);
+ return;
+ }
+ catch (Exception ex)
+ {
+ reportError(null, ex, ErrorManager.GENERIC_FAILURE);
+ return;
+ }
+
+ streamState = STATE_PUBLISHED;
+ }
+
+ try
+ {
+ formattedMessage = formatter.format(record);
+ }
+ catch (Exception ex)
+ {
+ reportError(null, ex, ErrorManager.FORMAT_FAILURE);
+ return;
+ }
+
+ try
+ {
+ writer.write(formattedMessage);
+ }
+ catch (Exception ex)
+ {
+ reportError(null, ex, ErrorManager.WRITE_FAILURE);
+ }
+ }
+
+
+ /**
+ * Checks whether or not a <code>LogRecord</code> would be logged
+ * if it was passed to this <code>StreamHandler</code> for publication.
+ *
+ * <p>The <code>StreamHandler</code> implementation first checks
+ * whether a writer is present and the handler's level is greater
+ * than or equal to the severity level threshold. In a second step,
+ * if a {@link Filter} has been installed, its {@link
+ * Filter#isLoggable(LogRecord) isLoggable} method is
+ * invoked. Subclasses of <code>StreamHandler</code> can override
+ * this method to impose their own constraints.
+ *
+ * @param record the <code>LogRecord</code> to be checked.
+ *
+ * @return <code>true</code> if <code>record</code> would
+ * be published by {@link #publish(LogRecord) publish},
+ * <code>false</code> if it would be discarded.
+ *
+ * @see #setLevel(Level)
+ * @see #setFilter(Filter)
+ * @see Filter#isLoggable(LogRecord)
+ *
+ * @throws NullPointerException if <code>record</code> is
+ * <code>null</code>. */
+ public boolean isLoggable(LogRecord record)
+ {
+ return (writer != null) && super.isLoggable(record);
+ }
+
+
+ /**
+ * Forces any data that may have been buffered to the underlying
+ * output device.
+ *
+ * <p>In case of an I/O failure, the <code>ErrorManager</code>
+ * of this <code>Handler</code> will be informed, but the caller
+ * of this method will not receive an exception.
+ *
+ * <p>If a <code>StreamHandler</code> that has been closed earlier
+ * is closed a second time, the Sun J2SE 1.4 reference can be
+ * observed to silently ignore the call. The GNU implementation,
+ * however, intentionally behaves differently by informing the
+ * <code>ErrorManager</code> associated with this
+ * <code>StreamHandler</code>. Since the condition indicates a
+ * programming error, the programmer should be informed. It also
+ * seems extremely unlikely that any application would depend on the
+ * exact behavior in this rather obscure, erroneous case --
+ * especially since the API specification does not prescribe what is
+ * supposed to happen.
+ */
+ public void flush()
+ {
+ try
+ {
+ checkOpen();
+ if (writer != null)
+ writer.flush();
+ }
+ catch (Exception ex)
+ {
+ reportError(null, ex, ErrorManager.FLUSH_FAILURE);
+ }
+ }
+
+
+ /**
+ * Closes this <code>StreamHandler</code> after having forced any
+ * data that may have been buffered to the underlying output
+ * device.
+ *
+ * <p>As soon as <code>close</code> has been called,
+ * a <code>Handler</code> should not be used anymore. Attempts
+ * to publish log records, to flush buffers, or to modify the
+ * <code>Handler</code> in any other way may throw runtime
+ * exceptions after calling <code>close</code>.</p>
+ *
+ * <p>In case of an I/O failure, the <code>ErrorManager</code>
+ * of this <code>Handler</code> will be informed, but the caller
+ * of this method will not receive an exception.</p>
+ *
+ * <p>If a <code>StreamHandler</code> that has been closed earlier
+ * is closed a second time, the Sun J2SE 1.4 reference can be
+ * observed to silently ignore the call. The GNU implementation,
+ * however, intentionally behaves differently by informing the
+ * <code>ErrorManager</code> associated with this
+ * <code>StreamHandler</code>. Since the condition indicates a
+ * programming error, the programmer should be informed. It also
+ * seems extremely unlikely that any application would depend on the
+ * exact behavior in this rather obscure, erroneous case --
+ * especially since the API specification does not prescribe what is
+ * supposed to happen.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ */
+ public void close()
+ throws SecurityException
+ {
+ LogManager.getLogManager().checkAccess();
+
+ try
+ {
+ /* Although flush also calls checkOpen, it catches
+ * any exceptions and reports them to the ErrorManager
+ * as flush failures. However, we want to report
+ * a closed stream as a close failure, not as a
+ * flush failure here. Therefore, we call checkOpen()
+ * before flush().
+ */
+ checkOpen();
+ flush();
+
+ if (writer != null)
+ {
+ if (formatter != null)
+ {
+ /* Even if the StreamHandler has never published a record,
+ * it emits head and tail upon closing. An earlier version
+ * of the GNU Classpath implementation did not emitted
+ * anything. However, this had caused XML log files to be
+ * entirely empty instead of containing no log records.
+ */
+ if (streamState == STATE_FRESH)
+ writer.write(formatter.getHead(this));
+ if (streamState != STATE_CLOSED)
+ writer.write(formatter.getTail(this));
+ }
+ streamState = STATE_CLOSED;
+ writer.close();
+ }
+ }
+ catch (Exception ex)
+ {
+ reportError(null, ex, ErrorManager.CLOSE_FAILURE);
+ }
+ }
+}
diff --git a/libjava/classpath/java/util/logging/XMLFormatter.java b/libjava/classpath/java/util/logging/XMLFormatter.java
new file mode 100644
index 000000000..da7c6ef29
--- /dev/null
+++ b/libjava/classpath/java/util/logging/XMLFormatter.java
@@ -0,0 +1,389 @@
+/* XMLFormatter.java --
+ A class for formatting log messages into a standard XML format
+ Copyright (C) 2002, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.logging;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.ResourceBundle;
+
+/**
+ * An <code>XMLFormatter</code> formats LogRecords into
+ * a standard XML format.
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class XMLFormatter
+ extends Formatter
+{
+ /**
+ * Constructs a new XMLFormatter.
+ */
+ public XMLFormatter()
+ {
+ }
+
+
+ /**
+ * The character sequence that is used to separate lines in the
+ * generated XML stream. Somewhat surprisingly, the Sun J2SE 1.4
+ * reference implementation always uses UNIX line endings, even on
+ * platforms that have different line ending conventions (i.e.,
+ * DOS). The GNU Classpath implementation does not replicates this
+ * bug.
+ *
+ * See also the Sun bug parade, bug #4462871,
+ * "java.util.logging.SimpleFormatter uses hard-coded line separator".
+ */
+ private static final String lineSep = SimpleFormatter.lineSep;
+
+
+ /**
+ * A DateFormat for emitting time in the ISO 8601 format.
+ * Since the API specification of SimpleDateFormat does not talk
+ * about its thread-safety, we cannot share a singleton instance.
+ */
+ private final SimpleDateFormat iso8601
+ = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
+
+
+ /**
+ * Appends a line consisting of indentation, opening element tag,
+ * element content, closing element tag and line separator to
+ * a CPStringBuilder, provided that the element content is
+ * actually existing.
+ *
+ * @param buf the CPStringBuilder to which the line will be appended.
+ *
+ * @param indent the indentation level.
+ *
+ * @param tag the element tag name, for instance <code>method</code>.
+ *
+ * @param content the element content, or <code>null</code> to
+ * have no output whatsoever appended to <code>buf</code>.
+ */
+ private static void appendTag(CPStringBuilder buf, int indent,
+ String tag, String content)
+ {
+ int i;
+
+ if (content == null)
+ return;
+
+ for (i = 0; i < indent * 2; i++)
+ buf.append(' ');
+
+ buf.append("<");
+ buf.append(tag);
+ buf.append('>');
+
+ /* Append the content, but escape for XML by replacing
+ * '&', '<', '>' and all non-ASCII characters with
+ * appropriate escape sequences.
+ * The Sun J2SE 1.4 reference implementation does not
+ * escape non-ASCII characters. This is a bug in their
+ * implementation which has been reported in the Java
+ * bug parade as bug number (FIXME: Insert number here).
+ */
+ for (i = 0; i < content.length(); i++)
+ {
+ char c = content.charAt(i);
+ switch (c)
+ {
+ case '&':
+ buf.append("&amp;");
+ break;
+
+ case '<':
+ buf.append("&lt;");
+ break;
+
+ case '>':
+ buf.append("&gt;");
+ break;
+
+ default:
+ if (((c >= 0x20) && (c <= 0x7e))
+ || (c == /* line feed */ 10)
+ || (c == /* carriage return */ 13))
+ buf.append(c);
+ else
+ {
+ buf.append("&#");
+ buf.append((int) c);
+ buf.append(';');
+ }
+ break;
+ } /* switch (c) */
+ } /* for i */
+
+ buf.append("</");
+ buf.append(tag);
+ buf.append(">");
+ buf.append(lineSep);
+ }
+
+
+ /**
+ * Appends a line consisting of indentation, opening element tag,
+ * numeric element content, closing element tag and line separator
+ * to a CPStringBuilder.
+ *
+ * @param buf the CPStringBuilder to which the line will be appended.
+ *
+ * @param indent the indentation level.
+ *
+ * @param tag the element tag name, for instance <code>method</code>.
+ *
+ * @param content the element content.
+ */
+ private static void appendTag(CPStringBuilder buf, int indent,
+ String tag, long content)
+ {
+ appendTag(buf, indent, tag, Long.toString(content));
+ }
+
+
+ public String format(LogRecord record)
+ {
+ CPStringBuilder buf = new CPStringBuilder(400);
+ Level level = record.getLevel();
+ long millis = record.getMillis();
+ Object[] params = record.getParameters();
+ ResourceBundle bundle = record.getResourceBundle();
+ String message;
+
+ buf.append("<record>");
+ buf.append(lineSep);
+
+
+ appendTag(buf, 1, "date", iso8601.format(new Date(millis)));
+ appendTag(buf, 1, "millis", millis);
+ appendTag(buf, 1, "sequence", record.getSequenceNumber());
+ appendTag(buf, 1, "logger", record.getLoggerName());
+
+ if (level.isStandardLevel())
+ appendTag(buf, 1, "level", level.toString());
+ else
+ appendTag(buf, 1, "level", level.intValue());
+
+ appendTag(buf, 1, "class", record.getSourceClassName());
+ appendTag(buf, 1, "method", record.getSourceMethodName());
+ appendTag(buf, 1, "thread", record.getThreadID());
+
+ /* The Sun J2SE 1.4 reference implementation does not emit the
+ * message in localized form. This is in violation of the API
+ * specification. The GNU Classpath implementation intentionally
+ * replicates the buggy behavior of the Sun implementation, as
+ * different log files might be a big nuisance to users.
+ */
+ try
+ {
+ record.setResourceBundle(null);
+ message = formatMessage(record);
+ }
+ finally
+ {
+ record.setResourceBundle(bundle);
+ }
+ appendTag(buf, 1, "message", message);
+
+ /* The Sun J2SE 1.4 reference implementation does not
+ * emit key, catalog and param tags. This is in violation
+ * of the API specification. The Classpath implementation
+ * intentionally replicates the buggy behavior of the
+ * Sun implementation, as different log files might be
+ * a big nuisance to users.
+ *
+ * FIXME: File a bug report with Sun. Insert bug number here.
+ *
+ *
+ * key = record.getMessage();
+ * if (key == null)
+ * key = "";
+ *
+ * if ((bundle != null) && !key.equals(message))
+ * {
+ * appendTag(buf, 1, "key", key);
+ * appendTag(buf, 1, "catalog", record.getResourceBundleName());
+ * }
+ *
+ * if (params != null)
+ * {
+ * for (int i = 0; i < params.length; i++)
+ * appendTag(buf, 1, "param", params[i].toString());
+ * }
+ */
+
+ /* FIXME: We have no way to obtain the stacktrace before free JVMs
+ * support the corresponding method in java.lang.Throwable. Well,
+ * it would be possible to parse the output of printStackTrace,
+ * but this would be pretty kludgy. Instead, we postpose the
+ * implementation until Throwable has made progress.
+ */
+ Throwable thrown = record.getThrown();
+ if (thrown != null)
+ {
+ buf.append(" <exception>");
+ buf.append(lineSep);
+
+ /* The API specification is not clear about what exactly
+ * goes into the XML record for a thrown exception: It
+ * could be the result of getMessage(), getLocalizedMessage(),
+ * or toString(). Therefore, it was necessary to write a
+ * Mauve testlet and run it with the Sun J2SE 1.4 reference
+ * implementation. It turned out that the we need to call
+ * toString().
+ *
+ * FIXME: File a bug report with Sun, asking for clearer
+ * specs.
+ */
+ appendTag(buf, 2, "message", thrown.toString());
+
+ /* FIXME: The Logging DTD specifies:
+ *
+ * <!ELEMENT exception (message?, frame+)>
+ *
+ * However, java.lang.Throwable.getStackTrace() is
+ * allowed to return an empty array. So, what frame should
+ * be emitted for an empty stack trace? We probably
+ * should file a bug report with Sun, asking for the DTD
+ * to be changed.
+ */
+
+ buf.append(" </exception>");
+ buf.append(lineSep);
+ }
+
+
+ buf.append("</record>");
+ buf.append(lineSep);
+
+ return buf.toString();
+ }
+
+
+ /**
+ * Returns a string that handlers are supposed to emit before
+ * the first log record. The base implementation returns an
+ * empty string, but subclasses such as {@link XMLFormatter}
+ * override this method in order to provide a suitable header.
+ *
+ * @return a string for the header.
+ *
+ * @param h the handler which will prepend the returned
+ * string in front of the first log record. This method
+ * will inspect certain properties of the handler, for
+ * example its encoding, in order to construct the header.
+ */
+ public String getHead(Handler h)
+ {
+ CPStringBuilder buf;
+ String encoding;
+
+ buf = new CPStringBuilder(80);
+ buf.append("<?xml version=\"1.0\" encoding=\"");
+
+ encoding = h.getEncoding();
+
+ /* file.encoding is a system property with the Sun JVM, indicating
+ * the platform-default file encoding. Unfortunately, the API
+ * specification for java.lang.System.getProperties() does not
+ * list this property.
+ */
+ if (encoding == null)
+ encoding = System.getProperty("file.encoding");
+
+ /* Since file.encoding is not listed with the API specification of
+ * java.lang.System.getProperties(), there might be some VMs that
+ * do not define this system property. Therefore, we use UTF-8 as
+ * a reasonable default. Please note that if the platform encoding
+ * uses the same codepoints as US-ASCII for the US-ASCII character
+ * set (e.g, 65 for A), it does not matter whether we emit the
+ * wrong encoding into the XML header -- the GNU Classpath will
+ * emit XML escape sequences like &#1234; for any non-ASCII
+ * character. Virtually all character encodings use the same code
+ * points as US-ASCII for ASCII characters. Probably, EBCDIC is
+ * the only exception.
+ */
+ if (encoding == null)
+ encoding = "UTF-8";
+
+ /* On Windows XP localized for Swiss German (this is one of
+ * my [Sascha Brawer's] test machines), the default encoding
+ * has the canonical name "windows-1252". The "historical" name
+ * of this encoding is "Cp1252" (see the Javadoc for the class
+ * java.nio.charset.Charset for the distinction). Now, that class
+ * does have a method for mapping historical to canonical encoding
+ * names. However, if we used it here, we would be come dependent
+ * on java.nio.*, which was only introduced with J2SE 1.4.
+ * Thus, we do this little hack here. As soon as Classpath supports
+ * java.nio.charset.CharSet, this hack should be replaced by
+ * code that correctly canonicalizes the encoding name.
+ */
+ if ((encoding.length() > 2) && encoding.startsWith("Cp"))
+ encoding = "windows-" + encoding.substring(2);
+
+ buf.append(encoding);
+
+ buf.append("\" standalone=\"no\"?>");
+ buf.append(lineSep);
+
+ /* SYSTEM is not a fully qualified URL so that validating
+ * XML parsers do not need to connect to the Internet in
+ * order to read in a log file. See also the Sun Bug Parade,
+ * bug #4372790, "Logging APIs: need to use relative URL for XML
+ * doctype".
+ */
+ buf.append("<!DOCTYPE log SYSTEM \"logger.dtd\">");
+ buf.append(lineSep);
+ buf.append("<log>");
+ buf.append(lineSep);
+
+ return buf.toString();
+ }
+
+
+ public String getTail(Handler h)
+ {
+ return "</log>" + lineSep;
+ }
+}
diff --git a/libjava/classpath/java/util/logging/package.html b/libjava/classpath/java/util/logging/package.html
new file mode 100644
index 000000000..31f0494fc
--- /dev/null
+++ b/libjava/classpath/java/util/logging/package.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in java.util.logging package.
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - java.util.logging</title></head>
+
+<body>
+<p>Utility classes for logging events.</p>
+
+</body>
+</html>
diff --git a/libjava/classpath/java/util/package.html b/libjava/classpath/java/util/package.html
new file mode 100644
index 000000000..ff2919c55
--- /dev/null
+++ b/libjava/classpath/java/util/package.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in java.util package.
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - java.util</title></head>
+
+<body>
+<p>Utility classes such as collections (maps, sets, lists, dictionaries and
+stacks), calendars, dates, locales, properties, timers, resource bundles and
+event objects.</p>
+
+</body>
+</html>
diff --git a/libjava/classpath/java/util/prefs/AbstractPreferences.java b/libjava/classpath/java/util/prefs/AbstractPreferences.java
new file mode 100644
index 000000000..14fed384a
--- /dev/null
+++ b/libjava/classpath/java/util/prefs/AbstractPreferences.java
@@ -0,0 +1,1392 @@
+/* AbstractPreferences -- Partial implementation of a Preference node
+ Copyright (C) 2001, 2003, 2004, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.prefs;
+
+import gnu.classpath.toolkit.DefaultDaemonThreadFactory;
+import gnu.java.lang.CPStringBuilder;
+import gnu.java.util.prefs.NodeWriter;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.TreeSet;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+/**
+ * Partial implementation of a Preference node.
+ *
+ * @since 1.4
+ * @author Mark Wielaard (mark@klomp.org)
+ */
+public abstract class AbstractPreferences extends Preferences {
+
+ // protected fields
+
+ /**
+ * Object used to lock this preference node. Any thread only locks nodes
+ * downwards when it has the lock on the current node. No method should
+ * synchronize on the lock of any of its parent nodes while holding the
+ * lock on the current node.
+ */
+ protected final Object lock = new Object();
+
+ /**
+ * Set to true in the contructor if the node did not exist in the backing
+ * store when this preference node object was created. Should be set in
+ * the constructor of a subclass. Defaults to false. Used to fire node
+ * changed events.
+ */
+ protected boolean newNode = false;
+
+ // private fields
+
+ /**
+ * The parent preferences node or null when this is the root node.
+ */
+ private final AbstractPreferences parent;
+
+ /**
+ * The name of this node.
+ * Only when this is a root node (parent == null) the name is empty.
+ * It has a maximum of 80 characters and cannot contain any '/' characters.
+ */
+ private final String name;
+
+ /** True when this node has been remove, false otherwise. */
+ private boolean removed = false;
+
+ /**
+ * Holds all the child names and nodes of this node that have been
+ * accessed by earlier <code>getChild()</code> or <code>childSpi()</code>
+ * invocations and that have not been removed.
+ */
+ private HashMap<String, AbstractPreferences> childCache
+ = new HashMap<String, AbstractPreferences>();
+
+ /**
+ * A list of all the registered NodeChangeListener objects.
+ */
+ private ArrayList<NodeChangeListener> nodeListeners;
+
+ /**
+ * A list of all the registered PreferenceChangeListener objects.
+ */
+ private ArrayList<PreferenceChangeListener> preferenceListeners;
+
+ // constructor
+
+ /**
+ * Creates a new AbstractPreferences node with the given parent and name.
+ *
+ * @param parent the parent of this node or null when this is the root node
+ * @param name the name of this node, can not be null, only 80 characters
+ * maximum, must be empty when parent is null and cannot
+ * contain any '/' characters
+ * @exception IllegalArgumentException when name is null, greater then 80
+ * characters, not the empty string but parent is null or
+ * contains a '/' character
+ */
+ protected AbstractPreferences(AbstractPreferences parent, String name) {
+ if ( (name == null) // name should be given
+ || (name.length() > MAX_NAME_LENGTH) // 80 characters max
+ || (parent == null && name.length() != 0) // root has no name
+ || (parent != null && name.length() == 0) // all other nodes do
+ || (name.indexOf('/') != -1)) // must not contain '/'
+ throw new IllegalArgumentException("Illegal name argument '"
+ + name
+ + "' (parent is "
+ + (parent == null ? "" : "not ")
+ + "null)");
+ this.parent = parent;
+ this.name = name;
+ }
+
+ // identification methods
+
+ /**
+ * Returns the absolute path name of this preference node.
+ * The absolute path name of a node is the path name of its parent node
+ * plus a '/' plus its own name. If the node is the root node and has no
+ * parent then its path name is "" and its absolute path name is "/".
+ */
+ public String absolutePath() {
+ if (parent == null)
+ return "/";
+ else
+ return parent.path() + '/' + name;
+ }
+
+ /**
+ * Private helper method for absolutePath. Returns the empty string for a
+ * root node and otherwise the parentPath of its parent plus a '/'.
+ */
+ private String path() {
+ if (parent == null)
+ return "";
+ else
+ return parent.path() + '/' + name;
+ }
+
+ /**
+ * Returns true if this node comes from the user preferences tree, false
+ * if it comes from the system preferences tree.
+ */
+ public boolean isUserNode() {
+ AbstractPreferences root = this;
+ while (root.parent != null)
+ root = root.parent;
+ return root == Preferences.userRoot();
+ }
+
+ /**
+ * Returns the name of this preferences node. The name of the node cannot
+ * be null, can be mostly 80 characters and cannot contain any '/'
+ * characters. The root node has as name "".
+ */
+ public String name() {
+ return name;
+ }
+
+ /**
+ * Returns the String given by
+ * <code>
+ * (isUserNode() ? "User":"System") + " Preference Node: " + absolutePath()
+ * </code>
+ */
+ public String toString() {
+ return (isUserNode() ? "User":"System")
+ + " Preference Node: "
+ + absolutePath();
+ }
+
+ /**
+ * Returns all known unremoved children of this node.
+ *
+ * @return All known unremoved children of this node
+ */
+ protected final AbstractPreferences[] cachedChildren()
+ {
+ Collection<AbstractPreferences> vals = childCache.values();
+ return vals.toArray(new AbstractPreferences[vals.size()]);
+ }
+
+ /**
+ * Returns all the direct sub nodes of this preferences node.
+ * Needs access to the backing store to give a meaningfull answer.
+ * <p>
+ * This implementation locks this node, checks if the node has not yet
+ * been removed and throws an <code>IllegalStateException</code> when it
+ * has been. Then it creates a new <code>TreeSet</code> and adds any
+ * already cached child nodes names. To get any uncached names it calls
+ * <code>childrenNamesSpi()</code> and adds the result to the set. Finally
+ * it calls <code>toArray()</code> on the created set. When the call to
+ * <code>childrenNamesSpi</code> thows an <code>BackingStoreException</code>
+ * this method will not catch that exception but propagate the exception
+ * to the caller.
+ *
+ * @exception BackingStoreException when the backing store cannot be
+ * reached
+ * @exception IllegalStateException when this node has been removed
+ */
+ public String[] childrenNames() throws BackingStoreException {
+ synchronized(lock) {
+ if (isRemoved())
+ throw new IllegalStateException("Node removed");
+
+ TreeSet<String> childrenNames = new TreeSet<String>();
+
+ // First get all cached node names
+ childrenNames.addAll(childCache.keySet());
+
+ // Then add any others
+ String names[] = childrenNamesSpi();
+ for (int i = 0; i < names.length; i++) {
+ childrenNames.add(names[i]);
+ }
+
+ // And return the array of names
+ String[] children = new String[childrenNames.size()];
+ childrenNames.toArray(children);
+ return children;
+
+ }
+ }
+
+ /**
+ * Returns a sub node of this preferences node if the given path is
+ * relative (does not start with a '/') or a sub node of the root
+ * if the path is absolute (does start with a '/').
+ * <p>
+ * This method first locks this node and checks if the node has not been
+ * removed, if it has been removed it throws an exception. Then if the
+ * path is relative (does not start with a '/') it checks if the path is
+ * legal (does not end with a '/' and has no consecutive '/' characters).
+ * Then it recursively gets a name from the path, gets the child node
+ * from the child-cache of this node or calls the <code>childSpi()</code>
+ * method to create a new child sub node. This is done recursively on the
+ * newly created sub node with the rest of the path till the path is empty.
+ * If the path is absolute (starts with a '/') the lock on this node is
+ * droped and this method is called on the root of the preferences tree
+ * with as argument the complete path minus the first '/'.
+ *
+ * @exception IllegalStateException if this node has been removed
+ * @exception IllegalArgumentException if the path contains two or more
+ * consecutive '/' characters, ends with a '/' charactor and is not the
+ * string "/" (indicating the root node) or any name on the path is more
+ * than 80 characters long
+ */
+ public Preferences node(String path) {
+ synchronized(lock) {
+ if (isRemoved())
+ throw new IllegalStateException("Node removed");
+
+ // Is it a relative path?
+ if (!path.startsWith("/")) {
+
+ // Check if it is a valid path
+ if (path.indexOf("//") != -1 || path.endsWith("/"))
+ throw new IllegalArgumentException(path);
+
+ return getNode(path);
+ }
+ }
+
+ // path started with a '/' so it is absolute
+ // we drop the lock and start from the root (omitting the first '/')
+ Preferences root = isUserNode() ? userRoot() : systemRoot();
+ return root.node(path.substring(1));
+
+ }
+
+ /**
+ * Private helper method for <code>node()</code>. Called with this node
+ * locked. Returns this node when path is the empty string, if it is not
+ * empty the next node name is taken from the path (all chars till the
+ * next '/' or end of path string) and the node is either taken from the
+ * child-cache of this node or the <code>childSpi()</code> method is called
+ * on this node with the name as argument. Then this method is called
+ * recursively on the just constructed child node with the rest of the
+ * path.
+ *
+ * @param path should not end with a '/' character and should not contain
+ * consecutive '/' characters
+ * @exception IllegalArgumentException if path begins with a name that is
+ * larger then 80 characters.
+ */
+ private Preferences getNode(String path) {
+ // if mark is dom then goto end
+
+ // Empty String "" indicates this node
+ if (path.length() == 0)
+ return this;
+
+ // Calculate child name and rest of path
+ String childName;
+ String childPath;
+ int nextSlash = path.indexOf('/');
+ if (nextSlash == -1) {
+ childName = path;
+ childPath = "";
+ } else {
+ childName = path.substring(0, nextSlash);
+ childPath = path.substring(nextSlash+1);
+ }
+
+ // Get the child node
+ AbstractPreferences child;
+ child = (AbstractPreferences)childCache.get(childName);
+ if (child == null) {
+
+ if (childName.length() > MAX_NAME_LENGTH)
+ throw new IllegalArgumentException(childName);
+
+ // Not in childCache yet so create a new sub node
+ child = childSpi(childName);
+ childCache.put(childName, child);
+ if (child.newNode && nodeListeners != null)
+ fire(new NodeChangeEvent(this, child), true);
+ }
+
+ // Lock the child and go down
+ synchronized(child.lock) {
+ return child.getNode(childPath);
+ }
+ }
+
+ /**
+ * Returns true if the node that the path points to exists in memory or
+ * in the backing store. Otherwise it returns false or an exception is
+ * thrown. When this node is removed the only valid parameter is the
+ * empty string (indicating this node), the return value in that case
+ * will be false.
+ *
+ * @exception BackingStoreException when the backing store cannot be
+ * reached
+ * @exception IllegalStateException if this node has been removed
+ * and the path is not the empty string (indicating this node)
+ * @exception IllegalArgumentException if the path contains two or more
+ * consecutive '/' characters, ends with a '/' charactor and is not the
+ * string "/" (indicating the root node) or any name on the path is more
+ * then 80 characters long
+ */
+ public boolean nodeExists(String path) throws BackingStoreException {
+ synchronized(lock) {
+ if (isRemoved() && path.length() != 0)
+ throw new IllegalStateException("Node removed");
+
+ // Is it a relative path?
+ if (!path.startsWith("/")) {
+
+ // Check if it is a valid path
+ if (path.indexOf("//") != -1 || path.endsWith("/"))
+ throw new IllegalArgumentException(path);
+
+ return existsNode(path);
+ }
+ }
+
+ // path started with a '/' so it is absolute
+ // we drop the lock and start from the root (omitting the first '/')
+ Preferences root = isUserNode() ? userRoot() : systemRoot();
+ return root.nodeExists(path.substring(1));
+
+ }
+
+ private boolean existsNode(String path) throws BackingStoreException {
+
+ // Empty String "" indicates this node
+ if (path.length() == 0)
+ return(!isRemoved());
+
+ // Calculate child name and rest of path
+ String childName;
+ String childPath;
+ int nextSlash = path.indexOf('/');
+ if (nextSlash == -1) {
+ childName = path;
+ childPath = "";
+ } else {
+ childName = path.substring(0, nextSlash);
+ childPath = path.substring(nextSlash+1);
+ }
+
+ // Get the child node
+ AbstractPreferences child;
+ child = (AbstractPreferences)childCache.get(childName);
+ if (child == null) {
+
+ if (childName.length() > MAX_NAME_LENGTH)
+ throw new IllegalArgumentException(childName);
+
+ // Not in childCache yet so create a new sub node
+ child = getChild(childName);
+
+ if (child == null)
+ return false;
+
+ childCache.put(childName, child);
+ }
+
+ // Lock the child and go down
+ synchronized(child.lock) {
+ return child.existsNode(childPath);
+ }
+ }
+
+ /**
+ * Returns the child sub node if it exists in the backing store or null
+ * if it does not exist. Called (indirectly) by <code>nodeExists()</code>
+ * when a child node name can not be found in the cache.
+ * <p>
+ * Gets the lock on this node, calls <code>childrenNamesSpi()</code> to
+ * get an array of all (possibly uncached) children and compares the
+ * given name with the names in the array. If the name is found in the
+ * array <code>childSpi()</code> is called to get an instance, otherwise
+ * null is returned.
+ *
+ * @exception BackingStoreException when the backing store cannot be
+ * reached
+ */
+ protected AbstractPreferences getChild(String name)
+ throws BackingStoreException
+ {
+ synchronized(lock) {
+ // Get all the names (not yet in the cache)
+ String[] names = childrenNamesSpi();
+ for (int i=0; i < names.length; i++)
+ if (name.equals(names[i]))
+ return childSpi(name);
+
+ // No child with that name found
+ return null;
+ }
+ }
+
+ /**
+ * Returns true if this node has been removed with the
+ * <code>removeNode()</code> method, false otherwise.
+ * <p>
+ * Gets the lock on this node and then returns a boolean field set by
+ * <code>removeNode</code> methods.
+ */
+ protected boolean isRemoved() {
+ synchronized(lock) {
+ return removed;
+ }
+ }
+
+ /**
+ * Returns the parent preferences node of this node or null if this is
+ * the root of the preferences tree.
+ * <p>
+ * Gets the lock on this node, checks that the node has not been removed
+ * and returns the parent given to the constructor.
+ *
+ * @exception IllegalStateException if this node has been removed
+ */
+ public Preferences parent() {
+ synchronized(lock) {
+ if (isRemoved())
+ throw new IllegalStateException("Node removed");
+
+ return parent;
+ }
+ }
+
+ // export methods
+
+ // Inherit javadoc.
+ public void exportNode(OutputStream os)
+ throws BackingStoreException,
+ IOException
+ {
+ NodeWriter nodeWriter = new NodeWriter(this, os);
+ nodeWriter.writePrefs();
+ }
+
+ // Inherit javadoc.
+ public void exportSubtree(OutputStream os)
+ throws BackingStoreException,
+ IOException
+ {
+ NodeWriter nodeWriter = new NodeWriter(this, os);
+ nodeWriter.writePrefsTree();
+ }
+
+ // preference entry manipulation methods
+
+ /**
+ * Returns an (possibly empty) array with all the keys of the preference
+ * entries of this node.
+ * <p>
+ * This method locks this node and checks if the node has not been
+ * removed, if it has been removed it throws an exception, then it returns
+ * the result of calling <code>keysSpi()</code>.
+ *
+ * @exception BackingStoreException when the backing store cannot be
+ * reached
+ * @exception IllegalStateException if this node has been removed
+ */
+ public String[] keys() throws BackingStoreException {
+ synchronized(lock) {
+ if (isRemoved())
+ throw new IllegalStateException("Node removed");
+
+ return keysSpi();
+ }
+ }
+
+
+ /**
+ * Returns the value associated with the key in this preferences node. If
+ * the default value of the key cannot be found in the preferences node
+ * entries or something goes wrong with the backing store the supplied
+ * default value is returned.
+ * <p>
+ * Checks that key is not null and not larger then 80 characters,
+ * locks this node, and checks that the node has not been removed.
+ * Then it calls <code>keySpi()</code> and returns
+ * the result of that method or the given default value if it returned
+ * null or throwed an exception.
+ *
+ * @exception IllegalArgumentException if key is larger then 80 characters
+ * @exception IllegalStateException if this node has been removed
+ * @exception NullPointerException if key is null
+ */
+ public String get(String key, String defaultVal) {
+ if (key.length() > MAX_KEY_LENGTH)
+ throw new IllegalArgumentException(key);
+
+ synchronized(lock) {
+ if (isRemoved())
+ throw new IllegalStateException("Node removed");
+
+ String value;
+ try {
+ value = getSpi(key);
+ } catch (ThreadDeath death) {
+ throw death;
+ } catch (Throwable t) {
+ value = null;
+ }
+
+ if (value != null) {
+ return value;
+ } else {
+ return defaultVal;
+ }
+ }
+ }
+
+ /**
+ * Convenience method for getting the given entry as a boolean.
+ * When the string representation of the requested entry is either
+ * "true" or "false" (ignoring case) then that value is returned,
+ * otherwise the given default boolean value is returned.
+ *
+ * @exception IllegalArgumentException if key is larger then 80 characters
+ * @exception IllegalStateException if this node has been removed
+ * @exception NullPointerException if key is null
+ */
+ public boolean getBoolean(String key, boolean defaultVal) {
+ String value = get(key, null);
+
+ if ("true".equalsIgnoreCase(value))
+ return true;
+
+ if ("false".equalsIgnoreCase(value))
+ return false;
+
+ return defaultVal;
+ }
+
+ /**
+ * Convenience method for getting the given entry as a byte array.
+ * When the string representation of the requested entry is a valid
+ * Base64 encoded string (without any other characters, such as newlines)
+ * then the decoded Base64 string is returned as byte array,
+ * otherwise the given default byte array value is returned.
+ *
+ * @exception IllegalArgumentException if key is larger then 80 characters
+ * @exception IllegalStateException if this node has been removed
+ * @exception NullPointerException if key is null
+ */
+ public byte[] getByteArray(String key, byte[] defaultVal) {
+ String value = get(key, null);
+
+ byte[] b = null;
+ if (value != null) {
+ b = decode64(value);
+ }
+
+ if (b != null)
+ return b;
+ else
+ return defaultVal;
+ }
+
+ /**
+ * Helper method for decoding a Base64 string as an byte array.
+ * Returns null on encoding error. This method does not allow any other
+ * characters present in the string then the 65 special base64 chars.
+ */
+ private static byte[] decode64(String s) {
+ ByteArrayOutputStream bs = new ByteArrayOutputStream((s.length()/4)*3);
+ char[] c = new char[s.length()];
+ s.getChars(0, s.length(), c, 0);
+
+ // Convert from base64 chars
+ int endchar = -1;
+ for(int j = 0; j < c.length && endchar == -1; j++) {
+ if (c[j] >= 'A' && c[j] <= 'Z') {
+ c[j] -= 'A';
+ } else if (c[j] >= 'a' && c[j] <= 'z') {
+ c[j] = (char) (c[j] + 26 - 'a');
+ } else if (c[j] >= '0' && c[j] <= '9') {
+ c[j] = (char) (c[j] + 52 - '0');
+ } else if (c[j] == '+') {
+ c[j] = 62;
+ } else if (c[j] == '/') {
+ c[j] = 63;
+ } else if (c[j] == '=') {
+ endchar = j;
+ } else {
+ return null; // encoding exception
+ }
+ }
+
+ int remaining = endchar == -1 ? c.length : endchar;
+ int i = 0;
+ while (remaining > 0) {
+ // Four input chars (6 bits) are decoded as three bytes as
+ // 000000 001111 111122 222222
+
+ byte b0 = (byte) (c[i] << 2);
+ if (remaining >= 2) {
+ b0 += (c[i+1] & 0x30) >> 4;
+ }
+ bs.write(b0);
+
+ if (remaining >= 3) {
+ byte b1 = (byte) ((c[i+1] & 0x0F) << 4);
+ b1 += (byte) ((c[i+2] & 0x3C) >> 2);
+ bs.write(b1);
+ }
+
+ if (remaining >= 4) {
+ byte b2 = (byte) ((c[i+2] & 0x03) << 6);
+ b2 += c[i+3];
+ bs.write(b2);
+ }
+
+ i += 4;
+ remaining -= 4;
+ }
+
+ return bs.toByteArray();
+ }
+
+ /**
+ * Convenience method for getting the given entry as a double.
+ * When the string representation of the requested entry can be decoded
+ * with <code>Double.parseDouble()</code> then that double is returned,
+ * otherwise the given default double value is returned.
+ *
+ * @exception IllegalArgumentException if key is larger then 80 characters
+ * @exception IllegalStateException if this node has been removed
+ * @exception NullPointerException if key is null
+ */
+ public double getDouble(String key, double defaultVal) {
+ String value = get(key, null);
+
+ if (value != null) {
+ try {
+ return Double.parseDouble(value);
+ } catch (NumberFormatException nfe) { /* ignore */ }
+ }
+
+ return defaultVal;
+ }
+
+ /**
+ * Convenience method for getting the given entry as a float.
+ * When the string representation of the requested entry can be decoded
+ * with <code>Float.parseFloat()</code> then that float is returned,
+ * otherwise the given default float value is returned.
+ *
+ * @exception IllegalArgumentException if key is larger then 80 characters
+ * @exception IllegalStateException if this node has been removed
+ * @exception NullPointerException if key is null
+ */
+ public float getFloat(String key, float defaultVal) {
+ String value = get(key, null);
+
+ if (value != null) {
+ try {
+ return Float.parseFloat(value);
+ } catch (NumberFormatException nfe) { /* ignore */ }
+ }
+
+ return defaultVal;
+ }
+
+ /**
+ * Convenience method for getting the given entry as an integer.
+ * When the string representation of the requested entry can be decoded
+ * with <code>Integer.parseInt()</code> then that integer is returned,
+ * otherwise the given default integer value is returned.
+ *
+ * @exception IllegalArgumentException if key is larger then 80 characters
+ * @exception IllegalStateException if this node has been removed
+ * @exception NullPointerException if key is null
+ */
+ public int getInt(String key, int defaultVal) {
+ String value = get(key, null);
+
+ if (value != null) {
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException nfe) { /* ignore */ }
+ }
+
+ return defaultVal;
+ }
+
+ /**
+ * Convenience method for getting the given entry as a long.
+ * When the string representation of the requested entry can be decoded
+ * with <code>Long.parseLong()</code> then that long is returned,
+ * otherwise the given default long value is returned.
+ *
+ * @exception IllegalArgumentException if key is larger then 80 characters
+ * @exception IllegalStateException if this node has been removed
+ * @exception NullPointerException if key is null
+ */
+ public long getLong(String key, long defaultVal) {
+ String value = get(key, null);
+
+ if (value != null) {
+ try {
+ return Long.parseLong(value);
+ } catch (NumberFormatException nfe) { /* ignore */ }
+ }
+
+ return defaultVal;
+ }
+
+ /**
+ * Sets the value of the given preferences entry for this node.
+ * Key and value cannot be null, the key cannot exceed 80 characters
+ * and the value cannot exceed 8192 characters.
+ * <p>
+ * The result will be immediately visible in this VM, but may not be
+ * immediately written to the backing store.
+ * <p>
+ * Checks that key and value are valid, locks this node, and checks that
+ * the node has not been removed. Then it calls <code>putSpi()</code>.
+ *
+ * @exception NullPointerException if either key or value are null
+ * @exception IllegalArgumentException if either key or value are to large
+ * @exception IllegalStateException when this node has been removed
+ */
+ public void put(String key, String value) {
+ if (key.length() > MAX_KEY_LENGTH
+ || value.length() > MAX_VALUE_LENGTH)
+ throw new IllegalArgumentException("key ("
+ + key.length() + ")"
+ + " or value ("
+ + value.length() + ")"
+ + " to large");
+ synchronized(lock) {
+ if (isRemoved())
+ throw new IllegalStateException("Node removed");
+
+ putSpi(key, value);
+
+ if (preferenceListeners != null)
+ fire(new PreferenceChangeEvent(this, key, value));
+ }
+
+ }
+
+ /**
+ * Convenience method for setting the given entry as a boolean.
+ * The boolean is converted with <code>Boolean.toString(value)</code>
+ * and then stored in the preference entry as that string.
+ *
+ * @exception NullPointerException if key is null
+ * @exception IllegalArgumentException if the key length is to large
+ * @exception IllegalStateException when this node has been removed
+ */
+ public void putBoolean(String key, boolean value) {
+ put(key, Boolean.toString(value));
+ }
+
+ /**
+ * Convenience method for setting the given entry as an array of bytes.
+ * The byte array is converted to a Base64 encoded string
+ * and then stored in the preference entry as that string.
+ * <p>
+ * Note that a byte array encoded as a Base64 string will be about 1.3
+ * times larger then the original length of the byte array, which means
+ * that the byte array may not be larger about 6 KB.
+ *
+ * @exception NullPointerException if either key or value are null
+ * @exception IllegalArgumentException if either key or value are to large
+ * @exception IllegalStateException when this node has been removed
+ */
+ public void putByteArray(String key, byte[] value) {
+ put(key, encode64(value));
+ }
+
+ /**
+ * Helper method for encoding an array of bytes as a Base64 String.
+ */
+ private static String encode64(byte[] b) {
+ CPStringBuilder sb = new CPStringBuilder((b.length/3)*4);
+
+ int i = 0;
+ int remaining = b.length;
+ char c[] = new char[4];
+ while (remaining > 0) {
+ // Three input bytes are encoded as four chars (6 bits) as
+ // 00000011 11112222 22333333
+
+ c[0] = (char) ((b[i] & 0xFC) >> 2);
+ c[1] = (char) ((b[i] & 0x03) << 4);
+ if (remaining >= 2) {
+ c[1] += (char) ((b[i+1] & 0xF0) >> 4);
+ c[2] = (char) ((b[i+1] & 0x0F) << 2);
+ if (remaining >= 3) {
+ c[2] += (char) ((b[i+2] & 0xC0) >> 6);
+ c[3] = (char) (b[i+2] & 0x3F);
+ } else {
+ c[3] = 64;
+ }
+ } else {
+ c[2] = 64;
+ c[3] = 64;
+ }
+
+ // Convert to base64 chars
+ for(int j = 0; j < 4; j++) {
+ if (c[j] < 26) {
+ c[j] += 'A';
+ } else if (c[j] < 52) {
+ c[j] = (char) (c[j] - 26 + 'a');
+ } else if (c[j] < 62) {
+ c[j] = (char) (c[j] - 52 + '0');
+ } else if (c[j] == 62) {
+ c[j] = '+';
+ } else if (c[j] == 63) {
+ c[j] = '/';
+ } else {
+ c[j] = '=';
+ }
+ }
+
+ sb.append(c);
+ i += 3;
+ remaining -= 3;
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Convenience method for setting the given entry as a double.
+ * The double is converted with <code>Double.toString(double)</code>
+ * and then stored in the preference entry as that string.
+ *
+ * @exception NullPointerException if the key is null
+ * @exception IllegalArgumentException if the key length is to large
+ * @exception IllegalStateException when this node has been removed
+ */
+ public void putDouble(String key, double value) {
+ put(key, Double.toString(value));
+ }
+
+ /**
+ * Convenience method for setting the given entry as a float.
+ * The float is converted with <code>Float.toString(float)</code>
+ * and then stored in the preference entry as that string.
+ *
+ * @exception NullPointerException if the key is null
+ * @exception IllegalArgumentException if the key length is to large
+ * @exception IllegalStateException when this node has been removed
+ */
+ public void putFloat(String key, float value) {
+ put(key, Float.toString(value));
+ }
+
+ /**
+ * Convenience method for setting the given entry as an integer.
+ * The integer is converted with <code>Integer.toString(int)</code>
+ * and then stored in the preference entry as that string.
+ *
+ * @exception NullPointerException if the key is null
+ * @exception IllegalArgumentException if the key length is to large
+ * @exception IllegalStateException when this node has been removed
+ */
+ public void putInt(String key, int value) {
+ put(key, Integer.toString(value));
+ }
+
+ /**
+ * Convenience method for setting the given entry as a long.
+ * The long is converted with <code>Long.toString(long)</code>
+ * and then stored in the preference entry as that string.
+ *
+ * @exception NullPointerException if the key is null
+ * @exception IllegalArgumentException if the key length is to large
+ * @exception IllegalStateException when this node has been removed
+ */
+ public void putLong(String key, long value) {
+ put(key, Long.toString(value));
+ }
+
+ /**
+ * Removes the preferences entry from this preferences node.
+ * <p>
+ * The result will be immediately visible in this VM, but may not be
+ * immediately written to the backing store.
+ * <p>
+ * This implementation checks that the key is not larger then 80
+ * characters, gets the lock of this node, checks that the node has
+ * not been removed and calls <code>removeSpi</code> with the given key.
+ *
+ * @exception NullPointerException if the key is null
+ * @exception IllegalArgumentException if the key length is to large
+ * @exception IllegalStateException when this node has been removed
+ */
+ public void remove(String key) {
+ if (key.length() > MAX_KEY_LENGTH)
+ throw new IllegalArgumentException(key);
+
+ synchronized(lock) {
+ if (isRemoved())
+ throw new IllegalStateException("Node removed");
+
+ removeSpi(key);
+
+ if (preferenceListeners != null)
+ fire(new PreferenceChangeEvent(this, key, null));
+ }
+ }
+
+ /**
+ * Removes all entries from this preferences node. May need access to the
+ * backing store to get and clear all entries.
+ * <p>
+ * The result will be immediately visible in this VM, but may not be
+ * immediatly written to the backing store.
+ * <p>
+ * This implementation locks this node, checks that the node has not been
+ * removed and calls <code>keys()</code> to get a complete array of keys
+ * for this node. For every key found <code>removeSpi()</code> is called.
+ *
+ * @exception BackingStoreException when the backing store cannot be
+ * reached
+ * @exception IllegalStateException if this node has been removed
+ */
+ public void clear() throws BackingStoreException {
+ synchronized(lock) {
+ if (isRemoved())
+ throw new IllegalStateException("Node Removed");
+
+ String[] keys = keys();
+ for (int i = 0; i < keys.length; i++) {
+ removeSpi(keys[i]);
+ }
+ }
+ }
+
+ /**
+ * Writes all preference changes on this and any subnode that have not
+ * yet been written to the backing store. This has no effect on the
+ * preference entries in this VM, but it makes sure that all changes
+ * are visible to other programs (other VMs might need to call the
+ * <code>sync()</code> method to actually see the changes to the backing
+ * store.
+ * <p>
+ * Locks this node, calls the <code>flushSpi()</code> method, gets all
+ * the (cached - already existing in this VM) subnodes and then calls
+ * <code>flushSpi()</code> on every subnode with this node unlocked and
+ * only that particular subnode locked.
+ *
+ * @exception BackingStoreException when the backing store cannot be
+ * reached
+ */
+ public void flush() throws BackingStoreException {
+ flushNode(false);
+ }
+
+ /**
+ * Writes and reads all preference changes to and from this and any
+ * subnodes. This makes sure that all local changes are written to the
+ * backing store and that all changes to the backing store are visible
+ * in this preference node (and all subnodes).
+ * <p>
+ * Checks that this node is not removed, locks this node, calls the
+ * <code>syncSpi()</code> method, gets all the subnodes and then calls
+ * <code>syncSpi()</code> on every subnode with this node unlocked and
+ * only that particular subnode locked.
+ *
+ * @exception BackingStoreException when the backing store cannot be
+ * reached
+ * @exception IllegalStateException if this node has been removed
+ */
+ public void sync() throws BackingStoreException {
+ flushNode(true);
+ }
+
+
+ /**
+ * Private helper method that locks this node and calls either
+ * <code>flushSpi()</code> if <code>sync</code> is false, or
+ * <code>flushSpi()</code> if <code>sync</code> is true. Then it gets all
+ * the currently cached subnodes. For every subnode it calls this method
+ * recursively with this node no longer locked.
+ * <p>
+ * Called by either <code>flush()</code> or <code>sync()</code>
+ */
+ private void flushNode(boolean sync) throws BackingStoreException {
+ String[] keys = null;
+ synchronized(lock) {
+ if (sync) {
+ syncSpi();
+ } else {
+ flushSpi();
+ }
+ keys = (String[]) childCache.keySet().toArray(new String[]{});
+ }
+
+ if (keys != null) {
+ for (int i = 0; i < keys.length; i++) {
+ // Have to lock this node again to access the childCache
+ AbstractPreferences subNode;
+ synchronized(lock) {
+ subNode = (AbstractPreferences) childCache.get(keys[i]);
+ }
+
+ // The child could already have been removed from the cache
+ if (subNode != null) {
+ subNode.flushNode(sync);
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes this and all subnodes from the backing store and clears all
+ * entries. After removal this instance will not be useable (except for
+ * a few methods that don't throw a <code>InvalidStateException</code>),
+ * even when a new node with the same path name is created this instance
+ * will not be usable again.
+ * <p>
+ * Checks that this is not a root node. If not it locks the parent node,
+ * then locks this node and checks that the node has not yet been removed.
+ * Then it makes sure that all subnodes of this node are in the child cache,
+ * by calling <code>childSpi()</code> on any children not yet in the cache.
+ * Then for all children it locks the subnode and removes it. After all
+ * subnodes have been purged the child cache is cleared, this nodes removed
+ * flag is set and any listeners are called. Finally this node is removed
+ * from the child cache of the parent node.
+ *
+ * @exception BackingStoreException when the backing store cannot be
+ * reached
+ * @exception IllegalStateException if this node has already been removed
+ * @exception UnsupportedOperationException if this is a root node
+ */
+ public void removeNode() throws BackingStoreException {
+ // Check if it is a root node
+ if (parent == null)
+ throw new UnsupportedOperationException("Cannot remove root node");
+
+ synchronized (parent.lock) {
+ synchronized(this.lock) {
+ if (isRemoved())
+ throw new IllegalStateException("Node Removed");
+
+ purge();
+ }
+ parent.childCache.remove(name);
+ }
+ }
+
+ /**
+ * Private helper method used to completely remove this node.
+ * Called by <code>removeNode</code> with the parent node and this node
+ * locked.
+ * <p>
+ * Makes sure that all subnodes of this node are in the child cache,
+ * by calling <code>childSpi()</code> on any children not yet in the
+ * cache. Then for all children it locks the subnode and calls this method
+ * on that node. After all subnodes have been purged the child cache is
+ * cleared, this nodes removed flag is set and any listeners are called.
+ */
+ private void purge() throws BackingStoreException
+ {
+ // Make sure all children have an AbstractPreferences node in cache
+ String children[] = childrenNamesSpi();
+ for (int i = 0; i < children.length; i++) {
+ if (childCache.get(children[i]) == null)
+ childCache.put(children[i], childSpi(children[i]));
+ }
+
+ // purge all children
+ Iterator i = childCache.values().iterator();
+ while (i.hasNext()) {
+ AbstractPreferences node = (AbstractPreferences) i.next();
+ synchronized(node.lock) {
+ node.purge();
+ }
+ }
+
+ // Cache is empty now
+ childCache.clear();
+
+ // remove this node
+ removeNodeSpi();
+ removed = true;
+
+ if (nodeListeners != null)
+ fire(new NodeChangeEvent(parent, this), false);
+ }
+
+ // listener methods
+
+ /**
+ * Add a listener which is notified when a sub-node of this node
+ * is added or removed.
+ * @param listener the listener to add
+ */
+ public void addNodeChangeListener(NodeChangeListener listener)
+ {
+ synchronized (lock)
+ {
+ if (isRemoved())
+ throw new IllegalStateException("node has been removed");
+ if (listener == null)
+ throw new NullPointerException("listener is null");
+ if (nodeListeners == null)
+ nodeListeners = new ArrayList<NodeChangeListener>();
+ nodeListeners.add(listener);
+ }
+ }
+
+ /**
+ * Add a listener which is notified when a value in this node
+ * is added, changed, or removed.
+ * @param listener the listener to add
+ */
+ public void addPreferenceChangeListener(PreferenceChangeListener listener)
+ {
+ synchronized (lock)
+ {
+ if (isRemoved())
+ throw new IllegalStateException("node has been removed");
+ if (listener == null)
+ throw new NullPointerException("listener is null");
+ if (preferenceListeners == null)
+ preferenceListeners = new ArrayList<PreferenceChangeListener>();
+ preferenceListeners.add(listener);
+ }
+ }
+
+ /**
+ * Remove the indicated node change listener from the list of
+ * listeners to notify.
+ * @param listener the listener to remove
+ */
+ public void removeNodeChangeListener(NodeChangeListener listener)
+ {
+ synchronized (lock)
+ {
+ if (isRemoved())
+ throw new IllegalStateException("node has been removed");
+ if (listener == null)
+ throw new NullPointerException("listener is null");
+ if (nodeListeners != null)
+ nodeListeners.remove(listener);
+ }
+ }
+
+ /**
+ * Remove the indicated preference change listener from the list of
+ * listeners to notify.
+ * @param listener the listener to remove
+ */
+ public void removePreferenceChangeListener (PreferenceChangeListener listener)
+ {
+ synchronized (lock)
+ {
+ if (isRemoved())
+ throw new IllegalStateException("node has been removed");
+ if (listener == null)
+ throw new NullPointerException("listener is null");
+ if (preferenceListeners != null)
+ preferenceListeners.remove(listener);
+ }
+ }
+
+ /**
+ * Send a preference change event to all listeners. Note that
+ * the caller is responsible for holding the node's lock, and
+ * for checking that the list of listeners is not null.
+ * @param event the event to send
+ */
+ private void fire(final PreferenceChangeEvent event)
+ {
+ for (final PreferenceChangeListener listener : preferenceListeners)
+ {
+ Runnable dispatcher = new Runnable() {
+ public void run()
+ {
+ listener.preferenceChange(event);
+ }
+ };
+
+ Executor executor =
+ Executors.newSingleThreadExecutor(new DefaultDaemonThreadFactory());
+ executor.execute(dispatcher);
+ }
+ }
+
+ /**
+ * Send a node change event to all listeners. Note that
+ * the caller is responsible for holding the node's lock, and
+ * for checking that the list of listeners is not null.
+ * @param event the event to send
+ */
+ private void fire(final NodeChangeEvent event, final boolean added)
+ {
+ for (final NodeChangeListener listener : nodeListeners)
+ {
+ Runnable dispatcher = new Runnable() {
+ public void run()
+ {
+ if (added)
+ listener.childAdded(event);
+ else
+ listener.childRemoved(event);
+ }
+ };
+
+ Executor executor =
+ Executors.newSingleThreadExecutor(new DefaultDaemonThreadFactory());
+ executor.execute(dispatcher);
+ }
+ }
+
+ // abstract spi methods
+
+ /**
+ * Returns the names of the sub nodes of this preference node.
+ * This method only has to return any not yet cached child names,
+ * but may return all names if that is easier. It must not return
+ * null when there are no children, it has to return an empty array
+ * in that case. Since this method must consult the backing store to
+ * get all the sub node names it may throw a BackingStoreException.
+ * <p>
+ * Called by <code>childrenNames()</code> with this node locked.
+ */
+ protected abstract String[] childrenNamesSpi() throws BackingStoreException;
+
+ /**
+ * Returns a child note with the given name.
+ * This method is called by the <code>node()</code> method (indirectly
+ * through the <code>getNode()</code> helper method) with this node locked
+ * if a sub node with this name does not already exist in the child cache.
+ * If the child node did not aleady exist in the backing store the boolean
+ * field <code>newNode</code> of the returned node should be set.
+ * <p>
+ * Note that this method should even return a non-null child node if the
+ * backing store is not available since it may not throw a
+ * <code>BackingStoreException</code>.
+ */
+ protected abstract AbstractPreferences childSpi(String name);
+
+ /**
+ * Returns an (possibly empty) array with all the keys of the preference
+ * entries of this node.
+ * <p>
+ * Called by <code>keys()</code> with this node locked if this node has
+ * not been removed. May throw an exception when the backing store cannot
+ * be accessed.
+ *
+ * @exception BackingStoreException when the backing store cannot be
+ * reached
+ */
+ protected abstract String[] keysSpi() throws BackingStoreException;
+
+ /**
+ * Returns the value associated with the key in this preferences node or
+ * null when the key does not exist in this preferences node.
+ * <p>
+ * Called by <code>key()</code> with this node locked after checking that
+ * key is valid, not null and that the node has not been removed.
+ * <code>key()</code> will catch any exceptions that this method throws.
+ */
+ protected abstract String getSpi(String key);
+
+ /**
+ * Sets the value of the given preferences entry for this node.
+ * The implementation is not required to propagate the change to the
+ * backing store immediately. It may not throw an exception when it tries
+ * to write to the backing store and that operation fails, the failure
+ * should be registered so a later invocation of <code>flush()</code>
+ * or <code>sync()</code> can signal the failure.
+ * <p>
+ * Called by <code>put()</code> with this node locked after checking that
+ * key and value are valid and non-null.
+ */
+ protected abstract void putSpi(String key, String value);
+
+ /**
+ * Removes the given key entry from this preferences node.
+ * The implementation is not required to propagate the change to the
+ * backing store immediately. It may not throw an exception when it tries
+ * to write to the backing store and that operation fails, the failure
+ * should be registered so a later invocation of <code>flush()</code>
+ * or <code>sync()</code> can signal the failure.
+ * <p>
+ * Called by <code>remove()</code> with this node locked after checking
+ * that the key is valid and non-null.
+ */
+ protected abstract void removeSpi(String key);
+
+ /**
+ * Writes all entries of this preferences node that have not yet been
+ * written to the backing store and possibly creates this node in the
+ * backing store, if it does not yet exist. Should only write changes to
+ * this node and not write changes to any subnodes.
+ * Note that the node can be already removed in this VM. To check if
+ * that is the case the implementation can call <code>isRemoved()</code>.
+ * <p>
+ * Called (indirectly) by <code>flush()</code> with this node locked.
+ */
+ protected abstract void flushSpi() throws BackingStoreException;
+
+ /**
+ * Writes all entries of this preferences node that have not yet been
+ * written to the backing store and reads any entries that have changed
+ * in the backing store but that are not yet visible in this VM.
+ * Should only sync this node and not change any of the subnodes.
+ * Note that the node can be already removed in this VM. To check if
+ * that is the case the implementation can call <code>isRemoved()</code>.
+ * <p>
+ * Called (indirectly) by <code>sync()</code> with this node locked.
+ */
+ protected abstract void syncSpi() throws BackingStoreException;
+
+ /**
+ * Clears this node from this VM and removes it from the backing store.
+ * After this method has been called the node is marked as removed.
+ * <p>
+ * Called (indirectly) by <code>removeNode()</code> with this node locked
+ * after all the sub nodes of this node have already been removed.
+ */
+ protected abstract void removeNodeSpi() throws BackingStoreException;
+}
diff --git a/libjava/classpath/java/util/prefs/BackingStoreException.java b/libjava/classpath/java/util/prefs/BackingStoreException.java
new file mode 100644
index 000000000..0ba358a56
--- /dev/null
+++ b/libjava/classpath/java/util/prefs/BackingStoreException.java
@@ -0,0 +1,104 @@
+/* BackingStoreException.java - chained exception thrown when backing store
+ fails
+ Copyright (C) 2001, 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.prefs;
+
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+/**
+ * Chained exception thrown when backing store fails. This exception is
+ * only thrown from methods that actually have to access the backing store,
+ * such as <code>clear(), keys(), childrenNames(), nodeExists(), removeNode(),
+ * flush(), sync(), exportNode(), exportSubTree()</code>; normal operations
+ * do not throw BackingStoreExceptions.
+ *
+ * <p>Note that although this class inherits the Serializable interface, an
+ * attempt to serialize will fail with a <code>NotSerializableException</code>.
+ *
+ * @author Mark Wielaard (mark@klomp.org)
+ * @since 1.4
+ * @status updated to 1.4
+ */
+public class BackingStoreException extends Exception
+{
+ static final long serialVersionUID = 859796500401108469L;
+
+ /**
+ * Creates a new exception with a descriptive message.
+ *
+ * @param message the message
+ */
+ public BackingStoreException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * Create a new exception with the given cause.
+ *
+ * @param cause the cause
+ */
+ public BackingStoreException(Throwable cause)
+ {
+ super(cause);
+ }
+
+ /**
+ * This class should not be serialized.
+ *
+ * @param o the output stream
+ */
+ private void writeObject(ObjectOutputStream o) throws NotSerializableException
+ {
+ throw new NotSerializableException
+ ("java.util.prefs.BackingStoreException");
+ }
+
+ /**
+ * This class should not be serialized.
+ *
+ * @param i the input stream
+ */
+ private void readObject(ObjectInputStream i) throws NotSerializableException
+ {
+ throw new NotSerializableException
+ ("java.util.prefs.BackingStoreException");
+ }
+}
diff --git a/libjava/classpath/java/util/prefs/InvalidPreferencesFormatException.java b/libjava/classpath/java/util/prefs/InvalidPreferencesFormatException.java
new file mode 100644
index 000000000..f929b56f6
--- /dev/null
+++ b/libjava/classpath/java/util/prefs/InvalidPreferencesFormatException.java
@@ -0,0 +1,116 @@
+/* InvalidPreferencesFormatException - indicates reading prefs from stream
+ failed
+ Copyright (C) 2001, 2002, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.prefs;
+
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+/**
+ * Indicates reading prefs from stream failed. Thrown by the
+ * <code>importPreferences()</code> method when the supplied input stream
+ * could not be read because it was not in the correct XML format.
+ *
+ * <p>Note that although this class inherits the Serializable interface, an
+ * attempt to serialize will fail with a <code>NotSerializableException</code>.
+ * </p>
+ *
+ * @author Mark Wielaard (mark@klomp.org)
+ * @see Preferences
+ * @since 1.4
+ * @status updated to 1.4
+ */
+public class InvalidPreferencesFormatException extends Exception
+{
+ static final long serialVersionUID = -791715184232119669L;
+
+ /**
+ * Creates a new exception with a descriptive message. The cause remains
+ * uninitialized.
+ *
+ * @param message the message
+ */
+ public InvalidPreferencesFormatException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * Creates a new exception with the given cause.
+ *
+ * @param cause the cause
+ */
+ public InvalidPreferencesFormatException(Throwable cause)
+ {
+ super(cause);
+ }
+
+ /**
+ * Creates a new exception with a descriptive message and a cause.
+ *
+ * @param message the message
+ * @param cause the cause
+ */
+ public InvalidPreferencesFormatException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+
+ /**
+ * This class should not be serialized.
+ *
+ * @param o the output stream
+ */
+ private void writeObject(ObjectOutputStream o) throws NotSerializableException
+ {
+ throw new NotSerializableException
+ ("java.util.prefs.InvalidPreferencesFormatException");
+ }
+
+ /**
+ * This class should not be serialized.
+ *
+ * @param i the input stream
+ */
+ private void readObject(ObjectInputStream i) throws NotSerializableException
+ {
+ throw new NotSerializableException
+ ("java.util.prefs.InvalidPreferencesFormatException");
+ }
+}
diff --git a/libjava/classpath/java/util/prefs/NodeChangeEvent.java b/libjava/classpath/java/util/prefs/NodeChangeEvent.java
new file mode 100644
index 000000000..5bdeb3346
--- /dev/null
+++ b/libjava/classpath/java/util/prefs/NodeChangeEvent.java
@@ -0,0 +1,111 @@
+/* NodeChangeEvent - ObjectEvent fired when a Preference node is added/removed
+ Copyright (C) 2001, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.prefs;
+
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.EventObject;
+
+/**
+ * ObjectEvent fired when a Preference node is added/removed.
+ * This event is only generated when a new subnode is added or a subnode is
+ * removed from a preference node. Changes in the entries of a preference node
+ * are indicated with a <code>PreferenceChangeEvent</code>.
+ * <p>
+ * Note that although this class is marked as serializable, attempts to
+ * serialize it will fail with NotSerializableException.
+ *
+ * @since 1.4
+ * @author Mark Wielaard (mark@klomp.org)
+ */
+public class NodeChangeEvent extends EventObject {
+
+ // We have this to placate the compiler.
+ private static final long serialVersionUID =8068949086596572957L;
+
+ /**
+ * The sub node that was added or removed.
+ * Defined transient just like <code>EventObject.source</code> since
+ * this object should be serializable, but Preferences is in general not
+ * serializable.
+ */
+ private final transient Preferences child;
+
+ /**
+ * Creates a new NodeChangeEvent.
+ *
+ * @param parentNode The source preference node from which a subnode was
+ * added or removed
+ * @param childNode The preference node that was added or removed
+ */
+ public NodeChangeEvent(Preferences parentNode, Preferences childNode) {
+ super(parentNode);
+ child = childNode;
+ }
+
+ /**
+ * Returns the source parent preference node from which a subnode was
+ * added or removed.
+ */
+ public Preferences getParent() {
+ return (Preferences) source;
+ }
+
+ /**
+ * Returns the child preference subnode that was added or removed.
+ * To see wether it is still a valid preference node one has to call
+ * <code>event.getChild().nodeExists("")</code>.
+ */
+ public Preferences getChild() {
+ return child;
+ }
+
+ private void readObject(ObjectInputStream ois)
+ throws IOException
+ {
+ throw new NotSerializableException("LineEvent is not serializable");
+ }
+
+ private void writeObject(ObjectOutputStream oos)
+ throws IOException
+ {
+ throw new NotSerializableException("LineEvent is not serializable");
+ }
+}
diff --git a/libjava/classpath/java/util/prefs/NodeChangeListener.java b/libjava/classpath/java/util/prefs/NodeChangeListener.java
new file mode 100644
index 000000000..8d018ab67
--- /dev/null
+++ b/libjava/classpath/java/util/prefs/NodeChangeListener.java
@@ -0,0 +1,64 @@
+/* NodeChangeListener - EventListener for Preferences node addition/removal
+ Copyright (C) 2001 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.prefs;
+
+import java.util.EventListener;
+
+/**
+ * EventListener for Preferences node addition/removal.
+ * <p>
+ * Note that these events are only generated for the addition and removal
+ * of sub nodes from the preference node. Entry changes in the preference
+ * node can be monitored with a <code>PreferenceChangeListener</code>.
+ *
+ * @since 1.4
+ * @author Mark Wielaard (mark@klomp.org)
+ */
+public interface NodeChangeListener extends EventListener {
+
+ /**
+ * Fired when a sub node is added to the preference node.
+ */
+ void childAdded(NodeChangeEvent event);
+
+ /**
+ * Fired when a sub node is removed from the preference node.
+ */
+ void childRemoved(NodeChangeEvent event);
+
+}
diff --git a/libjava/classpath/java/util/prefs/PreferenceChangeEvent.java b/libjava/classpath/java/util/prefs/PreferenceChangeEvent.java
new file mode 100644
index 000000000..f273361e1
--- /dev/null
+++ b/libjava/classpath/java/util/prefs/PreferenceChangeEvent.java
@@ -0,0 +1,125 @@
+/* PreferenceChangeEvent - ObjectEvent fired when a Preferences entry changes
+ Copyright (C) 2001, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.prefs;
+
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.EventObject;
+
+/**
+ * ObjectEvent fired when a Preferences entry changes.
+ * This event is generated when a entry is added, changed or removed.
+ * When an entry is removed then <code>getNewValue</code> will return null.
+ * <p>
+ * Preference change events are only generated for entries in one particular
+ * preference node. Notification of subnode addition/removal is given by a
+ * <code>NodeChangeEvent</code>.
+ * <p>
+ * Note that although this class is marked as serializable, attempts to
+ * serialize it will fail with NotSerializableException.
+ *
+ * @since 1.4
+ * @author Mark Wielaard (mark@klomp.org)
+ */
+public class PreferenceChangeEvent extends EventObject {
+
+ // We have this to placate the compiler.
+ private static final long serialVersionUID = 793724513368024975L;
+
+ /**
+ * The key of the changed entry.
+ */
+ private final String key;
+
+ /**
+ * The new value of the changed entry, or null when the entry was removed.
+ */
+ private final String newValue;
+
+ /**
+ * Creates a new PreferenceChangeEvent.
+ *
+ * @param node The source preference node for which an entry was added,
+ * changed or removed
+ * @param key The key of the entry that was added, changed or removed
+ * @param value The new value of the entry that was added or changed, or
+ * null when the entry was removed
+ */
+ public PreferenceChangeEvent(Preferences node, String key, String value) {
+ super(node);
+ this.key = key;
+ this.newValue = value;
+ }
+
+ /**
+ * Returns the source Preference node from which an entry was added,
+ * changed or removed.
+ */
+ public Preferences getNode() {
+ return (Preferences) source;
+ }
+
+ /**
+ * Returns the key of the entry that was added, changed or removed.
+ */
+ public String getKey() {
+ return key;
+ }
+
+ /**
+ * Returns the new value of the entry that was added or changed, or
+ * returns null when the entry was removed.
+ */
+ public String getNewValue() {
+ return newValue;
+ }
+
+ private void readObject(ObjectInputStream ois)
+ throws IOException
+ {
+ throw new NotSerializableException("LineEvent is not serializable");
+ }
+
+ private void writeObject(ObjectOutputStream oos)
+ throws IOException
+ {
+ throw new NotSerializableException("LineEvent is not serializable");
+ }
+}
diff --git a/libjava/classpath/java/util/prefs/PreferenceChangeListener.java b/libjava/classpath/java/util/prefs/PreferenceChangeListener.java
new file mode 100644
index 000000000..e6118bc31
--- /dev/null
+++ b/libjava/classpath/java/util/prefs/PreferenceChangeListener.java
@@ -0,0 +1,60 @@
+/* PreferenceChangeListener - EventListener for Preferences entry changes
+ Copyright (C) 2001 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.prefs;
+
+import java.util.EventListener;
+
+/**
+ * EventListener for Preferences entry addition, change or removal.
+ * <p>
+ * Preference change events are only generated for entries in one particular
+ * preference node. Notification of subnode addition/removal can be monitored
+ * with a <code>NodeChangeListener</code>.
+ *
+ * @since 1.4
+ * @author Mark Wielaard (mark@klomp.org)
+ */
+public interface PreferenceChangeListener extends EventListener {
+
+ /**
+ * Fired when a entry has been added, changed or removed from the
+ * preference node.
+ */
+ void preferenceChange(PreferenceChangeEvent event);
+
+}
diff --git a/libjava/classpath/java/util/prefs/Preferences.java b/libjava/classpath/java/util/prefs/Preferences.java
new file mode 100644
index 000000000..3ff9c85e3
--- /dev/null
+++ b/libjava/classpath/java/util/prefs/Preferences.java
@@ -0,0 +1,696 @@
+/* Preferences -- Preference node containing key value entries and subnodes
+ Copyright (C) 2001, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.prefs;
+
+import gnu.classpath.ServiceFactory;
+import gnu.java.util.prefs.NodeReader;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.AccessController;
+import java.security.Permission;
+import java.security.PrivilegedAction;
+import java.util.Iterator;
+
+/**
+ * Preference node containing key value entries and subnodes.
+ * <p>
+ * There are two preference node trees, a system tree which can be accessed
+ * by calling <code>systemRoot()</code> containing system preferences usefull
+ * for all users, and a user tree that can be accessed by calling
+ * <code>userRoot()</code> containing preferences that can differ between
+ * different users. How different users are identified is implementation
+ * depended. It can be determined by Thread, Access Control Context or Subject.
+ * <p>
+ * This implementation uses the "java.util.prefs.PreferencesFactory" system
+ * property to find a class that implement <code>PreferencesFactory</code>
+ * and initialized that class (if it has a public no arguments contructor)
+ * to get at the actual system or user root. If the system property is not set,
+ * or the class cannot be initialized it uses the default implementation
+ * <code>gnu.java.util.prefs.FileBasedFactory</code>.
+ * <p>
+ * Besides the two static method above to get the roots of the system and user
+ * preference node trees there are also two convenience methods to access the
+ * default preference node for a particular package an object is in. These are
+ * <code>userNodeForPackage()</code> and <code>systemNodeForPackage()</code>.
+ * Both methods take an Object as an argument so accessing preferences values
+ * can be as easy as calling <code>Preferences.userNodeForPackage(this)</code>.
+ * <p>
+ * Note that if a security manager is installed all static methods check for
+ * <code>RuntimePermission("preferences")</code>. But if this permission is
+ * given to the code then it can access and change all (user) preference nodes
+ * and entries. So you should be carefull not to store to sensitive information
+ * or make security decissions based on preference values since there is no
+ * more fine grained control over what preference values can be changed once
+ * code has been given the correct runtime permission.
+ * <p>
+ * XXX
+ *
+ * @since 1.4
+ * @author Mark Wielaard (mark@klomp.org)
+ */
+public abstract class Preferences {
+
+ // Static Fields
+
+ /**
+ * Default PreferencesFactory class used when the system property
+ * "java.util.prefs.PreferencesFactory" is not set.
+ */
+ private static final String defaultFactoryClass
+ = "gnu.java.util.prefs.FileBasedFactory";
+
+ /** Permission needed to access system or user root. */
+ private static final Permission prefsPermission
+ = new RuntimePermission("preferences");
+
+ /**
+ * The preferences factory object that supplies the system and user root.
+ * Set and returned by the getFactory() method.
+ */
+ private static PreferencesFactory factory;
+
+ /** Maximum node name length. 80 characters. */
+ public static final int MAX_NAME_LENGTH = 80;
+
+ /** Maximum entry key length. 80 characters. */
+ public static final int MAX_KEY_LENGTH = 80;
+
+ /** Maximum entry value length. 8192 characters. */
+ public static final int MAX_VALUE_LENGTH = 8192;
+
+ // Constructors
+
+ /**
+ * Creates a new Preferences node. Can only be used by subclasses.
+ * Empty implementation.
+ */
+ protected Preferences() {}
+
+ // Static methods
+
+ /**
+ * Returns the system preferences root node containing usefull preferences
+ * for all users. It is save to cache this value since it should always
+ * return the same preference node.
+ *
+ * @return the root system preference node
+ * @exception SecurityException when a security manager is installed and
+ * the caller does not have <code>RuntimePermission("preferences")</code>.
+ */
+ public static Preferences systemRoot() throws SecurityException {
+ // Get the preferences factory and check for permission
+ PreferencesFactory factory = getFactory();
+
+ return factory.systemRoot();
+ }
+
+ /**
+ * Returns the user preferences root node containing preferences for the
+ * the current user. How different users are identified is implementation
+ * depended. It can be determined by Thread, Access Control Context or
+ * Subject.
+ *
+ * @return the root user preference node
+ * @exception SecurityException when a security manager is installed and
+ * the caller does not have <code>RuntimePermission("preferences")</code>.
+ */
+ public static Preferences userRoot() throws SecurityException {
+ // Get the preferences factory and check for permission
+ PreferencesFactory factory = getFactory();
+ return factory.userRoot();
+ }
+
+ /**
+ * Private helper method for <code>systemRoot()</code> and
+ * <code>userRoot()</code>. Checks security permission and instantiates the
+ * correct factory if it has not yet been set.
+ * <p>
+ * When the preferences factory has not yet been set this method first
+ * tries to get the system propery "java.util.prefs.PreferencesFactory"
+ * and tries to initializes that class. If the system property is not set
+ * or initialization fails it returns an instance of the default factory
+ * <code>gnu.java.util.prefs.FileBasedPreferencesFactory</code>.
+ *
+ * @return the preferences factory to use
+ * @exception SecurityException when a security manager is installed and
+ * the caller does not have <code>RuntimePermission("preferences")</code>.
+ */
+ private static PreferencesFactory getFactory() throws SecurityException {
+
+ // First check for permission
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(prefsPermission);
+ }
+
+ // Get the factory
+ if (factory == null) {
+ // Caller might not have enough permissions
+ factory = AccessController.doPrivileged(
+ new PrivilegedAction<PreferencesFactory>() {
+ public PreferencesFactory run() {
+ PreferencesFactory pf = null;
+ String className = System.getProperty
+ ("java.util.prefs.PreferencesFactory");
+ if (className != null) {
+ try {
+ Class fc = Class.forName(className);
+ Object o = fc.newInstance();
+ pf = (PreferencesFactory) o;
+ } catch (ClassNotFoundException cnfe)
+ {/*ignore*/}
+ catch (InstantiationException ie)
+ {/*ignore*/}
+ catch (IllegalAccessException iae)
+ {/*ignore*/}
+ catch (ClassCastException cce)
+ {/*ignore*/}
+ }
+ return pf;
+ }
+ });
+
+ // Still no factory? Try to see if we have one defined
+ // as a System Preference
+ if (factory == null)
+ {
+ Iterator iter = ServiceFactory.lookupProviders
+ (PreferencesFactory.class, null);
+
+ if (iter != null && iter.hasNext())
+ factory = (PreferencesFactory) iter.next();
+ }
+
+ // Still no factory? Use our default.
+ if (factory == null)
+ {
+ try
+ {
+ Class cls = Class.forName (defaultFactoryClass);
+ factory = (PreferencesFactory) cls.newInstance();
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException ("Couldn't load default factory"
+ + " '"+ defaultFactoryClass +"'", e);
+ }
+ }
+
+ }
+
+ return factory;
+ }
+
+ /**
+ * Returns the system preferences node for the package of a class.
+ * The package node name of the class is determined by dropping the
+ * final component of the fully qualified class name and
+ * changing all '.' to '/' in the package name. If the class of the
+ * object has no package then the package node name is "&lt;unnamed&gt;".
+ * The returned node is <code>systemRoot().node(packageNodeName)</code>.
+ *
+ * @param c Object whose default system preference node is requested
+ * @returns system preferences node that should be used by class c
+ * @exception SecurityException when a security manager is installed and
+ * the caller does not have <code>RuntimePermission("preferences")</code>.
+ */
+ public static Preferences systemNodeForPackage(Class<?> c)
+ throws SecurityException
+ {
+ return nodeForPackage(c, systemRoot());
+ }
+
+ /**
+ * Returns the user preferences node for the package of a class.
+ * The package node name of the class is determined by dropping the
+ * final component of the fully qualified class name and
+ * changing all '.' to '/' in the package name. If the class of the
+ * object has no package then the package node name is "&lt;unnamed&gt;".
+ * The returned node is <code>userRoot().node(packageNodeName)</code>.
+ *
+ * @param c Object whose default userpreference node is requested
+ * @returns userpreferences node that should be used by class c
+ * @exception SecurityException when a security manager is installed and
+ * the caller does not have <code>RuntimePermission("preferences")</code>.
+ */
+ public static Preferences userNodeForPackage(Class<?> c)
+ throws SecurityException
+ {
+ return nodeForPackage(c, userRoot());
+ }
+
+ /**
+ * Private helper method for <code>systemNodeForPackage()</code> and
+ * <code>userNodeForPackage()</code>. Given the correct system or user
+ * root it returns the correct Preference node for the package node name
+ * of the given object.
+ */
+ private static Preferences nodeForPackage(Class c, Preferences root) {
+ // Get the package path
+ String className = c.getName();
+ String packagePath;
+ int index = className.lastIndexOf('.');
+ if(index == -1) {
+ packagePath = "<unnamed>";
+ } else {
+ packagePath = className.substring(0,index).replace('.','/');
+ }
+
+ return root.node(packagePath);
+ }
+
+ /**
+ * Import preferences from the given input stream. This expects
+ * preferences to be represented in XML as emitted by
+ * {@link #exportNode(OutputStream)} and
+ * {@link #exportSubtree(OutputStream)}.
+ * @throws IOException if there is an error while reading
+ * @throws InvalidPreferencesFormatException if the XML is not properly
+ * formatted
+ */
+ public static void importPreferences(InputStream is)
+ throws InvalidPreferencesFormatException,
+ IOException
+ {
+ PreferencesFactory factory = getFactory();
+ NodeReader reader = new NodeReader(is, factory);
+ reader.importPreferences();
+ }
+
+ // abstract methods (identification)
+
+ /**
+ * Returns the absolute path name of this preference node.
+ * The absolute path name of a node is the path name of its parent node
+ * plus a '/' plus its own name. If the node is the root node and has no
+ * parent then its name is "" and its absolute path name is "/".
+ */
+ public abstract String absolutePath();
+
+ /**
+ * Returns true if this node comes from the user preferences tree, false
+ * if it comes from the system preferences tree.
+ */
+ public abstract boolean isUserNode();
+
+ /**
+ * Returns the name of this preferences node. The name of the node cannot
+ * be null, can be mostly 80 characters and cannot contain any '/'
+ * characters. The root node has as name "".
+ */
+ public abstract String name();
+
+ /**
+ * Returns the String given by
+ * <code>
+ * (isUserNode() ? "User":"System") + " Preference Node: " + absolutePath()
+ * </code>
+ */
+ public abstract String toString();
+
+ // abstract methods (navigation)
+
+ /**
+ * Returns all the direct sub nodes of this preferences node.
+ * Needs access to the backing store to give a meaningfull answer.
+ *
+ * @exception BackingStoreException when the backing store cannot be
+ * reached
+ * @exception IllegalStateException when this node has been removed
+ */
+ public abstract String[] childrenNames() throws BackingStoreException;
+
+ /**
+ * Returns a sub node of this preferences node if the given path is
+ * relative (does not start with a '/') or a sub node of the root
+ * if the path is absolute (does start with a '/').
+ *
+ * @exception IllegalStateException if this node has been removed
+ * @exception IllegalArgumentException if the path contains two or more
+ * consecutive '/' characters, ends with a '/' charactor and is not the
+ * string "/" (indicating the root node) or any name on the path is more
+ * then 80 characters long
+ */
+ public abstract Preferences node(String path);
+
+ /**
+ * Returns true if the node that the path points to exists in memory or
+ * in the backing store. Otherwise it returns false or an exception is
+ * thrown. When this node is removed the only valid parameter is the
+ * empty string (indicating this node), the return value in that case
+ * will be false.
+ *
+ * @exception BackingStoreException when the backing store cannot be
+ * reached
+ * @exception IllegalStateException if this node has been removed
+ * and the path is not the empty string (indicating this node)
+ * @exception IllegalArgumentException if the path contains two or more
+ * consecutive '/' characters, ends with a '/' charactor and is not the
+ * string "/" (indicating the root node) or any name on the path is more
+ * then 80 characters long
+ */
+ public abstract boolean nodeExists(String path)
+ throws BackingStoreException;
+
+ /**
+ * Returns the parent preferences node of this node or null if this is
+ * the root of the preferences tree.
+ *
+ * @exception IllegalStateException if this node has been removed
+ */
+ public abstract Preferences parent();
+
+ // abstract methods (export)
+
+ /**
+ * Export this node, but not its descendants, as XML to the
+ * indicated output stream. The XML will be encoded using UTF-8
+ * and will use a specified document type:<br>
+ * <code>&lt;!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd"&gt;</code><br>
+ * @param os the output stream to which the XML is sent
+ * @throws BackingStoreException if preference data cannot be read
+ * @throws IOException if an error occurs while writing the XML
+ * @throws IllegalStateException if this node or an ancestor has been removed
+ */
+ public abstract void exportNode(OutputStream os)
+ throws BackingStoreException,
+ IOException;
+
+ /**
+ * Export this node and all its descendants as XML to the
+ * indicated output stream. The XML will be encoded using UTF-8
+ * and will use a specified document type:<br>
+ * <code>&lt;!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd"&gt;</code><br>
+ * @param os the output stream to which the XML is sent
+ * @throws BackingStoreException if preference data cannot be read
+ * @throws IOException if an error occurs while writing the XML
+ * @throws IllegalStateException if this node or an ancestor has been removed
+ */
+ public abstract void exportSubtree(OutputStream os)
+ throws BackingStoreException,
+ IOException;
+
+ // abstract methods (preference entry manipulation)
+
+ /**
+ * Returns an (possibly empty) array with all the keys of the preference
+ * entries of this node.
+ *
+ * @exception BackingStoreException when the backing store cannot be
+ * reached
+ * @exception IllegalStateException if this node has been removed
+ */
+ public abstract String[] keys() throws BackingStoreException;
+
+ /**
+ * Returns the value associated with the key in this preferences node. If
+ * the default value of the key cannot be found in the preferences node
+ * entries or something goes wrong with the backing store the supplied
+ * default value is returned.
+ *
+ * @exception IllegalArgumentException if key is larger then 80 characters
+ * @exception IllegalStateException if this node has been removed
+ * @exception NullPointerException if key is null
+ */
+ public abstract String get(String key, String defaultVal);
+
+ /**
+ * Convenience method for getting the given entry as a boolean.
+ * When the string representation of the requested entry is either
+ * "true" or "false" (ignoring case) then that value is returned,
+ * otherwise the given default boolean value is returned.
+ *
+ * @exception IllegalArgumentException if key is larger then 80 characters
+ * @exception IllegalStateException if this node has been removed
+ * @exception NullPointerException if key is null
+ */
+ public abstract boolean getBoolean(String key, boolean defaultVal);
+
+ /**
+ * Convenience method for getting the given entry as a byte array.
+ * When the string representation of the requested entry is a valid
+ * Base64 encoded string (without any other characters, such as newlines)
+ * then the decoded Base64 string is returned as byte array,
+ * otherwise the given default byte array value is returned.
+ *
+ * @exception IllegalArgumentException if key is larger then 80 characters
+ * @exception IllegalStateException if this node has been removed
+ * @exception NullPointerException if key is null
+ */
+ public abstract byte[] getByteArray(String key, byte[] defaultVal);
+
+ /**
+ * Convenience method for getting the given entry as a double.
+ * When the string representation of the requested entry can be decoded
+ * with <code>Double.parseDouble()</code> then that double is returned,
+ * otherwise the given default double value is returned.
+ *
+ * @exception IllegalArgumentException if key is larger then 80 characters
+ * @exception IllegalStateException if this node has been removed
+ * @exception NullPointerException if key is null
+ */
+ public abstract double getDouble(String key, double defaultVal);
+
+ /**
+ * Convenience method for getting the given entry as a float.
+ * When the string representation of the requested entry can be decoded
+ * with <code>Float.parseFloat()</code> then that float is returned,
+ * otherwise the given default float value is returned.
+ *
+ * @exception IllegalArgumentException if key is larger then 80 characters
+ * @exception IllegalStateException if this node has been removed
+ * @exception NullPointerException if key is null
+ */
+ public abstract float getFloat(String key, float defaultVal);
+
+ /**
+ * Convenience method for getting the given entry as an integer.
+ * When the string representation of the requested entry can be decoded
+ * with <code>Integer.parseInt()</code> then that integer is returned,
+ * otherwise the given default integer value is returned.
+ *
+ * @exception IllegalArgumentException if key is larger then 80 characters
+ * @exception IllegalStateException if this node has been removed
+ * @exception NullPointerException if key is null
+ */
+ public abstract int getInt(String key, int defaultVal);
+
+ /**
+ * Convenience method for getting the given entry as a long.
+ * When the string representation of the requested entry can be decoded
+ * with <code>Long.parseLong()</code> then that long is returned,
+ * otherwise the given default long value is returned.
+ *
+ * @exception IllegalArgumentException if key is larger then 80 characters
+ * @exception IllegalStateException if this node has been removed
+ * @exception NullPointerException if key is null
+ */
+ public abstract long getLong(String key, long defaultVal);
+
+ /**
+ * Sets the value of the given preferences entry for this node.
+ * Key and value cannot be null, the key cannot exceed 80 characters
+ * and the value cannot exceed 8192 characters.
+ * <p>
+ * The result will be immediatly visible in this VM, but may not be
+ * immediatly written to the backing store.
+ *
+ * @exception NullPointerException if either key or value are null
+ * @exception IllegalArgumentException if either key or value are to large
+ * @exception IllegalStateException when this node has been removed
+ */
+ public abstract void put(String key, String value);
+
+ /**
+ * Convenience method for setting the given entry as a boolean.
+ * The boolean is converted with <code>Boolean.toString(value)</code>
+ * and then stored in the preference entry as that string.
+ *
+ * @exception NullPointerException if key is null
+ * @exception IllegalArgumentException if the key length is to large
+ * @exception IllegalStateException when this node has been removed
+ */
+ public abstract void putBoolean(String key, boolean value);
+
+ /**
+ * Convenience method for setting the given entry as an array of bytes.
+ * The byte array is converted to a Base64 encoded string
+ * and then stored in the preference entry as that string.
+ * <p>
+ * Note that a byte array encoded as a Base64 string will be about 1.3
+ * times larger then the original length of the byte array, which means
+ * that the byte array may not be larger about 6 KB.
+ *
+ * @exception NullPointerException if either key or value are null
+ * @exception IllegalArgumentException if either key or value are to large
+ * @exception IllegalStateException when this node has been removed
+ */
+ public abstract void putByteArray(String key, byte[] value);
+
+ /**
+ * Convenience method for setting the given entry as a double.
+ * The double is converted with <code>Double.toString(double)</code>
+ * and then stored in the preference entry as that string.
+ *
+ * @exception NullPointerException if the key is null
+ * @exception IllegalArgumentException if the key length is to large
+ * @exception IllegalStateException when this node has been removed
+ */
+ public abstract void putDouble(String key, double value);
+
+ /**
+ * Convenience method for setting the given entry as a float.
+ * The float is converted with <code>Float.toString(float)</code>
+ * and then stored in the preference entry as that string.
+ *
+ * @exception NullPointerException if the key is null
+ * @exception IllegalArgumentException if the key length is to large
+ * @exception IllegalStateException when this node has been removed
+ */
+ public abstract void putFloat(String key, float value);
+
+ /**
+ * Convenience method for setting the given entry as an integer.
+ * The integer is converted with <code>Integer.toString(int)</code>
+ * and then stored in the preference entry as that string.
+ *
+ * @exception NullPointerException if the key is null
+ * @exception IllegalArgumentException if the key length is to large
+ * @exception IllegalStateException when this node has been removed
+ */
+ public abstract void putInt(String key, int value);
+
+ /**
+ * Convenience method for setting the given entry as a long.
+ * The long is converted with <code>Long.toString(long)</code>
+ * and then stored in the preference entry as that string.
+ *
+ * @exception NullPointerException if the key is null
+ * @exception IllegalArgumentException if the key length is to large
+ * @exception IllegalStateException when this node has been removed
+ */
+ public abstract void putLong(String key, long value);
+
+ /**
+ * Removes the preferences entry from this preferences node.
+ * <p>
+ * The result will be immediatly visible in this VM, but may not be
+ * immediatly written to the backing store.
+ *
+ * @exception NullPointerException if the key is null
+ * @exception IllegalArgumentException if the key length is to large
+ * @exception IllegalStateException when this node has been removed
+ */
+ public abstract void remove(String key);
+
+ // abstract methods (preference node manipulation)
+
+ /**
+ * Removes all entries from this preferences node. May need access to the
+ * backing store to get and clear all entries.
+ * <p>
+ * The result will be immediatly visible in this VM, but may not be
+ * immediatly written to the backing store.
+ *
+ * @exception BackingStoreException when the backing store cannot be
+ * reached
+ * @exception IllegalStateException if this node has been removed
+ */
+ public abstract void clear() throws BackingStoreException;
+
+ /**
+ * Writes all preference changes on this and any subnode that have not
+ * yet been written to the backing store. This has no effect on the
+ * preference entries in this VM, but it makes sure that all changes
+ * are visible to other programs (other VMs might need to call the
+ * <code>sync()</code> method to actually see the changes to the backing
+ * store.
+ *
+ * @exception BackingStoreException when the backing store cannot be
+ * reached
+ * @exception IllegalStateException if this node has been removed
+ */
+ public abstract void flush() throws BackingStoreException;
+
+ /**
+ * Writes and reads all preference changes to and from this and any
+ * subnodes. This makes sure that all local changes are written to the
+ * backing store and that all changes to the backing store are visible
+ * in this preference node (and all subnodes).
+ *
+ * @exception BackingStoreException when the backing store cannot be
+ * reached
+ * @exception IllegalStateException if this node has been removed
+ */
+ public abstract void sync() throws BackingStoreException;
+
+ /**
+ * Removes this and all subnodes from the backing store and clears all
+ * entries. After removal this instance will not be useable (except for
+ * a few methods that don't throw a <code>InvalidStateException</code>),
+ * even when a new node with the same path name is created this instance
+ * will not be usable again. The root (system or user) may never be removed.
+ * <p>
+ * Note that according to the specification an implementation may delay
+ * removal of the node from the backing store till the <code>flush()</code>
+ * method is called. But the <code>flush()</code> method may throw a
+ * <code>IllegalStateException</code> when the node has been removed.
+ * So most implementations will actually remove the node and any subnodes
+ * from the backing store immediatly.
+ *
+ * @exception BackingStoreException when the backing store cannot be
+ * reached
+ * @exception IllegalStateException if this node has already been removed
+ * @exception UnsupportedOperationException if this is a root node
+ */
+ public abstract void removeNode() throws BackingStoreException;
+
+ // abstract methods (listeners)
+
+ public abstract void addNodeChangeListener(NodeChangeListener listener);
+
+ public abstract void addPreferenceChangeListener
+ (PreferenceChangeListener listener);
+
+ public abstract void removeNodeChangeListener(NodeChangeListener listener);
+
+ public abstract void removePreferenceChangeListener
+ (PreferenceChangeListener listener);
+}
diff --git a/libjava/classpath/java/util/prefs/PreferencesFactory.java b/libjava/classpath/java/util/prefs/PreferencesFactory.java
new file mode 100644
index 000000000..5674f806d
--- /dev/null
+++ b/libjava/classpath/java/util/prefs/PreferencesFactory.java
@@ -0,0 +1,65 @@
+/* PreferencesFactory - Preferences system and user root factory interface
+ Copyright (C) 2001 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.prefs;
+
+/**
+ * Preferences system and user root factory interface. Defines how to get
+ * to the system and user root preferences objects. Should be implemented by
+ * new preferences backends.
+ *
+ * @since 1.4
+ * @author Mark Wielaard (mark@klomp.org)
+ */
+public interface PreferencesFactory {
+
+ /**
+ * Returns the system root preferences node. Should always return the
+ * same object.
+ */
+ Preferences systemRoot();
+
+ /**
+ * Returns the user root preferences node. May return different objects
+ * depending on the user that called this method. The user may for example
+ * be determined by the current Thread or the Subject associated with the
+ * current AccessControllContext.
+ */
+ Preferences userRoot();
+
+}
diff --git a/libjava/classpath/java/util/prefs/package.html b/libjava/classpath/java/util/prefs/package.html
new file mode 100644
index 000000000..65fc1ac79
--- /dev/null
+++ b/libjava/classpath/java/util/prefs/package.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in java.util.prefs package.
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - java.util.prefs</title></head>
+
+<body>
+<p>Utility classes for storing and retrieving user and system preferences.</p>
+
+</body>
+</html>
diff --git a/libjava/classpath/java/util/regex/MatchResult.java b/libjava/classpath/java/util/regex/MatchResult.java
new file mode 100644
index 000000000..605873dd0
--- /dev/null
+++ b/libjava/classpath/java/util/regex/MatchResult.java
@@ -0,0 +1,81 @@
+/* MatchResult.java -- Result of a regular expression match.
+ Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.regex;
+
+/**
+ * This interface represents the result of a regular expression match.
+ * It can be used to query the contents of the match, but not to modify
+ * them.
+ * @since 1.5
+ */
+public interface MatchResult
+{
+ /** Returns the index just after the last matched character. */
+ int end();
+
+ /**
+ * Returns the index just after the last matched character of the
+ * given sub-match group.
+ * @param group the sub-match group
+ */
+ int end(int group);
+
+ /** Returns the substring of the input which was matched. */
+ String group();
+
+ /**
+ * Returns the substring of the input which was matched by the
+ * given sub-match group.
+ * @param group the sub-match group
+ */
+ String group(int group);
+
+ /** Returns the number of sub-match groups in the matching pattern. */
+ int groupCount();
+
+ /** Returns the index of the first character of the match. */
+ int start();
+
+ /**
+ * Returns the index of the first character of the given sub-match
+ * group.
+ * @param group the sub-match group
+ */
+ int start(int group);
+}
diff --git a/libjava/classpath/java/util/regex/Matcher.java b/libjava/classpath/java/util/regex/Matcher.java
new file mode 100644
index 000000000..be57471de
--- /dev/null
+++ b/libjava/classpath/java/util/regex/Matcher.java
@@ -0,0 +1,611 @@
+/* Matcher.java -- Instance of a regular expression applied to a char sequence.
+ Copyright (C) 2002, 2004, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.regex;
+
+import gnu.java.lang.CPStringBuilder;
+
+import gnu.java.util.regex.CharIndexed;
+import gnu.java.util.regex.RE;
+import gnu.java.util.regex.REMatch;
+
+/**
+ * Instance of a regular expression applied to a char sequence.
+ *
+ * @since 1.4
+ */
+public final class Matcher implements MatchResult
+{
+ private Pattern pattern;
+ private CharSequence input;
+ // We use CharIndexed as an input object to the getMatch method in order
+ // that /\G/ (the end of the previous match) may work. The information
+ // of the previous match is stored in the CharIndexed object.
+ private CharIndexed inputCharIndexed;
+ private int position;
+ private int appendPosition;
+ private REMatch match;
+
+ /**
+ * The start of the region of the input on which to match.
+ */
+ private int regionStart;
+
+ /**
+ * The end of the region of the input on which to match.
+ */
+ private int regionEnd;
+
+ /**
+ * True if the match process should look beyond the
+ * region marked by regionStart to regionEnd when
+ * performing lookAhead, lookBehind and boundary
+ * matching.
+ */
+ private boolean transparentBounds;
+
+ /**
+ * The flags that affect the anchoring bounds.
+ * If {@link #hasAnchoringBounds()} is {@code true},
+ * the match process will honour the
+ * anchoring bounds: ^, \A, \Z, \z and $. If
+ * {@link #hasAnchoringBounds()} is {@code false},
+ * the anchors are ignored and appropriate flags,
+ * stored in this variable, are used to provide this
+ * behaviour.
+ */
+ private int anchoringBounds;
+
+ Matcher(Pattern pattern, CharSequence input)
+ {
+ this.pattern = pattern;
+ this.input = input;
+ this.inputCharIndexed = RE.makeCharIndexed(input, 0);
+ regionStart = 0;
+ regionEnd = input.length();
+ transparentBounds = false;
+ anchoringBounds = 0;
+ }
+
+ /**
+ * @param sb The target string buffer
+ * @param replacement The replacement string
+ *
+ * @exception IllegalStateException If no match has yet been attempted,
+ * or if the previous match operation failed
+ * @exception IndexOutOfBoundsException If the replacement string refers
+ * to a capturing group that does not exist in the pattern
+ */
+ public Matcher appendReplacement (StringBuffer sb, String replacement)
+ throws IllegalStateException
+ {
+ assertMatchOp();
+ sb.append(input.subSequence(appendPosition,
+ match.getStartIndex()).toString());
+ sb.append(RE.getReplacement(replacement, match,
+ RE.REG_REPLACE_USE_BACKSLASHESCAPE));
+ appendPosition = match.getEndIndex();
+ return this;
+ }
+
+ /**
+ * @param sb The target string buffer
+ */
+ public StringBuffer appendTail (StringBuffer sb)
+ {
+ sb.append(input.subSequence(appendPosition, input.length()).toString());
+ return sb;
+ }
+
+ /**
+ * @exception IllegalStateException If no match has yet been attempted,
+ * or if the previous match operation failed
+ */
+ public int end ()
+ throws IllegalStateException
+ {
+ assertMatchOp();
+ return match.getEndIndex();
+ }
+
+ /**
+ * @param group The index of a capturing group in this matcher's pattern
+ *
+ * @exception IllegalStateException If no match has yet been attempted,
+ * or if the previous match operation failed
+ * @exception IndexOutOfBoundsException If the replacement string refers
+ * to a capturing group that does not exist in the pattern
+ */
+ public int end (int group)
+ throws IllegalStateException
+ {
+ assertMatchOp();
+ return match.getEndIndex(group);
+ }
+
+ public boolean find ()
+ {
+ boolean first = (match == null);
+ if (transparentBounds || (regionStart == 0 && regionEnd == input.length()))
+ match = pattern.getRE().getMatch(inputCharIndexed, position, anchoringBounds);
+ else
+ match = pattern.getRE().getMatch(input.subSequence(regionStart, regionEnd),
+ position, anchoringBounds);
+ if (match != null)
+ {
+ int endIndex = match.getEndIndex();
+ // Are we stuck at the same position?
+ if (!first && endIndex == position)
+ {
+ match = null;
+ // Not at the end of the input yet?
+ if (position < input.length() - 1)
+ {
+ position++;
+ return find(position);
+ }
+ else
+ return false;
+ }
+ position = endIndex;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @param start The index to start the new pattern matching
+ *
+ * @exception IndexOutOfBoundsException If the replacement string refers
+ * to a capturing group that does not exist in the pattern
+ */
+ public boolean find (int start)
+ {
+ if (transparentBounds || (regionStart == 0 && regionEnd == input.length()))
+ match = pattern.getRE().getMatch(inputCharIndexed, start, anchoringBounds);
+ else
+ match = pattern.getRE().getMatch(input.subSequence(regionStart, regionEnd),
+ start, anchoringBounds);
+ if (match != null)
+ {
+ position = match.getEndIndex();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @exception IllegalStateException If no match has yet been attempted,
+ * or if the previous match operation failed
+ */
+ public String group ()
+ {
+ assertMatchOp();
+ return match.toString();
+ }
+
+ /**
+ * @param group The index of a capturing group in this matcher's pattern
+ *
+ * @exception IllegalStateException If no match has yet been attempted,
+ * or if the previous match operation failed
+ * @exception IndexOutOfBoundsException If the replacement string refers
+ * to a capturing group that does not exist in the pattern
+ */
+ public String group (int group)
+ throws IllegalStateException
+ {
+ assertMatchOp();
+ return match.toString(group);
+ }
+
+ /**
+ * @param replacement The replacement string
+ */
+ public String replaceFirst (String replacement)
+ {
+ reset();
+ // Semantics might not quite match
+ return pattern.getRE().substitute(input, replacement, position,
+ RE.REG_REPLACE_USE_BACKSLASHESCAPE);
+ }
+
+ /**
+ * @param replacement The replacement string
+ */
+ public String replaceAll (String replacement)
+ {
+ reset();
+ return pattern.getRE().substituteAll(input, replacement, position,
+ RE.REG_REPLACE_USE_BACKSLASHESCAPE);
+ }
+
+ public int groupCount ()
+ {
+ return pattern.getRE().getNumSubs();
+ }
+
+ public boolean lookingAt ()
+ {
+ if (transparentBounds || (regionStart == 0 && regionEnd == input.length()))
+ match = pattern.getRE().getMatch(inputCharIndexed, regionStart,
+ anchoringBounds|RE.REG_FIX_STARTING_POSITION|RE.REG_ANCHORINDEX);
+ else
+ match = pattern.getRE().getMatch(input.subSequence(regionStart, regionEnd), 0,
+ anchoringBounds|RE.REG_FIX_STARTING_POSITION);
+ if (match != null)
+ {
+ if (match.getStartIndex() == 0)
+ {
+ position = match.getEndIndex();
+ return true;
+ }
+ match = null;
+ }
+ return false;
+ }
+
+ /**
+ * Attempts to match the entire input sequence against the pattern.
+ *
+ * If the match succeeds then more information can be obtained via the
+ * start, end, and group methods.
+ *
+ * @see #start()
+ * @see #end()
+ * @see #group()
+ */
+ public boolean matches ()
+ {
+ if (transparentBounds || (regionStart == 0 && regionEnd == input.length()))
+ match = pattern.getRE().getMatch(inputCharIndexed, regionStart,
+ anchoringBounds|RE.REG_TRY_ENTIRE_MATCH|RE.REG_FIX_STARTING_POSITION|RE.REG_ANCHORINDEX);
+ else
+ match = pattern.getRE().getMatch(input.subSequence(regionStart, regionEnd), 0,
+ anchoringBounds|RE.REG_TRY_ENTIRE_MATCH|RE.REG_FIX_STARTING_POSITION);
+ if (match != null)
+ {
+ if (match.getStartIndex() == 0)
+ {
+ position = match.getEndIndex();
+ if (position == input.length())
+ return true;
+ }
+ match = null;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the Pattern that is interpreted by this Matcher
+ */
+ public Pattern pattern ()
+ {
+ return pattern;
+ }
+
+ /**
+ * Resets the internal state of the matcher, including
+ * resetting the region to its default state of encompassing
+ * the whole input. The state of {@link #hasTransparentBounds()}
+ * and {@link #hasAnchoringBounds()} are unaffected.
+ *
+ * @return a reference to this matcher.
+ * @see #regionStart()
+ * @see #regionEnd()
+ * @see #hasTransparentBounds()
+ * @see #hasAnchoringBounds()
+ */
+ public Matcher reset ()
+ {
+ position = 0;
+ match = null;
+ regionStart = 0;
+ regionEnd = input.length();
+ appendPosition = 0;
+ return this;
+ }
+
+ /**
+ * Resets the internal state of the matcher, including
+ * resetting the region to its default state of encompassing
+ * the whole input. The state of {@link #hasTransparentBounds()}
+ * and {@link #hasAnchoringBounds()} are unaffected.
+ *
+ * @param input The new input character sequence.
+ * @return a reference to this matcher.
+ * @see #regionStart()
+ * @see #regionEnd()
+ * @see #hasTransparentBounds()
+ * @see #hasAnchoringBounds()
+ */
+ public Matcher reset (CharSequence input)
+ {
+ this.input = input;
+ this.inputCharIndexed = RE.makeCharIndexed(input, 0);
+ return reset();
+ }
+
+ /**
+ * @return the index of a capturing group in this matcher's pattern
+ *
+ * @exception IllegalStateException If no match has yet been attempted,
+ * or if the previous match operation failed
+ */
+ public int start ()
+ throws IllegalStateException
+ {
+ assertMatchOp();
+ return match.getStartIndex();
+ }
+
+ /**
+ * @param group The index of a capturing group in this matcher's pattern
+ *
+ * @exception IllegalStateException If no match has yet been attempted,
+ * or if the previous match operation failed
+ * @exception IndexOutOfBoundsException If the replacement string refers
+ * to a capturing group that does not exist in the pattern
+ */
+ public int start (int group)
+ throws IllegalStateException
+ {
+ assertMatchOp();
+ return match.getStartIndex(group);
+ }
+
+ /**
+ * @return True if and only if the matcher hit the end of input.
+ * @since 1.5
+ */
+ public boolean hitEnd()
+ {
+ return inputCharIndexed.hitEnd();
+ }
+
+ /**
+ * @return A string expression of this matcher.
+ */
+ public String toString()
+ {
+ CPStringBuilder sb = new CPStringBuilder();
+ sb.append(this.getClass().getName())
+ .append("[pattern=").append(pattern.pattern())
+ .append(" region=").append(regionStart).append(",").append(regionEnd)
+ .append(" anchoringBounds=").append(anchoringBounds == 0)
+ .append(" transparentBounds=").append(transparentBounds)
+ .append(" lastmatch=").append(match == null ? "" : match.toString())
+ .append("]");
+ return sb.toString();
+ }
+
+ private void assertMatchOp()
+ {
+ if (match == null) throw new IllegalStateException();
+ }
+
+ /**
+ * <p>
+ * Defines the region of the input on which to match.
+ * By default, the {@link Matcher} attempts to match
+ * the whole string (from 0 to the length of the input),
+ * but a region between {@code start} (inclusive) and
+ * {@code end} (exclusive) on which to match may instead
+ * be defined using this method.
+ * </p>
+ * <p>
+ * The behaviour of region matching is further affected
+ * by the use of transparent or opaque bounds (see
+ * {@link #useTransparentBounds(boolean)}) and whether or not
+ * anchors ({@code ^} and {@code $}) are in use
+ * (see {@link #useAnchoringBounds(boolean)}). With transparent
+ * bounds, the matcher is aware of input outside the bounds
+ * set by this method, whereas, with opaque bounds (the default)
+ * only the input within the bounds is used. The use of
+ * anchors are affected by this setting; with transparent
+ * bounds, anchors will match the beginning of the real input,
+ * while with opaque bounds they match the beginning of the
+ * region. {@link #useAnchoringBounds(boolean)} can be used
+ * to turn on or off the matching of anchors.
+ * </p>
+ *
+ * @param start the start of the region (inclusive).
+ * @param end the end of the region (exclusive).
+ * @return a reference to this matcher.
+ * @throws IndexOutOfBoundsException if either {@code start} or
+ * {@code end} are less than zero,
+ * if either {@code start} or
+ * {@code end} are greater than the
+ * length of the input, or if
+ * {@code start} is greater than
+ * {@code end}.
+ * @see #regionStart()
+ * @see #regionEnd()
+ * @see #hasTransparentBounds()
+ * @see #useTransparentBounds(boolean)
+ * @see #hasAnchoringBounds()
+ * @see #useAnchoringBounds(boolean)
+ * @since 1.5
+ */
+ public Matcher region(int start, int end)
+ {
+ int length = input.length();
+ if (start < 0)
+ throw new IndexOutOfBoundsException("The start position was less than zero.");
+ if (start >= length)
+ throw new IndexOutOfBoundsException("The start position is after the end of the input.");
+ if (end < 0)
+ throw new IndexOutOfBoundsException("The end position was less than zero.");
+ if (end > length)
+ throw new IndexOutOfBoundsException("The end position is after the end of the input.");
+ if (start > end)
+ throw new IndexOutOfBoundsException("The start position is after the end position.");
+ reset();
+ regionStart = start;
+ regionEnd = end;
+ return this;
+ }
+
+ /**
+ * The start of the region on which to perform matches (inclusive).
+ *
+ * @return the start index of the region.
+ * @see #region(int,int)
+ * #see #regionEnd()
+ * @since 1.5
+ */
+ public int regionStart()
+ {
+ return regionStart;
+ }
+
+ /**
+ * The end of the region on which to perform matches (exclusive).
+ *
+ * @return the end index of the region.
+ * @see #region(int,int)
+ * @see #regionStart()
+ * @since 1.5
+ */
+ public int regionEnd()
+ {
+ return regionEnd;
+ }
+
+ /**
+ * Returns true if the bounds of the region marked by
+ * {@link #regionStart()} and {@link #regionEnd()} are
+ * transparent. When these bounds are transparent, the
+ * matching process can look beyond them to perform
+ * lookahead, lookbehind and boundary matching operations.
+ * By default, the bounds are opaque.
+ *
+ * @return true if the bounds of the matching region are
+ * transparent.
+ * @see #useTransparentBounds(boolean)
+ * @see #region(int,int)
+ * @see #regionStart()
+ * @see #regionEnd()
+ * @since 1.5
+ */
+ public boolean hasTransparentBounds()
+ {
+ return transparentBounds;
+ }
+
+ /**
+ * Sets the transparency of the bounds of the region
+ * marked by {@link #regionStart()} and {@link #regionEnd()}.
+ * A value of {@code true} makes the bounds transparent,
+ * so the matcher can see beyond them to perform lookahead,
+ * lookbehind and boundary matching operations. A value
+ * of {@code false} (the default) makes the bounds opaque,
+ * restricting the match to the input region denoted
+ * by {@link #regionStart()} and {@link #regionEnd()}.
+ *
+ * @param transparent true if the bounds should be transparent.
+ * @return a reference to this matcher.
+ * @see #hasTransparentBounds()
+ * @see #region(int,int)
+ * @see #regionStart()
+ * @see #regionEnd()
+ * @since 1.5
+ */
+ public Matcher useTransparentBounds(boolean transparent)
+ {
+ transparentBounds = transparent;
+ return this;
+ }
+
+ /**
+ * Returns true if the matcher will honour the use of
+ * the anchoring bounds: {@code ^}, {@code \A}, {@code \Z},
+ * {@code \z} and {@code $}. By default, the anchors
+ * are used. Note that the effect of the anchors is
+ * also affected by {@link #hasTransparentBounds()}.
+ *
+ * @return true if the matcher will attempt to match
+ * the anchoring bounds.
+ * @see #useAnchoringBounds(boolean)
+ * @see #hasTransparentBounds()
+ * @since 1.5
+ */
+ public boolean hasAnchoringBounds()
+ {
+ return anchoringBounds == 0;
+ }
+
+ /**
+ * Enables or disables the use of the anchoring bounds:
+ * {@code ^}, {@code \A}, {@code \Z}, {@code \z} and
+ * {@code $}. By default, their use is enabled. When
+ * disabled, the matcher will not attempt to match
+ * the anchors.
+ *
+ * @param useAnchors true if anchoring bounds should be used.
+ * @return a reference to this matcher.
+ * @since 1.5
+ * @see #hasAnchoringBounds()
+ */
+ public Matcher useAnchoringBounds(boolean useAnchors)
+ {
+ if (useAnchors)
+ anchoringBounds = 0;
+ else
+ anchoringBounds = RE.REG_NOTBOL|RE.REG_NOTEOL;
+ return this;
+ }
+
+ /**
+ * Returns a read-only snapshot of the current state of
+ * the {@link Matcher} as a {@link MatchResult}. Any
+ * subsequent changes to this instance are not reflected
+ * in the returned {@link MatchResult}.
+ *
+ * @return a {@link MatchResult} instance representing the
+ * current state of the {@link Matcher}.
+ */
+ public MatchResult toMatchResult()
+ {
+ Matcher snapshot = new Matcher(pattern, input);
+ if (match != null)
+ snapshot.match = (REMatch) match.clone();
+ return snapshot;
+ }
+
+}
diff --git a/libjava/classpath/java/util/regex/Pattern.java b/libjava/classpath/java/util/regex/Pattern.java
new file mode 100644
index 000000000..7d1fc84b4
--- /dev/null
+++ b/libjava/classpath/java/util/regex/Pattern.java
@@ -0,0 +1,271 @@
+/* Pattern.java -- Compiled regular expression ready to be applied.
+ Copyright (C) 2002, 2004, 2005, 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.regex;
+
+import gnu.java.util.regex.RE;
+import gnu.java.util.regex.REException;
+import gnu.java.util.regex.RESyntax;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+
+
+/**
+ * Compiled regular expression ready to be applied.
+ *
+ * @since 1.4
+ */
+public final class Pattern implements Serializable
+{
+ private static final long serialVersionUID = 5073258162644648461L;
+
+ public static final int CANON_EQ = 128;
+ public static final int CASE_INSENSITIVE = 2;
+ public static final int COMMENTS = 4;
+ public static final int DOTALL = 32;
+ public static final int MULTILINE = 8;
+ public static final int UNICODE_CASE = 64;
+ public static final int UNIX_LINES = 1;
+
+ private final String regex;
+ private final int flags;
+
+ private final RE re;
+
+ private Pattern (String regex, int flags)
+ throws PatternSyntaxException
+ {
+ this.regex = regex;
+ this.flags = flags;
+
+ RESyntax syntax = RESyntax.RE_SYNTAX_JAVA_1_4;
+ int gnuFlags = 0;
+ gnuFlags |= RE.REG_ICASE_USASCII;
+ if ((flags & CASE_INSENSITIVE) != 0)
+ gnuFlags |= RE.REG_ICASE;
+ if ((flags & MULTILINE) != 0)
+ {
+ gnuFlags |= RE.REG_MULTILINE;
+ syntax = new RESyntax(syntax);
+ syntax.setLineSeparator(null);
+ }
+ if ((flags & DOTALL) != 0)
+ gnuFlags |= RE.REG_DOT_NEWLINE;
+ if ((flags & UNICODE_CASE) != 0)
+ gnuFlags &= ~RE.REG_ICASE_USASCII;
+ // not yet supported:
+ // if ((flags & CANON_EQ) != 0) gnuFlags =
+
+ if ((flags & UNIX_LINES) != 0)
+ {
+ // Use a syntax set with \n for linefeeds?
+ syntax = new RESyntax(syntax);
+ syntax.setLineSeparator("\n");
+ }
+
+ if ((flags & COMMENTS) != 0)
+ {
+ gnuFlags |= RE.REG_X_COMMENTS;
+ }
+
+ try
+ {
+ this.re = new RE(regex, gnuFlags, syntax);
+ }
+ catch (REException e)
+ {
+ PatternSyntaxException pse;
+ pse = new PatternSyntaxException(e.getMessage(),
+ regex, e.getPosition());
+ pse.initCause(e);
+ throw pse;
+ }
+ }
+
+ // package private accessor method
+ RE getRE()
+ {
+ return re;
+ }
+
+ /**
+ * @param regex The regular expression
+ *
+ * @exception PatternSyntaxException If the expression's syntax is invalid
+ */
+ public static Pattern compile (String regex)
+ throws PatternSyntaxException
+ {
+ return compile(regex, 0);
+ }
+
+ /**
+ * @param regex The regular expression
+ * @param flags The match flags, a bit mask
+ *
+ * @exception PatternSyntaxException If the expression's syntax is invalid
+ * @exception IllegalArgumentException If bit values other than those
+ * corresponding to the defined match flags are set in flags
+ */
+ public static Pattern compile (String regex, int flags)
+ throws PatternSyntaxException
+ {
+ // FIXME: check which flags are really accepted
+ if ((flags & ~0xEF) != 0)
+ throw new IllegalArgumentException ();
+
+ return new Pattern (regex, flags);
+ }
+
+ public int flags ()
+ {
+ return this.flags;
+ }
+
+ /**
+ * @param regex The regular expression
+ * @param input The character sequence to be matched
+ *
+ * @exception PatternSyntaxException If the expression's syntax is invalid
+ */
+ public static boolean matches (String regex, CharSequence input)
+ {
+ return compile(regex).matcher(input).matches();
+ }
+
+ /**
+ * @param input The character sequence to be matched
+ */
+ public Matcher matcher (CharSequence input)
+ {
+ return new Matcher(this, input);
+ }
+
+ /**
+ * @param input The character sequence to be matched
+ */
+ public String[] split (CharSequence input)
+ {
+ return split(input, 0);
+ }
+
+ /**
+ * @param input The character sequence to be matched
+ * @param limit The result threshold
+ */
+ public String[] split (CharSequence input, int limit)
+ {
+ Matcher matcher = new Matcher(this, input);
+ ArrayList<String> list = new ArrayList<String>();
+ int empties = 0;
+ int count = 0;
+ int start = 0;
+ int end;
+ boolean matched = matcher.find();
+
+ while (matched && (limit <= 0 || count < limit - 1))
+ {
+ ++count;
+ end = matcher.start();
+ if (start == end)
+ empties++;
+ else
+ {
+ while (empties > 0)
+ {
+ list.add("");
+ empties--;
+ }
+
+ String text = input.subSequence(start, end).toString();
+ list.add(text);
+ }
+ start = matcher.end();
+ matched = matcher.find();
+ }
+
+ // We matched nothing.
+ if (!matched && count == 0)
+ return new String[] { input.toString() };
+
+ // Is the last token empty?
+ boolean emptyLast = (start == input.length());
+
+ // Can/Must we add empties or an extra last token at the end?
+ if (list.size() < limit || limit < 0 || (limit == 0 && !emptyLast))
+ {
+ if (limit > list.size())
+ {
+ int max = limit - list.size();
+ empties = (empties > max) ? max : empties;
+ }
+ while (empties > 0)
+ {
+ list.add("");
+ empties--;
+ }
+ }
+
+ // last token at end
+ if (limit != 0 || (limit == 0 && !emptyLast))
+ {
+ String t = input.subSequence(start, input.length()).toString();
+ if ("".equals(t) && limit == 0)
+ { /* Don't add. */ }
+ else
+ list.add(t);
+ }
+
+ return list.toArray(new String[list.size()]);
+ }
+
+ public String pattern ()
+ {
+ return regex;
+ }
+
+ /**
+ * Return the regular expression used to construct this object.
+ * @specnote Prior to JDK 1.5 this method had a different behavior
+ * @since 1.5
+ */
+ public String toString()
+ {
+ return regex;
+ }
+}
diff --git a/libjava/classpath/java/util/regex/PatternSyntaxException.java b/libjava/classpath/java/util/regex/PatternSyntaxException.java
new file mode 100644
index 000000000..db73d06e2
--- /dev/null
+++ b/libjava/classpath/java/util/regex/PatternSyntaxException.java
@@ -0,0 +1,135 @@
+/* PatternSyntaxException - Indicates illegal pattern for regular expression.
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.regex;
+
+import gnu.java.lang.CPStringBuilder;
+
+/**
+ * Indicates illegal pattern for regular expression.
+ * Includes state to inspect the pattern and what and where the expression
+ * was not valid regular expression.
+ * @since 1.4
+ */
+public class PatternSyntaxException extends IllegalArgumentException
+{
+ private static final long serialVersionUID = -3864639126226059218L;
+
+ /**
+ * Human readable escription of the syntax error.
+ */
+ private final String desc;
+
+ /**
+ * The original pattern that contained the syntax error.
+ */
+ private final String pattern;
+
+ /**
+ * Index of the first character in the String that was probably invalid,
+ * or -1 when unknown.
+ */
+ private final int index;
+
+ /**
+ * Creates a new PatternSyntaxException.
+ *
+ * @param description Human readable escription of the syntax error.
+ * @param pattern The original pattern that contained the syntax error.
+ * @param index Index of the first character in the String that was
+ * probably invalid, or -1 when unknown.
+ */
+ public PatternSyntaxException(String description,
+ String pattern,
+ int index)
+ {
+ super(description);
+ this.desc = description;
+ this.pattern = pattern;
+ this.index = index;
+ }
+
+ /**
+ * Returns a human readable escription of the syntax error.
+ */
+ public String getDescription()
+ {
+ return desc;
+ }
+
+ /**
+ * Returns the original pattern that contained the syntax error.
+ */
+ public String getPattern()
+ {
+ return pattern;
+ }
+
+ /**
+ * Returns the index of the first character in the String that was probably
+ * invalid, or -1 when unknown.
+ */
+ public int getIndex()
+ {
+ return index;
+ }
+
+ /**
+ * Returns a string containing a line with the description, a line with
+ * the original pattern and a line indicating with a ^ which character is
+ * probably the first invalid character in the pattern if the index is not
+ * negative.
+ */
+ public String getMessage()
+ {
+ String lineSep = System.getProperty("line.separator");
+ CPStringBuilder sb = new CPStringBuilder(desc);
+ sb.append(lineSep);
+ sb.append('\t');
+ sb.append(pattern);
+ if (index != -1)
+ {
+ sb.append(lineSep);
+ sb.append('\t');
+ for (int i=0; i<index; i++)
+ sb.append(' ');
+ sb.append('^');
+ }
+ return sb.toString();
+ }
+
+}
diff --git a/libjava/classpath/java/util/regex/package.html b/libjava/classpath/java/util/regex/package.html
new file mode 100644
index 000000000..0573a360c
--- /dev/null
+++ b/libjava/classpath/java/util/regex/package.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in java.util.regex package.
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - java.util.regex</title></head>
+
+<body>
+<p>Regular expression patterns and matchers.</p>
+
+</body>
+</html>
diff --git a/libjava/classpath/java/util/spi/CurrencyNameProvider.java b/libjava/classpath/java/util/spi/CurrencyNameProvider.java
new file mode 100644
index 000000000..14fae4d87
--- /dev/null
+++ b/libjava/classpath/java/util/spi/CurrencyNameProvider.java
@@ -0,0 +1,100 @@
+/* CurrencyNameProvider.java -- Providers of localized currency symbols
+ Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.spi;
+
+import java.util.Locale;
+
+/**
+ * A {@link CurrencyNameProvider} provides localized
+ * versions of the symbols that represent a particular
+ * currency. Note that currency symbols are regarded
+ * as names, and thus a <code>null</code> value may
+ * be returned, which should be treated as a lack of
+ * support for the specified {@link Locale}.
+ *
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.6
+ */
+public abstract class CurrencyNameProvider
+ extends LocaleServiceProvider
+{
+
+ /**
+ * Constructs a new {@link CurrencyNameProvider}.
+ * Provided for implicit invocation by subclasses.
+ */
+ protected CurrencyNameProvider()
+ {
+ }
+
+ /**
+ * <p>
+ * This method returns the symbol which precedes or follows a
+ * value in this particular currency. The returned value is
+ * the symbol used to denote the currency in the specified locale.
+ * </p>
+ * <p>
+ * For example, a supplied locale may specify a different symbol
+ * for the currency, due to conflicts with its own currency.
+ * This would be the case with the American currency, the dollar.
+ * Locales that also use a dollar-based currency (e.g. Canada, Australia)
+ * need to differentiate the American dollar using 'US$' rather than '$'.
+ * So, supplying one of these locales to <code>getSymbol()</code> would
+ * return this value, rather than the standard '$'.
+ * </p>
+ * <p>
+ * In cases where there is no such symbol for a particular currency,
+ * <code>null</code> should be returned.
+ * </p>
+ *
+ * @param currencyCode the ISO 4217 currency code, consisting
+ * of three uppercase letters from 'A' to 'Z'
+ * @param locale the locale to express the symbol in.
+ * @return the currency symbol, or <code>null</code> if one is
+ * unavailable.
+ * @throws NullPointerException if the locale is null.
+ * @throws IllegalArgumentException if the currency code is
+ * not in the correct format
+ * or the locale is not one
+ * returned by
+ * {@link getAvailableLocales()}
+ * @see java.util.Currency#getSymbol(java.util.Locale)
+ */
+ public abstract String getSymbol(String currencyCode, Locale locale);
+
+}
diff --git a/libjava/classpath/java/util/spi/LocaleNameProvider.java b/libjava/classpath/java/util/spi/LocaleNameProvider.java
new file mode 100644
index 000000000..44250feb5
--- /dev/null
+++ b/libjava/classpath/java/util/spi/LocaleNameProvider.java
@@ -0,0 +1,135 @@
+/* LocaleNameProvider.java -- Providers of localized locale names
+ Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.spi;
+
+import java.util.Locale;
+
+/**
+ * A {@link LocaleNameProvider} provides localized
+ * versions of the names that represent a particular
+ * locale. Note that a <code>null</code> value may
+ * be returned, which should be treated as a lack of
+ * support for the specified {@link Locale}.
+ *
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.6
+ */
+public abstract class LocaleNameProvider
+ extends LocaleServiceProvider
+{
+
+ /**
+ * Constructs a new {@link LocaleNameProvider}.
+ * Provided for implicit invocation by subclasses.
+ */
+ protected LocaleNameProvider()
+ {
+ }
+
+ /**
+ * Returns the localized name for the specified ISO 3166
+ * country in the supplied {@link java.util.Locale}.
+ * For example, if the country code is <code>"DE"</code>,
+ * this method will return <code>"Germany"</code> for
+ * {@link Locale.ENGLISH} but <code>"Deutschland"</code>
+ * for {@link Locale.GERMANY}. If the name of the country
+ * in the given locale is not supported, <code>null</code>
+ * is returned.
+ *
+ * @param countryCode the ISO 3166 country code, consisting
+ * of two uppercase letters from 'A' to 'Z'
+ * @param locale the locale to express the country in.
+ * @return the country name, or <code>null</code> if one is
+ * not available.
+ * @throws NullPointerException if the locale is null.
+ * @throws IllegalArgumentException if the country code is
+ * not in the correct format
+ * or the locale is not one
+ * returned by
+ * {@link getAvailableLocales()}
+ * @see java.util.Locale#getDisplayCountry(java.util.Locale)
+ */
+ public abstract String getDisplayCountry(String countryCode,
+ Locale locale);
+
+ /**
+ * Returns the localized name for the specified ISO 639
+ * language in the supplied {@link java.util.Locale}.
+ * For example, if the language code is <code>"de"</code>,
+ * this method will return <code>"German"</code> for
+ * {@link Locale.ENGLISH} but <code>"Deutsch"</code>
+ * for {@link Locale.GERMANY}. If the name of the language
+ * in the given locale is not supported, <code>null</code>
+ * is returned.
+ *
+ * @param langCode the ISO 639 language code, consisting
+ * of two lowercase letters from 'a' to 'z'
+ * @param locale the locale to express the language in.
+ * @return the country name, or <code>null</code> if one is
+ * not available.
+ * @throws NullPointerException if the locale is null.
+ * @throws IllegalArgumentException if the language code is
+ * not in the correct format
+ * or the locale is not one
+ * returned by
+ * {@link getAvailableLocales()}
+ * @see java.util.Locale#getDisplayLanguage(java.util.Locale)
+ */
+ public abstract String getDisplayLanguage(String langCode,
+ Locale locale);
+
+ /**
+ * Returns the localized name for the specified variant
+ * in the supplied {@link java.util.Locale}. If the name
+ * of the variant in the given locale is not supported,
+ * <code>null</code> is returned.
+ *
+ * @param variant the variant.
+ * @param locale the locale to express the variant in.
+ * @return the localized variant, or <code>null</code> if one is
+ * not available.
+ * @throws NullPointerException if the locale is null.
+ * @throws IllegalArgumentException if the locale is not one
+ * returned by
+ * {@link getAvailableLocales()}
+ * @see java.util.Locale#getDisplayVariant(java.util.Locale)
+ */
+ public abstract String getDisplayVariant(String variant,
+ Locale locale);
+
+}
diff --git a/libjava/classpath/java/util/spi/LocaleServiceProvider.java b/libjava/classpath/java/util/spi/LocaleServiceProvider.java
new file mode 100644
index 000000000..bb5b68527
--- /dev/null
+++ b/libjava/classpath/java/util/spi/LocaleServiceProvider.java
@@ -0,0 +1,125 @@
+/* LocaleServiceProvider.java -- Superclass of locale SPIs
+ Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.spi;
+
+import java.util.Locale;
+
+/**
+ * <p>
+ * This is the superclass of all the {@link Locale} service
+ * provider interfaces or SPIs. The locale SPIs are used
+ * to allow for the provision of additional support for
+ * locale-specific data. The runtime environment has its
+ * own collection of locale data, but these interfaces allow
+ * this to be extended by external classes.
+ * </p>
+ * <p>
+ * Service providers are created as concrete implementations
+ * of these interfaces, and accessed using the extension
+ * mechanism, realised by {@link ServiceLoader}. When a factory
+ * method of one of the locale-specific classes (such as
+ * {@link java.text.DateFormatSymbols} or {@link java.util.Currency})
+ * is called, the runtime environment is first asked to
+ * provide data for the specified locale. If the runtime
+ * environment fails to provide this, then the offer is
+ * made to service providers which implement the appropriate
+ * interface.
+ * </p>
+ * <p>
+ * Each provider implements the method specified by this
+ * class, {@link #getAvailableLocales()}. This method is
+ * called first to determine whether the provider will be of
+ * any use in providing data for the specified locale. If
+ * a provider is found to be capable, then a more specific
+ * method appropriate to the class requiring the data will
+ * be called. In the case of {@link java.text.DateFormatSymbols},
+ * this would be
+ * {@link java.text.spi.DateFormatSymbols#getInstance(Locale)}.
+ * </p>
+ * <p>
+ * If neither a service provider nor the runtime environment
+ * itself can fulfill the request, a fallback procedure is
+ * engaged. The locale is modified by applying the first
+ * applicable rule:
+ * </p>
+ * <ol>
+ * <li>If the variant contains a <code>'_'</code>, then
+ * this and everything following it is trimmed.</li>
+ * <li>If the variant is non-empty, it is converted to
+ * an empty string.</li>
+ * <li>If the country is non-empty, it is converted to
+ * an empty string.</li>
+ * <li>If the language is non-empty, it is converted to
+ * an empty string.</li>
+ * </ol>
+ * <p>
+ * The modified locale is then used to start the same
+ * process again. The root locale (@link java.util.Locale#ROOT}
+ * must be supported by the runtime environment in order
+ * to terminate this cycle.
+ * </p>
+ * <p>
+ * Note that any names returned by the providers may
+ * be <code>null</code>. Returning a <code>null</code>
+ * name is considered equivalent to not supporting a
+ * particular locale.
+ * </p>
+ *
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.6
+ */
+public abstract class LocaleServiceProvider
+{
+
+ /**
+ * Constructs a new {@link LocaleServiceProvider}.
+ * Provided for implicit invocation by subclasses.
+ */
+ protected LocaleServiceProvider()
+ {
+ }
+
+ /**
+ * Returns an array of {@link Locale} instances,
+ * for which the provider can supply localized data.
+ *
+ * @return an array of supported locales.
+ */
+ public abstract Locale[] getAvailableLocales();
+
+}
diff --git a/libjava/classpath/java/util/spi/TimeZoneNameProvider.java b/libjava/classpath/java/util/spi/TimeZoneNameProvider.java
new file mode 100644
index 000000000..afd56eb1b
--- /dev/null
+++ b/libjava/classpath/java/util/spi/TimeZoneNameProvider.java
@@ -0,0 +1,97 @@
+/* TimeZoneNameProvider.java -- Providers of localized currency symbols
+ Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.spi;
+
+import java.util.Locale;
+
+/**
+ * A {@link TimeZoneNameProvider} provides localized
+ * versions of the names that represent a particular
+ * timezone. A <code>null</code> value may
+ * be returned, which should be treated as a lack of
+ * support for the specified {@link Locale}. The names
+ * from this class are also used by
+ * {@link DateFormatSymbols#getZoneStrings()}.
+ *
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @since 1.6
+ */
+public abstract class TimeZoneNameProvider
+ extends LocaleServiceProvider
+{
+
+ /**
+ * Constructs a new {@link TimeZoneNameProvider}.
+ * Provided for implicit invocation by subclasses.
+ */
+ protected TimeZoneNameProvider()
+ {
+ }
+
+ /**
+ * Returns a name for the specified time zone identifier
+ * localized to the supplied {@link java.util.Locale}.
+ * The time zone identifier is either <code>"GMT"</code>
+ * or one of the identifiers from the public domain "tz
+ * database" found at <a href="ftp://elsie.nci.nih.gov/pub/">
+ * ftp://elsie.nci.nih.gov/pub</a>. Note that a translated
+ * name for the daylight savings time variant should be returned,
+ * even if the timezone has not observed daylight savings
+ * time in the past. If the name of the timezone
+ * in the given locale is not supported, <code>null</code>
+ * is returned.
+ *
+ * @param id a time zone identifier.
+ * @param daylight true if the daylight savings time variant
+ * should be returned.
+ * @param style either {@link java.util.TimeZone.LONG} or
+ * {@link java.util.TimeZone.SHORT}
+ * @param locale the locale to express the timezone in.
+ * @return the localized time zone name, or <code>null</code>
+ * if one is not available.
+ * @throws NullPointerException if the identifer or locale is null.
+ * @throws IllegalArgumentException if the style is invalid
+ * or the locale is not one
+ * returned by
+ * {@link getAvailableLocales()}
+ * @see java.util.TimeZone#getDisplayName(boolean,int,java.util.Locale)
+ */
+ public abstract String getDisplayName(String id, boolean daylight,
+ int style, Locale locale);
+
+}
diff --git a/libjava/classpath/java/util/spi/package.html b/libjava/classpath/java/util/spi/package.html
new file mode 100644
index 000000000..1abdeb8b4
--- /dev/null
+++ b/libjava/classpath/java/util/spi/package.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in java.util.spi package.
+ Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - java.util.spi</title></head>
+
+<body>
+
+<p>
+A series of service provider interfaces for use by the
+classes in <code>java.util</code>.
+</p>
+<p><span style="font-weight: bold;">Since</span>: 1.6</p>
+</body>
+</html>
diff --git a/libjava/classpath/java/util/zip/Adler32.java b/libjava/classpath/java/util/zip/Adler32.java
new file mode 100644
index 000000000..cc27da927
--- /dev/null
+++ b/libjava/classpath/java/util/zip/Adler32.java
@@ -0,0 +1,205 @@
+/* Adler32.java - Computes Adler32 data checksum of a data stream
+ Copyright (C) 1999, 2000, 2001 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+/*
+ * Written using on-line Java Platform 1.2 API Specification, as well
+ * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
+ * The actual Adler32 algorithm is taken from RFC 1950.
+ * Status: Believed complete and correct.
+ */
+
+/**
+ * Computes Adler32 checksum for a stream of data. An Adler32
+ * checksum is not as reliable as a CRC32 checksum, but a lot faster to
+ * compute.
+ *<p>
+ * The specification for Adler32 may be found in RFC 1950.
+ * (ZLIB Compressed Data Format Specification version 3.3)
+ *<p>
+ *<p>
+ * From that document:
+ *<p>
+ * "ADLER32 (Adler-32 checksum)
+ * This contains a checksum value of the uncompressed data
+ * (excluding any dictionary data) computed according to Adler-32
+ * algorithm. This algorithm is a 32-bit extension and improvement
+ * of the Fletcher algorithm, used in the ITU-T X.224 / ISO 8073
+ * standard.
+ *<p>
+ * Adler-32 is composed of two sums accumulated per byte: s1 is
+ * the sum of all bytes, s2 is the sum of all s1 values. Both sums
+ * are done modulo 65521. s1 is initialized to 1, s2 to zero. The
+ * Adler-32 checksum is stored as s2*65536 + s1 in most-
+ * significant-byte first (network) order."
+ *<p>
+ * "8.2. The Adler-32 algorithm
+ *<p>
+ * The Adler-32 algorithm is much faster than the CRC32 algorithm yet
+ * still provides an extremely low probability of undetected errors.
+ *<p>
+ * The modulo on unsigned long accumulators can be delayed for 5552
+ * bytes, so the modulo operation time is negligible. If the bytes
+ * are a, b, c, the second sum is 3a + 2b + c + 3, and so is position
+ * and order sensitive, unlike the first sum, which is just a
+ * checksum. That 65521 is prime is important to avoid a possible
+ * large class of two-byte errors that leave the check unchanged.
+ * (The Fletcher checksum uses 255, which is not prime and which also
+ * makes the Fletcher check insensitive to single byte changes 0 <->
+ * 255.)
+ *<p>
+ * The sum s1 is initialized to 1 instead of zero to make the length
+ * of the sequence part of s2, so that the length does not have to be
+ * checked separately. (Any sequence of zeroes has a Fletcher
+ * checksum of zero.)"
+ *
+ * @author John Leuner, Per Bothner
+ * @since JDK 1.1
+ *
+ * @see InflaterInputStream
+ * @see DeflaterOutputStream
+ */
+public class Adler32 implements Checksum
+{
+
+ /** largest prime smaller than 65536 */
+ private static final int BASE = 65521;
+
+ private int checksum; //we do all in int.
+
+ //Note that java doesn't have unsigned integers,
+ //so we have to be careful with what arithmetic
+ //we do. We return the checksum as a long to
+ //avoid sign confusion.
+
+ /**
+ * Creates a new instance of the <code>Adler32</code> class.
+ * The checksum starts off with a value of 1.
+ */
+ public Adler32 ()
+ {
+ reset();
+ }
+
+ /**
+ * Resets the Adler32 checksum to the initial value.
+ */
+ public void reset ()
+ {
+ checksum = 1; //Initialize to 1
+ }
+
+ /**
+ * Updates the checksum with the byte b.
+ *
+ * @param bval the data value to add. The high byte of the int is ignored.
+ */
+ public void update (int bval)
+ {
+ //We could make a length 1 byte array and call update again, but I
+ //would rather not have that overhead
+ int s1 = checksum & 0xffff;
+ int s2 = checksum >>> 16;
+
+ s1 = (s1 + (bval & 0xFF)) % BASE;
+ s2 = (s1 + s2) % BASE;
+
+ checksum = (s2 << 16) + s1;
+ }
+
+ /**
+ * Updates the checksum with the bytes taken from the array.
+ *
+ * @param buffer an array of bytes
+ */
+ public void update (byte[] buffer)
+ {
+ update(buffer, 0, buffer.length);
+ }
+
+ /**
+ * Updates the checksum with the bytes taken from the array.
+ *
+ * @param buf an array of bytes
+ * @param off the start of the data used for this update
+ * @param len the number of bytes to use for this update
+ */
+ public void update (byte[] buf, int off, int len)
+ {
+ //(By Per Bothner)
+ int s1 = checksum & 0xffff;
+ int s2 = checksum >>> 16;
+
+ while (len > 0)
+ {
+ // We can defer the modulo operation:
+ // s1 maximally grows from 65521 to 65521 + 255 * 3800
+ // s2 maximally grows by 3800 * median(s1) = 2090079800 < 2^31
+ int n = 3800;
+ if (n > len)
+ n = len;
+ len -= n;
+ while (--n >= 0)
+ {
+ s1 = s1 + (buf[off++] & 0xFF);
+ s2 = s2 + s1;
+ }
+ s1 %= BASE;
+ s2 %= BASE;
+ }
+
+ /*Old implementation, borrowed from somewhere:
+ int n;
+
+ while (len-- > 0) {
+
+ s1 = (s1 + (bs[offset++] & 0xff)) % BASE;
+ s2 = (s2 + s1) % BASE;
+ }*/
+
+ checksum = (s2 << 16) | s1;
+ }
+
+ /**
+ * Returns the Adler32 data checksum computed so far.
+ */
+ public long getValue()
+ {
+ return (long) checksum & 0xffffffffL;
+ }
+}
diff --git a/libjava/classpath/java/util/zip/CRC32.java b/libjava/classpath/java/util/zip/CRC32.java
new file mode 100644
index 000000000..de2e7083d
--- /dev/null
+++ b/libjava/classpath/java/util/zip/CRC32.java
@@ -0,0 +1,132 @@
+/* CRC32.java - Computes CRC32 data checksum of a data stream
+ Copyright (C) 1999. 2000, 2001 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+/*
+ * Written using on-line Java Platform 1.2 API Specification, as well
+ * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
+ * The actual CRC32 algorithm is taken from RFC 1952.
+ * Status: Believed complete and correct.
+ */
+
+/**
+ * Computes CRC32 data checksum of a data stream.
+ * The actual CRC32 algorithm is described in RFC 1952
+ * (GZIP file format specification version 4.3).
+ * Can be used to get the CRC32 over a stream if used with checked input/output
+ * streams.
+ *
+ * @see InflaterInputStream
+ * @see DeflaterOutputStream
+ *
+ * @author Per Bothner
+ * @date April 1, 1999.
+ */
+public class CRC32 implements Checksum
+{
+ /** The crc data checksum so far. */
+ private int crc = 0;
+
+ /** The fast CRC table. Computed once when the CRC32 class is loaded. */
+ private static int[] crc_table = make_crc_table();
+
+ /** Make the table for a fast CRC. */
+ private static int[] make_crc_table ()
+ {
+ int[] crc_table = new int[256];
+ for (int n = 0; n < 256; n++)
+ {
+ int c = n;
+ for (int k = 8; --k >= 0; )
+ {
+ if ((c & 1) != 0)
+ c = 0xedb88320 ^ (c >>> 1);
+ else
+ c = c >>> 1;
+ }
+ crc_table[n] = c;
+ }
+ return crc_table;
+ }
+
+ /**
+ * Returns the CRC32 data checksum computed so far.
+ */
+ public long getValue ()
+ {
+ return (long) crc & 0xffffffffL;
+ }
+
+ /**
+ * Resets the CRC32 data checksum as if no update was ever called.
+ */
+ public void reset () { crc = 0; }
+
+ /**
+ * Updates the checksum with the int bval.
+ *
+ * @param bval (the byte is taken as the lower 8 bits of bval)
+ */
+
+ public void update (int bval)
+ {
+ int c = ~crc;
+ c = crc_table[(c ^ bval) & 0xff] ^ (c >>> 8);
+ crc = ~c;
+ }
+
+ /**
+ * Adds the byte array to the data checksum.
+ *
+ * @param buf the buffer which contains the data
+ * @param off the offset in the buffer where the data starts
+ * @param len the length of the data
+ */
+ public void update (byte[] buf, int off, int len)
+ {
+ int c = ~crc;
+ while (--len >= 0)
+ c = crc_table[(c ^ buf[off++]) & 0xff] ^ (c >>> 8);
+ crc = ~c;
+ }
+
+ /**
+ * Adds the complete byte array to the data checksum.
+ */
+ public void update (byte[] buf) { update(buf, 0, buf.length); }
+}
diff --git a/libjava/classpath/java/util/zip/CheckedInputStream.java b/libjava/classpath/java/util/zip/CheckedInputStream.java
new file mode 100644
index 000000000..163a4c4aa
--- /dev/null
+++ b/libjava/classpath/java/util/zip/CheckedInputStream.java
@@ -0,0 +1,135 @@
+/* CheckedInputStream.java - Compute checksum of data being read
+ Copyright (C) 1999, 2000, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/* Written using on-line Java Platform 1.2 API Specification
+ * and JCL book.
+ * Believed complete and correct.
+ */
+
+/**
+ * InputStream that computes a checksum of the data being read using a
+ * supplied Checksum object.
+ *
+ * @see Checksum
+ *
+ * @author Tom Tromey
+ * @date May 17, 1999
+ */
+public class CheckedInputStream extends FilterInputStream
+{
+ /**
+ * Creates a new CheckInputStream on top of the supplied OutputStream
+ * using the supplied Checksum.
+ */
+ public CheckedInputStream (InputStream in, Checksum sum)
+ {
+ super (in);
+ this.sum = sum;
+ }
+
+ /**
+ * Returns the Checksum object used. To get the data checksum computed so
+ * far call <code>getChecksum.getValue()</code>.
+ */
+ public Checksum getChecksum ()
+ {
+ return sum;
+ }
+
+ /**
+ * Reads one byte, updates the checksum and returns the read byte
+ * (or -1 when the end of file was reached).
+ */
+ public int read () throws IOException
+ {
+ int x = in.read();
+ if (x != -1)
+ sum.update(x);
+ return x;
+ }
+
+ /**
+ * Reads at most len bytes in the supplied buffer and updates the checksum
+ * with it. Returns the number of bytes actually read or -1 when the end
+ * of file was reached.
+ */
+ public int read (byte[] buf, int off, int len) throws IOException
+ {
+ int r = in.read(buf, off, len);
+ if (r != -1)
+ sum.update(buf, off, r);
+ return r;
+ }
+
+ /**
+ * Skips n bytes by reading them in a temporary buffer and updating the
+ * the checksum with that buffer. Returns the actual number of bytes skiped
+ * which can be less then requested when the end of file is reached.
+ */
+ public long skip (long n) throws IOException
+ {
+ if (n == 0)
+ return 0;
+
+ int min = (int) Math.min(n, 1024);
+ byte[] buf = new byte[min];
+
+ long s = 0;
+ while (n > 0)
+ {
+ int r = in.read(buf, 0, min);
+ if (r == -1)
+ break;
+ n -= r;
+ s += r;
+ min = (int) Math.min(n, 1024);
+ sum.update(buf, 0, r);
+ }
+
+ return s;
+ }
+
+ /** The checksum object. */
+ private Checksum sum;
+}
diff --git a/libjava/classpath/java/util/zip/CheckedOutputStream.java b/libjava/classpath/java/util/zip/CheckedOutputStream.java
new file mode 100644
index 000000000..e0222258a
--- /dev/null
+++ b/libjava/classpath/java/util/zip/CheckedOutputStream.java
@@ -0,0 +1,100 @@
+/* CheckedOutputStream.java - Compute checksum of data being written.
+ Copyright (C) 1999, 2000 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/* Written using on-line Java Platform 1.2 API Specification
+ * and JCL book.
+ * Believed complete and correct.
+ */
+
+/**
+ * OutputStream that computes a checksum of data being written using a
+ * supplied Checksum object.
+ *
+ * @see Checksum
+ *
+ * @author Tom Tromey
+ * @date May 17, 1999
+ */
+public class CheckedOutputStream extends FilterOutputStream
+{
+ /**
+ * Creates a new CheckInputStream on top of the supplied OutputStream
+ * using the supplied Checksum.
+ */
+ public CheckedOutputStream (OutputStream out, Checksum cksum)
+ {
+ super (out);
+ this.sum = cksum;
+ }
+
+ /**
+ * Returns the Checksum object used. To get the data checksum computed so
+ * far call <code>getChecksum.getValue()</code>.
+ */
+ public Checksum getChecksum ()
+ {
+ return sum;
+ }
+
+ /**
+ * Writes one byte to the OutputStream and updates the Checksum.
+ */
+ public void write (int bval) throws IOException
+ {
+ out.write(bval);
+ sum.update(bval);
+ }
+
+ /**
+ * Writes the byte array to the OutputStream and updates the Checksum.
+ */
+ public void write (byte[] buf, int off, int len) throws IOException
+ {
+ out.write(buf, off, len);
+ sum.update(buf, off, len);
+ }
+
+ /** The checksum object. */
+ private Checksum sum;
+}
diff --git a/libjava/classpath/java/util/zip/Checksum.java b/libjava/classpath/java/util/zip/Checksum.java
new file mode 100644
index 000000000..3342ba3a6
--- /dev/null
+++ b/libjava/classpath/java/util/zip/Checksum.java
@@ -0,0 +1,86 @@
+/* Checksum.java - Interface to compute a data checksum
+ Copyright (C) 1999, 2000, 2001 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+/*
+ * Written using on-line Java Platform 1.2 API Specification, as well
+ * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
+ * Status: Believed complete and correct.
+ */
+
+/**
+ * Interface to compute a data checksum used by checked input/output streams.
+ * A data checksum can be updated by one byte or with a byte array. After each
+ * update the value of the current checksum can be returned by calling
+ * <code>getValue</code>. The complete checksum object can also be reset
+ * so it can be used again with new data.
+ *
+ * @see CheckedInputStream
+ * @see CheckedOutputStream
+ *
+ * @author Per Bothner
+ * @author Jochen Hoenicke
+ */
+public interface Checksum
+{
+ /**
+ * Returns the data checksum computed so far.
+ */
+ long getValue();
+
+ /**
+ * Resets the data checksum as if no update was ever called.
+ */
+ void reset();
+
+ /**
+ * Adds one byte to the data checksum.
+ *
+ * @param bval the data value to add. The high byte of the int is ignored.
+ */
+ void update (int bval);
+
+ /**
+ * Adds the byte array to the data checksum.
+ *
+ * @param buf the buffer which contains the data
+ * @param off the offset in the buffer where the data starts
+ * @param len the length of the data
+ */
+ void update (byte[] buf, int off, int len);
+}
diff --git a/libjava/classpath/java/util/zip/DataFormatException.java b/libjava/classpath/java/util/zip/DataFormatException.java
new file mode 100644
index 000000000..dc5b10dec
--- /dev/null
+++ b/libjava/classpath/java/util/zip/DataFormatException.java
@@ -0,0 +1,71 @@
+/* DataformatException.java -- thrown when compressed data is corrupt
+ Copyright (C) 1999, 2000, 2001, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+/**
+ * Exception thrown when compressed data is corrupt.
+ *
+ * @author Tom Tromey
+ * @author John Leuner
+ * @since 1.1
+ * @status updated to 1.4
+ */
+public class DataFormatException extends Exception
+{
+ /**
+ * Compatible with JDK 1.1+.
+ */
+ private static final long serialVersionUID = 2219632870893641452L;
+
+ /**
+ * Create an exception without a message.
+ */
+ public DataFormatException()
+ {
+ }
+
+ /**
+ * Create an exception with a message.
+ *
+ * @param msg the message
+ */
+ public DataFormatException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/libjava/classpath/java/util/zip/Deflater.java b/libjava/classpath/java/util/zip/Deflater.java
new file mode 100644
index 000000000..dd81fe748
--- /dev/null
+++ b/libjava/classpath/java/util/zip/Deflater.java
@@ -0,0 +1,537 @@
+/* Deflater.java - Compress a data stream
+ Copyright (C) 1999, 2000, 2001, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+/**
+ * This is the Deflater class. The deflater class compresses input
+ * with the deflate algorithm described in RFC 1951. It has several
+ * compression levels and three different strategies described below.
+ *
+ * This class is <i>not</i> thread safe. This is inherent in the API, due
+ * to the split of deflate and setInput.
+ *
+ * @author Jochen Hoenicke
+ * @author Tom Tromey
+ */
+public class Deflater
+{
+ /**
+ * The best and slowest compression level. This tries to find very
+ * long and distant string repetitions.
+ */
+ public static final int BEST_COMPRESSION = 9;
+ /**
+ * The worst but fastest compression level.
+ */
+ public static final int BEST_SPEED = 1;
+ /**
+ * The default compression level.
+ */
+ public static final int DEFAULT_COMPRESSION = -1;
+ /**
+ * This level won't compress at all but output uncompressed blocks.
+ */
+ public static final int NO_COMPRESSION = 0;
+
+ /**
+ * The default strategy.
+ */
+ public static final int DEFAULT_STRATEGY = 0;
+ /**
+ * This strategy will only allow longer string repetitions. It is
+ * useful for random data with a small character set.
+ */
+ public static final int FILTERED = 1;
+
+ /**
+ * This strategy will not look for string repetitions at all. It
+ * only encodes with Huffman trees (which means, that more common
+ * characters get a smaller encoding.
+ */
+ public static final int HUFFMAN_ONLY = 2;
+
+ /**
+ * The compression method. This is the only method supported so far.
+ * There is no need to use this constant at all.
+ */
+ public static final int DEFLATED = 8;
+
+ /*
+ * The Deflater can do the following state transitions:
+ *
+ * (1) -> INIT_STATE ----> INIT_FINISHING_STATE ---.
+ * / | (2) (5) |
+ * / v (5) |
+ * (3)| SETDICT_STATE ---> SETDICT_FINISHING_STATE |(3)
+ * \ | (3) | ,-------'
+ * | | | (3) /
+ * v v (5) v v
+ * (1) -> BUSY_STATE ----> FINISHING_STATE
+ * | (6)
+ * v
+ * FINISHED_STATE
+ * \_____________________________________/
+ * | (7)
+ * v
+ * CLOSED_STATE
+ *
+ * (1) If we should produce a header we start in INIT_STATE, otherwise
+ * we start in BUSY_STATE.
+ * (2) A dictionary may be set only when we are in INIT_STATE, then
+ * we change the state as indicated.
+ * (3) Whether a dictionary is set or not, on the first call of deflate
+ * we change to BUSY_STATE.
+ * (4) -- intentionally left blank -- :)
+ * (5) FINISHING_STATE is entered, when flush() is called to indicate that
+ * there is no more INPUT. There are also states indicating, that
+ * the header wasn't written yet.
+ * (6) FINISHED_STATE is entered, when everything has been flushed to the
+ * internal pending output buffer.
+ * (7) At any time (7)
+ *
+ */
+
+ private static final int IS_SETDICT = 0x01;
+ private static final int IS_FLUSHING = 0x04;
+ private static final int IS_FINISHING = 0x08;
+
+ private static final int INIT_STATE = 0x00;
+ private static final int SETDICT_STATE = 0x01;
+ private static final int INIT_FINISHING_STATE = 0x08;
+ private static final int SETDICT_FINISHING_STATE = 0x09;
+ private static final int BUSY_STATE = 0x10;
+ private static final int FLUSHING_STATE = 0x14;
+ private static final int FINISHING_STATE = 0x1c;
+ private static final int FINISHED_STATE = 0x1e;
+ private static final int CLOSED_STATE = 0x7f;
+
+ /** Compression level. */
+ private int level;
+
+ /** should we include a header. */
+ private boolean noHeader;
+
+ /** The current state. */
+ private int state;
+
+ /** The total bytes of output written. */
+ private long totalOut;
+
+ /** The pending output. */
+ private DeflaterPending pending;
+
+ /** The deflater engine. */
+ private DeflaterEngine engine;
+
+ /**
+ * Creates a new deflater with default compression level.
+ */
+ public Deflater()
+ {
+ this(DEFAULT_COMPRESSION, false);
+ }
+
+ /**
+ * Creates a new deflater with given compression level.
+ * @param lvl the compression level, a value between NO_COMPRESSION
+ * and BEST_COMPRESSION, or DEFAULT_COMPRESSION.
+ * @exception IllegalArgumentException if lvl is out of range.
+ */
+ public Deflater(int lvl)
+ {
+ this(lvl, false);
+ }
+
+ /**
+ * Creates a new deflater with given compression level.
+ * @param lvl the compression level, a value between NO_COMPRESSION
+ * and BEST_COMPRESSION.
+ * @param nowrap true, iff we should suppress the deflate header at the
+ * beginning and the adler checksum at the end of the output. This is
+ * useful for the GZIP format.
+ * @exception IllegalArgumentException if lvl is out of range.
+ */
+ public Deflater(int lvl, boolean nowrap)
+ {
+ if (lvl == DEFAULT_COMPRESSION)
+ lvl = 6;
+ else if (lvl < NO_COMPRESSION || lvl > BEST_COMPRESSION)
+ throw new IllegalArgumentException();
+
+ pending = new DeflaterPending();
+ engine = new DeflaterEngine(pending);
+ this.noHeader = nowrap;
+ setStrategy(DEFAULT_STRATEGY);
+ setLevel(lvl);
+ reset();
+ }
+
+ /**
+ * Resets the deflater. The deflater acts afterwards as if it was
+ * just created with the same compression level and strategy as it
+ * had before.
+ */
+ public void reset()
+ {
+ state = (noHeader ? BUSY_STATE : INIT_STATE);
+ totalOut = 0;
+ pending.reset();
+ engine.reset();
+ }
+
+ /**
+ * Frees all objects allocated by the compressor. There's no
+ * reason to call this, since you can just rely on garbage
+ * collection. Exists only for compatibility against Sun's JDK,
+ * where the compressor allocates native memory.
+ * If you call any method (even reset) afterwards the behaviour is
+ * <i>undefined</i>.
+ */
+ public void end()
+ {
+ engine = null;
+ pending = null;
+ state = CLOSED_STATE;
+ }
+
+ /**
+ * Gets the current adler checksum of the data that was processed so
+ * far.
+ */
+ public int getAdler()
+ {
+ return engine.getAdler();
+ }
+
+ /**
+ * Gets the number of input bytes processed so far.
+ */
+ public int getTotalIn()
+ {
+ return (int) engine.getTotalIn();
+ }
+
+ /**
+ * Gets the number of input bytes processed so far.
+ * @since 1.5
+ */
+ public long getBytesRead()
+ {
+ return engine.getTotalIn();
+ }
+
+ /**
+ * Gets the number of output bytes so far.
+ */
+ public int getTotalOut()
+ {
+ return (int) totalOut;
+ }
+
+ /**
+ * Gets the number of output bytes so far.
+ * @since 1.5
+ */
+ public long getBytesWritten()
+ {
+ return totalOut;
+ }
+
+ /**
+ * Finalizes this object.
+ */
+ protected void finalize()
+ {
+ /* Exists solely for compatibility. We don't have any native state. */
+ }
+
+ /**
+ * Flushes the current input block. Further calls to deflate() will
+ * produce enough output to inflate everything in the current input
+ * block. This is not part of Sun's JDK so I have made it package
+ * private. It is used by DeflaterOutputStream to implement
+ * flush().
+ */
+ void flush() {
+ state |= IS_FLUSHING;
+ }
+
+ /**
+ * Finishes the deflater with the current input block. It is an error
+ * to give more input after this method was called. This method must
+ * be called to force all bytes to be flushed.
+ */
+ public void finish() {
+ state |= IS_FLUSHING | IS_FINISHING;
+ }
+
+ /**
+ * Returns true iff the stream was finished and no more output bytes
+ * are available.
+ */
+ public boolean finished()
+ {
+ return state == FINISHED_STATE && pending.isFlushed();
+ }
+
+ /**
+ * Returns true, if the input buffer is empty.
+ * You should then call setInput(). <br>
+ *
+ * <em>NOTE</em>: This method can also return true when the stream
+ * was finished.
+ */
+ public boolean needsInput()
+ {
+ return engine.needsInput();
+ }
+
+ /**
+ * Sets the data which should be compressed next. This should be only
+ * called when needsInput indicates that more input is needed.
+ * If you call setInput when needsInput() returns false, the
+ * previous input that is still pending will be thrown away.
+ * The given byte array should not be changed, before needsInput() returns
+ * true again.
+ * This call is equivalent to <code>setInput(input, 0, input.length)</code>.
+ * @param input the buffer containing the input data.
+ * @exception IllegalStateException if the buffer was finished() or ended().
+ */
+ public void setInput(byte[] input)
+ {
+ setInput(input, 0, input.length);
+ }
+
+ /**
+ * Sets the data which should be compressed next. This should be
+ * only called when needsInput indicates that more input is needed.
+ * The given byte array should not be changed, before needsInput() returns
+ * true again.
+ * @param input the buffer containing the input data.
+ * @param off the start of the data.
+ * @param len the length of the data.
+ * @exception IllegalStateException if the buffer was finished() or ended()
+ * or if previous input is still pending.
+ */
+ public void setInput(byte[] input, int off, int len)
+ {
+ if ((state & IS_FINISHING) != 0)
+ throw new IllegalStateException("finish()/end() already called");
+ engine.setInput(input, off, len);
+ }
+
+ /**
+ * Sets the compression level. There is no guarantee of the exact
+ * position of the change, but if you call this when needsInput is
+ * true the change of compression level will occur somewhere near
+ * before the end of the so far given input.
+ * @param lvl the new compression level.
+ */
+ public void setLevel(int lvl)
+ {
+ if (lvl == DEFAULT_COMPRESSION)
+ lvl = 6;
+ else if (lvl < NO_COMPRESSION || lvl > BEST_COMPRESSION)
+ throw new IllegalArgumentException();
+
+
+ if (level != lvl)
+ {
+ level = lvl;
+ engine.setLevel(lvl);
+ }
+ }
+
+ /**
+ * Sets the compression strategy. Strategy is one of
+ * DEFAULT_STRATEGY, HUFFMAN_ONLY and FILTERED. For the exact
+ * position where the strategy is changed, the same as for
+ * setLevel() applies.
+ * @param stgy the new compression strategy.
+ */
+ public void setStrategy(int stgy)
+ {
+ if (stgy != DEFAULT_STRATEGY && stgy != FILTERED
+ && stgy != HUFFMAN_ONLY)
+ throw new IllegalArgumentException();
+ engine.setStrategy(stgy);
+ }
+
+ /**
+ * Deflates the current input block to the given array. It returns
+ * the number of bytes compressed, or 0 if either
+ * needsInput() or finished() returns true or length is zero.
+ * @param output the buffer where to write the compressed data.
+ */
+ public int deflate(byte[] output)
+ {
+ return deflate(output, 0, output.length);
+ }
+
+ /**
+ * Deflates the current input block to the given array. It returns
+ * the number of bytes compressed, or 0 if either
+ * needsInput() or finished() returns true or length is zero.
+ * @param output the buffer where to write the compressed data.
+ * @param offset the offset into the output array.
+ * @param length the maximum number of bytes that may be written.
+ * @exception IllegalStateException if end() was called.
+ * @exception IndexOutOfBoundsException if offset and/or length
+ * don't match the array length.
+ */
+ public int deflate(byte[] output, int offset, int length)
+ {
+ int origLength = length;
+
+ if (state == CLOSED_STATE)
+ throw new IllegalStateException("Deflater closed");
+
+ if (state < BUSY_STATE)
+ {
+ /* output header */
+ int header = (DEFLATED +
+ ((DeflaterConstants.MAX_WBITS - 8) << 4)) << 8;
+ int level_flags = (level - 1) >> 1;
+ if (level_flags < 0 || level_flags > 3)
+ level_flags = 3;
+ header |= level_flags << 6;
+ if ((state & IS_SETDICT) != 0)
+ /* Dictionary was set */
+ header |= DeflaterConstants.PRESET_DICT;
+ header += 31 - (header % 31);
+
+ pending.writeShortMSB(header);
+ if ((state & IS_SETDICT) != 0)
+ {
+ int chksum = engine.getAdler();
+ engine.resetAdler();
+ pending.writeShortMSB(chksum >> 16);
+ pending.writeShortMSB(chksum & 0xffff);
+ }
+
+ state = BUSY_STATE | (state & (IS_FLUSHING | IS_FINISHING));
+ }
+
+ for (;;)
+ {
+ int count = pending.flush(output, offset, length);
+ offset += count;
+ totalOut += count;
+ length -= count;
+ if (length == 0 || state == FINISHED_STATE)
+ break;
+
+ if (!engine.deflate((state & IS_FLUSHING) != 0,
+ (state & IS_FINISHING) != 0))
+ {
+ if (state == BUSY_STATE)
+ /* We need more input now */
+ return origLength - length;
+ else if (state == FLUSHING_STATE)
+ {
+ if (level != NO_COMPRESSION)
+ {
+ /* We have to supply some lookahead. 8 bit lookahead
+ * are needed by the zlib inflater, and we must fill
+ * the next byte, so that all bits are flushed.
+ */
+ int neededbits = 8 + ((-pending.getBitCount()) & 7);
+ while (neededbits > 0)
+ {
+ /* write a static tree block consisting solely of
+ * an EOF:
+ */
+ pending.writeBits(2, 10);
+ neededbits -= 10;
+ }
+ }
+ state = BUSY_STATE;
+ }
+ else if (state == FINISHING_STATE)
+ {
+ pending.alignToByte();
+ /* We have completed the stream */
+ if (!noHeader)
+ {
+ int adler = engine.getAdler();
+ pending.writeShortMSB(adler >> 16);
+ pending.writeShortMSB(adler & 0xffff);
+ }
+ state = FINISHED_STATE;
+ }
+ }
+ }
+
+ return origLength - length;
+ }
+
+ /**
+ * Sets the dictionary which should be used in the deflate process.
+ * This call is equivalent to <code>setDictionary(dict, 0,
+ * dict.length)</code>.
+ * @param dict the dictionary.
+ * @exception IllegalStateException if setInput () or deflate ()
+ * were already called or another dictionary was already set.
+ */
+ public void setDictionary(byte[] dict)
+ {
+ setDictionary(dict, 0, dict.length);
+ }
+
+ /**
+ * Sets the dictionary which should be used in the deflate process.
+ * The dictionary should be a byte array containing strings that are
+ * likely to occur in the data which should be compressed. The
+ * dictionary is not stored in the compressed output, only a
+ * checksum. To decompress the output you need to supply the same
+ * dictionary again.
+ * @param dict the dictionary.
+ * @param offset an offset into the dictionary.
+ * @param length the length of the dictionary.
+ * @exception IllegalStateException if setInput () or deflate () were
+ * already called or another dictionary was already set.
+ */
+ public void setDictionary(byte[] dict, int offset, int length)
+ {
+ if (state != INIT_STATE)
+ throw new IllegalStateException();
+
+ state = SETDICT_STATE;
+ engine.setDictionary(dict, offset, length);
+ }
+}
diff --git a/libjava/classpath/java/util/zip/DeflaterConstants.java b/libjava/classpath/java/util/zip/DeflaterConstants.java
new file mode 100644
index 000000000..bfef86344
--- /dev/null
+++ b/libjava/classpath/java/util/zip/DeflaterConstants.java
@@ -0,0 +1,78 @@
+/* java.util.zip.DeflaterConstants
+ Copyright (C) 2001 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+interface DeflaterConstants
+{
+ boolean DEBUGGING = false;
+
+ int STORED_BLOCK = 0;
+ int STATIC_TREES = 1;
+ int DYN_TREES = 2;
+ int PRESET_DICT = 0x20;
+
+ int DEFAULT_MEM_LEVEL = 8;
+
+ int MAX_MATCH = 258;
+ int MIN_MATCH = 3;
+
+ int MAX_WBITS = 15;
+ int WSIZE = 1 << MAX_WBITS;
+ int WMASK = WSIZE - 1;
+
+ int HASH_BITS = DEFAULT_MEM_LEVEL + 7;
+ int HASH_SIZE = 1 << HASH_BITS;
+ int HASH_MASK = HASH_SIZE - 1;
+ int HASH_SHIFT = (HASH_BITS + MIN_MATCH - 1) / MIN_MATCH;
+
+ int MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1;
+ int MAX_DIST = WSIZE - MIN_LOOKAHEAD;
+
+ int PENDING_BUF_SIZE = 1 << (DEFAULT_MEM_LEVEL + 8);
+ int MAX_BLOCK_SIZE = Math.min(65535, PENDING_BUF_SIZE-5);
+
+ int DEFLATE_STORED = 0;
+ int DEFLATE_FAST = 1;
+ int DEFLATE_SLOW = 2;
+
+ int GOOD_LENGTH[] = { 0,4, 4, 4, 4, 8, 8, 8, 32, 32 };
+ int MAX_LAZY[] = { 0,4, 5, 6, 4,16, 16, 32, 128, 258 };
+ int NICE_LENGTH[] = { 0,8,16,32,16,32,128,128, 258, 258 };
+ int MAX_CHAIN[] = { 0,4, 8,32,16,32,128,256,1024,4096 };
+ int COMPR_FUNC[] = { 0,1, 1, 1, 1, 2, 2, 2, 2, 2 };
+}
diff --git a/libjava/classpath/java/util/zip/DeflaterEngine.java b/libjava/classpath/java/util/zip/DeflaterEngine.java
new file mode 100644
index 000000000..287558e3e
--- /dev/null
+++ b/libjava/classpath/java/util/zip/DeflaterEngine.java
@@ -0,0 +1,698 @@
+/* DeflaterEngine.java --
+ Copyright (C) 2001, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+class DeflaterEngine implements DeflaterConstants
+{
+ private static final int TOO_FAR = 4096;
+
+ private int ins_h;
+
+ /**
+ * Hashtable, hashing three characters to an index for window, so
+ * that window[index]..window[index+2] have this hash code.
+ * Note that the array should really be unsigned short, so you need
+ * to and the values with 0xffff.
+ */
+ private short[] head;
+
+ /**
+ * prev[index & WMASK] points to the previous index that has the
+ * same hash code as the string starting at index. This way
+ * entries with the same hash code are in a linked list.
+ * Note that the array should really be unsigned short, so you need
+ * to and the values with 0xffff.
+ */
+ private short[] prev;
+
+ private int matchStart, matchLen;
+ private boolean prevAvailable;
+ private int blockStart;
+
+ /**
+ * strstart points to the current character in window.
+ */
+ private int strstart;
+
+ /**
+ * lookahead is the number of characters starting at strstart in
+ * window that are valid.
+ * So window[strstart] until window[strstart+lookahead-1] are valid
+ * characters.
+ */
+ private int lookahead;
+
+ /**
+ * This array contains the part of the uncompressed stream that
+ * is of relevance. The current character is indexed by strstart.
+ */
+ private byte[] window;
+
+ private int strategy, max_chain, max_lazy, niceLength, goodLength;
+
+ /** The current compression function. */
+ private int comprFunc;
+
+ /** The input data for compression. */
+ private byte[] inputBuf;
+
+ /** The total bytes of input read. */
+ private long totalIn;
+
+ /** The offset into inputBuf, where input data starts. */
+ private int inputOff;
+
+ /** The end offset of the input data. */
+ private int inputEnd;
+
+ private DeflaterPending pending;
+ private DeflaterHuffman huffman;
+
+ /** The adler checksum */
+ private Adler32 adler;
+
+ /* DEFLATE ALGORITHM:
+ *
+ * The uncompressed stream is inserted into the window array. When
+ * the window array is full the first half is thrown away and the
+ * second half is copied to the beginning.
+ *
+ * The head array is a hash table. Three characters build a hash value
+ * and they the value points to the corresponding index in window of
+ * the last string with this hash. The prev array implements a
+ * linked list of matches with the same hash: prev[index & WMASK] points
+ * to the previous index with the same hash.
+ *
+ *
+ */
+
+
+ DeflaterEngine(DeflaterPending pending) {
+ this.pending = pending;
+ huffman = new DeflaterHuffman(pending);
+ adler = new Adler32();
+
+ window = new byte[2*WSIZE];
+ head = new short[HASH_SIZE];
+ prev = new short[WSIZE];
+
+ /* We start at index 1, to avoid a implementation deficiency, that
+ * we cannot build a repeat pattern at index 0.
+ */
+ blockStart = strstart = 1;
+ }
+
+ public void reset()
+ {
+ huffman.reset();
+ adler.reset();
+ blockStart = strstart = 1;
+ lookahead = 0;
+ totalIn = 0;
+ prevAvailable = false;
+ matchLen = MIN_MATCH - 1;
+ for (int i = 0; i < HASH_SIZE; i++)
+ head[i] = 0;
+ for (int i = 0; i < WSIZE; i++)
+ prev[i] = 0;
+ }
+
+ public final void resetAdler()
+ {
+ adler.reset();
+ }
+
+ public final int getAdler()
+ {
+ int chksum = (int) adler.getValue();
+ return chksum;
+ }
+
+ public final long getTotalIn()
+ {
+ return totalIn;
+ }
+
+ public final void setStrategy(int strat)
+ {
+ strategy = strat;
+ }
+
+ public void setLevel(int lvl)
+ {
+ goodLength = DeflaterConstants.GOOD_LENGTH[lvl];
+ max_lazy = DeflaterConstants.MAX_LAZY[lvl];
+ niceLength = DeflaterConstants.NICE_LENGTH[lvl];
+ max_chain = DeflaterConstants.MAX_CHAIN[lvl];
+
+ if (DeflaterConstants.COMPR_FUNC[lvl] != comprFunc)
+ {
+ if (DeflaterConstants.DEBUGGING)
+ System.err.println("Change from "+comprFunc +" to "
+ + DeflaterConstants.COMPR_FUNC[lvl]);
+ switch (comprFunc)
+ {
+ case DEFLATE_STORED:
+ if (strstart > blockStart)
+ {
+ huffman.flushStoredBlock(window, blockStart,
+ strstart - blockStart, false);
+ blockStart = strstart;
+ }
+ updateHash();
+ break;
+ case DEFLATE_FAST:
+ if (strstart > blockStart)
+ {
+ huffman.flushBlock(window, blockStart, strstart - blockStart,
+ false);
+ blockStart = strstart;
+ }
+ break;
+ case DEFLATE_SLOW:
+ if (prevAvailable)
+ huffman.tallyLit(window[strstart-1] & 0xff);
+ if (strstart > blockStart)
+ {
+ huffman.flushBlock(window, blockStart, strstart - blockStart,
+ false);
+ blockStart = strstart;
+ }
+ prevAvailable = false;
+ matchLen = MIN_MATCH - 1;
+ break;
+ }
+ comprFunc = COMPR_FUNC[lvl];
+ }
+ }
+
+ private void updateHash() {
+ if (DEBUGGING)
+ System.err.println("updateHash: "+strstart);
+ ins_h = (window[strstart] << HASH_SHIFT) ^ window[strstart + 1];
+ }
+
+ /**
+ * Inserts the current string in the head hash and returns the previous
+ * value for this hash.
+ */
+ private int insertString() {
+ short match;
+ int hash = ((ins_h << HASH_SHIFT) ^ window[strstart + (MIN_MATCH -1)])
+ & HASH_MASK;
+
+ if (DEBUGGING)
+ {
+ if (hash != (((window[strstart] << (2*HASH_SHIFT))
+ ^ (window[strstart + 1] << HASH_SHIFT)
+ ^ (window[strstart + 2])) & HASH_MASK))
+ throw new InternalError("hash inconsistent: "+hash+"/"
+ +window[strstart]+","
+ +window[strstart+1]+","
+ +window[strstart+2]+","+HASH_SHIFT);
+ }
+
+ prev[strstart & WMASK] = match = head[hash];
+ head[hash] = (short) strstart;
+ ins_h = hash;
+ return match & 0xffff;
+ }
+
+ private void slideWindow()
+ {
+ System.arraycopy(window, WSIZE, window, 0, WSIZE);
+ matchStart -= WSIZE;
+ strstart -= WSIZE;
+ blockStart -= WSIZE;
+
+ /* Slide the hash table (could be avoided with 32 bit values
+ * at the expense of memory usage).
+ */
+ for (int i = 0; i < HASH_SIZE; i++)
+ {
+ int m = head[i] & 0xffff;
+ head[i] = m >= WSIZE ? (short) (m - WSIZE) : 0;
+ }
+
+ /* Slide the prev table.
+ */
+ for (int i = 0; i < WSIZE; i++)
+ {
+ int m = prev[i] & 0xffff;
+ prev[i] = m >= WSIZE ? (short) (m - WSIZE) : 0;
+ }
+ }
+
+ /**
+ * Fill the window when the lookahead becomes insufficient.
+ * Updates strstart and lookahead.
+ *
+ * OUT assertions: strstart + lookahead <= 2*WSIZE
+ * lookahead >= MIN_LOOKAHEAD or inputOff == inputEnd
+ */
+ private void fillWindow()
+ {
+ /* If the window is almost full and there is insufficient lookahead,
+ * move the upper half to the lower one to make room in the upper half.
+ */
+ if (strstart >= WSIZE + MAX_DIST)
+ slideWindow();
+
+ /* If there is not enough lookahead, but still some input left,
+ * read in the input
+ */
+ while (lookahead < DeflaterConstants.MIN_LOOKAHEAD && inputOff < inputEnd)
+ {
+ int more = 2*WSIZE - lookahead - strstart;
+
+ if (more > inputEnd - inputOff)
+ more = inputEnd - inputOff;
+
+ System.arraycopy(inputBuf, inputOff,
+ window, strstart + lookahead, more);
+ adler.update(inputBuf, inputOff, more);
+ inputOff += more;
+ totalIn += more;
+ lookahead += more;
+ }
+
+ if (lookahead >= MIN_MATCH)
+ updateHash();
+ }
+
+ /**
+ * Find the best (longest) string in the window matching the
+ * string starting at strstart.
+ *
+ * Preconditions:
+ * strstart + MAX_MATCH <= window.length.
+ *
+ *
+ * @param curMatch
+ */
+ private boolean findLongestMatch(int curMatch) {
+ int chainLength = this.max_chain;
+ int niceLength = this.niceLength;
+ short[] prev = this.prev;
+ int scan = this.strstart;
+ int match;
+ int best_end = this.strstart + matchLen;
+ int best_len = Math.max(matchLen, MIN_MATCH - 1);
+
+ int limit = Math.max(strstart - MAX_DIST, 0);
+
+ int strend = scan + MAX_MATCH - 1;
+ byte scan_end1 = window[best_end - 1];
+ byte scan_end = window[best_end];
+
+ /* Do not waste too much time if we already have a good match: */
+ if (best_len >= this.goodLength)
+ chainLength >>= 2;
+
+ /* Do not look for matches beyond the end of the input. This is necessary
+ * to make deflate deterministic.
+ */
+ if (niceLength > lookahead)
+ niceLength = lookahead;
+
+ if (DeflaterConstants.DEBUGGING
+ && strstart > 2*WSIZE - MIN_LOOKAHEAD)
+ throw new InternalError("need lookahead");
+
+ do {
+ if (DeflaterConstants.DEBUGGING && curMatch >= strstart)
+ throw new InternalError("future match");
+ if (window[curMatch + best_len] != scan_end
+ || window[curMatch + best_len - 1] != scan_end1
+ || window[curMatch] != window[scan]
+ || window[curMatch+1] != window[scan + 1])
+ continue;
+
+ match = curMatch + 2;
+ scan += 2;
+
+ /* We check for insufficient lookahead only every 8th comparison;
+ * the 256th check will be made at strstart+258.
+ */
+ while (window[++scan] == window[++match]
+ && window[++scan] == window[++match]
+ && window[++scan] == window[++match]
+ && window[++scan] == window[++match]
+ && window[++scan] == window[++match]
+ && window[++scan] == window[++match]
+ && window[++scan] == window[++match]
+ && window[++scan] == window[++match]
+ && scan < strend)
+ ;
+
+ if (scan > best_end) {
+// if (DeflaterConstants.DEBUGGING && ins_h == 0)
+// System.err.println("Found match: "+curMatch+"-"+(scan-strstart));
+ matchStart = curMatch;
+ best_end = scan;
+ best_len = scan - strstart;
+ if (best_len >= niceLength)
+ break;
+
+ scan_end1 = window[best_end-1];
+ scan_end = window[best_end];
+ }
+ scan = strstart;
+ } while ((curMatch = (prev[curMatch & WMASK] & 0xffff)) > limit
+ && --chainLength != 0);
+
+ matchLen = Math.min(best_len, lookahead);
+ return matchLen >= MIN_MATCH;
+ }
+
+ void setDictionary(byte[] buffer, int offset, int length) {
+ if (DeflaterConstants.DEBUGGING && strstart != 1)
+ throw new IllegalStateException("strstart not 1");
+ adler.update(buffer, offset, length);
+ if (length < MIN_MATCH)
+ return;
+ if (length > MAX_DIST) {
+ offset += length - MAX_DIST;
+ length = MAX_DIST;
+ }
+
+ System.arraycopy(buffer, offset, window, strstart, length);
+
+ updateHash();
+ length--;
+ while (--length > 0)
+ {
+ insertString();
+ strstart++;
+ }
+ strstart += 2;
+ blockStart = strstart;
+ }
+
+ private boolean deflateStored(boolean flush, boolean finish)
+ {
+ if (!flush && lookahead == 0)
+ return false;
+
+ strstart += lookahead;
+ lookahead = 0;
+
+ int storedLen = strstart - blockStart;
+
+ if ((storedLen >= DeflaterConstants.MAX_BLOCK_SIZE)
+ /* Block is full */
+ || (blockStart < WSIZE && storedLen >= MAX_DIST)
+ /* Block may move out of window */
+ || flush)
+ {
+ boolean lastBlock = finish;
+ if (storedLen > DeflaterConstants.MAX_BLOCK_SIZE)
+ {
+ storedLen = DeflaterConstants.MAX_BLOCK_SIZE;
+ lastBlock = false;
+ }
+
+ if (DeflaterConstants.DEBUGGING)
+ System.err.println("storedBlock["+storedLen+","+lastBlock+"]");
+
+ huffman.flushStoredBlock(window, blockStart, storedLen, lastBlock);
+ blockStart += storedLen;
+ return !lastBlock;
+ }
+ return true;
+ }
+
+ private boolean deflateFast(boolean flush, boolean finish)
+ {
+ if (lookahead < MIN_LOOKAHEAD && !flush)
+ return false;
+
+ while (lookahead >= MIN_LOOKAHEAD || flush)
+ {
+ if (lookahead == 0)
+ {
+ /* We are flushing everything */
+ huffman.flushBlock(window, blockStart, strstart - blockStart,
+ finish);
+ blockStart = strstart;
+ return false;
+ }
+
+ if (strstart > 2 * WSIZE - MIN_LOOKAHEAD)
+ {
+ /* slide window, as findLongestMatch need this.
+ * This should only happen when flushing and the window
+ * is almost full.
+ */
+ slideWindow();
+ }
+
+ int hashHead;
+ if (lookahead >= MIN_MATCH
+ && (hashHead = insertString()) != 0
+ && strategy != Deflater.HUFFMAN_ONLY
+ && strstart - hashHead <= MAX_DIST
+ && findLongestMatch(hashHead))
+ {
+ /* longestMatch sets matchStart and matchLen */
+ if (DeflaterConstants.DEBUGGING)
+ {
+ for (int i = 0 ; i < matchLen; i++)
+ {
+ if (window[strstart+i] != window[matchStart + i])
+ throw new InternalError();
+ }
+ }
+ boolean full = huffman.tallyDist(strstart - matchStart, matchLen);
+
+ lookahead -= matchLen;
+ if (matchLen <= max_lazy && lookahead >= MIN_MATCH)
+ {
+ while (--matchLen > 0)
+ {
+ strstart++;
+ insertString();
+ }
+ strstart++;
+ }
+ else
+ {
+ strstart += matchLen;
+ if (lookahead >= MIN_MATCH - 1)
+ updateHash();
+ }
+ matchLen = MIN_MATCH - 1;
+ if (!full)
+ continue;
+ }
+ else
+ {
+ /* No match found */
+ huffman.tallyLit(window[strstart] & 0xff);
+ strstart++;
+ lookahead--;
+ }
+
+ if (huffman.isFull())
+ {
+ boolean lastBlock = finish && lookahead == 0;
+ huffman.flushBlock(window, blockStart, strstart - blockStart,
+ lastBlock);
+ blockStart = strstart;
+ return !lastBlock;
+ }
+ }
+ return true;
+ }
+
+ private boolean deflateSlow(boolean flush, boolean finish)
+ {
+ if (lookahead < MIN_LOOKAHEAD && !flush)
+ return false;
+
+ while (lookahead >= MIN_LOOKAHEAD || flush)
+ {
+ if (lookahead == 0)
+ {
+ if (prevAvailable)
+ huffman.tallyLit(window[strstart-1] & 0xff);
+ prevAvailable = false;
+
+ /* We are flushing everything */
+ if (DeflaterConstants.DEBUGGING && !flush)
+ throw new InternalError("Not flushing, but no lookahead");
+ huffman.flushBlock(window, blockStart, strstart - blockStart,
+ finish);
+ blockStart = strstart;
+ return false;
+ }
+
+ if (strstart >= 2 * WSIZE - MIN_LOOKAHEAD)
+ {
+ /* slide window, as findLongestMatch need this.
+ * This should only happen when flushing and the window
+ * is almost full.
+ */
+ slideWindow();
+ }
+
+ int prevMatch = matchStart;
+ int prevLen = matchLen;
+ if (lookahead >= MIN_MATCH)
+ {
+ int hashHead = insertString();
+ if (strategy != Deflater.HUFFMAN_ONLY
+ && hashHead != 0 && strstart - hashHead <= MAX_DIST
+ && findLongestMatch(hashHead))
+ {
+ /* longestMatch sets matchStart and matchLen */
+
+ /* Discard match if too small and too far away */
+ if (matchLen <= 5
+ && (strategy == Deflater.FILTERED
+ || (matchLen == MIN_MATCH
+ && strstart - matchStart > TOO_FAR))) {
+ matchLen = MIN_MATCH - 1;
+ }
+ }
+ }
+
+ /* previous match was better */
+ if (prevLen >= MIN_MATCH && matchLen <= prevLen)
+ {
+ if (DeflaterConstants.DEBUGGING)
+ {
+ for (int i = 0 ; i < matchLen; i++)
+ {
+ if (window[strstart-1+i] != window[prevMatch + i])
+ throw new InternalError();
+ }
+ }
+ huffman.tallyDist(strstart - 1 - prevMatch, prevLen);
+ prevLen -= 2;
+ do
+ {
+ strstart++;
+ lookahead--;
+ if (lookahead >= MIN_MATCH)
+ insertString();
+ }
+ while (--prevLen > 0);
+ strstart ++;
+ lookahead--;
+ prevAvailable = false;
+ matchLen = MIN_MATCH - 1;
+ }
+ else
+ {
+ if (prevAvailable)
+ huffman.tallyLit(window[strstart-1] & 0xff);
+ prevAvailable = true;
+ strstart++;
+ lookahead--;
+ }
+
+ if (huffman.isFull())
+ {
+ int len = strstart - blockStart;
+ if (prevAvailable)
+ len--;
+ boolean lastBlock = (finish && lookahead == 0 && !prevAvailable);
+ huffman.flushBlock(window, blockStart, len, lastBlock);
+ blockStart += len;
+ return !lastBlock;
+ }
+ }
+ return true;
+ }
+
+ public boolean deflate(boolean flush, boolean finish)
+ {
+ boolean progress;
+ do
+ {
+ fillWindow();
+ boolean canFlush = flush && inputOff == inputEnd;
+ if (DeflaterConstants.DEBUGGING)
+ System.err.println("window: ["+blockStart+","+strstart+","
+ +lookahead+"], "+comprFunc+","+canFlush);
+ switch (comprFunc)
+ {
+ case DEFLATE_STORED:
+ progress = deflateStored(canFlush, finish);
+ break;
+ case DEFLATE_FAST:
+ progress = deflateFast(canFlush, finish);
+ break;
+ case DEFLATE_SLOW:
+ progress = deflateSlow(canFlush, finish);
+ break;
+ default:
+ throw new InternalError();
+ }
+ }
+ while (pending.isFlushed() /* repeat while we have no pending output */
+ && progress); /* and progress was made */
+
+ return progress;
+ }
+
+ public void setInput(byte[] buf, int off, int len)
+ {
+ if (inputOff < inputEnd)
+ throw new IllegalStateException
+ ("Old input was not completely processed");
+
+ int end = off + len;
+
+ /* We want to throw an ArrayIndexOutOfBoundsException early. The
+ * check is very tricky: it also handles integer wrap around.
+ */
+ if (0 > off || off > end || end > buf.length)
+ throw new ArrayIndexOutOfBoundsException();
+
+ inputBuf = buf;
+ inputOff = off;
+ inputEnd = end;
+ }
+
+ public final boolean needsInput()
+ {
+ return inputEnd == inputOff;
+ }
+}
diff --git a/libjava/classpath/java/util/zip/DeflaterHuffman.java b/libjava/classpath/java/util/zip/DeflaterHuffman.java
new file mode 100644
index 000000000..8da987e0f
--- /dev/null
+++ b/libjava/classpath/java/util/zip/DeflaterHuffman.java
@@ -0,0 +1,776 @@
+/* DeflaterHuffman.java --
+ Copyright (C) 2001, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+/**
+ * This is the DeflaterHuffman class.
+ *
+ * This class is <i>not</i> thread safe. This is inherent in the API, due
+ * to the split of deflate and setInput.
+ *
+ * @author Jochen Hoenicke
+ * @date Jan 6, 2000
+ */
+class DeflaterHuffman
+{
+ private static final int BUFSIZE = 1 << (DeflaterConstants.DEFAULT_MEM_LEVEL + 6);
+ private static final int LITERAL_NUM = 286;
+ private static final int DIST_NUM = 30;
+ private static final int BITLEN_NUM = 19;
+ private static final int REP_3_6 = 16;
+ private static final int REP_3_10 = 17;
+ private static final int REP_11_138 = 18;
+ private static final int EOF_SYMBOL = 256;
+ private static final int[] BL_ORDER =
+ { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
+
+ private static final String bit4Reverse =
+ "\000\010\004\014\002\012\006\016\001\011\005\015\003\013\007\017";
+
+ class Tree {
+ short[] freqs;
+ short[] codes;
+ byte[] length;
+ int[] bl_counts;
+ int minNumCodes, numCodes;
+ int maxLength;
+
+ Tree(int elems, int minCodes, int maxLength) {
+ this.minNumCodes = minCodes;
+ this.maxLength = maxLength;
+ freqs = new short[elems];
+ bl_counts = new int[maxLength];
+ }
+
+ void reset() {
+ for (int i = 0; i < freqs.length; i++)
+ freqs[i] = 0;
+ codes = null;
+ length = null;
+ }
+
+ final void writeSymbol(int code)
+ {
+ if (DeflaterConstants.DEBUGGING)
+ {
+ freqs[code]--;
+// System.err.print("writeSymbol("+freqs.length+","+code+"): ");
+ }
+ pending.writeBits(codes[code] & 0xffff, length[code]);
+ }
+
+ final void checkEmpty()
+ {
+ boolean empty = true;
+ for (int i = 0; i < freqs.length; i++)
+ if (freqs[i] != 0)
+ {
+ System.err.println("freqs["+i+"] == "+freqs[i]);
+ empty = false;
+ }
+ if (!empty)
+ throw new InternalError();
+ System.err.println("checkEmpty suceeded!");
+ }
+
+ void setStaticCodes(short[] stCodes, byte[] stLength)
+ {
+ codes = stCodes;
+ length = stLength;
+ }
+
+ public void buildCodes() {
+ int[] nextCode = new int[maxLength];
+ int code = 0;
+ codes = new short[freqs.length];
+
+ if (DeflaterConstants.DEBUGGING)
+ System.err.println("buildCodes: "+freqs.length);
+ for (int bits = 0; bits < maxLength; bits++)
+ {
+ nextCode[bits] = code;
+ code += bl_counts[bits] << (15 - bits);
+ if (DeflaterConstants.DEBUGGING)
+ System.err.println("bits: "+(bits+1)+" count: "+bl_counts[bits]
+ +" nextCode: "+Integer.toHexString(code));
+ }
+ if (DeflaterConstants.DEBUGGING && code != 65536)
+ throw new RuntimeException("Inconsistent bl_counts!");
+
+ for (int i=0; i < numCodes; i++)
+ {
+ int bits = length[i];
+ if (bits > 0)
+ {
+ if (DeflaterConstants.DEBUGGING)
+ System.err.println("codes["+i+"] = rev("
+ +Integer.toHexString(nextCode[bits-1])+"),"
+ +bits);
+ codes[i] = bitReverse(nextCode[bits-1]);
+ nextCode[bits-1] += 1 << (16 - bits);
+ }
+ }
+ }
+
+ private void buildLength(int childs[])
+ {
+ this.length = new byte [freqs.length];
+ int numNodes = childs.length / 2;
+ int numLeafs = (numNodes + 1) / 2;
+ int overflow = 0;
+
+ for (int i = 0; i < maxLength; i++)
+ bl_counts[i] = 0;
+
+ /* First calculate optimal bit lengths */
+ int lengths[] = new int[numNodes];
+ lengths[numNodes-1] = 0;
+ for (int i = numNodes - 1; i >= 0; i--)
+ {
+ if (childs[2*i+1] != -1)
+ {
+ int bitLength = lengths[i] + 1;
+ if (bitLength > maxLength)
+ {
+ bitLength = maxLength;
+ overflow++;
+ }
+ lengths[childs[2*i]] = lengths[childs[2*i+1]] = bitLength;
+ }
+ else
+ {
+ /* A leaf node */
+ int bitLength = lengths[i];
+ bl_counts[bitLength - 1]++;
+ this.length[childs[2*i]] = (byte) lengths[i];
+ }
+ }
+
+ if (DeflaterConstants.DEBUGGING)
+ {
+ System.err.println("Tree "+freqs.length+" lengths:");
+ for (int i=0; i < numLeafs; i++)
+ System.err.println("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]]
+ + " len: "+length[childs[2*i]]);
+ }
+
+ if (overflow == 0)
+ return;
+
+ int incrBitLen = maxLength - 1;
+ do
+ {
+ /* Find the first bit length which could increase: */
+ while (bl_counts[--incrBitLen] == 0)
+ ;
+
+ /* Move this node one down and remove a corresponding
+ * amount of overflow nodes.
+ */
+ do
+ {
+ bl_counts[incrBitLen]--;
+ bl_counts[++incrBitLen]++;
+ overflow -= 1 << (maxLength - 1 - incrBitLen);
+ }
+ while (overflow > 0 && incrBitLen < maxLength - 1);
+ }
+ while (overflow > 0);
+
+ /* We may have overshot above. Move some nodes from maxLength to
+ * maxLength-1 in that case.
+ */
+ bl_counts[maxLength-1] += overflow;
+ bl_counts[maxLength-2] -= overflow;
+
+ /* Now recompute all bit lengths, scanning in increasing
+ * frequency. It is simpler to reconstruct all lengths instead of
+ * fixing only the wrong ones. This idea is taken from 'ar'
+ * written by Haruhiko Okumura.
+ *
+ * The nodes were inserted with decreasing frequency into the childs
+ * array.
+ */
+ int nodePtr = 2 * numLeafs;
+ for (int bits = maxLength; bits != 0; bits--)
+ {
+ int n = bl_counts[bits-1];
+ while (n > 0)
+ {
+ int childPtr = 2*childs[nodePtr++];
+ if (childs[childPtr + 1] == -1)
+ {
+ /* We found another leaf */
+ length[childs[childPtr]] = (byte) bits;
+ n--;
+ }
+ }
+ }
+ if (DeflaterConstants.DEBUGGING)
+ {
+ System.err.println("*** After overflow elimination. ***");
+ for (int i=0; i < numLeafs; i++)
+ System.err.println("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]]
+ + " len: "+length[childs[2*i]]);
+ }
+ }
+
+ void buildTree()
+ {
+ int numSymbols = freqs.length;
+
+ /* heap is a priority queue, sorted by frequency, least frequent
+ * nodes first. The heap is a binary tree, with the property, that
+ * the parent node is smaller than both child nodes. This assures
+ * that the smallest node is the first parent.
+ *
+ * The binary tree is encoded in an array: 0 is root node and
+ * the nodes 2*n+1, 2*n+2 are the child nodes of node n.
+ */
+ int[] heap = new int[numSymbols];
+ int heapLen = 0;
+ int maxCode = 0;
+ for (int n = 0; n < numSymbols; n++)
+ {
+ int freq = freqs[n];
+ if (freq != 0)
+ {
+ /* Insert n into heap */
+ int pos = heapLen++;
+ int ppos;
+ while (pos > 0 &&
+ freqs[heap[ppos = (pos - 1) / 2]] > freq) {
+ heap[pos] = heap[ppos];
+ pos = ppos;
+ }
+ heap[pos] = n;
+ maxCode = n;
+ }
+ }
+
+ /* We could encode a single literal with 0 bits but then we
+ * don't see the literals. Therefore we force at least two
+ * literals to avoid this case. We don't care about order in
+ * this case, both literals get a 1 bit code.
+ */
+ while (heapLen < 2)
+ {
+ int node = maxCode < 2 ? ++maxCode : 0;
+ heap[heapLen++] = node;
+ }
+
+ numCodes = Math.max(maxCode + 1, minNumCodes);
+
+ int numLeafs = heapLen;
+ int[] childs = new int[4*heapLen - 2];
+ int[] values = new int[2*heapLen - 1];
+ int numNodes = numLeafs;
+ for (int i = 0; i < heapLen; i++)
+ {
+ int node = heap[i];
+ childs[2*i] = node;
+ childs[2*i+1] = -1;
+ values[i] = freqs[node] << 8;
+ heap[i] = i;
+ }
+
+ /* Construct the Huffman tree by repeatedly combining the least two
+ * frequent nodes.
+ */
+ do
+ {
+ int first = heap[0];
+ int last = heap[--heapLen];
+
+ /* Propagate the hole to the leafs of the heap */
+ int ppos = 0;
+ int path = 1;
+ while (path < heapLen)
+ {
+ if (path + 1 < heapLen
+ && values[heap[path]] > values[heap[path+1]])
+ path++;
+
+ heap[ppos] = heap[path];
+ ppos = path;
+ path = path * 2 + 1;
+ }
+
+ /* Now propagate the last element down along path. Normally
+ * it shouldn't go too deep.
+ */
+ int lastVal = values[last];
+ while ((path = ppos) > 0
+ && values[heap[ppos = (path - 1)/2]] > lastVal)
+ heap[path] = heap[ppos];
+ heap[path] = last;
+
+
+ int second = heap[0];
+
+ /* Create a new node father of first and second */
+ last = numNodes++;
+ childs[2*last] = first;
+ childs[2*last+1] = second;
+ int mindepth = Math.min(values[first] & 0xff, values[second] & 0xff);
+ values[last] = lastVal = values[first] + values[second] - mindepth + 1;
+
+ /* Again, propagate the hole to the leafs */
+ ppos = 0;
+ path = 1;
+ while (path < heapLen)
+ {
+ if (path + 1 < heapLen
+ && values[heap[path]] > values[heap[path+1]])
+ path++;
+
+ heap[ppos] = heap[path];
+ ppos = path;
+ path = ppos * 2 + 1;
+ }
+
+ /* Now propagate the new element down along path */
+ while ((path = ppos) > 0
+ && values[heap[ppos = (path - 1)/2]] > lastVal)
+ heap[path] = heap[ppos];
+ heap[path] = last;
+ }
+ while (heapLen > 1);
+
+ if (heap[0] != childs.length / 2 - 1)
+ throw new RuntimeException("Weird!");
+
+ buildLength(childs);
+ }
+
+ int getEncodedLength()
+ {
+ int len = 0;
+ for (int i = 0; i < freqs.length; i++)
+ len += freqs[i] * length[i];
+ return len;
+ }
+
+ void calcBLFreq(Tree blTree) {
+ int max_count; /* max repeat count */
+ int min_count; /* min repeat count */
+ int count; /* repeat count of the current code */
+ int curlen = -1; /* length of current code */
+
+ int i = 0;
+ while (i < numCodes)
+ {
+ count = 1;
+ int nextlen = length[i];
+ if (nextlen == 0)
+ {
+ max_count = 138;
+ min_count = 3;
+ }
+ else
+ {
+ max_count = 6;
+ min_count = 3;
+ if (curlen != nextlen)
+ {
+ blTree.freqs[nextlen]++;
+ count = 0;
+ }
+ }
+ curlen = nextlen;
+ i++;
+
+ while (i < numCodes && curlen == length[i])
+ {
+ i++;
+ if (++count >= max_count)
+ break;
+ }
+
+ if (count < min_count)
+ blTree.freqs[curlen] += count;
+ else if (curlen != 0)
+ blTree.freqs[REP_3_6]++;
+ else if (count <= 10)
+ blTree.freqs[REP_3_10]++;
+ else
+ blTree.freqs[REP_11_138]++;
+ }
+ }
+
+ void writeTree(Tree blTree)
+ {
+ int max_count; /* max repeat count */
+ int min_count; /* min repeat count */
+ int count; /* repeat count of the current code */
+ int curlen = -1; /* length of current code */
+
+ int i = 0;
+ while (i < numCodes)
+ {
+ count = 1;
+ int nextlen = length[i];
+ if (nextlen == 0)
+ {
+ max_count = 138;
+ min_count = 3;
+ }
+ else
+ {
+ max_count = 6;
+ min_count = 3;
+ if (curlen != nextlen)
+ {
+ blTree.writeSymbol(nextlen);
+ count = 0;
+ }
+ }
+ curlen = nextlen;
+ i++;
+
+ while (i < numCodes && curlen == length[i])
+ {
+ i++;
+ if (++count >= max_count)
+ break;
+ }
+
+ if (count < min_count)
+ {
+ while (count-- > 0)
+ blTree.writeSymbol(curlen);
+ }
+ else if (curlen != 0)
+ {
+ blTree.writeSymbol(REP_3_6);
+ pending.writeBits(count - 3, 2);
+ }
+ else if (count <= 10)
+ {
+ blTree.writeSymbol(REP_3_10);
+ pending.writeBits(count - 3, 3);
+ }
+ else
+ {
+ blTree.writeSymbol(REP_11_138);
+ pending.writeBits(count - 11, 7);
+ }
+ }
+ }
+ }
+
+
+
+ DeflaterPending pending;
+ private Tree literalTree, distTree, blTree;
+
+ private short d_buf[];
+ private byte l_buf[];
+ private int last_lit;
+ private int extra_bits;
+
+ private static short staticLCodes[];
+ private static byte staticLLength[];
+ private static short staticDCodes[];
+ private static byte staticDLength[];
+
+ /**
+ * Reverse the bits of a 16 bit value.
+ */
+ static short bitReverse(int value) {
+ return (short) (bit4Reverse.charAt(value & 0xf) << 12
+ | bit4Reverse.charAt((value >> 4) & 0xf) << 8
+ | bit4Reverse.charAt((value >> 8) & 0xf) << 4
+ | bit4Reverse.charAt(value >> 12));
+ }
+
+ static {
+ /* See RFC 1951 3.2.6 */
+ /* Literal codes */
+ staticLCodes = new short[LITERAL_NUM];
+ staticLLength = new byte[LITERAL_NUM];
+ int i = 0;
+ while (i < 144) {
+ staticLCodes[i] = bitReverse((0x030 + i) << 8);
+ staticLLength[i++] = 8;
+ }
+ while (i < 256) {
+ staticLCodes[i] = bitReverse((0x190 - 144 + i) << 7);
+ staticLLength[i++] = 9;
+ }
+ while (i < 280) {
+ staticLCodes[i] = bitReverse((0x000 - 256 + i) << 9);
+ staticLLength[i++] = 7;
+ }
+ while (i < LITERAL_NUM) {
+ staticLCodes[i] = bitReverse((0x0c0 - 280 + i) << 8);
+ staticLLength[i++] = 8;
+ }
+
+ /* Distant codes */
+ staticDCodes = new short[DIST_NUM];
+ staticDLength = new byte[DIST_NUM];
+ for (i = 0; i < DIST_NUM; i++) {
+ staticDCodes[i] = bitReverse(i << 11);
+ staticDLength[i] = 5;
+ }
+ }
+
+ public DeflaterHuffman(DeflaterPending pending)
+ {
+ this.pending = pending;
+
+ literalTree = new Tree(LITERAL_NUM, 257, 15);
+ distTree = new Tree(DIST_NUM, 1, 15);
+ blTree = new Tree(BITLEN_NUM, 4, 7);
+
+ d_buf = new short[BUFSIZE];
+ l_buf = new byte [BUFSIZE];
+ }
+
+ public final void reset() {
+ last_lit = 0;
+ extra_bits = 0;
+ literalTree.reset();
+ distTree.reset();
+ blTree.reset();
+ }
+
+ private int l_code(int len) {
+ if (len == 255)
+ return 285;
+
+ int code = 257;
+ while (len >= 8)
+ {
+ code += 4;
+ len >>= 1;
+ }
+ return code + len;
+ }
+
+ private int d_code(int distance) {
+ int code = 0;
+ while (distance >= 4)
+ {
+ code += 2;
+ distance >>= 1;
+ }
+ return code + distance;
+ }
+
+ public void sendAllTrees(int blTreeCodes) {
+ blTree.buildCodes();
+ literalTree.buildCodes();
+ distTree.buildCodes();
+ pending.writeBits(literalTree.numCodes - 257, 5);
+ pending.writeBits(distTree.numCodes - 1, 5);
+ pending.writeBits(blTreeCodes - 4, 4);
+ for (int rank = 0; rank < blTreeCodes; rank++)
+ pending.writeBits(blTree.length[BL_ORDER[rank]], 3);
+ literalTree.writeTree(blTree);
+ distTree.writeTree(blTree);
+ if (DeflaterConstants.DEBUGGING)
+ blTree.checkEmpty();
+ }
+
+ public void compressBlock() {
+ for (int i = 0; i < last_lit; i++)
+ {
+ int litlen = l_buf[i] & 0xff;
+ int dist = d_buf[i];
+ if (dist-- != 0)
+ {
+ if (DeflaterConstants.DEBUGGING)
+ System.err.print("["+(dist+1)+","+(litlen+3)+"]: ");
+
+ int lc = l_code(litlen);
+ literalTree.writeSymbol(lc);
+
+ int bits = (lc - 261) / 4;
+ if (bits > 0 && bits <= 5)
+ pending.writeBits(litlen & ((1 << bits) - 1), bits);
+
+ int dc = d_code(dist);
+ distTree.writeSymbol(dc);
+
+ bits = dc / 2 - 1;
+ if (bits > 0)
+ pending.writeBits(dist & ((1 << bits) - 1), bits);
+ }
+ else
+ {
+ if (DeflaterConstants.DEBUGGING)
+ {
+ if (litlen > 32 && litlen < 127)
+ System.err.print("("+(char)litlen+"): ");
+ else
+ System.err.print("{"+litlen+"}: ");
+ }
+ literalTree.writeSymbol(litlen);
+ }
+ }
+ if (DeflaterConstants.DEBUGGING)
+ System.err.print("EOF: ");
+ literalTree.writeSymbol(EOF_SYMBOL);
+ if (DeflaterConstants.DEBUGGING)
+ {
+ literalTree.checkEmpty();
+ distTree.checkEmpty();
+ }
+ }
+
+ public void flushStoredBlock(byte[] stored,
+ int stored_offset, int stored_len,
+ boolean lastBlock) {
+ if (DeflaterConstants.DEBUGGING)
+ System.err.println("Flushing stored block "+ stored_len);
+ pending.writeBits((DeflaterConstants.STORED_BLOCK << 1)
+ + (lastBlock ? 1 : 0), 3);
+ pending.alignToByte();
+ pending.writeShort(stored_len);
+ pending.writeShort(~stored_len);
+ pending.writeBlock(stored, stored_offset, stored_len);
+ reset();
+ }
+
+ public void flushBlock(byte[] stored, int stored_offset, int stored_len,
+ boolean lastBlock) {
+ literalTree.freqs[EOF_SYMBOL]++;
+
+ /* Build trees */
+ literalTree.buildTree();
+ distTree.buildTree();
+
+ /* Calculate bitlen frequency */
+ literalTree.calcBLFreq(blTree);
+ distTree.calcBLFreq(blTree);
+
+ /* Build bitlen tree */
+ blTree.buildTree();
+
+ int blTreeCodes = 4;
+ for (int i = 18; i > blTreeCodes; i--)
+ {
+ if (blTree.length[BL_ORDER[i]] > 0)
+ blTreeCodes = i+1;
+ }
+ int opt_len = 14 + blTreeCodes * 3 + blTree.getEncodedLength()
+ + literalTree.getEncodedLength() + distTree.getEncodedLength()
+ + extra_bits;
+
+ int static_len = extra_bits;
+ for (int i = 0; i < LITERAL_NUM; i++)
+ static_len += literalTree.freqs[i] * staticLLength[i];
+ for (int i = 0; i < DIST_NUM; i++)
+ static_len += distTree.freqs[i] * staticDLength[i];
+ if (opt_len >= static_len)
+ {
+ /* Force static trees */
+ opt_len = static_len;
+ }
+
+ if (stored_offset >= 0 && stored_len+4 < opt_len >> 3)
+ {
+ /* Store Block */
+ if (DeflaterConstants.DEBUGGING)
+ System.err.println("Storing, since " + stored_len + " < " + opt_len
+ + " <= " + static_len);
+ flushStoredBlock(stored, stored_offset, stored_len, lastBlock);
+ }
+ else if (opt_len == static_len)
+ {
+ /* Encode with static tree */
+ pending.writeBits((DeflaterConstants.STATIC_TREES << 1)
+ + (lastBlock ? 1 : 0), 3);
+ literalTree.setStaticCodes(staticLCodes, staticLLength);
+ distTree.setStaticCodes(staticDCodes, staticDLength);
+ compressBlock();
+ reset();
+ }
+ else
+ {
+ /* Encode with dynamic tree */
+ pending.writeBits((DeflaterConstants.DYN_TREES << 1)
+ + (lastBlock ? 1 : 0), 3);
+ sendAllTrees(blTreeCodes);
+ compressBlock();
+ reset();
+ }
+ }
+
+ public final boolean isFull()
+ {
+ return last_lit == BUFSIZE;
+ }
+
+ public final boolean tallyLit(int lit)
+ {
+ if (DeflaterConstants.DEBUGGING)
+ {
+ if (lit > 32 && lit < 127)
+ System.err.println("("+(char)lit+")");
+ else
+ System.err.println("{"+lit+"}");
+ }
+ d_buf[last_lit] = 0;
+ l_buf[last_lit++] = (byte) lit;
+ literalTree.freqs[lit]++;
+ return last_lit == BUFSIZE;
+ }
+
+ public final boolean tallyDist(int dist, int len)
+ {
+ if (DeflaterConstants.DEBUGGING)
+ System.err.println("["+dist+","+len+"]");
+
+ d_buf[last_lit] = (short) dist;
+ l_buf[last_lit++] = (byte) (len - 3);
+
+ int lc = l_code(len-3);
+ literalTree.freqs[lc]++;
+ if (lc >= 265 && lc < 285)
+ extra_bits += (lc - 261) / 4;
+
+ int dc = d_code(dist-1);
+ distTree.freqs[dc]++;
+ if (dc >= 4)
+ extra_bits += dc / 2 - 1;
+ return last_lit == BUFSIZE;
+ }
+}
diff --git a/libjava/classpath/java/util/zip/DeflaterOutputStream.java b/libjava/classpath/java/util/zip/DeflaterOutputStream.java
new file mode 100644
index 000000000..6fd1c5cfb
--- /dev/null
+++ b/libjava/classpath/java/util/zip/DeflaterOutputStream.java
@@ -0,0 +1,198 @@
+/* DeflaterOutputStream.java - Output filter for compressing.
+ Copyright (C) 1999, 2000, 2001, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/* Written using on-line Java Platform 1.2 API Specification
+ * and JCL book.
+ * Believed complete and correct.
+ */
+
+/**
+ * This is a special FilterOutputStream deflating the bytes that are
+ * written through it. It uses the Deflater for deflating.
+ *
+ * A special thing to be noted is that flush() doesn't flush
+ * everything in Sun's JDK, but it does so in jazzlib. This is because
+ * Sun's Deflater doesn't have a way to flush() everything, without
+ * finishing the stream.
+ *
+ * @author Tom Tromey, Jochen Hoenicke
+ * @date Jan 11, 2001
+ */
+public class DeflaterOutputStream extends FilterOutputStream
+{
+ /**
+ * This buffer is used temporarily to retrieve the bytes from the
+ * deflater and write them to the underlying output stream.
+ */
+ protected byte[] buf;
+
+ /**
+ * The deflater which is used to deflate the stream.
+ */
+ protected Deflater def;
+
+ /**
+ * Deflates everything in the def's input buffers. This will call
+ * <code>def.deflate()</code> until all bytes from the input buffers
+ * are processed.
+ */
+ protected void deflate() throws IOException
+ {
+ while (! def.needsInput())
+ {
+ int len = def.deflate(buf, 0, buf.length);
+
+ // System.err.println("DOS deflated " + len + " out of " + buf.length);
+ if (len <= 0)
+ break;
+ out.write(buf, 0, len);
+ }
+
+ if (! def.needsInput())
+ throw new InternalError("Can't deflate all input?");
+ }
+
+ /**
+ * Creates a new DeflaterOutputStream with a default Deflater and
+ * default buffer size.
+ * @param out the output stream where deflated output should be written.
+ */
+ public DeflaterOutputStream(OutputStream out)
+ {
+ this(out, new Deflater(), 4096);
+ }
+
+ /**
+ * Creates a new DeflaterOutputStream with the given Deflater and
+ * default buffer size.
+ * @param out the output stream where deflated output should be written.
+ * @param defl the underlying deflater.
+ */
+ public DeflaterOutputStream(OutputStream out, Deflater defl)
+ {
+ this(out, defl, 4096);
+ }
+
+ /**
+ * Creates a new DeflaterOutputStream with the given Deflater and
+ * buffer size.
+ * @param out the output stream where deflated output should be written.
+ * @param defl the underlying deflater.
+ * @param bufsize the buffer size.
+ * @exception IllegalArgumentException if bufsize isn't positive.
+ */
+ public DeflaterOutputStream(OutputStream out, Deflater defl, int bufsize)
+ {
+ super(out);
+ if (bufsize <= 0)
+ throw new IllegalArgumentException("bufsize <= 0");
+ buf = new byte[bufsize];
+ def = defl;
+ }
+
+ /**
+ * Flushes the stream by calling flush() on the deflater and then
+ * on the underlying stream. This ensures that all bytes are
+ * flushed. This function doesn't work in Sun's JDK, but only in
+ * jazzlib.
+ */
+ public void flush() throws IOException
+ {
+ def.flush();
+ deflate();
+ out.flush();
+ }
+
+ /**
+ * Finishes the stream by calling finish() on the deflater. This
+ * was the only way to ensure that all bytes are flushed in Sun's
+ * JDK.
+ */
+ public void finish() throws IOException
+ {
+ def.finish();
+ while (! def.finished())
+ {
+ int len = def.deflate(buf, 0, buf.length);
+ if (len <= 0)
+ break;
+ out.write(buf, 0, len);
+ }
+ if (! def.finished())
+ throw new InternalError("Can't deflate all input?");
+ out.flush();
+ }
+
+ /**
+ * Calls finish() and closes the stream.
+ */
+ public void close() throws IOException
+ {
+ finish();
+ out.close();
+ }
+
+ /**
+ * Writes a single byte to the compressed output stream.
+ * @param bval the byte value.
+ */
+ public void write(int bval) throws IOException
+ {
+ byte[] b = new byte[1];
+ b[0] = (byte) bval;
+ write(b, 0, 1);
+ }
+
+ /**
+ * Writes a len bytes from an array to the compressed stream.
+ * @param buf the byte array.
+ * @param off the offset into the byte array where to start.
+ * @param len the number of bytes to write.
+ */
+ public void write(byte[] buf, int off, int len) throws IOException
+ {
+ def.setInput(buf, off, len);
+ deflate();
+ }
+}
diff --git a/libjava/classpath/java/util/zip/DeflaterPending.java b/libjava/classpath/java/util/zip/DeflaterPending.java
new file mode 100644
index 000000000..fabc2264e
--- /dev/null
+++ b/libjava/classpath/java/util/zip/DeflaterPending.java
@@ -0,0 +1,53 @@
+/* java.util.zip.DeflaterPending
+ Copyright (C) 2001 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+/**
+ * This class stores the pending output of the Deflater.
+ *
+ * @author Jochen Hoenicke
+ * @date Jan 5, 2000
+ */
+
+class DeflaterPending extends PendingBuffer
+{
+ public DeflaterPending()
+ {
+ super(DeflaterConstants.PENDING_BUF_SIZE);
+ }
+}
diff --git a/libjava/classpath/java/util/zip/GZIPInputStream.java b/libjava/classpath/java/util/zip/GZIPInputStream.java
new file mode 100644
index 000000000..ed99ee92c
--- /dev/null
+++ b/libjava/classpath/java/util/zip/GZIPInputStream.java
@@ -0,0 +1,355 @@
+/* GZIPInputStream.java - Input filter for reading gzip file
+ Copyright (C) 1999, 2000, 2001, 2002, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * This filter stream is used to decompress a "GZIP" format stream.
+ * The "GZIP" format is described in RFC 1952.
+ *
+ * @author John Leuner
+ * @author Tom Tromey
+ * @since JDK 1.1
+ */
+public class GZIPInputStream
+ extends InflaterInputStream
+{
+ /**
+ * The magic number found at the start of a GZIP stream.
+ */
+ public static final int GZIP_MAGIC = 0x8b1f;
+
+ /**
+ * The mask for bit 0 of the flag byte.
+ */
+ static final int FTEXT = 0x1;
+
+ /**
+ * The mask for bit 1 of the flag byte.
+ */
+ static final int FHCRC = 0x2;
+
+ /**
+ * The mask for bit 2 of the flag byte.
+ */
+ static final int FEXTRA = 0x4;
+
+ /**
+ * The mask for bit 3 of the flag byte.
+ */
+ static final int FNAME = 0x8;
+
+ /**
+ * The mask for bit 4 of the flag byte.
+ */
+ static final int FCOMMENT = 0x10;
+
+ /**
+ * The CRC-32 checksum value for uncompressed data.
+ */
+ protected CRC32 crc;
+
+ /**
+ * Indicates whether or not the end of the stream has been reached.
+ */
+ protected boolean eos;
+
+ /**
+ * Indicates whether or not the GZIP header has been read in.
+ */
+ private boolean readGZIPHeader;
+
+ /**
+ * Creates a GZIPInputStream with the default buffer size.
+ *
+ * @param in The stream to read compressed data from
+ * (in GZIP format).
+ *
+ * @throws IOException if an error occurs during an I/O operation.
+ */
+ public GZIPInputStream(InputStream in)
+ throws IOException
+ {
+ this(in, 4096);
+ }
+
+ /**
+ * Creates a GZIPInputStream with the specified buffer size.
+ *
+ * @param in The stream to read compressed data from
+ * (in GZIP format).
+ * @param size The size of the buffer to use.
+ *
+ * @throws IOException if an error occurs during an I/O operation.
+ * @throws IllegalArgumentException if <code>size</code>
+ * is less than or equal to 0.
+ */
+ public GZIPInputStream(InputStream in, int size)
+ throws IOException
+ {
+ super(in, new Inflater(true), size);
+ crc = new CRC32();
+ readHeader();
+ }
+
+ /**
+ * Closes the input stream.
+ *
+ * @throws IOException if an error occurs during an I/O operation.
+ */
+ public void close()
+ throws IOException
+ {
+ // Nothing to do here.
+ super.close();
+ }
+
+ /**
+ * Reads in GZIP-compressed data and stores it in uncompressed form
+ * into an array of bytes. The method will block until either
+ * enough input data becomes available or the compressed stream
+ * reaches its end.
+ *
+ * @param buf the buffer into which the uncompressed data will
+ * be stored.
+ * @param offset the offset indicating where in <code>buf</code>
+ * the uncompressed data should be placed.
+ * @param len the number of uncompressed bytes to be read.
+ */
+ public int read(byte[] buf, int offset, int len) throws IOException
+ {
+ // We first have to slurp in the GZIP header, then we feed all the
+ // rest of the data to the superclass.
+ //
+ // As we do that we continually update the CRC32. Once the data is
+ // finished, we check the CRC32.
+ //
+ // This means we don't need our own buffer, as everything is done
+ // in the superclass.
+ if (!readGZIPHeader)
+ readHeader();
+
+ if (eos)
+ return -1;
+
+ // System.err.println("GZIPIS.read(byte[], off, len ... " + offset + " and len " + len);
+
+ /* We don't have to read the header,
+ * so we just grab data from the superclass.
+ */
+ int numRead = super.read(buf, offset, len);
+ if (numRead > 0)
+ crc.update(buf, offset, numRead);
+
+ if (inf.finished())
+ readFooter();
+ return numRead;
+ }
+
+
+ /**
+ * Reads in the GZIP header.
+ */
+ private void readHeader() throws IOException
+ {
+ /* 1. Check the two magic bytes */
+ CRC32 headCRC = new CRC32();
+ int magic = in.read();
+ if (magic < 0)
+ {
+ eos = true;
+ return;
+ }
+ int magic2 = in.read();
+ if ((magic + (magic2 << 8)) != GZIP_MAGIC)
+ throw new IOException("Error in GZIP header, bad magic code");
+ headCRC.update(magic);
+ headCRC.update(magic2);
+
+ /* 2. Check the compression type (must be 8) */
+ int CM = in.read();
+ if (CM != Deflater.DEFLATED)
+ throw new IOException("Error in GZIP header, data not in deflate format");
+ headCRC.update(CM);
+
+ /* 3. Check the flags */
+ int flags = in.read();
+ if (flags < 0)
+ throw new EOFException("Early EOF in GZIP header");
+ headCRC.update(flags);
+
+ /* This flag byte is divided into individual bits as follows:
+
+ bit 0 FTEXT
+ bit 1 FHCRC
+ bit 2 FEXTRA
+ bit 3 FNAME
+ bit 4 FCOMMENT
+ bit 5 reserved
+ bit 6 reserved
+ bit 7 reserved
+ */
+
+ /* 3.1 Check the reserved bits are zero */
+ if ((flags & 0xd0) != 0)
+ throw new IOException("Reserved flag bits in GZIP header != 0");
+
+ /* 4.-6. Skip the modification time, extra flags, and OS type */
+ for (int i=0; i< 6; i++)
+ {
+ int readByte = in.read();
+ if (readByte < 0)
+ throw new EOFException("Early EOF in GZIP header");
+ headCRC.update(readByte);
+ }
+
+ /* 7. Read extra field */
+ if ((flags & FEXTRA) != 0)
+ {
+ /* Skip subfield id */
+ for (int i=0; i< 2; i++)
+ {
+ int readByte = in.read();
+ if (readByte < 0)
+ throw new EOFException("Early EOF in GZIP header");
+ headCRC.update(readByte);
+ }
+ if (in.read() < 0 || in.read() < 0)
+ throw new EOFException("Early EOF in GZIP header");
+
+ int len1, len2, extraLen;
+ len1 = in.read();
+ len2 = in.read();
+ if ((len1 < 0) || (len2 < 0))
+ throw new EOFException("Early EOF in GZIP header");
+ headCRC.update(len1);
+ headCRC.update(len2);
+
+ extraLen = (len1 << 8) | len2;
+ for (int i = 0; i < extraLen;i++)
+ {
+ int readByte = in.read();
+ if (readByte < 0)
+ throw new EOFException("Early EOF in GZIP header");
+ headCRC.update(readByte);
+ }
+ }
+
+ /* 8. Read file name */
+ if ((flags & FNAME) != 0)
+ {
+ int readByte;
+ while ( (readByte = in.read()) > 0)
+ headCRC.update(readByte);
+ if (readByte < 0)
+ throw new EOFException("Early EOF in GZIP file name");
+ headCRC.update(readByte);
+ }
+
+ /* 9. Read comment */
+ if ((flags & FCOMMENT) != 0)
+ {
+ int readByte;
+ while ( (readByte = in.read()) > 0)
+ headCRC.update(readByte);
+
+ if (readByte < 0)
+ throw new EOFException("Early EOF in GZIP comment");
+ headCRC.update(readByte);
+ }
+
+ /* 10. Read header CRC */
+ if ((flags & FHCRC) != 0)
+ {
+ int tempByte;
+ int crcval = in.read();
+ if (crcval < 0)
+ throw new EOFException("Early EOF in GZIP header");
+
+ tempByte = in.read();
+ if (tempByte < 0)
+ throw new EOFException("Early EOF in GZIP header");
+
+ crcval = (crcval << 8) | tempByte;
+ if (crcval != ((int) headCRC.getValue() & 0xffff))
+ throw new IOException("Header CRC value mismatch");
+ }
+
+ readGZIPHeader = true;
+ //System.err.println("Read GZIP header");
+ }
+
+ private void readFooter() throws IOException
+ {
+ byte[] footer = new byte[8];
+ int avail = inf.getRemaining();
+ if (avail > 8)
+ avail = 8;
+ System.arraycopy(buf, len - inf.getRemaining(), footer, 0, avail);
+ int needed = 8 - avail;
+ while (needed > 0)
+ {
+ int count = in.read(footer, 8-needed, needed);
+ if (count <= 0)
+ throw new EOFException("Early EOF in GZIP footer");
+ needed -= count; //Jewel Jan 16
+ }
+
+ int crcval = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8)
+ | ((footer[2] & 0xff) << 16) | (footer[3] << 24);
+ if (crcval != (int) crc.getValue())
+ throw new IOException("GZIP crc sum mismatch, theirs \""
+ + Integer.toHexString(crcval)
+ + "\" and ours \""
+ + Integer.toHexString( (int) crc.getValue()));
+
+ int total = (footer[4] & 0xff) | ((footer[5] & 0xff) << 8)
+ | ((footer[6] & 0xff) << 16) | (footer[7] << 24);
+ if (total != inf.getTotalOut())
+ throw new IOException("Number of bytes mismatch");
+
+ /* FIXME" XXX Should we support multiple members.
+ * Difficult, since there may be some bytes still in buf
+ */
+ eos = true;
+ }
+}
diff --git a/libjava/classpath/java/util/zip/GZIPOutputStream.java b/libjava/classpath/java/util/zip/GZIPOutputStream.java
new file mode 100644
index 000000000..0080ab645
--- /dev/null
+++ b/libjava/classpath/java/util/zip/GZIPOutputStream.java
@@ -0,0 +1,151 @@
+/* GZIPOutputStream.java - Create a file in gzip format
+ Copyright (C) 1999, 2000, 2001 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * This filter stream is used to compress a stream into a "GZIP" stream.
+ * The "GZIP" format is described in RFC 1952.
+ *
+ * @author John Leuner
+ * @author Tom Tromey
+ * @since JDK 1.1
+ */
+
+/* Written using on-line Java Platform 1.2 API Specification
+ * and JCL book.
+ * Believed complete and correct.
+ */
+
+public class GZIPOutputStream extends DeflaterOutputStream
+{
+ /**
+ * CRC-32 value for uncompressed data
+ */
+ protected CRC32 crc;
+
+ /**
+ * Creates a GZIPOutputStream with the default buffer size
+ *
+ * @param out The stream to read data (to be compressed) from
+ *
+ */
+ public GZIPOutputStream(OutputStream out) throws IOException
+ {
+ this(out, 4096);
+ }
+
+ /**
+ * Creates a GZIPOutputStream with the specified buffer size
+ *
+ * @param out The stream to read compressed data from
+ * @param size Size of the buffer to use
+ */
+ public GZIPOutputStream(OutputStream out, int size) throws IOException
+ {
+ super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true), size);
+ crc = new CRC32();
+ int mod_time = (int) (System.currentTimeMillis() / 1000L);
+ byte[] gzipHeader =
+ {
+ /* The two magic bytes */
+ (byte) GZIPInputStream.GZIP_MAGIC,
+ (byte) (GZIPInputStream.GZIP_MAGIC >> 8),
+
+ /* The compression type */
+ (byte) Deflater.DEFLATED,
+
+ /* The flags (not set) */
+ 0,
+
+ /* The modification time */
+ (byte) mod_time, (byte) (mod_time >> 8),
+ (byte) (mod_time >> 16), (byte) (mod_time >> 24),
+
+ /* The extra flags */
+ 0,
+
+ /* The OS type (unknown) */
+ (byte) 255
+ };
+
+ out.write(gzipHeader);
+ // System.err.println("wrote GZIP header (" + gzipHeader.length + " bytes )");
+ }
+
+ public synchronized void write(byte[] buf, int off, int len)
+ throws IOException
+ {
+ super.write(buf, off, len);
+ crc.update(buf, off, len);
+ }
+
+ /**
+ * Writes remaining compressed output data to the output stream
+ * and closes it.
+ */
+ public void close() throws IOException
+ {
+ finish();
+ out.close();
+ }
+
+ public void finish() throws IOException
+ {
+ super.finish();
+
+ int totalin = def.getTotalIn();
+ int crcval = (int) (crc.getValue() & 0xffffffff);
+
+ // System.err.println("CRC val is " + Integer.toHexString( crcval ) + " and length " + Integer.toHexString(totalin));
+
+ byte[] gzipFooter =
+ {
+ (byte) crcval, (byte) (crcval >> 8),
+ (byte) (crcval >> 16), (byte) (crcval >> 24),
+
+ (byte) totalin, (byte) (totalin >> 8),
+ (byte) (totalin >> 16), (byte) (totalin >> 24)
+ };
+
+ out.write(gzipFooter);
+ // System.err.println("wrote GZIP trailer (" + gzipFooter.length + " bytes )");
+ }
+}
diff --git a/libjava/classpath/java/util/zip/Inflater.java b/libjava/classpath/java/util/zip/Inflater.java
new file mode 100644
index 000000000..0f094d667
--- /dev/null
+++ b/libjava/classpath/java/util/zip/Inflater.java
@@ -0,0 +1,726 @@
+/* Inflater.java - Decompress a data stream
+ Copyright (C) 1999, 2000, 2001, 2003, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+/* Written using on-line Java Platform 1.2 API Specification
+ * and JCL book.
+ * Believed complete and correct.
+ */
+
+/**
+ * Inflater is used to decompress data that has been compressed according
+ * to the "deflate" standard described in rfc1950.
+ *
+ * The usage is as following. First you have to set some input with
+ * <code>setInput()</code>, then inflate() it. If inflate doesn't
+ * inflate any bytes there may be three reasons:
+ * <ul>
+ * <li>needsInput() returns true because the input buffer is empty.
+ * You have to provide more input with <code>setInput()</code>.
+ * NOTE: needsInput() also returns true when, the stream is finished.
+ * </li>
+ * <li>needsDictionary() returns true, you have to provide a preset
+ * dictionary with <code>setDictionary()</code>.</li>
+ * <li>finished() returns true, the inflater has finished.</li>
+ * </ul>
+ * Once the first output byte is produced, a dictionary will not be
+ * needed at a later stage.
+ *
+ * @author John Leuner, Jochen Hoenicke
+ * @author Tom Tromey
+ * @date May 17, 1999
+ * @since JDK 1.1
+ */
+public class Inflater
+{
+ /* Copy lengths for literal codes 257..285 */
+ private static final int CPLENS[] =
+ {
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
+ };
+
+ /* Extra bits for literal codes 257..285 */
+ private static final int CPLEXT[] =
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+ 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
+ };
+
+ /* Copy offsets for distance codes 0..29 */
+ private static final int CPDIST[] = {
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577
+ };
+
+ /* Extra bits for distance codes */
+ private static final int CPDEXT[] = {
+ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
+ 12, 12, 13, 13
+ };
+
+ /* This are the state in which the inflater can be. */
+ private static final int DECODE_HEADER = 0;
+ private static final int DECODE_DICT = 1;
+ private static final int DECODE_BLOCKS = 2;
+ private static final int DECODE_STORED_LEN1 = 3;
+ private static final int DECODE_STORED_LEN2 = 4;
+ private static final int DECODE_STORED = 5;
+ private static final int DECODE_DYN_HEADER = 6;
+ private static final int DECODE_HUFFMAN = 7;
+ private static final int DECODE_HUFFMAN_LENBITS = 8;
+ private static final int DECODE_HUFFMAN_DIST = 9;
+ private static final int DECODE_HUFFMAN_DISTBITS = 10;
+ private static final int DECODE_CHKSUM = 11;
+ private static final int FINISHED = 12;
+
+ /** This variable contains the current state. */
+ private int mode;
+
+ /**
+ * The adler checksum of the dictionary or of the decompressed
+ * stream, as it is written in the header resp. footer of the
+ * compressed stream. <br>
+ *
+ * Only valid if mode is DECODE_DICT or DECODE_CHKSUM.
+ */
+ private int readAdler;
+ /**
+ * The number of bits needed to complete the current state. This
+ * is valid, if mode is DECODE_DICT, DECODE_CHKSUM,
+ * DECODE_HUFFMAN_LENBITS or DECODE_HUFFMAN_DISTBITS.
+ */
+ private int neededBits;
+ private int repLength, repDist;
+ private int uncomprLen;
+ /**
+ * True, if the last block flag was set in the last block of the
+ * inflated stream. This means that the stream ends after the
+ * current block.
+ */
+ private boolean isLastBlock;
+
+ /**
+ * The total number of inflated bytes.
+ */
+ private long totalOut;
+ /**
+ * The total number of bytes set with setInput(). This is not the
+ * value returned by getTotalIn(), since this also includes the
+ * unprocessed input.
+ */
+ private long totalIn;
+ /**
+ * This variable stores the nowrap flag that was given to the constructor.
+ * True means, that the inflated stream doesn't contain a header nor the
+ * checksum in the footer.
+ */
+ private boolean nowrap;
+
+ private StreamManipulator input;
+ private OutputWindow outputWindow;
+ private InflaterDynHeader dynHeader;
+ private InflaterHuffmanTree litlenTree, distTree;
+ private Adler32 adler;
+
+ /**
+ * Creates a new inflater.
+ */
+ public Inflater ()
+ {
+ this (false);
+ }
+
+ /**
+ * Creates a new inflater.
+ * @param nowrap true if no header and checksum field appears in the
+ * stream. This is used for GZIPed input. For compatibility with
+ * Sun JDK you should provide one byte of input more than needed in
+ * this case.
+ */
+ public Inflater (boolean nowrap)
+ {
+ this.nowrap = nowrap;
+ this.adler = new Adler32();
+ input = new StreamManipulator();
+ outputWindow = new OutputWindow();
+ mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER;
+ }
+
+ /**
+ * Finalizes this object.
+ */
+ protected void finalize ()
+ {
+ /* Exists only for compatibility */
+ }
+
+ /**
+ * Frees all objects allocated by the inflater. There's no reason
+ * to call this, since you can just rely on garbage collection (even
+ * for the Sun implementation). Exists only for compatibility
+ * with Sun's JDK, where the compressor allocates native memory.
+ * If you call any method (even reset) afterwards the behaviour is
+ * <i>undefined</i>.
+ */
+ public void end ()
+ {
+ outputWindow = null;
+ input = null;
+ dynHeader = null;
+ litlenTree = null;
+ distTree = null;
+ adler = null;
+ }
+
+ /**
+ * Returns true, if the inflater has finished. This means, that no
+ * input is needed and no output can be produced.
+ */
+ public boolean finished()
+ {
+ return mode == FINISHED && outputWindow.getAvailable() == 0;
+ }
+
+ /**
+ * Gets the adler checksum. This is either the checksum of all
+ * uncompressed bytes returned by inflate(), or if needsDictionary()
+ * returns true (and thus no output was yet produced) this is the
+ * adler checksum of the expected dictionary.
+ * @returns the adler checksum.
+ */
+ public int getAdler()
+ {
+ return needsDictionary() ? readAdler : (int) adler.getValue();
+ }
+
+ /**
+ * Gets the number of unprocessed input. Useful, if the end of the
+ * stream is reached and you want to further process the bytes after
+ * the deflate stream.
+ * @return the number of bytes of the input which were not processed.
+ */
+ public int getRemaining()
+ {
+ return input.getAvailableBytes();
+ }
+
+ /**
+ * Gets the total number of processed compressed input bytes.
+ * @return the total number of bytes of processed input bytes.
+ */
+ public int getTotalIn()
+ {
+ return (int) (totalIn - getRemaining());
+ }
+
+ /**
+ * Gets the total number of processed compressed input bytes.
+ * @return the total number of bytes of processed input bytes.
+ * @since 1.5
+ */
+ public long getBytesRead()
+ {
+ return totalIn - getRemaining();
+ }
+
+ /**
+ * Gets the total number of output bytes returned by inflate().
+ * @return the total number of output bytes.
+ */
+ public int getTotalOut()
+ {
+ return (int) totalOut;
+ }
+
+ /**
+ * Gets the total number of output bytes returned by inflate().
+ * @return the total number of output bytes.
+ * @since 1.5
+ */
+ public long getBytesWritten()
+ {
+ return totalOut;
+ }
+
+ /**
+ * Inflates the compressed stream to the output buffer. If this
+ * returns 0, you should check, whether needsDictionary(),
+ * needsInput() or finished() returns true, to determine why no
+ * further output is produced.
+ * @param buf the output buffer.
+ * @return the number of bytes written to the buffer, 0 if no further
+ * output can be produced.
+ * @exception DataFormatException if deflated stream is invalid.
+ * @exception IllegalArgumentException if buf has length 0.
+ */
+ public int inflate (byte[] buf) throws DataFormatException
+ {
+ return inflate (buf, 0, buf.length);
+ }
+
+ /**
+ * Inflates the compressed stream to the output buffer. If this
+ * returns 0, you should check, whether needsDictionary(),
+ * needsInput() or finished() returns true, to determine why no
+ * further output is produced.
+ * @param buf the output buffer.
+ * @param off the offset into buffer where the output should start.
+ * @param len the maximum length of the output.
+ * @return the number of bytes written to the buffer, 0 if no further
+ * output can be produced.
+ * @exception DataFormatException if deflated stream is invalid.
+ * @exception IndexOutOfBoundsException if the off and/or len are wrong.
+ */
+ public int inflate (byte[] buf, int off, int len) throws DataFormatException
+ {
+ /* Check for correct buff, off, len triple */
+ if (0 > off || off > off + len || off + len > buf.length)
+ throw new ArrayIndexOutOfBoundsException();
+ int count = 0;
+ for (;;)
+ {
+ if (outputWindow.getAvailable() == 0)
+ {
+ if (!decode())
+ break;
+ }
+ else if (len > 0)
+ {
+ int more = outputWindow.copyOutput(buf, off, len);
+ adler.update(buf, off, more);
+ off += more;
+ count += more;
+ totalOut += more;
+ len -= more;
+ }
+ else
+ break;
+ }
+ return count;
+ }
+
+ /**
+ * Returns true, if a preset dictionary is needed to inflate the input.
+ */
+ public boolean needsDictionary ()
+ {
+ return mode == DECODE_DICT && neededBits == 0;
+ }
+
+ /**
+ * Returns true, if the input buffer is empty.
+ * You should then call setInput(). <br>
+ *
+ * <em>NOTE</em>: This method also returns true when the stream is finished.
+ */
+ public boolean needsInput ()
+ {
+ return input.needsInput ();
+ }
+
+ /**
+ * Resets the inflater so that a new stream can be decompressed. All
+ * pending input and output will be discarded.
+ */
+ public void reset ()
+ {
+ mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER;
+ totalIn = totalOut = 0;
+ input.reset();
+ outputWindow.reset();
+ dynHeader = null;
+ litlenTree = null;
+ distTree = null;
+ isLastBlock = false;
+ adler.reset();
+ }
+
+ /**
+ * Sets the preset dictionary. This should only be called, if
+ * needsDictionary() returns true and it should set the same
+ * dictionary, that was used for deflating. The getAdler()
+ * function returns the checksum of the dictionary needed.
+ * @param buffer the dictionary.
+ * @exception IllegalStateException if no dictionary is needed.
+ * @exception IllegalArgumentException if the dictionary checksum is
+ * wrong.
+ */
+ public void setDictionary (byte[] buffer)
+ {
+ setDictionary(buffer, 0, buffer.length);
+ }
+
+ /**
+ * Sets the preset dictionary. This should only be called, if
+ * needsDictionary() returns true and it should set the same
+ * dictionary, that was used for deflating. The getAdler()
+ * function returns the checksum of the dictionary needed.
+ * @param buffer the dictionary.
+ * @param off the offset into buffer where the dictionary starts.
+ * @param len the length of the dictionary.
+ * @exception IllegalStateException if no dictionary is needed.
+ * @exception IllegalArgumentException if the dictionary checksum is
+ * wrong.
+ * @exception IndexOutOfBoundsException if the off and/or len are wrong.
+ */
+ public void setDictionary (byte[] buffer, int off, int len)
+ {
+ if (!needsDictionary())
+ throw new IllegalStateException();
+
+ adler.update(buffer, off, len);
+ if ((int) adler.getValue() != readAdler)
+ throw new IllegalArgumentException("Wrong adler checksum");
+ adler.reset();
+ outputWindow.copyDict(buffer, off, len);
+ mode = DECODE_BLOCKS;
+ }
+
+ /**
+ * Sets the input. This should only be called, if needsInput()
+ * returns true.
+ * @param buf the input.
+ * @exception IllegalStateException if no input is needed.
+ */
+ public void setInput (byte[] buf)
+ {
+ setInput (buf, 0, buf.length);
+ }
+
+ /**
+ * Sets the input. This should only be called, if needsInput()
+ * returns true.
+ * @param buf the input.
+ * @param off the offset into buffer where the input starts.
+ * @param len the length of the input.
+ * @exception IllegalStateException if no input is needed.
+ * @exception IndexOutOfBoundsException if the off and/or len are wrong.
+ */
+ public void setInput (byte[] buf, int off, int len)
+ {
+ input.setInput (buf, off, len);
+ totalIn += len;
+ }
+
+ /**
+ * Decodes the deflate header.
+ * @return false if more input is needed.
+ * @exception DataFormatException if header is invalid.
+ */
+ private boolean decodeHeader () throws DataFormatException
+ {
+ int header = input.peekBits(16);
+ if (header < 0)
+ return false;
+ input.dropBits(16);
+
+ /* The header is written in "wrong" byte order */
+ header = ((header << 8) | (header >> 8)) & 0xffff;
+ if (header % 31 != 0)
+ throw new DataFormatException("Header checksum illegal");
+
+ if ((header & 0x0f00) != (Deflater.DEFLATED << 8))
+ throw new DataFormatException("Compression Method unknown");
+
+ /* Maximum size of the backwards window in bits.
+ * We currently ignore this, but we could use it to make the
+ * inflater window more space efficient. On the other hand the
+ * full window (15 bits) is needed most times, anyway.
+ int max_wbits = ((header & 0x7000) >> 12) + 8;
+ */
+
+ if ((header & 0x0020) == 0) // Dictionary flag?
+ {
+ mode = DECODE_BLOCKS;
+ }
+ else
+ {
+ mode = DECODE_DICT;
+ neededBits = 32;
+ }
+ return true;
+ }
+
+ /**
+ * Decodes the dictionary checksum after the deflate header.
+ * @return false if more input is needed.
+ */
+ private boolean decodeDict ()
+ {
+ while (neededBits > 0)
+ {
+ int dictByte = input.peekBits(8);
+ if (dictByte < 0)
+ return false;
+ input.dropBits(8);
+ readAdler = (readAdler << 8) | dictByte;
+ neededBits -= 8;
+ }
+ return false;
+ }
+
+ /**
+ * Decodes the huffman encoded symbols in the input stream.
+ * @return false if more input is needed, true if output window is
+ * full or the current block ends.
+ * @exception DataFormatException if deflated stream is invalid.
+ */
+ private boolean decodeHuffman () throws DataFormatException
+ {
+ int free = outputWindow.getFreeSpace();
+ while (free >= 258)
+ {
+ int symbol;
+ switch (mode)
+ {
+ case DECODE_HUFFMAN:
+ /* This is the inner loop so it is optimized a bit */
+ while (((symbol = litlenTree.getSymbol(input)) & ~0xff) == 0)
+ {
+ outputWindow.write(symbol);
+ if (--free < 258)
+ return true;
+ }
+ if (symbol < 257)
+ {
+ if (symbol < 0)
+ return false;
+ else
+ {
+ /* symbol == 256: end of block */
+ distTree = null;
+ litlenTree = null;
+ mode = DECODE_BLOCKS;
+ return true;
+ }
+ }
+
+ try
+ {
+ repLength = CPLENS[symbol - 257];
+ neededBits = CPLEXT[symbol - 257];
+ }
+ catch (ArrayIndexOutOfBoundsException ex)
+ {
+ throw new DataFormatException("Illegal rep length code");
+ }
+ /* fall through */
+ case DECODE_HUFFMAN_LENBITS:
+ if (neededBits > 0)
+ {
+ mode = DECODE_HUFFMAN_LENBITS;
+ int i = input.peekBits(neededBits);
+ if (i < 0)
+ return false;
+ input.dropBits(neededBits);
+ repLength += i;
+ }
+ mode = DECODE_HUFFMAN_DIST;
+ /* fall through */
+ case DECODE_HUFFMAN_DIST:
+ symbol = distTree.getSymbol(input);
+ if (symbol < 0)
+ return false;
+ try
+ {
+ repDist = CPDIST[symbol];
+ neededBits = CPDEXT[symbol];
+ }
+ catch (ArrayIndexOutOfBoundsException ex)
+ {
+ throw new DataFormatException("Illegal rep dist code");
+ }
+ /* fall through */
+ case DECODE_HUFFMAN_DISTBITS:
+ if (neededBits > 0)
+ {
+ mode = DECODE_HUFFMAN_DISTBITS;
+ int i = input.peekBits(neededBits);
+ if (i < 0)
+ return false;
+ input.dropBits(neededBits);
+ repDist += i;
+ }
+ outputWindow.repeat(repLength, repDist);
+ free -= repLength;
+ mode = DECODE_HUFFMAN;
+ break;
+ default:
+ throw new IllegalStateException();
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Decodes the adler checksum after the deflate stream.
+ * @return false if more input is needed.
+ * @exception DataFormatException if checksum doesn't match.
+ */
+ private boolean decodeChksum () throws DataFormatException
+ {
+ while (neededBits > 0)
+ {
+ int chkByte = input.peekBits(8);
+ if (chkByte < 0)
+ return false;
+ input.dropBits(8);
+ readAdler = (readAdler << 8) | chkByte;
+ neededBits -= 8;
+ }
+ if ((int) adler.getValue() != readAdler)
+ throw new DataFormatException("Adler chksum doesn't match: "
+ +Integer.toHexString((int)adler.getValue())
+ +" vs. "+Integer.toHexString(readAdler));
+ mode = FINISHED;
+ return false;
+ }
+
+ /**
+ * Decodes the deflated stream.
+ * @return false if more input is needed, or if finished.
+ * @exception DataFormatException if deflated stream is invalid.
+ */
+ private boolean decode () throws DataFormatException
+ {
+ switch (mode)
+ {
+ case DECODE_HEADER:
+ return decodeHeader();
+ case DECODE_DICT:
+ return decodeDict();
+ case DECODE_CHKSUM:
+ return decodeChksum();
+
+ case DECODE_BLOCKS:
+ if (isLastBlock)
+ {
+ if (nowrap)
+ {
+ mode = FINISHED;
+ return false;
+ }
+ else
+ {
+ input.skipToByteBoundary();
+ neededBits = 32;
+ mode = DECODE_CHKSUM;
+ return true;
+ }
+ }
+
+ int type = input.peekBits(3);
+ if (type < 0)
+ return false;
+ input.dropBits(3);
+
+ if ((type & 1) != 0)
+ isLastBlock = true;
+ switch (type >> 1)
+ {
+ case DeflaterConstants.STORED_BLOCK:
+ input.skipToByteBoundary();
+ mode = DECODE_STORED_LEN1;
+ break;
+ case DeflaterConstants.STATIC_TREES:
+ litlenTree = InflaterHuffmanTree.defLitLenTree;
+ distTree = InflaterHuffmanTree.defDistTree;
+ mode = DECODE_HUFFMAN;
+ break;
+ case DeflaterConstants.DYN_TREES:
+ dynHeader = new InflaterDynHeader();
+ mode = DECODE_DYN_HEADER;
+ break;
+ default:
+ throw new DataFormatException("Unknown block type "+type);
+ }
+ return true;
+
+ case DECODE_STORED_LEN1:
+ {
+ if ((uncomprLen = input.peekBits(16)) < 0)
+ return false;
+ input.dropBits(16);
+ mode = DECODE_STORED_LEN2;
+ }
+ /* fall through */
+ case DECODE_STORED_LEN2:
+ {
+ int nlen = input.peekBits(16);
+ if (nlen < 0)
+ return false;
+ input.dropBits(16);
+ if (nlen != (uncomprLen ^ 0xffff))
+ throw new DataFormatException("broken uncompressed block");
+ mode = DECODE_STORED;
+ }
+ /* fall through */
+ case DECODE_STORED:
+ {
+ int more = outputWindow.copyStored(input, uncomprLen);
+ uncomprLen -= more;
+ if (uncomprLen == 0)
+ {
+ mode = DECODE_BLOCKS;
+ return true;
+ }
+ return !input.needsInput();
+ }
+
+ case DECODE_DYN_HEADER:
+ if (!dynHeader.decode(input))
+ return false;
+ litlenTree = dynHeader.buildLitLenTree();
+ distTree = dynHeader.buildDistTree();
+ mode = DECODE_HUFFMAN;
+ /* fall through */
+ case DECODE_HUFFMAN:
+ case DECODE_HUFFMAN_LENBITS:
+ case DECODE_HUFFMAN_DIST:
+ case DECODE_HUFFMAN_DISTBITS:
+ return decodeHuffman();
+ case FINISHED:
+ return false;
+ default:
+ throw new IllegalStateException();
+ }
+ }
+}
diff --git a/libjava/classpath/java/util/zip/InflaterDynHeader.java b/libjava/classpath/java/util/zip/InflaterDynHeader.java
new file mode 100644
index 000000000..64e08d6bb
--- /dev/null
+++ b/libjava/classpath/java/util/zip/InflaterDynHeader.java
@@ -0,0 +1,203 @@
+/* java.util.zip.InflaterDynHeader
+ Copyright (C) 2001 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+class InflaterDynHeader
+{
+ private static final int LNUM = 0;
+ private static final int DNUM = 1;
+ private static final int BLNUM = 2;
+ private static final int BLLENS = 3;
+ private static final int LENS = 4;
+ private static final int REPS = 5;
+
+ private static final int repMin[] = { 3, 3, 11 };
+ private static final int repBits[] = { 2, 3, 7 };
+
+
+ private byte[] blLens;
+ private byte[] litdistLens;
+
+ private InflaterHuffmanTree blTree;
+
+ private int mode;
+ private int lnum, dnum, blnum, num;
+ private int repSymbol;
+ private byte lastLen;
+ private int ptr;
+
+ private static final int[] BL_ORDER =
+ { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
+
+ public InflaterDynHeader()
+ {
+ }
+
+ public boolean decode(StreamManipulator input) throws DataFormatException
+ {
+ decode_loop:
+ for (;;)
+ {
+ switch (mode)
+ {
+ case LNUM:
+ lnum = input.peekBits(5);
+ if (lnum < 0)
+ return false;
+ lnum += 257;
+ input.dropBits(5);
+// System.err.println("LNUM: "+lnum);
+ mode = DNUM;
+ /* fall through */
+ case DNUM:
+ dnum = input.peekBits(5);
+ if (dnum < 0)
+ return false;
+ dnum++;
+ input.dropBits(5);
+// System.err.println("DNUM: "+dnum);
+ num = lnum+dnum;
+ litdistLens = new byte[num];
+ mode = BLNUM;
+ /* fall through */
+ case BLNUM:
+ blnum = input.peekBits(4);
+ if (blnum < 0)
+ return false;
+ blnum += 4;
+ input.dropBits(4);
+ blLens = new byte[19];
+ ptr = 0;
+// System.err.println("BLNUM: "+blnum);
+ mode = BLLENS;
+ /* fall through */
+ case BLLENS:
+ while (ptr < blnum)
+ {
+ int len = input.peekBits(3);
+ if (len < 0)
+ return false;
+ input.dropBits(3);
+// System.err.println("blLens["+BL_ORDER[ptr]+"]: "+len);
+ blLens[BL_ORDER[ptr]] = (byte) len;
+ ptr++;
+ }
+ blTree = new InflaterHuffmanTree(blLens);
+ blLens = null;
+ ptr = 0;
+ mode = LENS;
+ /* fall through */
+ case LENS:
+ {
+ int symbol;
+ while (((symbol = blTree.getSymbol(input)) & ~15) == 0)
+ {
+ /* Normal case: symbol in [0..15] */
+
+// System.err.println("litdistLens["+ptr+"]: "+symbol);
+ litdistLens[ptr++] = lastLen = (byte) symbol;
+
+ if (ptr == num)
+ {
+ /* Finished */
+ return true;
+ }
+ }
+
+ /* need more input ? */
+ if (symbol < 0)
+ return false;
+
+ /* otherwise repeat code */
+ if (symbol >= 17)
+ {
+ /* repeat zero */
+// System.err.println("repeating zero");
+ lastLen = 0;
+ }
+ else
+ {
+ if (ptr == 0)
+ throw new DataFormatException();
+ }
+ repSymbol = symbol-16;
+ mode = REPS;
+ }
+ /* fall through */
+
+ case REPS:
+ {
+ int bits = repBits[repSymbol];
+ int count = input.peekBits(bits);
+ if (count < 0)
+ return false;
+ input.dropBits(bits);
+ count += repMin[repSymbol];
+// System.err.println("litdistLens repeated: "+count);
+
+ if (ptr + count > num)
+ throw new DataFormatException();
+ while (count-- > 0)
+ litdistLens[ptr++] = lastLen;
+
+ if (ptr == num)
+ {
+ /* Finished */
+ return true;
+ }
+ }
+ mode = LENS;
+ continue decode_loop;
+ }
+ }
+ }
+
+ public InflaterHuffmanTree buildLitLenTree() throws DataFormatException
+ {
+ byte[] litlenLens = new byte[lnum];
+ System.arraycopy(litdistLens, 0, litlenLens, 0, lnum);
+ return new InflaterHuffmanTree(litlenLens);
+ }
+
+ public InflaterHuffmanTree buildDistTree() throws DataFormatException
+ {
+ byte[] distLens = new byte[dnum];
+ System.arraycopy(litdistLens, lnum, distLens, 0, dnum);
+ return new InflaterHuffmanTree(distLens);
+ }
+}
diff --git a/libjava/classpath/java/util/zip/InflaterHuffmanTree.java b/libjava/classpath/java/util/zip/InflaterHuffmanTree.java
new file mode 100644
index 000000000..c12c732e0
--- /dev/null
+++ b/libjava/classpath/java/util/zip/InflaterHuffmanTree.java
@@ -0,0 +1,217 @@
+/* InflaterHuffmanTree.java --
+ Copyright (C) 2001, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+class InflaterHuffmanTree
+{
+ private static final int MAX_BITLEN = 15;
+
+ private short[] tree;
+
+ static InflaterHuffmanTree defLitLenTree, defDistTree;
+
+ static
+ {
+ try
+ {
+ byte[] codeLengths = new byte[288];
+ int i = 0;
+ while (i < 144)
+ codeLengths[i++] = 8;
+ while (i < 256)
+ codeLengths[i++] = 9;
+ while (i < 280)
+ codeLengths[i++] = 7;
+ while (i < 288)
+ codeLengths[i++] = 8;
+ defLitLenTree = new InflaterHuffmanTree(codeLengths);
+
+ codeLengths = new byte[32];
+ i = 0;
+ while (i < 32)
+ codeLengths[i++] = 5;
+ defDistTree = new InflaterHuffmanTree(codeLengths);
+ }
+ catch (DataFormatException ex)
+ {
+ throw new InternalError
+ ("InflaterHuffmanTree: static tree length illegal");
+ }
+ }
+
+ /**
+ * Constructs a Huffman tree from the array of code lengths.
+ *
+ * @param codeLengths the array of code lengths
+ */
+ InflaterHuffmanTree(byte[] codeLengths) throws DataFormatException
+ {
+ buildTree(codeLengths);
+ }
+
+ private void buildTree(byte[] codeLengths) throws DataFormatException
+ {
+ int[] blCount = new int[MAX_BITLEN+1];
+ int[] nextCode = new int[MAX_BITLEN+1];
+ for (int i = 0; i < codeLengths.length; i++)
+ {
+ int bits = codeLengths[i];
+ if (bits > 0)
+ blCount[bits]++;
+ }
+
+ int code = 0;
+ int treeSize = 512;
+ for (int bits = 1; bits <= MAX_BITLEN; bits++)
+ {
+ nextCode[bits] = code;
+ code += blCount[bits] << (16 - bits);
+ if (bits >= 10)
+ {
+ /* We need an extra table for bit lengths >= 10. */
+ int start = nextCode[bits] & 0x1ff80;
+ int end = code & 0x1ff80;
+ treeSize += (end - start) >> (16 - bits);
+ }
+ }
+ if (code != 65536)
+ throw new DataFormatException("Code lengths don't add up properly.");
+
+ /* Now create and fill the extra tables from longest to shortest
+ * bit len. This way the sub trees will be aligned.
+ */
+ tree = new short[treeSize];
+ int treePtr = 512;
+ for (int bits = MAX_BITLEN; bits >= 10; bits--)
+ {
+ int end = code & 0x1ff80;
+ code -= blCount[bits] << (16 - bits);
+ int start = code & 0x1ff80;
+ for (int i = start; i < end; i += 1 << 7)
+ {
+ tree[DeflaterHuffman.bitReverse(i)]
+ = (short) ((-treePtr << 4) | bits);
+ treePtr += 1 << (bits-9);
+ }
+ }
+
+ for (int i = 0; i < codeLengths.length; i++)
+ {
+ int bits = codeLengths[i];
+ if (bits == 0)
+ continue;
+ code = nextCode[bits];
+ int revcode = DeflaterHuffman.bitReverse(code);
+ if (bits <= 9)
+ {
+ do
+ {
+ tree[revcode] = (short) ((i << 4) | bits);
+ revcode += 1 << bits;
+ }
+ while (revcode < 512);
+ }
+ else
+ {
+ int subTree = tree[revcode & 511];
+ int treeLen = 1 << (subTree & 15);
+ subTree = -(subTree >> 4);
+ do
+ {
+ tree[subTree | (revcode >> 9)] = (short) ((i << 4) | bits);
+ revcode += 1 << bits;
+ }
+ while (revcode < treeLen);
+ }
+ nextCode[bits] = code + (1 << (16 - bits));
+ }
+ }
+
+ /**
+ * Reads the next symbol from input. The symbol is encoded using the
+ * huffman tree.
+ * @param input the input source.
+ * @return the next symbol, or -1 if not enough input is available.
+ */
+ int getSymbol(StreamManipulator input) throws DataFormatException
+ {
+ int lookahead, symbol;
+ if ((lookahead = input.peekBits(9)) >= 0)
+ {
+ if ((symbol = tree[lookahead]) >= 0)
+ {
+ input.dropBits(symbol & 15);
+ return symbol >> 4;
+ }
+ int subtree = -(symbol >> 4);
+ int bitlen = symbol & 15;
+ if ((lookahead = input.peekBits(bitlen)) >= 0)
+ {
+ symbol = tree[subtree | (lookahead >> 9)];
+ input.dropBits(symbol & 15);
+ return symbol >> 4;
+ }
+ else
+ {
+ int bits = input.getAvailableBits();
+ lookahead = input.peekBits(bits);
+ symbol = tree[subtree | (lookahead >> 9)];
+ if ((symbol & 15) <= bits)
+ {
+ input.dropBits(symbol & 15);
+ return symbol >> 4;
+ }
+ else
+ return -1;
+ }
+ }
+ else
+ {
+ int bits = input.getAvailableBits();
+ lookahead = input.peekBits(bits);
+ symbol = tree[lookahead];
+ if (symbol >= 0 && (symbol & 15) <= bits)
+ {
+ input.dropBits(symbol & 15);
+ return symbol >> 4;
+ }
+ else
+ return -1;
+ }
+ }
+}
diff --git a/libjava/classpath/java/util/zip/InflaterInputStream.java b/libjava/classpath/java/util/zip/InflaterInputStream.java
new file mode 100644
index 000000000..1c5e9f2dd
--- /dev/null
+++ b/libjava/classpath/java/util/zip/InflaterInputStream.java
@@ -0,0 +1,265 @@
+/* InflaterInputStream.java - Input stream filter for decompressing
+ Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2006
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * This filter stream is used to decompress data compressed in the "deflate"
+ * format. The "deflate" format is described in RFC 1951.
+ *
+ * This stream may form the basis for other decompression filters, such
+ * as the <code>GZIPInputStream</code>.
+ *
+ * @author John Leuner
+ * @author Tom Tromey
+ * @since 1.1
+ */
+public class InflaterInputStream extends FilterInputStream
+{
+ /**
+ * Decompressor for this filter
+ */
+ protected Inflater inf;
+
+ /**
+ * Byte array used as a buffer
+ */
+ protected byte[] buf;
+
+ /**
+ * Size of buffer
+ */
+ protected int len;
+
+ // We just use this if we are decoding one byte at a time with the
+ // read() call.
+ private byte[] onebytebuffer = new byte[1];
+
+ /**
+ * Create an InflaterInputStream with the default decompresseor
+ * and a default buffer size.
+ *
+ * @param in the InputStream to read bytes from
+ */
+ public InflaterInputStream(InputStream in)
+ {
+ this(in, new Inflater(), 4096);
+ }
+
+ /**
+ * Create an InflaterInputStream with the specified decompresseor
+ * and a default buffer size.
+ *
+ * @param in the InputStream to read bytes from
+ * @param inf the decompressor used to decompress data read from in
+ */
+ public InflaterInputStream(InputStream in, Inflater inf)
+ {
+ this(in, inf, 4096);
+ }
+
+ /**
+ * Create an InflaterInputStream with the specified decompresseor
+ * and a specified buffer size.
+ *
+ * @param in the InputStream to read bytes from
+ * @param inf the decompressor used to decompress data read from in
+ * @param size size of the buffer to use
+ */
+ public InflaterInputStream(InputStream in, Inflater inf, int size)
+ {
+ super(in);
+
+ if (in == null)
+ throw new NullPointerException("in may not be null");
+ if (inf == null)
+ throw new NullPointerException("inf may not be null");
+ if (size < 0)
+ throw new IllegalArgumentException("size may not be negative");
+
+ this.inf = inf;
+ this.buf = new byte [size];
+ }
+
+ /**
+ * Returns 0 once the end of the stream (EOF) has been reached.
+ * Otherwise returns 1.
+ */
+ public int available() throws IOException
+ {
+ // According to the JDK 1.2 docs, this should only ever return 0
+ // or 1 and should not be relied upon by Java programs.
+ if (inf == null)
+ throw new IOException("stream closed");
+ return inf.finished() ? 0 : 1;
+ }
+
+ /**
+ * Closes the input stream
+ */
+ public synchronized void close() throws IOException
+ {
+ if (in != null)
+ in.close();
+ in = null;
+ }
+
+ /**
+ * Fills the buffer with more data to decompress.
+ */
+ protected void fill() throws IOException
+ {
+ if (in == null)
+ throw new ZipException ("InflaterInputStream is closed");
+
+ len = in.read(buf, 0, buf.length);
+
+ if (len < 0)
+ throw new ZipException("Deflated stream ends early.");
+
+ inf.setInput(buf, 0, len);
+ }
+
+ /**
+ * Reads one byte of decompressed data.
+ *
+ * The byte is in the lower 8 bits of the int.
+ */
+ public int read() throws IOException
+ {
+ int nread = read(onebytebuffer, 0, 1);
+ if (nread > 0)
+ return onebytebuffer[0] & 0xff;
+ return -1;
+ }
+
+ /**
+ * Decompresses data into the byte array
+ *
+ * @param b the array to read and decompress data into
+ * @param off the offset indicating where the data should be placed
+ * @param len the number of bytes to decompress
+ */
+ public int read(byte[] b, int off, int len) throws IOException
+ {
+ if (inf == null)
+ throw new IOException("stream closed");
+ if (len == 0)
+ return 0;
+ if (inf.finished())
+ return -1;
+
+ int count = 0;
+ while (count == 0)
+ {
+ if (inf.needsInput())
+ fill();
+
+ try
+ {
+ count = inf.inflate(b, off, len);
+ if (count == 0)
+ {
+ if (this.len == -1)
+ {
+ // Couldn't get any more data to feed to the Inflater
+ return -1;
+ }
+ if (inf.needsDictionary())
+ throw new ZipException("Inflater needs Dictionary");
+ }
+ }
+ catch (DataFormatException dfe)
+ {
+ throw new ZipException(dfe.getMessage());
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Skip specified number of bytes of uncompressed data
+ *
+ * @param n number of bytes to skip
+ */
+ public long skip(long n) throws IOException
+ {
+ if (inf == null)
+ throw new IOException("stream closed");
+ if (n < 0)
+ throw new IllegalArgumentException();
+
+ if (n == 0)
+ return 0;
+
+ int buflen = (int) Math.min(n, 2048);
+ byte[] tmpbuf = new byte[buflen];
+
+ long skipped = 0L;
+ while (n > 0L)
+ {
+ int numread = read(tmpbuf, 0, buflen);
+ if (numread <= 0)
+ break;
+ n -= numread;
+ skipped += numread;
+ buflen = (int) Math.min(n, 2048);
+ }
+
+ return skipped;
+ }
+
+ public boolean markSupported()
+ {
+ return false;
+ }
+
+ public void mark(int readLimit)
+ {
+ }
+
+ public void reset() throws IOException
+ {
+ throw new IOException("reset not supported");
+ }
+}
diff --git a/libjava/classpath/java/util/zip/OutputWindow.java b/libjava/classpath/java/util/zip/OutputWindow.java
new file mode 100644
index 000000000..59dadb5f3
--- /dev/null
+++ b/libjava/classpath/java/util/zip/OutputWindow.java
@@ -0,0 +1,175 @@
+/* OutputWindow.java --
+ Copyright (C) 2001, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+/**
+ * Contains the output from the Inflation process.
+ *
+ * We need to have a window so that we can refer backwards into the output stream
+ * to repeat stuff.
+ *
+ * @author John Leuner
+ * @since 1.1
+ */
+class OutputWindow
+{
+ private static final int WINDOW_SIZE = 1 << 15;
+ private static final int WINDOW_MASK = WINDOW_SIZE - 1;
+
+ private byte[] window = new byte[WINDOW_SIZE]; //The window is 2^15 bytes
+ private int window_end = 0;
+ private int window_filled = 0;
+
+ public void write(int abyte)
+ {
+ if (window_filled++ == WINDOW_SIZE)
+ throw new IllegalStateException("Window full");
+ window[window_end++] = (byte) abyte;
+ window_end &= WINDOW_MASK;
+ }
+
+ private void slowRepeat(int rep_start, int len, int dist)
+ {
+ while (len-- > 0)
+ {
+ window[window_end++] = window[rep_start++];
+ window_end &= WINDOW_MASK;
+ rep_start &= WINDOW_MASK;
+ }
+ }
+
+ public void repeat(int len, int dist)
+ {
+ if ((window_filled += len) > WINDOW_SIZE)
+ throw new IllegalStateException("Window full");
+
+ int rep_start = (window_end - dist) & WINDOW_MASK;
+ int border = WINDOW_SIZE - len;
+ if (rep_start <= border && window_end < border)
+ {
+ if (len <= dist)
+ {
+ System.arraycopy(window, rep_start, window, window_end, len);
+ window_end += len;
+ }
+ else
+ {
+ /* We have to copy manually, since the repeat pattern overlaps.
+ */
+ while (len-- > 0)
+ window[window_end++] = window[rep_start++];
+ }
+ }
+ else
+ slowRepeat(rep_start, len, dist);
+ }
+
+ public int copyStored(StreamManipulator input, int len)
+ {
+ len = Math.min(Math.min(len, WINDOW_SIZE - window_filled),
+ input.getAvailableBytes());
+ int copied;
+
+ int tailLen = WINDOW_SIZE - window_end;
+ if (len > tailLen)
+ {
+ copied = input.copyBytes(window, window_end, tailLen);
+ if (copied == tailLen)
+ copied += input.copyBytes(window, 0, len - tailLen);
+ }
+ else
+ copied = input.copyBytes(window, window_end, len);
+
+ window_end = (window_end + copied) & WINDOW_MASK;
+ window_filled += copied;
+ return copied;
+ }
+
+ public void copyDict(byte[] dict, int offset, int len)
+ {
+ if (window_filled > 0)
+ throw new IllegalStateException();
+
+ if (len > WINDOW_SIZE)
+ {
+ offset += len - WINDOW_SIZE;
+ len = WINDOW_SIZE;
+ }
+ System.arraycopy(dict, offset, window, 0, len);
+ window_end = len & WINDOW_MASK;
+ }
+
+ public int getFreeSpace()
+ {
+ return WINDOW_SIZE - window_filled;
+ }
+
+ public int getAvailable()
+ {
+ return window_filled;
+ }
+
+ public int copyOutput(byte[] output, int offset, int len)
+ {
+ int copy_end = window_end;
+ if (len > window_filled)
+ len = window_filled;
+ else
+ copy_end = (window_end - window_filled + len) & WINDOW_MASK;
+
+ int copied = len;
+ int tailLen = len - copy_end;
+
+ if (tailLen > 0)
+ {
+ System.arraycopy(window, WINDOW_SIZE - tailLen,
+ output, offset, tailLen);
+ offset += tailLen;
+ len = copy_end;
+ }
+ System.arraycopy(window, copy_end - len, output, offset, len);
+ window_filled -= copied;
+ if (window_filled < 0)
+ throw new IllegalStateException();
+ return copied;
+ }
+
+ public void reset() {
+ window_filled = window_end = 0;
+ }
+}
diff --git a/libjava/classpath/java/util/zip/PendingBuffer.java b/libjava/classpath/java/util/zip/PendingBuffer.java
new file mode 100644
index 000000000..50f561f49
--- /dev/null
+++ b/libjava/classpath/java/util/zip/PendingBuffer.java
@@ -0,0 +1,199 @@
+/* java.util.zip.PendingBuffer
+ Copyright (C) 2001 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+/**
+ * This class is general purpose class for writing data to a buffer.
+ *
+ * It allows you to write bits as well as bytes
+ *
+ * Based on DeflaterPending.java
+ *
+ * @author Jochen Hoenicke
+ * @date Jan 5, 2000
+ */
+
+class PendingBuffer
+{
+ protected byte[] buf;
+ int start;
+ int end;
+
+ int bits;
+ int bitCount;
+
+ public PendingBuffer()
+ {
+ this( 4096 );
+ }
+
+ public PendingBuffer(int bufsize)
+ {
+ buf = new byte[bufsize];
+ }
+
+ public final void reset() {
+ start = end = bitCount = 0;
+ }
+
+ public final void writeByte(int b)
+ {
+ if (DeflaterConstants.DEBUGGING && start != 0)
+ throw new IllegalStateException();
+ buf[end++] = (byte) b;
+ }
+
+ public final void writeShort(int s)
+ {
+ if (DeflaterConstants.DEBUGGING && start != 0)
+ throw new IllegalStateException();
+ buf[end++] = (byte) s;
+ buf[end++] = (byte) (s >> 8);
+ }
+
+ public final void writeInt(int s)
+ {
+ if (DeflaterConstants.DEBUGGING && start != 0)
+ throw new IllegalStateException();
+ buf[end++] = (byte) s;
+ buf[end++] = (byte) (s >> 8);
+ buf[end++] = (byte) (s >> 16);
+ buf[end++] = (byte) (s >> 24);
+ }
+
+ public final void writeBlock(byte[] block, int offset, int len)
+ {
+ if (DeflaterConstants.DEBUGGING && start != 0)
+ throw new IllegalStateException();
+ System.arraycopy(block, offset, buf, end, len);
+ end += len;
+ }
+
+ public final int getBitCount() {
+ return bitCount;
+ }
+
+ public final void alignToByte() {
+ if (DeflaterConstants.DEBUGGING && start != 0)
+ throw new IllegalStateException();
+ if (bitCount > 0)
+ {
+ buf[end++] = (byte) bits;
+ if (bitCount > 8)
+ buf[end++] = (byte) (bits >>> 8);
+ }
+ bits = 0;
+ bitCount = 0;
+ }
+
+ public final void writeBits(int b, int count)
+ {
+ if (DeflaterConstants.DEBUGGING && start != 0)
+ throw new IllegalStateException();
+ if (DeflaterConstants.DEBUGGING)
+ System.err.println("writeBits("+Integer.toHexString(b)+","+count+")");
+ bits |= b << bitCount;
+ bitCount += count;
+ if (bitCount >= 16) {
+ buf[end++] = (byte) bits;
+ buf[end++] = (byte) (bits >>> 8);
+ bits >>>= 16;
+ bitCount -= 16;
+ }
+ }
+
+ public final void writeShortMSB(int s) {
+ if (DeflaterConstants.DEBUGGING && start != 0)
+ throw new IllegalStateException();
+ buf[end++] = (byte) (s >> 8);
+ buf[end++] = (byte) s;
+ }
+
+ public final boolean isFlushed() {
+ return end == 0;
+ }
+
+ /**
+ * Flushes the pending buffer into the given output array. If the
+ * output array is to small, only a partial flush is done.
+ *
+ * @param output the output array;
+ * @param offset the offset into output array;
+ * @param length the maximum number of bytes to store;
+ * @exception IndexOutOfBoundsException if offset or length are
+ * invalid.
+ */
+ public final int flush(byte[] output, int offset, int length) {
+ if (bitCount >= 8)
+ {
+ buf[end++] = (byte) bits;
+ bits >>>= 8;
+ bitCount -= 8;
+ }
+ if (length > end - start)
+ {
+ length = end - start;
+ System.arraycopy(buf, start, output, offset, length);
+ start = 0;
+ end = 0;
+ }
+ else
+ {
+ System.arraycopy(buf, start, output, offset, length);
+ start += length;
+ }
+ return length;
+ }
+
+ /**
+ * Flushes the pending buffer and returns that data in a new array
+ *
+ * @return the output stream
+ */
+
+ public final byte[] toByteArray()
+ {
+ byte[] ret = new byte[ end - start ];
+ System.arraycopy(buf, start, ret, 0, ret.length);
+ start = 0;
+ end = 0;
+ return ret;
+ }
+
+
+}
diff --git a/libjava/classpath/java/util/zip/StreamManipulator.java b/libjava/classpath/java/util/zip/StreamManipulator.java
new file mode 100644
index 000000000..105d807e4
--- /dev/null
+++ b/libjava/classpath/java/util/zip/StreamManipulator.java
@@ -0,0 +1,215 @@
+/* java.util.zip.StreamManipulator
+ Copyright (C) 2001 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+/**
+ * This class allows us to retrieve a specified amount of bits from
+ * the input buffer, as well as copy big byte blocks.
+ *
+ * It uses an int buffer to store up to 31 bits for direct
+ * manipulation. This guarantees that we can get at least 16 bits,
+ * but we only need at most 15, so this is all safe.
+ *
+ * There are some optimizations in this class, for example, you must
+ * never peek more then 8 bits more than needed, and you must first
+ * peek bits before you may drop them. This is not a general purpose
+ * class but optimized for the behaviour of the Inflater.
+ *
+ * @author John Leuner, Jochen Hoenicke
+ */
+
+class StreamManipulator
+{
+ private byte[] window;
+ private int window_start = 0;
+ private int window_end = 0;
+
+ private int buffer = 0;
+ private int bits_in_buffer = 0;
+
+ /**
+ * Get the next n bits but don't increase input pointer. n must be
+ * less or equal 16 and if you if this call succeeds, you must drop
+ * at least n-8 bits in the next call.
+ *
+ * @return the value of the bits, or -1 if not enough bits available. */
+ public final int peekBits(int n)
+ {
+ if (bits_in_buffer < n)
+ {
+ if (window_start == window_end)
+ return -1;
+ buffer |= (window[window_start++] & 0xff
+ | (window[window_start++] & 0xff) << 8) << bits_in_buffer;
+ bits_in_buffer += 16;
+ }
+ return buffer & ((1 << n) - 1);
+ }
+
+ /* Drops the next n bits from the input. You should have called peekBits
+ * with a bigger or equal n before, to make sure that enough bits are in
+ * the bit buffer.
+ */
+ public final void dropBits(int n)
+ {
+ buffer >>>= n;
+ bits_in_buffer -= n;
+ }
+
+ /**
+ * Gets the next n bits and increases input pointer. This is equivalent
+ * to peekBits followed by dropBits, except for correct error handling.
+ * @return the value of the bits, or -1 if not enough bits available.
+ */
+ public final int getBits(int n)
+ {
+ int bits = peekBits(n);
+ if (bits >= 0)
+ dropBits(n);
+ return bits;
+ }
+ /**
+ * Gets the number of bits available in the bit buffer. This must be
+ * only called when a previous peekBits() returned -1.
+ * @return the number of bits available.
+ */
+ public final int getAvailableBits()
+ {
+ return bits_in_buffer;
+ }
+
+ /**
+ * Gets the number of bytes available.
+ * @return the number of bytes available.
+ */
+ public final int getAvailableBytes()
+ {
+ return window_end - window_start + (bits_in_buffer >> 3);
+ }
+
+ /**
+ * Skips to the next byte boundary.
+ */
+ public void skipToByteBoundary()
+ {
+ buffer >>= (bits_in_buffer & 7);
+ bits_in_buffer &= ~7;
+ }
+
+ public final boolean needsInput() {
+ return window_start == window_end;
+ }
+
+
+ /* Copies length bytes from input buffer to output buffer starting
+ * at output[offset]. You have to make sure, that the buffer is
+ * byte aligned. If not enough bytes are available, copies fewer
+ * bytes.
+ * @param length the length to copy, 0 is allowed.
+ * @return the number of bytes copied, 0 if no byte is available.
+ */
+ public int copyBytes(byte[] output, int offset, int length)
+ {
+ if (length < 0)
+ throw new IllegalArgumentException("length negative");
+ if ((bits_in_buffer & 7) != 0)
+ /* bits_in_buffer may only be 0 or 8 */
+ throw new IllegalStateException("Bit buffer is not aligned!");
+
+ int count = 0;
+ while (bits_in_buffer > 0 && length > 0)
+ {
+ output[offset++] = (byte) buffer;
+ buffer >>>= 8;
+ bits_in_buffer -= 8;
+ length--;
+ count++;
+ }
+ if (length == 0)
+ return count;
+
+ int avail = window_end - window_start;
+ if (length > avail)
+ length = avail;
+ System.arraycopy(window, window_start, output, offset, length);
+ window_start += length;
+
+ if (((window_start - window_end) & 1) != 0)
+ {
+ /* We always want an even number of bytes in input, see peekBits */
+ buffer = (window[window_start++] & 0xff);
+ bits_in_buffer = 8;
+ }
+ return count + length;
+ }
+
+ public StreamManipulator()
+ {
+ }
+
+ public void reset()
+ {
+ window_start = window_end = buffer = bits_in_buffer = 0;
+ }
+
+ public void setInput(byte[] buf, int off, int len)
+ {
+ if (window_start < window_end)
+ throw new IllegalStateException
+ ("Old input was not completely processed");
+
+ int end = off + len;
+
+ /* We want to throw an ArrayIndexOutOfBoundsException early. The
+ * check is very tricky: it also handles integer wrap around.
+ */
+ if (0 > off || off > end || end > buf.length)
+ throw new ArrayIndexOutOfBoundsException();
+
+ if ((len & 1) != 0)
+ {
+ /* We always want an even number of bytes in input, see peekBits */
+ buffer |= (buf[off++] & 0xff) << bits_in_buffer;
+ bits_in_buffer += 8;
+ }
+
+ window = buf;
+ window_start = off;
+ window_end = end;
+ }
+}
diff --git a/libjava/classpath/java/util/zip/ZipConstants.java b/libjava/classpath/java/util/zip/ZipConstants.java
new file mode 100644
index 000000000..69f589a31
--- /dev/null
+++ b/libjava/classpath/java/util/zip/ZipConstants.java
@@ -0,0 +1,93 @@
+/* java.util.zip.ZipConstants
+ Copyright (C) 2001, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+interface ZipConstants
+{
+ /* The local file header */
+ int LOCHDR = 30;
+ long LOCSIG = 'P'|('K'<<8)|(3<<16)|(4<<24);
+
+ int LOCVER = 4;
+ int LOCFLG = 6;
+ int LOCHOW = 8;
+ int LOCTIM = 10;
+ int LOCCRC = 14;
+ int LOCSIZ = 18;
+ int LOCLEN = 22;
+ int LOCNAM = 26;
+ int LOCEXT = 28;
+
+ /* The Data descriptor */
+ long EXTSIG = 'P'|('K'<<8)|(7<<16)|(8<<24);
+ int EXTHDR = 16;
+
+ int EXTCRC = 4;
+ int EXTSIZ = 8;
+ int EXTLEN = 12;
+
+ /* The central directory file header */
+ long CENSIG = 'P'|('K'<<8)|(1<<16)|(2<<24);
+ int CENHDR = 46;
+
+ int CENVEM = 4;
+ int CENVER = 6;
+ int CENFLG = 8;
+ int CENHOW = 10;
+ int CENTIM = 12;
+ int CENCRC = 16;
+ int CENSIZ = 20;
+ int CENLEN = 24;
+ int CENNAM = 28;
+ int CENEXT = 30;
+ int CENCOM = 32;
+ int CENDSK = 34;
+ int CENATT = 36;
+ int CENATX = 38;
+ int CENOFF = 42;
+
+ /* The entries in the end of central directory */
+ long ENDSIG = 'P'|('K'<<8)|(5<<16)|(6<<24);
+ int ENDHDR = 22;
+
+ int ENDSUB = 8;
+ int ENDTOT = 10;
+ int ENDSIZ = 12;
+ int ENDOFF = 16;
+ int ENDCOM = 20;
+}
diff --git a/libjava/classpath/java/util/zip/ZipEntry.java b/libjava/classpath/java/util/zip/ZipEntry.java
new file mode 100644
index 000000000..73afc893b
--- /dev/null
+++ b/libjava/classpath/java/util/zip/ZipEntry.java
@@ -0,0 +1,457 @@
+/* ZipEntry.java --
+ Copyright (C) 2001, 2002, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+import java.util.Calendar;
+
+/**
+ * This class represents a member of a zip archive. ZipFile and
+ * ZipInputStream will give you instances of this class as information
+ * about the members in an archive. On the other hand ZipOutputStream
+ * needs an instance of this class to create a new member.
+ *
+ * @author Jochen Hoenicke
+ */
+public class ZipEntry implements ZipConstants, Cloneable
+{
+ private static final byte KNOWN_SIZE = 1;
+ private static final byte KNOWN_CSIZE = 2;
+ private static final byte KNOWN_CRC = 4;
+ private static final byte KNOWN_TIME = 8;
+ private static final byte KNOWN_DOSTIME = 16;
+ private static final byte KNOWN_EXTRA = 32;
+
+ /** Immutable name of the entry */
+ private final String name;
+ /** Uncompressed size */
+ private int size;
+ /** Compressed size */
+ private long compressedSize = -1;
+ /** CRC of uncompressed data */
+ private int crc;
+ /** Comment or null if none */
+ private String comment = null;
+ /** The compression method. Either DEFLATED or STORED, by default -1. */
+ private byte method = -1;
+ /** Flags specifying what we know about this entry */
+ private byte known = 0;
+ /**
+ * The 32bit DOS encoded format for the time of this entry. Only valid if
+ * KNOWN_DOSTIME is set in known.
+ */
+ private int dostime;
+ /**
+ * The 64bit Java encoded millisecond time since the beginning of the epoch.
+ * Only valid if KNOWN_TIME is set in known.
+ */
+ private long time;
+ /** Extra data */
+ private byte[] extra = null;
+
+ int flags; /* used by ZipOutputStream */
+ int offset; /* used by ZipFile and ZipOutputStream */
+
+ /**
+ * Compression method. This method doesn't compress at all.
+ */
+ public static final int STORED = 0;
+ /**
+ * Compression method. This method uses the Deflater.
+ */
+ public static final int DEFLATED = 8;
+
+ /**
+ * Creates a zip entry with the given name.
+ * @param name the name. May include directory components separated
+ * by '/'.
+ *
+ * @exception NullPointerException when name is null.
+ * @exception IllegalArgumentException when name is bigger then 65535 chars.
+ */
+ public ZipEntry(String name)
+ {
+ int length = name.length();
+ if (length > 65535)
+ throw new IllegalArgumentException("name length is " + length);
+ this.name = name;
+ }
+
+ /**
+ * Creates a copy of the given zip entry.
+ * @param e the entry to copy.
+ */
+ public ZipEntry(ZipEntry e)
+ {
+ this(e, e.name);
+ }
+
+ ZipEntry(ZipEntry e, String name)
+ {
+ this.name = name;
+ known = e.known;
+ size = e.size;
+ compressedSize = e.compressedSize;
+ crc = e.crc;
+ dostime = e.dostime;
+ time = e.time;
+ method = e.method;
+ extra = e.extra;
+ comment = e.comment;
+ }
+
+ final void setDOSTime(int dostime)
+ {
+ this.dostime = dostime;
+ known |= KNOWN_DOSTIME;
+ known &= ~KNOWN_TIME;
+ }
+
+ final int getDOSTime()
+ {
+ if ((known & KNOWN_DOSTIME) != 0)
+ return dostime;
+ else if ((known & KNOWN_TIME) != 0)
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(time);
+ dostime = (cal.get(Calendar.YEAR) - 1980 & 0x7f) << 25
+ | (cal.get(Calendar.MONTH) + 1) << 21
+ | (cal.get(Calendar.DAY_OF_MONTH)) << 16
+ | (cal.get(Calendar.HOUR_OF_DAY)) << 11
+ | (cal.get(Calendar.MINUTE)) << 5
+ | (cal.get(Calendar.SECOND)) >> 1;
+ known |= KNOWN_DOSTIME;
+ return dostime;
+ }
+ else
+ return 0;
+ }
+
+ /**
+ * Creates a copy of this zip entry.
+ */
+ public Object clone()
+ {
+ // JCL defines this as being the same as the copy constructor above,
+ // except that value of the "extra" field is also copied. Take care
+ // that in the case of a subclass we use clone() rather than the copy
+ // constructor.
+ ZipEntry clone;
+ if (this.getClass() == ZipEntry.class)
+ clone = new ZipEntry(this);
+ else
+ {
+ try
+ {
+ clone = (ZipEntry) super.clone();
+ }
+ catch (CloneNotSupportedException e)
+ {
+ throw new InternalError();
+ }
+ }
+ if (extra != null)
+ {
+ clone.extra = new byte[extra.length];
+ System.arraycopy(extra, 0, clone.extra, 0, extra.length);
+ }
+ return clone;
+ }
+
+ /**
+ * Returns the entry name. The path components in the entry are
+ * always separated by slashes ('/').
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Sets the time of last modification of the entry.
+ * @time the time of last modification of the entry.
+ */
+ public void setTime(long time)
+ {
+ this.time = time;
+ this.known |= KNOWN_TIME;
+ this.known &= ~KNOWN_DOSTIME;
+ }
+
+ /**
+ * Gets the time of last modification of the entry.
+ * @return the time of last modification of the entry, or -1 if unknown.
+ */
+ public long getTime()
+ {
+ // The extra bytes might contain the time (posix/unix extension)
+ parseExtra();
+
+ if ((known & KNOWN_TIME) != 0)
+ return time;
+ else if ((known & KNOWN_DOSTIME) != 0)
+ {
+ int sec = 2 * (dostime & 0x1f);
+ int min = (dostime >> 5) & 0x3f;
+ int hrs = (dostime >> 11) & 0x1f;
+ int day = (dostime >> 16) & 0x1f;
+ int mon = ((dostime >> 21) & 0xf) - 1;
+ int year = ((dostime >> 25) & 0x7f) + 1980; /* since 1900 */
+
+ try
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.set(year, mon, day, hrs, min, sec);
+ time = cal.getTimeInMillis();
+ known |= KNOWN_TIME;
+ return time;
+ }
+ catch (RuntimeException ex)
+ {
+ /* Ignore illegal time stamp */
+ known &= ~KNOWN_TIME;
+ return -1;
+ }
+ }
+ else
+ return -1;
+ }
+
+ /**
+ * Sets the size of the uncompressed data.
+ * @exception IllegalArgumentException if size is not in 0..0xffffffffL
+ */
+ public void setSize(long size)
+ {
+ if ((size & 0xffffffff00000000L) != 0)
+ throw new IllegalArgumentException();
+ this.size = (int) size;
+ this.known |= KNOWN_SIZE;
+ }
+
+ /**
+ * Gets the size of the uncompressed data.
+ * @return the size or -1 if unknown.
+ */
+ public long getSize()
+ {
+ return (known & KNOWN_SIZE) != 0 ? size & 0xffffffffL : -1L;
+ }
+
+ /**
+ * Sets the size of the compressed data.
+ */
+ public void setCompressedSize(long csize)
+ {
+ this.compressedSize = csize;
+ }
+
+ /**
+ * Gets the size of the compressed data.
+ * @return the size or -1 if unknown.
+ */
+ public long getCompressedSize()
+ {
+ return compressedSize;
+ }
+
+ /**
+ * Sets the crc of the uncompressed data.
+ * @exception IllegalArgumentException if crc is not in 0..0xffffffffL
+ */
+ public void setCrc(long crc)
+ {
+ if ((crc & 0xffffffff00000000L) != 0)
+ throw new IllegalArgumentException();
+ this.crc = (int) crc;
+ this.known |= KNOWN_CRC;
+ }
+
+ /**
+ * Gets the crc of the uncompressed data.
+ * @return the crc or -1 if unknown.
+ */
+ public long getCrc()
+ {
+ return (known & KNOWN_CRC) != 0 ? crc & 0xffffffffL : -1L;
+ }
+
+ /**
+ * Sets the compression method. Only DEFLATED and STORED are
+ * supported.
+ * @exception IllegalArgumentException if method is not supported.
+ * @see ZipOutputStream#DEFLATED
+ * @see ZipOutputStream#STORED
+ */
+ public void setMethod(int method)
+ {
+ if (method != ZipOutputStream.STORED
+ && method != ZipOutputStream.DEFLATED)
+ throw new IllegalArgumentException();
+ this.method = (byte) method;
+ }
+
+ /**
+ * Gets the compression method.
+ * @return the compression method or -1 if unknown.
+ */
+ public int getMethod()
+ {
+ return method;
+ }
+
+ /**
+ * Sets the extra data.
+ * @exception IllegalArgumentException if extra is longer than 0xffff bytes.
+ */
+ public void setExtra(byte[] extra)
+ {
+ if (extra == null)
+ {
+ this.extra = null;
+ return;
+ }
+ if (extra.length > 0xffff)
+ throw new IllegalArgumentException();
+ this.extra = extra;
+ }
+
+ private void parseExtra()
+ {
+ // Already parsed?
+ if ((known & KNOWN_EXTRA) != 0)
+ return;
+
+ if (extra == null)
+ {
+ known |= KNOWN_EXTRA;
+ return;
+ }
+
+ try
+ {
+ int pos = 0;
+ while (pos < extra.length)
+ {
+ int sig = (extra[pos++] & 0xff)
+ | (extra[pos++] & 0xff) << 8;
+ int len = (extra[pos++] & 0xff)
+ | (extra[pos++] & 0xff) << 8;
+ if (sig == 0x5455)
+ {
+ /* extended time stamp */
+ int flags = extra[pos];
+ if ((flags & 1) != 0)
+ {
+ long time = ((extra[pos+1] & 0xff)
+ | (extra[pos+2] & 0xff) << 8
+ | (extra[pos+3] & 0xff) << 16
+ | (extra[pos+4] & 0xff) << 24);
+ setTime(time*1000);
+ }
+ }
+ pos += len;
+ }
+ }
+ catch (ArrayIndexOutOfBoundsException ex)
+ {
+ /* be lenient */
+ }
+
+ known |= KNOWN_EXTRA;
+ return;
+ }
+
+ /**
+ * Gets the extra data.
+ * @return the extra data or null if not set.
+ */
+ public byte[] getExtra()
+ {
+ return extra;
+ }
+
+ /**
+ * Sets the entry comment.
+ * @exception IllegalArgumentException if comment is longer than 0xffff.
+ */
+ public void setComment(String comment)
+ {
+ if (comment != null && comment.length() > 0xffff)
+ throw new IllegalArgumentException();
+ this.comment = comment;
+ }
+
+ /**
+ * Gets the comment.
+ * @return the comment or null if not set.
+ */
+ public String getComment()
+ {
+ return comment;
+ }
+
+ /**
+ * Gets true, if the entry is a directory. This is solely
+ * determined by the name, a trailing slash '/' marks a directory.
+ */
+ public boolean isDirectory()
+ {
+ int nlen = name.length();
+ return nlen > 0 && name.charAt(nlen - 1) == '/';
+ }
+
+ /**
+ * Gets the string representation of this ZipEntry. This is just
+ * the name as returned by getName().
+ */
+ public String toString()
+ {
+ return name;
+ }
+
+ /**
+ * Gets the hashCode of this ZipEntry. This is just the hashCode
+ * of the name. Note that the equals method isn't changed, though.
+ */
+ public int hashCode()
+ {
+ return name.hashCode();
+ }
+}
diff --git a/libjava/classpath/java/util/zip/ZipException.java b/libjava/classpath/java/util/zip/ZipException.java
new file mode 100644
index 000000000..c5bfc1e7c
--- /dev/null
+++ b/libjava/classpath/java/util/zip/ZipException.java
@@ -0,0 +1,72 @@
+/* ZipException.java - exception representing a zip related error
+ Copyright (C) 1998, 1999, 2000, 2001, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+import java.io.IOException;
+
+/**
+ * Thrown during the creation or input of a zip file.
+ *
+ * @author Jochen Hoenicke
+ * @author Per Bothner
+ * @status updated to 1.4
+ */
+public class ZipException extends IOException
+{
+ /**
+ * Compatible with JDK 1.0+.
+ */
+ private static final long serialVersionUID = 8000196834066748623L;
+
+ /**
+ * Create an exception without a message.
+ */
+ public ZipException()
+ {
+ }
+
+ /**
+ * Create an exception with a message.
+ *
+ * @param msg the message
+ */
+ public ZipException (String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/libjava/classpath/java/util/zip/ZipFile.java b/libjava/classpath/java/util/zip/ZipFile.java
new file mode 100644
index 000000000..3963bcb1e
--- /dev/null
+++ b/libjava/classpath/java/util/zip/ZipFile.java
@@ -0,0 +1,782 @@
+/* ZipFile.java --
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+import gnu.java.util.EmptyEnumeration;
+
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+
+/**
+ * This class represents a Zip archive. You can ask for the contained
+ * entries, or get an input stream for a file entry. The entry is
+ * automatically decompressed.
+ *
+ * This class is thread safe: You can open input streams for arbitrary
+ * entries in different threads.
+ *
+ * @author Jochen Hoenicke
+ * @author Artur Biesiadowski
+ */
+public class ZipFile implements ZipConstants
+{
+
+ /**
+ * Mode flag to open a zip file for reading.
+ */
+ public static final int OPEN_READ = 0x1;
+
+ /**
+ * Mode flag to delete a zip file after reading.
+ */
+ public static final int OPEN_DELETE = 0x4;
+
+ /**
+ * This field isn't defined in the JDK's ZipConstants, but should be.
+ */
+ static final int ENDNRD = 4;
+
+ // Name of this zip file.
+ private final String name;
+
+ // File from which zip entries are read.
+ private final RandomAccessFile raf;
+
+ // The entries of this zip file when initialized and not yet closed.
+ private LinkedHashMap<String, ZipEntry> entries;
+
+ private boolean closed = false;
+
+
+ /**
+ * Helper function to open RandomAccessFile and throw the proper
+ * ZipException in case opening the file fails.
+ *
+ * @param name the file name, or null if file is provided
+ *
+ * @param file the file, or null if name is provided
+ *
+ * @return the newly open RandomAccessFile, never null
+ */
+ private RandomAccessFile openFile(String name,
+ File file)
+ throws ZipException, IOException
+ {
+ try
+ {
+ return
+ (name != null)
+ ? new RandomAccessFile(name, "r")
+ : new RandomAccessFile(file, "r");
+ }
+ catch (FileNotFoundException f)
+ {
+ ZipException ze = new ZipException(f.getMessage());
+ ze.initCause(f);
+ throw ze;
+ }
+ }
+
+
+ /**
+ * Opens a Zip file with the given name for reading.
+ * @exception IOException if a i/o error occured.
+ * @exception ZipException if the file doesn't contain a valid zip
+ * archive.
+ */
+ public ZipFile(String name) throws ZipException, IOException
+ {
+ this.raf = openFile(name,null);
+ this.name = name;
+ checkZipFile();
+ }
+
+ /**
+ * Opens a Zip file reading the given File.
+ * @exception IOException if a i/o error occured.
+ * @exception ZipException if the file doesn't contain a valid zip
+ * archive.
+ */
+ public ZipFile(File file) throws ZipException, IOException
+ {
+ this.raf = openFile(null,file);
+ this.name = file.getPath();
+ checkZipFile();
+ }
+
+ /**
+ * Opens a Zip file reading the given File in the given mode.
+ *
+ * If the OPEN_DELETE mode is specified, the zip file will be deleted at
+ * some time moment after it is opened. It will be deleted before the zip
+ * file is closed or the Virtual Machine exits.
+ *
+ * The contents of the zip file will be accessible until it is closed.
+ *
+ * @since JDK1.3
+ * @param mode Must be one of OPEN_READ or OPEN_READ | OPEN_DELETE
+ *
+ * @exception IOException if a i/o error occured.
+ * @exception ZipException if the file doesn't contain a valid zip
+ * archive.
+ */
+ public ZipFile(File file, int mode) throws ZipException, IOException
+ {
+ if (mode != OPEN_READ && mode != (OPEN_READ | OPEN_DELETE))
+ throw new IllegalArgumentException("invalid mode");
+ if ((mode & OPEN_DELETE) != 0)
+ file.deleteOnExit();
+ this.raf = openFile(null,file);
+ this.name = file.getPath();
+ checkZipFile();
+ }
+
+ private void checkZipFile() throws ZipException
+ {
+ boolean valid = false;
+
+ try
+ {
+ byte[] buf = new byte[4];
+ raf.readFully(buf);
+ int sig = buf[0] & 0xFF
+ | ((buf[1] & 0xFF) << 8)
+ | ((buf[2] & 0xFF) << 16)
+ | ((buf[3] & 0xFF) << 24);
+ valid = sig == LOCSIG;
+ }
+ catch (IOException _)
+ {
+ }
+
+ if (!valid)
+ {
+ try
+ {
+ raf.close();
+ }
+ catch (IOException _)
+ {
+ }
+ throw new ZipException("Not a valid zip file");
+ }
+ }
+
+ /**
+ * Checks if file is closed and throws an exception.
+ */
+ private void checkClosed()
+ {
+ if (closed)
+ throw new IllegalStateException("ZipFile has closed: " + name);
+ }
+
+ /**
+ * Read the central directory of a zip file and fill the entries
+ * array. This is called exactly once when first needed. It is called
+ * while holding the lock on <code>raf</code>.
+ *
+ * @exception IOException if a i/o error occured.
+ * @exception ZipException if the central directory is malformed
+ */
+ private void readEntries() throws ZipException, IOException
+ {
+ /* Search for the End Of Central Directory. When a zip comment is
+ * present the directory may start earlier.
+ * Note that a comment has a maximum length of 64K, so that is the
+ * maximum we search backwards.
+ */
+ PartialInputStream inp = new PartialInputStream(raf, 4096);
+ long pos = raf.length() - ENDHDR;
+ long top = Math.max(0, pos - 65536);
+ do
+ {
+ if (pos < top)
+ throw new ZipException
+ ("central directory not found, probably not a zip file: " + name);
+ inp.seek(pos--);
+ }
+ while (inp.readLeInt() != ENDSIG);
+
+ if (inp.skip(ENDTOT - ENDNRD) != ENDTOT - ENDNRD)
+ throw new EOFException(name);
+ int count = inp.readLeShort();
+ if (inp.skip(ENDOFF - ENDSIZ) != ENDOFF - ENDSIZ)
+ throw new EOFException(name);
+ int centralOffset = inp.readLeInt();
+
+ entries = new LinkedHashMap<String, ZipEntry> (count+count/2);
+ inp.seek(centralOffset);
+
+ for (int i = 0; i < count; i++)
+ {
+ if (inp.readLeInt() != CENSIG)
+ throw new ZipException("Wrong Central Directory signature: " + name);
+
+ inp.skip(6);
+ int method = inp.readLeShort();
+ int dostime = inp.readLeInt();
+ int crc = inp.readLeInt();
+ int csize = inp.readLeInt();
+ int size = inp.readLeInt();
+ int nameLen = inp.readLeShort();
+ int extraLen = inp.readLeShort();
+ int commentLen = inp.readLeShort();
+ inp.skip(8);
+ int offset = inp.readLeInt();
+ String name = inp.readString(nameLen);
+
+ ZipEntry entry = new ZipEntry(name);
+ entry.setMethod(method);
+ entry.setCrc(crc & 0xffffffffL);
+ entry.setSize(size & 0xffffffffL);
+ entry.setCompressedSize(csize & 0xffffffffL);
+ entry.setDOSTime(dostime);
+ if (extraLen > 0)
+ {
+ byte[] extra = new byte[extraLen];
+ inp.readFully(extra);
+ entry.setExtra(extra);
+ }
+ if (commentLen > 0)
+ {
+ entry.setComment(inp.readString(commentLen));
+ }
+ entry.offset = offset;
+ entries.put(name, entry);
+ }
+ }
+
+ /**
+ * Closes the ZipFile. This also closes all input streams given by
+ * this class. After this is called, no further method should be
+ * called.
+ *
+ * @exception IOException if a i/o error occured.
+ */
+ public void close() throws IOException
+ {
+ RandomAccessFile raf = this.raf;
+ if (raf == null)
+ return;
+
+ synchronized (raf)
+ {
+ closed = true;
+ entries = null;
+ raf.close();
+ }
+ }
+
+ /**
+ * Calls the <code>close()</code> method when this ZipFile has not yet
+ * been explicitly closed.
+ */
+ protected void finalize() throws IOException
+ {
+ if (!closed && raf != null) close();
+ }
+
+ /**
+ * Returns an enumeration of all Zip entries in this Zip file.
+ *
+ * @exception IllegalStateException when the ZipFile has already been closed
+ */
+ public Enumeration<? extends ZipEntry> entries()
+ {
+ checkClosed();
+
+ try
+ {
+ return new ZipEntryEnumeration(getEntries().values().iterator());
+ }
+ catch (IOException ioe)
+ {
+ return new EmptyEnumeration<ZipEntry>();
+ }
+ }
+
+ /**
+ * Checks that the ZipFile is still open and reads entries when necessary.
+ *
+ * @exception IllegalStateException when the ZipFile has already been closed.
+ * @exception IOException when the entries could not be read.
+ */
+ private LinkedHashMap<String, ZipEntry> getEntries() throws IOException
+ {
+ synchronized(raf)
+ {
+ checkClosed();
+
+ if (entries == null)
+ readEntries();
+
+ return entries;
+ }
+ }
+
+ /**
+ * Searches for a zip entry in this archive with the given name.
+ *
+ * @param name the name. May contain directory components separated by
+ * slashes ('/').
+ * @return the zip entry, or null if no entry with that name exists.
+ *
+ * @exception IllegalStateException when the ZipFile has already been closed
+ */
+ public ZipEntry getEntry(String name)
+ {
+ checkClosed();
+
+ try
+ {
+ LinkedHashMap<String, ZipEntry> entries = getEntries();
+ ZipEntry entry = entries.get(name);
+ // If we didn't find it, maybe it's a directory.
+ if (entry == null && !name.endsWith("/"))
+ entry = entries.get(name + '/');
+ return entry != null ? new ZipEntry(entry, name) : null;
+ }
+ catch (IOException ioe)
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Creates an input stream reading the given zip entry as
+ * uncompressed data. Normally zip entry should be an entry
+ * returned by getEntry() or entries().
+ *
+ * This implementation returns null if the requested entry does not
+ * exist. This decision is not obviously correct, however, it does
+ * appear to mirror Sun's implementation, and it is consistant with
+ * their javadoc. On the other hand, the old JCL book, 2nd Edition,
+ * claims that this should return a "non-null ZIP entry". We have
+ * chosen for now ignore the old book, as modern versions of Ant (an
+ * important application) depend on this behaviour. See discussion
+ * in this thread:
+ * http://gcc.gnu.org/ml/java-patches/2004-q2/msg00602.html
+ *
+ * @param entry the entry to create an InputStream for.
+ * @return the input stream, or null if the requested entry does not exist.
+ *
+ * @exception IllegalStateException when the ZipFile has already been closed
+ * @exception IOException if a i/o error occured.
+ * @exception ZipException if the Zip archive is malformed.
+ */
+ public InputStream getInputStream(ZipEntry entry) throws IOException
+ {
+ checkClosed();
+
+ LinkedHashMap<String, ZipEntry> entries = getEntries();
+ String name = entry.getName();
+ ZipEntry zipEntry = entries.get(name);
+ if (zipEntry == null)
+ return null;
+
+ PartialInputStream inp = new PartialInputStream(raf, 1024);
+ inp.seek(zipEntry.offset);
+
+ if (inp.readLeInt() != LOCSIG)
+ throw new ZipException("Wrong Local header signature: " + name);
+
+ inp.skip(4);
+
+ if (zipEntry.getMethod() != inp.readLeShort())
+ throw new ZipException("Compression method mismatch: " + name);
+
+ inp.skip(16);
+
+ int nameLen = inp.readLeShort();
+ int extraLen = inp.readLeShort();
+ inp.skip(nameLen + extraLen);
+
+ inp.setLength(zipEntry.getCompressedSize());
+
+ int method = zipEntry.getMethod();
+ switch (method)
+ {
+ case ZipOutputStream.STORED:
+ return inp;
+ case ZipOutputStream.DEFLATED:
+ inp.addDummyByte();
+ final Inflater inf = new Inflater(true);
+ final int sz = (int) entry.getSize();
+ return new InflaterInputStream(inp, inf)
+ {
+ public int available() throws IOException
+ {
+ if (sz == -1)
+ return super.available();
+ if (super.available() != 0)
+ return sz - inf.getTotalOut();
+ return 0;
+ }
+ };
+ default:
+ throw new ZipException("Unknown compression method " + method);
+ }
+ }
+
+ /**
+ * Returns the (path) name of this zip file.
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Returns the number of entries in this zip file.
+ *
+ * @exception IllegalStateException when the ZipFile has already been closed
+ */
+ public int size()
+ {
+ checkClosed();
+
+ try
+ {
+ return getEntries().size();
+ }
+ catch (IOException ioe)
+ {
+ return 0;
+ }
+ }
+
+ private static class ZipEntryEnumeration implements Enumeration<ZipEntry>
+ {
+ private final Iterator<ZipEntry> elements;
+
+ public ZipEntryEnumeration(Iterator<ZipEntry> elements)
+ {
+ this.elements = elements;
+ }
+
+ public boolean hasMoreElements()
+ {
+ return elements.hasNext();
+ }
+
+ public ZipEntry nextElement()
+ {
+ /* We return a clone, just to be safe that the user doesn't
+ * change the entry.
+ */
+ return (ZipEntry) (elements.next().clone());
+ }
+ }
+
+ private static final class PartialInputStream extends InputStream
+ {
+ /**
+ * The UTF-8 charset use for decoding the filenames.
+ */
+ private static final Charset UTF8CHARSET = Charset.forName("UTF-8");
+
+ /**
+ * The actual UTF-8 decoder. Created on demand.
+ */
+ private CharsetDecoder utf8Decoder;
+
+ private final RandomAccessFile raf;
+ private final byte[] buffer;
+ private long bufferOffset;
+ private int pos;
+ private long end;
+ // We may need to supply an extra dummy byte to our reader.
+ // See Inflater. We use a count here to simplify the logic
+ // elsewhere in this class. Note that we ignore the dummy
+ // byte in methods where we know it is not needed.
+ private int dummyByteCount;
+
+ public PartialInputStream(RandomAccessFile raf, int bufferSize)
+ throws IOException
+ {
+ this.raf = raf;
+ buffer = new byte[bufferSize];
+ bufferOffset = -buffer.length;
+ pos = buffer.length;
+ end = raf.length();
+ }
+
+ void setLength(long length)
+ {
+ end = bufferOffset + pos + length;
+ }
+
+ private void fillBuffer() throws IOException
+ {
+ synchronized (raf)
+ {
+ long len = end - bufferOffset;
+ if (len == 0 && dummyByteCount > 0)
+ {
+ buffer[0] = 0;
+ dummyByteCount = 0;
+ }
+ else
+ {
+ raf.seek(bufferOffset);
+ raf.readFully(buffer, 0, (int) Math.min(buffer.length, len));
+ }
+ }
+ }
+
+ public int available()
+ {
+ long amount = end - (bufferOffset + pos);
+ if (amount > Integer.MAX_VALUE)
+ return Integer.MAX_VALUE;
+ return (int) amount;
+ }
+
+ public int read() throws IOException
+ {
+ if (bufferOffset + pos >= end + dummyByteCount)
+ return -1;
+ if (pos == buffer.length)
+ {
+ bufferOffset += buffer.length;
+ pos = 0;
+ fillBuffer();
+ }
+
+ return buffer[pos++] & 0xFF;
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException
+ {
+ if (len > end + dummyByteCount - (bufferOffset + pos))
+ {
+ len = (int) (end + dummyByteCount - (bufferOffset + pos));
+ if (len == 0)
+ return -1;
+ }
+
+ int totalBytesRead = Math.min(buffer.length - pos, len);
+ System.arraycopy(buffer, pos, b, off, totalBytesRead);
+ pos += totalBytesRead;
+ off += totalBytesRead;
+ len -= totalBytesRead;
+
+ while (len > 0)
+ {
+ bufferOffset += buffer.length;
+ pos = 0;
+ fillBuffer();
+ int remain = Math.min(buffer.length, len);
+ System.arraycopy(buffer, pos, b, off, remain);
+ pos += remain;
+ off += remain;
+ len -= remain;
+ totalBytesRead += remain;
+ }
+
+ return totalBytesRead;
+ }
+
+ public long skip(long amount) throws IOException
+ {
+ if (amount < 0)
+ return 0;
+ if (amount > end - (bufferOffset + pos))
+ amount = end - (bufferOffset + pos);
+ seek(bufferOffset + pos + amount);
+ return amount;
+ }
+
+ void seek(long newpos) throws IOException
+ {
+ long offset = newpos - bufferOffset;
+ if (offset >= 0 && offset <= buffer.length)
+ {
+ pos = (int) offset;
+ }
+ else
+ {
+ bufferOffset = newpos;
+ pos = 0;
+ fillBuffer();
+ }
+ }
+
+ void readFully(byte[] buf) throws IOException
+ {
+ if (read(buf, 0, buf.length) != buf.length)
+ throw new EOFException();
+ }
+
+ void readFully(byte[] buf, int off, int len) throws IOException
+ {
+ if (read(buf, off, len) != len)
+ throw new EOFException();
+ }
+
+ int readLeShort() throws IOException
+ {
+ int result;
+ if(pos + 1 < buffer.length)
+ {
+ result = ((buffer[pos + 0] & 0xff) | (buffer[pos + 1] & 0xff) << 8);
+ pos += 2;
+ }
+ else
+ {
+ int b0 = read();
+ int b1 = read();
+ if (b1 == -1)
+ throw new EOFException();
+ result = (b0 & 0xff) | (b1 & 0xff) << 8;
+ }
+ return result;
+ }
+
+ int readLeInt() throws IOException
+ {
+ int result;
+ if(pos + 3 < buffer.length)
+ {
+ result = (((buffer[pos + 0] & 0xff) | (buffer[pos + 1] & 0xff) << 8)
+ | ((buffer[pos + 2] & 0xff)
+ | (buffer[pos + 3] & 0xff) << 8) << 16);
+ pos += 4;
+ }
+ else
+ {
+ int b0 = read();
+ int b1 = read();
+ int b2 = read();
+ int b3 = read();
+ if (b3 == -1)
+ throw new EOFException();
+ result = (((b0 & 0xff) | (b1 & 0xff) << 8) | ((b2 & 0xff)
+ | (b3 & 0xff) << 8) << 16);
+ }
+ return result;
+ }
+
+ /**
+ * Decode chars from byte buffer using UTF8 encoding. This
+ * operation is performance-critical since a jar file contains a
+ * large number of strings for the name of each file in the
+ * archive. This routine therefore avoids using the expensive
+ * utf8Decoder when decoding is straightforward.
+ *
+ * @param buffer the buffer that contains the encoded character
+ * data
+ * @param pos the index in buffer of the first byte of the encoded
+ * data
+ * @param length the length of the encoded data in number of
+ * bytes.
+ *
+ * @return a String that contains the decoded characters.
+ */
+ private String decodeChars(byte[] buffer, int pos, int length)
+ throws IOException
+ {
+ String result;
+ int i=length - 1;
+ while ((i >= 0) && (buffer[i] <= 0x7f))
+ {
+ i--;
+ }
+ if (i < 0)
+ {
+ result = new String(buffer, 0, pos, length);
+ }
+ else
+ {
+ ByteBuffer bufferBuffer = ByteBuffer.wrap(buffer, pos, length);
+ if (utf8Decoder == null)
+ utf8Decoder = UTF8CHARSET.newDecoder();
+ utf8Decoder.reset();
+ char [] characters = utf8Decoder.decode(bufferBuffer).array();
+ result = String.valueOf(characters);
+ }
+ return result;
+ }
+
+ String readString(int length) throws IOException
+ {
+ if (length > end - (bufferOffset + pos))
+ throw new EOFException();
+
+ String result = null;
+ try
+ {
+ if (buffer.length - pos >= length)
+ {
+ result = decodeChars(buffer, pos, length);
+ pos += length;
+ }
+ else
+ {
+ byte[] b = new byte[length];
+ readFully(b);
+ result = decodeChars(b, 0, length);
+ }
+ }
+ catch (UnsupportedEncodingException uee)
+ {
+ throw new AssertionError(uee);
+ }
+ return result;
+ }
+
+ public void addDummyByte()
+ {
+ dummyByteCount = 1;
+ }
+ }
+}
diff --git a/libjava/classpath/java/util/zip/ZipInputStream.java b/libjava/classpath/java/util/zip/ZipInputStream.java
new file mode 100644
index 000000000..3eae026da
--- /dev/null
+++ b/libjava/classpath/java/util/zip/ZipInputStream.java
@@ -0,0 +1,381 @@
+/* ZipInputStream.java --
+ Copyright (C) 2001, 2002, 2003, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * This is a FilterInputStream that reads the files in an zip archive
+ * one after another. It has a special method to get the zip entry of
+ * the next file. The zip entry contains information about the file name
+ * size, compressed size, CRC, etc.
+ *
+ * It includes support for STORED and DEFLATED entries.
+ *
+ * @author Jochen Hoenicke
+ */
+public class ZipInputStream extends InflaterInputStream implements ZipConstants
+{
+ private CRC32 crc = new CRC32();
+ private ZipEntry entry = null;
+
+ private int csize;
+ private int size;
+ private int method;
+ private int flags;
+ private int avail;
+ private boolean entryAtEOF;
+
+ /**
+ * Creates a new Zip input stream, reading a zip archive.
+ */
+ public ZipInputStream(InputStream in)
+ {
+ super(in, new Inflater(true));
+ }
+
+ private void fillBuf() throws IOException
+ {
+ avail = len = in.read(buf, 0, buf.length);
+ }
+
+ private int readBuf(byte[] out, int offset, int length) throws IOException
+ {
+ if (avail <= 0)
+ {
+ fillBuf();
+ if (avail <= 0)
+ return -1;
+ }
+ if (length > avail)
+ length = avail;
+ System.arraycopy(buf, len - avail, out, offset, length);
+ avail -= length;
+ return length;
+ }
+
+ private void readFully(byte[] out) throws IOException
+ {
+ int off = 0;
+ int len = out.length;
+ while (len > 0)
+ {
+ int count = readBuf(out, off, len);
+ if (count == -1)
+ throw new EOFException();
+ off += count;
+ len -= count;
+ }
+ }
+
+ private int readLeByte() throws IOException
+ {
+ if (avail <= 0)
+ {
+ fillBuf();
+ if (avail <= 0)
+ throw new ZipException("EOF in header");
+ }
+ return buf[len - avail--] & 0xff;
+ }
+
+ /**
+ * Read an unsigned short in little endian byte order.
+ */
+ private int readLeShort() throws IOException
+ {
+ return readLeByte() | (readLeByte() << 8);
+ }
+
+ /**
+ * Read an int in little endian byte order.
+ */
+ private int readLeInt() throws IOException
+ {
+ return readLeShort() | (readLeShort() << 16);
+ }
+
+ /**
+ * Open the next entry from the zip archive, and return its description.
+ * If the previous entry wasn't closed, this method will close it.
+ */
+ public ZipEntry getNextEntry() throws IOException
+ {
+ if (crc == null)
+ throw new IOException("Stream closed.");
+ if (entry != null)
+ closeEntry();
+
+ int header = readLeInt();
+ if (header == CENSIG)
+ {
+ /* Central Header reached. */
+ close();
+ return null;
+ }
+ if (header != LOCSIG)
+ throw new ZipException("Wrong Local header signature: "
+ + Integer.toHexString(header));
+ /* skip version */
+ readLeShort();
+ flags = readLeShort();
+ method = readLeShort();
+ int dostime = readLeInt();
+ int crc = readLeInt();
+ csize = readLeInt();
+ size = readLeInt();
+ int nameLen = readLeShort();
+ int extraLen = readLeShort();
+
+ if (method == ZipOutputStream.STORED && csize != size)
+ throw new ZipException("Stored, but compressed != uncompressed");
+
+
+ byte[] buffer = new byte[nameLen];
+ readFully(buffer);
+ String name;
+ try
+ {
+ name = new String(buffer, "UTF-8");
+ }
+ catch (UnsupportedEncodingException uee)
+ {
+ throw new AssertionError(uee);
+ }
+
+ entry = createZipEntry(name);
+ entryAtEOF = false;
+ entry.setMethod(method);
+ if ((flags & 8) == 0)
+ {
+ entry.setCrc(crc & 0xffffffffL);
+ entry.setSize(size & 0xffffffffL);
+ entry.setCompressedSize(csize & 0xffffffffL);
+ }
+ entry.setDOSTime(dostime);
+ if (extraLen > 0)
+ {
+ byte[] extra = new byte[extraLen];
+ readFully(extra);
+ entry.setExtra(extra);
+ }
+
+ if (method == ZipOutputStream.DEFLATED && avail > 0)
+ {
+ System.arraycopy(buf, len - avail, buf, 0, avail);
+ len = avail;
+ avail = 0;
+ inf.setInput(buf, 0, len);
+ }
+ return entry;
+ }
+
+ private void readDataDescr() throws IOException
+ {
+ if (readLeInt() != EXTSIG)
+ throw new ZipException("Data descriptor signature not found");
+ entry.setCrc(readLeInt() & 0xffffffffL);
+ csize = readLeInt();
+ size = readLeInt();
+ entry.setSize(size & 0xffffffffL);
+ entry.setCompressedSize(csize & 0xffffffffL);
+ }
+
+ /**
+ * Closes the current zip entry and moves to the next one.
+ */
+ public void closeEntry() throws IOException
+ {
+ if (crc == null)
+ throw new IOException("Stream closed.");
+ if (entry == null)
+ return;
+
+ if (method == ZipOutputStream.DEFLATED)
+ {
+ if ((flags & 8) != 0)
+ {
+ /* We don't know how much we must skip, read until end. */
+ byte[] tmp = new byte[2048];
+ while (read(tmp) > 0)
+ ;
+
+ /* read will close this entry */
+ return;
+ }
+ csize -= inf.getTotalIn();
+ avail = inf.getRemaining();
+ }
+
+ if (avail > csize && csize >= 0)
+ avail -= csize;
+ else
+ {
+ csize -= avail;
+ avail = 0;
+ while (csize != 0)
+ {
+ long skipped = in.skip(csize & 0xffffffffL);
+ if (skipped <= 0)
+ throw new ZipException("zip archive ends early.");
+ csize -= skipped;
+ }
+ }
+
+ size = 0;
+ crc.reset();
+ if (method == ZipOutputStream.DEFLATED)
+ inf.reset();
+ entry = null;
+ entryAtEOF = true;
+ }
+
+ public int available() throws IOException
+ {
+ return entryAtEOF ? 0 : 1;
+ }
+
+ /**
+ * Reads a byte from the current zip entry.
+ * @return the byte or -1 on EOF.
+ * @exception IOException if a i/o error occured.
+ * @exception ZipException if the deflated stream is corrupted.
+ */
+ public int read() throws IOException
+ {
+ byte[] b = new byte[1];
+ if (read(b, 0, 1) <= 0)
+ return -1;
+ return b[0] & 0xff;
+ }
+
+ /**
+ * Reads a block of bytes from the current zip entry.
+ * @return the number of bytes read (may be smaller, even before
+ * EOF), or -1 on EOF.
+ * @exception IOException if a i/o error occured.
+ * @exception ZipException if the deflated stream is corrupted.
+ */
+ public int read(byte[] b, int off, int len) throws IOException
+ {
+ if (len == 0)
+ return 0;
+ if (crc == null)
+ throw new IOException("Stream closed.");
+ if (entry == null)
+ return -1;
+ boolean finished = false;
+ switch (method)
+ {
+ case ZipOutputStream.DEFLATED:
+ len = super.read(b, off, len);
+ if (len < 0)
+ {
+ if (!inf.finished())
+ throw new ZipException("Inflater not finished!?");
+ avail = inf.getRemaining();
+ if ((flags & 8) != 0)
+ readDataDescr();
+
+ if (inf.getTotalIn() != csize
+ || inf.getTotalOut() != size)
+ throw new ZipException("size mismatch: "+csize+";"+size+" <-> "+inf.getTotalIn()+";"+inf.getTotalOut());
+ inf.reset();
+ finished = true;
+ }
+ break;
+
+ case ZipOutputStream.STORED:
+
+ if (len > csize && csize >= 0)
+ len = csize;
+
+ len = readBuf(b, off, len);
+ if (len > 0)
+ {
+ csize -= len;
+ size -= len;
+ }
+
+ if (csize == 0)
+ finished = true;
+ else if (len < 0)
+ throw new ZipException("EOF in stored block");
+ break;
+ }
+
+ if (len > 0)
+ crc.update(b, off, len);
+
+ if (finished)
+ {
+ if ((crc.getValue() & 0xffffffffL) != entry.getCrc())
+ throw new ZipException("CRC mismatch");
+ crc.reset();
+ entry = null;
+ entryAtEOF = true;
+ }
+ return len;
+ }
+
+ /**
+ * Closes the zip file.
+ * @exception IOException if a i/o error occured.
+ */
+ public void close() throws IOException
+ {
+ super.close();
+ crc = null;
+ entry = null;
+ entryAtEOF = true;
+ }
+
+ /**
+ * Creates a new zip entry for the given name. This is equivalent
+ * to new ZipEntry(name).
+ * @param name the name of the zip entry.
+ */
+ protected ZipEntry createZipEntry(String name)
+ {
+ return new ZipEntry(name);
+ }
+}
diff --git a/libjava/classpath/java/util/zip/ZipOutputStream.java b/libjava/classpath/java/util/zip/ZipOutputStream.java
new file mode 100644
index 000000000..bc1c3e99c
--- /dev/null
+++ b/libjava/classpath/java/util/zip/ZipOutputStream.java
@@ -0,0 +1,440 @@
+/* ZipOutputStream.java --
+ Copyright (C) 2001, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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.zip;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * This is a FilterOutputStream that writes the files into a zip
+ * archive one after another. It has a special method to start a new
+ * zip entry. The zip entries contains information about the file name
+ * size, compressed size, CRC, etc.
+ *
+ * It includes support for STORED and DEFLATED entries.
+ *
+ * This class is not thread safe.
+ *
+ * @author Jochen Hoenicke
+ */
+public class ZipOutputStream extends DeflaterOutputStream implements ZipConstants
+{
+ private Vector entries = new Vector();
+ private CRC32 crc = new CRC32();
+ private ZipEntry curEntry = null;
+
+ private int curMethod;
+ private int size;
+ private int offset = 0;
+
+ private byte[] zipComment = new byte[0];
+ private int defaultMethod = DEFLATED;
+
+ /**
+ * Our Zip version is hard coded to 1.0 resp. 2.0
+ */
+ private static final int ZIP_STORED_VERSION = 10;
+ private static final int ZIP_DEFLATED_VERSION = 20;
+
+ /**
+ * Compression method. This method doesn't compress at all.
+ */
+ public static final int STORED = 0;
+
+ /**
+ * Compression method. This method uses the Deflater.
+ */
+ public static final int DEFLATED = 8;
+
+ /**
+ * Creates a new Zip output stream, writing a zip archive.
+ * @param out the output stream to which the zip archive is written.
+ */
+ public ZipOutputStream(OutputStream out)
+ {
+ super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
+ }
+
+ /**
+ * Set the zip file comment.
+ * @param comment the comment.
+ * @exception IllegalArgumentException if encoding of comment is
+ * longer than 0xffff bytes.
+ */
+ public void setComment(String comment)
+ {
+ byte[] commentBytes;
+ try
+ {
+ commentBytes = comment.getBytes("UTF-8");
+ }
+ catch (UnsupportedEncodingException uee)
+ {
+ throw new AssertionError(uee);
+ }
+ if (commentBytes.length > 0xffff)
+ throw new IllegalArgumentException("Comment too long.");
+ zipComment = commentBytes;
+ }
+
+ /**
+ * Sets default compression method. If the Zip entry specifies
+ * another method its method takes precedence.
+ * @param method the method.
+ * @exception IllegalArgumentException if method is not supported.
+ * @see #STORED
+ * @see #DEFLATED
+ */
+ public void setMethod(int method)
+ {
+ if (method != STORED && method != DEFLATED)
+ throw new IllegalArgumentException("Method not supported.");
+ defaultMethod = method;
+ }
+
+ /**
+ * Sets default compression level. The new level will be activated
+ * immediately.
+ * @exception IllegalArgumentException if level is not supported.
+ * @see Deflater
+ */
+ public void setLevel(int level)
+ {
+ def.setLevel(level);
+ }
+
+ /**
+ * Write an unsigned short in little endian byte order.
+ */
+ private void writeLeShort(int value) throws IOException
+ {
+ out.write(value & 0xff);
+ out.write((value >> 8) & 0xff);
+ }
+
+ /**
+ * Write an int in little endian byte order.
+ */
+ private void writeLeInt(int value) throws IOException
+ {
+ writeLeShort(value);
+ writeLeShort(value >> 16);
+ }
+
+ /**
+ * Write a long value as an int. Some of the zip constants
+ * are declared as longs even though they fit perfectly well
+ * into integers.
+ */
+ private void writeLeInt(long value) throws IOException
+ {
+ writeLeInt((int) value);
+ }
+
+ /**
+ * Starts a new Zip entry. It automatically closes the previous
+ * entry if present. If the compression method is stored, the entry
+ * must have a valid size and crc, otherwise all elements (except
+ * name) are optional, but must be correct if present. If the time
+ * is not set in the entry, the current time is used.
+ * @param entry the entry.
+ * @exception IOException if an I/O error occured.
+ * @exception ZipException if stream was finished.
+ */
+ public void putNextEntry(ZipEntry entry) throws IOException
+ {
+ if (entries == null)
+ throw new ZipException("ZipOutputStream was finished");
+
+ int method = entry.getMethod();
+ int flags = 0;
+ if (method == -1)
+ method = defaultMethod;
+
+ if (method == STORED)
+ {
+ if (entry.getCompressedSize() >= 0)
+ {
+ if (entry.getSize() < 0)
+ entry.setSize(entry.getCompressedSize());
+ else if (entry.getSize() != entry.getCompressedSize())
+ throw new ZipException
+ ("Method STORED, but compressed size != size");
+ }
+ else
+ entry.setCompressedSize(entry.getSize());
+
+ if (entry.getSize() < 0)
+ throw new ZipException("Method STORED, but size not set");
+ if (entry.getCrc() < 0)
+ throw new ZipException("Method STORED, but crc not set");
+ }
+ else if (method == DEFLATED)
+ {
+ if (entry.getCompressedSize() < 0
+ || entry.getSize() < 0 || entry.getCrc() < 0)
+ flags |= 8;
+ }
+
+ if (curEntry != null)
+ closeEntry();
+
+ if (entry.getTime() < 0)
+ entry.setTime(System.currentTimeMillis());
+
+ entry.flags = flags;
+ entry.offset = offset;
+ entry.setMethod(method);
+ curMethod = method;
+ /* Write the local file header */
+ writeLeInt(LOCSIG);
+ writeLeShort(method == STORED
+ ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION);
+ writeLeShort(flags);
+ writeLeShort(method);
+ writeLeInt(entry.getDOSTime());
+ if ((flags & 8) == 0)
+ {
+ writeLeInt((int)entry.getCrc());
+ writeLeInt((int)entry.getCompressedSize());
+ writeLeInt((int)entry.getSize());
+ }
+ else
+ {
+ writeLeInt(0);
+ writeLeInt(0);
+ writeLeInt(0);
+ }
+ byte[] name;
+ try
+ {
+ name = entry.getName().getBytes("UTF-8");
+ }
+ catch (UnsupportedEncodingException uee)
+ {
+ throw new AssertionError(uee);
+ }
+ if (name.length > 0xffff)
+ throw new ZipException("Name too long.");
+ byte[] extra = entry.getExtra();
+ if (extra == null)
+ extra = new byte[0];
+ writeLeShort(name.length);
+ writeLeShort(extra.length);
+ out.write(name);
+ out.write(extra);
+
+ offset += LOCHDR + name.length + extra.length;
+
+ /* Activate the entry. */
+
+ curEntry = entry;
+ crc.reset();
+ if (method == DEFLATED)
+ def.reset();
+ size = 0;
+ }
+
+ /**
+ * Closes the current entry.
+ * @exception IOException if an I/O error occured.
+ * @exception ZipException if no entry is active.
+ */
+ public void closeEntry() throws IOException
+ {
+ if (curEntry == null)
+ throw new ZipException("No open entry");
+
+ /* First finish the deflater, if appropriate */
+ if (curMethod == DEFLATED)
+ super.finish();
+
+ int csize = curMethod == DEFLATED ? def.getTotalOut() : size;
+
+ if (curEntry.getSize() < 0)
+ curEntry.setSize(size);
+ else if (curEntry.getSize() != size)
+ throw new ZipException("size was "+size
+ +", but I expected "+curEntry.getSize());
+
+ if (curEntry.getCompressedSize() < 0)
+ curEntry.setCompressedSize(csize);
+ else if (curEntry.getCompressedSize() != csize)
+ throw new ZipException("compressed size was "+csize
+ +", but I expected "+curEntry.getSize());
+
+ if (curEntry.getCrc() < 0)
+ curEntry.setCrc(crc.getValue());
+ else if (curEntry.getCrc() != crc.getValue())
+ throw new ZipException("crc was " + Long.toHexString(crc.getValue())
+ + ", but I expected "
+ + Long.toHexString(curEntry.getCrc()));
+
+ offset += csize;
+
+ /* Now write the data descriptor entry if needed. */
+ if (curMethod == DEFLATED && (curEntry.flags & 8) != 0)
+ {
+ writeLeInt(EXTSIG);
+ writeLeInt((int)curEntry.getCrc());
+ writeLeInt((int)curEntry.getCompressedSize());
+ writeLeInt((int)curEntry.getSize());
+ offset += EXTHDR;
+ }
+
+ entries.addElement(curEntry);
+ curEntry = null;
+ }
+
+ /**
+ * Writes the given buffer to the current entry.
+ * @exception IOException if an I/O error occured.
+ * @exception ZipException if no entry is active.
+ */
+ public void write(byte[] b, int off, int len) throws IOException
+ {
+ if (curEntry == null)
+ throw new ZipException("No open entry.");
+
+ switch (curMethod)
+ {
+ case DEFLATED:
+ super.write(b, off, len);
+ break;
+
+ case STORED:
+ out.write(b, off, len);
+ break;
+ }
+
+ crc.update(b, off, len);
+ size += len;
+ }
+
+ /**
+ * Finishes the stream. This will write the central directory at the
+ * end of the zip file and flush the stream.
+ * @exception IOException if an I/O error occured.
+ */
+ public void finish() throws IOException
+ {
+ if (entries == null)
+ return;
+ if (curEntry != null)
+ closeEntry();
+
+ int numEntries = 0;
+ int sizeEntries = 0;
+
+ Enumeration e = entries.elements();
+ while (e.hasMoreElements())
+ {
+ ZipEntry entry = (ZipEntry) e.nextElement();
+
+ int method = entry.getMethod();
+ writeLeInt(CENSIG);
+ writeLeShort(method == STORED
+ ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION);
+ writeLeShort(method == STORED
+ ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION);
+ writeLeShort(entry.flags);
+ writeLeShort(method);
+ writeLeInt(entry.getDOSTime());
+ writeLeInt((int)entry.getCrc());
+ writeLeInt((int)entry.getCompressedSize());
+ writeLeInt((int)entry.getSize());
+
+ byte[] name;
+ try
+ {
+ name = entry.getName().getBytes("UTF-8");
+ }
+ catch (UnsupportedEncodingException uee)
+ {
+ throw new AssertionError(uee);
+ }
+ if (name.length > 0xffff)
+ throw new ZipException("Name too long.");
+ byte[] extra = entry.getExtra();
+ if (extra == null)
+ extra = new byte[0];
+ String str = entry.getComment();
+ byte[] comment;
+ try
+ {
+ comment = str != null ? str.getBytes("UTF-8") : new byte[0];
+ }
+ catch (UnsupportedEncodingException uee)
+ {
+ throw new AssertionError(uee);
+ }
+ if (comment.length > 0xffff)
+ throw new ZipException("Comment too long.");
+
+ writeLeShort(name.length);
+ writeLeShort(extra.length);
+ writeLeShort(comment.length);
+ writeLeShort(0); /* disk number */
+ writeLeShort(0); /* internal file attr */
+ writeLeInt(0); /* external file attr */
+ writeLeInt(entry.offset);
+
+ out.write(name);
+ out.write(extra);
+ out.write(comment);
+ numEntries++;
+ sizeEntries += CENHDR + name.length + extra.length + comment.length;
+ }
+
+ writeLeInt(ENDSIG);
+ writeLeShort(0); /* disk number */
+ writeLeShort(0); /* disk with start of central dir */
+ writeLeShort(numEntries);
+ writeLeShort(numEntries);
+ writeLeInt(sizeEntries);
+ writeLeInt(offset);
+ writeLeShort(zipComment.length);
+ out.write(zipComment);
+ out.flush();
+ entries = null;
+ }
+}
diff --git a/libjava/classpath/java/util/zip/package.html b/libjava/classpath/java/util/zip/package.html
new file mode 100644
index 000000000..5f3ac4917
--- /dev/null
+++ b/libjava/classpath/java/util/zip/package.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in java.util.zip package.
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 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. -->
+
+<html>
+<head><title>GNU Classpath - java.util.zip</title></head>
+
+<body>
+<p>Utility classes to manipulate zip and gzip archives as files or streams,
+includes checksum and compression support.</p>
+
+</body>
+</html>