# HG changeset patch # User Martin Soch # Date 1360080262 -3600 # Node ID 1ff540c1650fe0c1f045e92b9498e6e7efe46502 # Parent eecf6077ec4e677086299ca071360827eb4a845f# Parent b4354a30d4dde0549b09c64127276d1ae1f7f6e6 merge with trunk diff -r eecf6077ec4e -r 1ff540c1650f core/pom.xml --- a/core/pom.xml Tue Feb 05 16:40:01 2013 +0100 +++ b/core/pom.xml Tue Feb 05 17:04:22 2013 +0100 @@ -38,6 +38,7 @@ org.netbeans.api org-openide-util-lookup + provided Contains esential annotations for associating JavaScript code with diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/pom.xml --- a/emul/compact/pom.xml Tue Feb 05 16:40:01 2013 +0100 +++ b/emul/compact/pom.xml Tue Feb 05 17:04:22 2013 +0100 @@ -27,6 +27,11 @@ ${project.version} test + + org.netbeans.api + org-openide-util-lookup + test + diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/beans/ChangeListenerMap.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/beans/ChangeListenerMap.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.beans; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.EventListener; +import java.util.EventListenerProxy; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import org.apidesign.bck2brwsr.emul.lang.System; + +/** + * This is an abstract class that provides base functionality + * for the {@link PropertyChangeSupport PropertyChangeSupport} class + * and the {@link VetoableChangeSupport VetoableChangeSupport} class. + * + * @see PropertyChangeListenerMap + * @see VetoableChangeListenerMap + * + * @author Sergey A. Malenkov + */ +abstract class ChangeListenerMap { + private Map map; + + /** + * Creates an array of listeners. + * This method can be optimized by using + * the same instance of the empty array + * when {@code length} is equal to {@code 0}. + * + * @param length the array length + * @return an array with specified length + */ + protected abstract L[] newArray(int length); + + /** + * Creates a proxy listener for the specified property. + * + * @param name the name of the property to listen on + * @param listener the listener to process events + * @return a proxy listener + */ + protected abstract L newProxy(String name, L listener); + + /** + * Adds a listener to the list of listeners for the specified property. + * This listener is called as many times as it was added. + * + * @param name the name of the property to listen on + * @param listener the listener to process events + */ + public final synchronized void add(String name, L listener) { + if (this.map == null) { + this.map = new HashMap(); + } + L[] array = this.map.get(name); + int size = (array != null) + ? array.length + : 0; + + L[] clone = newArray(size + 1); + clone[size] = listener; + if (array != null) { + System.arraycopy(array, 0, clone, 0, size); + } + this.map.put(name, clone); + } + + /** + * Removes a listener from the list of listeners for the specified property. + * If the listener was added more than once to the same event source, + * this listener will be notified one less time after being removed. + * + * @param name the name of the property to listen on + * @param listener the listener to process events + */ + public final synchronized void remove(String name, L listener) { + if (this.map != null) { + L[] array = this.map.get(name); + if (array != null) { + for (int i = 0; i < array.length; i++) { + if (listener.equals(array[i])) { + int size = array.length - 1; + if (size > 0) { + L[] clone = newArray(size); + System.arraycopy(array, 0, clone, 0, i); + System.arraycopy(array, i + 1, clone, i, size - i); + this.map.put(name, clone); + } + else { + this.map.remove(name); + if (this.map.isEmpty()) { + this.map = null; + } + } + break; + } + } + } + } + } + + /** + * Returns the list of listeners for the specified property. + * + * @param name the name of the property + * @return the corresponding list of listeners + */ + public final synchronized L[] get(String name) { + return (this.map != null) + ? this.map.get(name) + : null; + } + + /** + * Sets new list of listeners for the specified property. + * + * @param name the name of the property + * @param listeners new list of listeners + */ + public final void set(String name, L[] listeners) { + if (listeners != null) { + if (this.map == null) { + this.map = new HashMap(); + } + this.map.put(name, listeners); + } + else if (this.map != null) { + this.map.remove(name); + if (this.map.isEmpty()) { + this.map = null; + } + } + } + + /** + * Returns all listeners in the map. + * + * @return an array of all listeners + */ + public final synchronized L[] getListeners() { + if (this.map == null) { + return newArray(0); + } + List list = new ArrayList(); + + L[] listeners = this.map.get(null); + if (listeners != null) { + for (L listener : listeners) { + list.add(listener); + } + } + for (Entry entry : this.map.entrySet()) { + String name = entry.getKey(); + if (name != null) { + for (L listener : entry.getValue()) { + list.add(newProxy(name, listener)); + } + } + } + return list.toArray(newArray(list.size())); + } + + /** + * Returns listeners that have been associated with the named property. + * + * @param name the name of the property + * @return an array of listeners for the named property + */ + public final L[] getListeners(String name) { + if (name != null) { + L[] listeners = get(name); + if (listeners != null) { + return listeners.clone(); + } + } + return newArray(0); + } + + /** + * Indicates whether the map contains + * at least one listener to be notified. + * + * @param name the name of the property + * @return {@code true} if at least one listener exists or + * {@code false} otherwise + */ + public final synchronized boolean hasListeners(String name) { + if (this.map == null) { + return false; + } + L[] array = this.map.get(null); + return (array != null) || ((name != null) && (null != this.map.get(name))); + } + + /** + * Returns a set of entries from the map. + * Each entry is a pair consisted of the property name + * and the corresponding list of listeners. + * + * @return a set of entries from the map + */ + public final Set> getEntries() { + return (this.map != null) + ? this.map.entrySet() + : Collections.>emptySet(); + } + + /** + * Extracts a real listener from the proxy listener. + * It is necessary because default proxy class is not serializable. + * + * @return a real listener + */ + public final L extract(L listener) { + while (listener instanceof EventListenerProxy) { + EventListenerProxy proxy = (EventListenerProxy) listener; + listener = proxy.getListener(); + } + return listener; + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/beans/IndexedPropertyChangeEvent.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/beans/IndexedPropertyChangeEvent.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.beans; + +/** + * An "IndexedPropertyChange" event gets delivered whenever a component that + * conforms to the JavaBeans™ specification (a "bean") changes a bound + * indexed property. This class is an extension of PropertyChangeEvent + * but contains the index of the property that has changed. + *

+ * Null values may be provided for the old and the new values if their + * true values are not known. + *

+ * An event source may send a null object as the name to indicate that an + * arbitrary set of if its properties have changed. In this case the + * old and new values should also be null. + * + * @since 1.5 + * @author Mark Davidson + */ +public class IndexedPropertyChangeEvent extends PropertyChangeEvent { + private static final long serialVersionUID = -320227448495806870L; + + private int index; + + /** + * Constructs a new IndexedPropertyChangeEvent object. + * + * @param source The bean that fired the event. + * @param propertyName The programmatic name of the property that + * was changed. + * @param oldValue The old value of the property. + * @param newValue The new value of the property. + * @param index index of the property element that was changed. + */ + public IndexedPropertyChangeEvent(Object source, String propertyName, + Object oldValue, Object newValue, + int index) { + super (source, propertyName, oldValue, newValue); + this.index = index; + } + + /** + * Gets the index of the property that was changed. + * + * @return The index specifying the property element that was + * changed. + */ + public int getIndex() { + return index; + } + + void appendTo(StringBuilder sb) { + sb.append("; index=").append(getIndex()); + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/beans/PropertyChangeEvent.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/beans/PropertyChangeEvent.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,164 @@ +/* + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.beans; + +/** + * A "PropertyChange" event gets delivered whenever a bean changes a "bound" + * or "constrained" property. A PropertyChangeEvent object is sent as an + * argument to the PropertyChangeListener and VetoableChangeListener methods. + *

+ * Normally PropertyChangeEvents are accompanied by the name and the old + * and new value of the changed property. If the new value is a primitive + * type (such as int or boolean) it must be wrapped as the + * corresponding java.lang.* Object type (such as Integer or Boolean). + *

+ * Null values may be provided for the old and the new values if their + * true values are not known. + *

+ * An event source may send a null object as the name to indicate that an + * arbitrary set of if its properties have changed. In this case the + * old and new values should also be null. + */ + +public class PropertyChangeEvent extends java.util.EventObject { + private static final long serialVersionUID = 7042693688939648123L; + + /** + * Constructs a new PropertyChangeEvent. + * + * @param source The bean that fired the event. + * @param propertyName The programmatic name of the property + * that was changed. + * @param oldValue The old value of the property. + * @param newValue The new value of the property. + */ + public PropertyChangeEvent(Object source, String propertyName, + Object oldValue, Object newValue) { + super(source); + this.propertyName = propertyName; + this.newValue = newValue; + this.oldValue = oldValue; + } + + /** + * Gets the programmatic name of the property that was changed. + * + * @return The programmatic name of the property that was changed. + * May be null if multiple properties have changed. + */ + public String getPropertyName() { + return propertyName; + } + + /** + * Gets the new value for the property, expressed as an Object. + * + * @return The new value for the property, expressed as an Object. + * May be null if multiple properties have changed. + */ + public Object getNewValue() { + return newValue; + } + + /** + * Gets the old value for the property, expressed as an Object. + * + * @return The old value for the property, expressed as an Object. + * May be null if multiple properties have changed. + */ + public Object getOldValue() { + return oldValue; + } + + /** + * Sets the propagationId object for the event. + * + * @param propagationId The propagationId object for the event. + */ + public void setPropagationId(Object propagationId) { + this.propagationId = propagationId; + } + + /** + * The "propagationId" field is reserved for future use. In Beans 1.0 + * the sole requirement is that if a listener catches a PropertyChangeEvent + * and then fires a PropertyChangeEvent of its own, then it should + * make sure that it propagates the propagationId field from its + * incoming event to its outgoing event. + * + * @return the propagationId object associated with a bound/constrained + * property update. + */ + public Object getPropagationId() { + return propagationId; + } + + /** + * name of the property that changed. May be null, if not known. + * @serial + */ + private String propertyName; + + /** + * New value for property. May be null if not known. + * @serial + */ + private Object newValue; + + /** + * Previous value for property. May be null if not known. + * @serial + */ + private Object oldValue; + + /** + * Propagation ID. May be null. + * @serial + * @see #getPropagationId + */ + private Object propagationId; + + /** + * Returns a string representation of the object. + * + * @return a string representation of the object + * + * @since 1.7 + */ + public String toString() { + StringBuilder sb = new StringBuilder(getClass().getName()); + sb.append("[propertyName=").append(getPropertyName()); + appendTo(sb); + sb.append("; oldValue=").append(getOldValue()); + sb.append("; newValue=").append(getNewValue()); + sb.append("; propagationId=").append(getPropagationId()); + sb.append("; source=").append(getSource()); + return sb.append("]").toString(); + } + + void appendTo(StringBuilder sb) { + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/beans/PropertyChangeListener.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/beans/PropertyChangeListener.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 1996, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.beans; + +/** + * A "PropertyChange" event gets fired whenever a bean changes a "bound" + * property. You can register a PropertyChangeListener with a source + * bean so as to be notified of any bound property updates. + */ + +public interface PropertyChangeListener extends java.util.EventListener { + + /** + * This method gets called when a bound property is changed. + * @param evt A PropertyChangeEvent object describing the event source + * and the property that has changed. + */ + + void propertyChange(PropertyChangeEvent evt); + +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/beans/PropertyChangeListenerProxy.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/beans/PropertyChangeListenerProxy.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2000, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.beans; + +import java.util.EventListenerProxy; + +/** + * A class which extends the {@code EventListenerProxy} + * specifically for adding a {@code PropertyChangeListener} + * with a "bound" property. + * Instances of this class can be added + * as {@code PropertyChangeListener}s to a bean + * which supports firing property change events. + *

+ * If the object has a {@code getPropertyChangeListeners} method + * then the array returned could be a mixture of {@code PropertyChangeListener} + * and {@code PropertyChangeListenerProxy} objects. + * + * @see java.util.EventListenerProxy + * @see PropertyChangeSupport#getPropertyChangeListeners + * @since 1.4 + */ +public class PropertyChangeListenerProxy + extends EventListenerProxy + implements PropertyChangeListener { + + private final String propertyName; + + /** + * Constructor which binds the {@code PropertyChangeListener} + * to a specific property. + * + * @param propertyName the name of the property to listen on + * @param listener the listener object + */ + public PropertyChangeListenerProxy(String propertyName, PropertyChangeListener listener) { + super(listener); + this.propertyName = propertyName; + } + + /** + * Forwards the property change event to the listener delegate. + * + * @param event the property change event + */ + public void propertyChange(PropertyChangeEvent event) { + getListener().propertyChange(event); + } + + /** + * Returns the name of the named property associated with the listener. + * + * @return the name of the named property associated with the listener + */ + public String getPropertyName() { + return this.propertyName; + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/beans/PropertyChangeSupport.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/beans/PropertyChangeSupport.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,536 @@ +/* + * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.beans; + +import java.io.Serializable; +import java.io.ObjectStreamField; +import java.io.ObjectOutputStream; +import java.io.ObjectInputStream; +import java.io.IOException; +import java.util.Hashtable; +import java.util.Map.Entry; + +/** + * This is a utility class that can be used by beans that support bound + * properties. It manages a list of listeners and dispatches + * {@link PropertyChangeEvent}s to them. You can use an instance of this class + * as a member field of your bean and delegate these types of work to it. + * The {@link PropertyChangeListener} can be registered for all properties + * or for a property specified by name. + *

+ * Here is an example of {@code PropertyChangeSupport} usage that follows + * the rules and recommendations laid out in the JavaBeans™ specification: + *

+ * public class MyBean {
+ *     private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
+ *
+ *     public void addPropertyChangeListener(PropertyChangeListener listener) {
+ *         this.pcs.addPropertyChangeListener(listener);
+ *     }
+ *
+ *     public void removePropertyChangeListener(PropertyChangeListener listener) {
+ *         this.pcs.removePropertyChangeListener(listener);
+ *     }
+ *
+ *     private String value;
+ *
+ *     public String getValue() {
+ *         return this.value;
+ *     }
+ *
+ *     public void setValue(String newValue) {
+ *         String oldValue = this.value;
+ *         this.value = newValue;
+ *         this.pcs.firePropertyChange("value", oldValue, newValue);
+ *     }
+ *
+ *     [...]
+ * }
+ * 
+ *

+ * A {@code PropertyChangeSupport} instance is thread-safe. + *

+ * This class is serializable. When it is serialized it will save + * (and restore) any listeners that are themselves serializable. Any + * non-serializable listeners will be skipped during serialization. + * + * @see VetoableChangeSupport + */ +public class PropertyChangeSupport implements Serializable { + private PropertyChangeListenerMap map = new PropertyChangeListenerMap(); + + /** + * Constructs a PropertyChangeSupport object. + * + * @param sourceBean The bean to be given as the source for any events. + */ + public PropertyChangeSupport(Object sourceBean) { + if (sourceBean == null) { + throw new NullPointerException(); + } + source = sourceBean; + } + + /** + * Add a PropertyChangeListener to the listener list. + * The listener is registered for all properties. + * The same listener object may be added more than once, and will be called + * as many times as it is added. + * If listener is null, no exception is thrown and no action + * is taken. + * + * @param listener The PropertyChangeListener to be added + */ + public void addPropertyChangeListener(PropertyChangeListener listener) { + if (listener == null) { + return; + } + if (listener instanceof PropertyChangeListenerProxy) { + PropertyChangeListenerProxy proxy = + (PropertyChangeListenerProxy)listener; + // Call two argument add method. + addPropertyChangeListener(proxy.getPropertyName(), + proxy.getListener()); + } else { + this.map.add(null, listener); + } + } + + /** + * Remove a PropertyChangeListener from the listener list. + * This removes a PropertyChangeListener that was registered + * for all properties. + * If listener was added more than once to the same event + * source, it will be notified one less time after being removed. + * If listener is null, or was never added, no exception is + * thrown and no action is taken. + * + * @param listener The PropertyChangeListener to be removed + */ + public void removePropertyChangeListener(PropertyChangeListener listener) { + if (listener == null) { + return; + } + if (listener instanceof PropertyChangeListenerProxy) { + PropertyChangeListenerProxy proxy = + (PropertyChangeListenerProxy)listener; + // Call two argument remove method. + removePropertyChangeListener(proxy.getPropertyName(), + proxy.getListener()); + } else { + this.map.remove(null, listener); + } + } + + /** + * Returns an array of all the listeners that were added to the + * PropertyChangeSupport object with addPropertyChangeListener(). + *

+ * If some listeners have been added with a named property, then + * the returned array will be a mixture of PropertyChangeListeners + * and PropertyChangeListenerProxys. If the calling + * method is interested in distinguishing the listeners then it must + * test each element to see if it's a + * PropertyChangeListenerProxy, perform the cast, and examine + * the parameter. + * + *

+     * PropertyChangeListener[] listeners = bean.getPropertyChangeListeners();
+     * for (int i = 0; i < listeners.length; i++) {
+     *   if (listeners[i] instanceof PropertyChangeListenerProxy) {
+     *     PropertyChangeListenerProxy proxy =
+     *                    (PropertyChangeListenerProxy)listeners[i];
+     *     if (proxy.getPropertyName().equals("foo")) {
+     *       // proxy is a PropertyChangeListener which was associated
+     *       // with the property named "foo"
+     *     }
+     *   }
+     * }
+     *
+ * + * @see PropertyChangeListenerProxy + * @return all of the PropertyChangeListeners added or an + * empty array if no listeners have been added + * @since 1.4 + */ + public PropertyChangeListener[] getPropertyChangeListeners() { + return this.map.getListeners(); + } + + /** + * Add a PropertyChangeListener for a specific property. The listener + * will be invoked only when a call on firePropertyChange names that + * specific property. + * The same listener object may be added more than once. For each + * property, the listener will be invoked the number of times it was added + * for that property. + * If propertyName or listener is null, no + * exception is thrown and no action is taken. + * + * @param propertyName The name of the property to listen on. + * @param listener The PropertyChangeListener to be added + */ + public void addPropertyChangeListener( + String propertyName, + PropertyChangeListener listener) { + if (listener == null || propertyName == null) { + return; + } + listener = this.map.extract(listener); + if (listener != null) { + this.map.add(propertyName, listener); + } + } + + /** + * Remove a PropertyChangeListener for a specific property. + * If listener was added more than once to the same event + * source for the specified property, it will be notified one less time + * after being removed. + * If propertyName is null, no exception is thrown and no + * action is taken. + * If listener is null, or was never added for the specified + * property, no exception is thrown and no action is taken. + * + * @param propertyName The name of the property that was listened on. + * @param listener The PropertyChangeListener to be removed + */ + public void removePropertyChangeListener( + String propertyName, + PropertyChangeListener listener) { + if (listener == null || propertyName == null) { + return; + } + listener = this.map.extract(listener); + if (listener != null) { + this.map.remove(propertyName, listener); + } + } + + /** + * Returns an array of all the listeners which have been associated + * with the named property. + * + * @param propertyName The name of the property being listened to + * @return all of the PropertyChangeListeners associated with + * the named property. If no such listeners have been added, + * or if propertyName is null, an empty array is + * returned. + * @since 1.4 + */ + public PropertyChangeListener[] getPropertyChangeListeners(String propertyName) { + return this.map.getListeners(propertyName); + } + + /** + * Reports a bound property update to listeners + * that have been registered to track updates of + * all properties or a property with the specified name. + *

+ * No event is fired if old and new values are equal and non-null. + *

+ * This is merely a convenience wrapper around the more general + * {@link #firePropertyChange(PropertyChangeEvent)} method. + * + * @param propertyName the programmatic name of the property that was changed + * @param oldValue the old value of the property + * @param newValue the new value of the property + */ + public void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + if (oldValue == null || newValue == null || !oldValue.equals(newValue)) { + firePropertyChange(new PropertyChangeEvent(this.source, propertyName, oldValue, newValue)); + } + } + + /** + * Reports an integer bound property update to listeners + * that have been registered to track updates of + * all properties or a property with the specified name. + *

+ * No event is fired if old and new values are equal. + *

+ * This is merely a convenience wrapper around the more general + * {@link #firePropertyChange(String, Object, Object)} method. + * + * @param propertyName the programmatic name of the property that was changed + * @param oldValue the old value of the property + * @param newValue the new value of the property + */ + public void firePropertyChange(String propertyName, int oldValue, int newValue) { + if (oldValue != newValue) { + firePropertyChange(propertyName, Integer.valueOf(oldValue), Integer.valueOf(newValue)); + } + } + + /** + * Reports a boolean bound property update to listeners + * that have been registered to track updates of + * all properties or a property with the specified name. + *

+ * No event is fired if old and new values are equal. + *

+ * This is merely a convenience wrapper around the more general + * {@link #firePropertyChange(String, Object, Object)} method. + * + * @param propertyName the programmatic name of the property that was changed + * @param oldValue the old value of the property + * @param newValue the new value of the property + */ + public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { + if (oldValue != newValue) { + firePropertyChange(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue)); + } + } + + /** + * Fires a property change event to listeners + * that have been registered to track updates of + * all properties or a property with the specified name. + *

+ * No event is fired if the given event's old and new values are equal and non-null. + * + * @param event the {@code PropertyChangeEvent} to be fired + */ + public void firePropertyChange(PropertyChangeEvent event) { + Object oldValue = event.getOldValue(); + Object newValue = event.getNewValue(); + if (oldValue == null || newValue == null || !oldValue.equals(newValue)) { + String name = event.getPropertyName(); + + PropertyChangeListener[] common = this.map.get(null); + PropertyChangeListener[] named = (name != null) + ? this.map.get(name) + : null; + + fire(common, event); + fire(named, event); + } + } + + private static void fire(PropertyChangeListener[] listeners, PropertyChangeEvent event) { + if (listeners != null) { + for (PropertyChangeListener listener : listeners) { + listener.propertyChange(event); + } + } + } + + /** + * Reports a bound indexed property update to listeners + * that have been registered to track updates of + * all properties or a property with the specified name. + *

+ * No event is fired if old and new values are equal and non-null. + *

+ * This is merely a convenience wrapper around the more general + * {@link #firePropertyChange(PropertyChangeEvent)} method. + * + * @param propertyName the programmatic name of the property that was changed + * @param index the index of the property element that was changed + * @param oldValue the old value of the property + * @param newValue the new value of the property + * @since 1.5 + */ + public void fireIndexedPropertyChange(String propertyName, int index, Object oldValue, Object newValue) { + if (oldValue == null || newValue == null || !oldValue.equals(newValue)) { + firePropertyChange(new IndexedPropertyChangeEvent(source, propertyName, oldValue, newValue, index)); + } + } + + /** + * Reports an integer bound indexed property update to listeners + * that have been registered to track updates of + * all properties or a property with the specified name. + *

+ * No event is fired if old and new values are equal. + *

+ * This is merely a convenience wrapper around the more general + * {@link #fireIndexedPropertyChange(String, int, Object, Object)} method. + * + * @param propertyName the programmatic name of the property that was changed + * @param index the index of the property element that was changed + * @param oldValue the old value of the property + * @param newValue the new value of the property + * @since 1.5 + */ + public void fireIndexedPropertyChange(String propertyName, int index, int oldValue, int newValue) { + if (oldValue != newValue) { + fireIndexedPropertyChange(propertyName, index, Integer.valueOf(oldValue), Integer.valueOf(newValue)); + } + } + + /** + * Reports a boolean bound indexed property update to listeners + * that have been registered to track updates of + * all properties or a property with the specified name. + *

+ * No event is fired if old and new values are equal. + *

+ * This is merely a convenience wrapper around the more general + * {@link #fireIndexedPropertyChange(String, int, Object, Object)} method. + * + * @param propertyName the programmatic name of the property that was changed + * @param index the index of the property element that was changed + * @param oldValue the old value of the property + * @param newValue the new value of the property + * @since 1.5 + */ + public void fireIndexedPropertyChange(String propertyName, int index, boolean oldValue, boolean newValue) { + if (oldValue != newValue) { + fireIndexedPropertyChange(propertyName, index, Boolean.valueOf(oldValue), Boolean.valueOf(newValue)); + } + } + + /** + * Check if there are any listeners for a specific property, including + * those registered on all properties. If propertyName + * is null, only check for listeners registered on all properties. + * + * @param propertyName the property name. + * @return true if there are one or more listeners for the given property + */ + public boolean hasListeners(String propertyName) { + return this.map.hasListeners(propertyName); + } + + /** + * @serialData Null terminated list of PropertyChangeListeners. + *

+ * At serialization time we skip non-serializable listeners and + * only serialize the serializable listeners. + */ + private void writeObject(ObjectOutputStream s) throws IOException { + Hashtable children = null; + PropertyChangeListener[] listeners = null; + synchronized (this.map) { + for (Entry entry : this.map.getEntries()) { + String property = entry.getKey(); + if (property == null) { + listeners = entry.getValue(); + } else { + if (children == null) { + children = new Hashtable(); + } + PropertyChangeSupport pcs = new PropertyChangeSupport(this.source); + pcs.map.set(null, entry.getValue()); + children.put(property, pcs); + } + } + } + ObjectOutputStream.PutField fields = s.putFields(); + fields.put("children", children); + fields.put("source", this.source); + fields.put("propertyChangeSupportSerializedDataVersion", 2); + s.writeFields(); + + if (listeners != null) { + for (PropertyChangeListener l : listeners) { + if (l instanceof Serializable) { + s.writeObject(l); + } + } + } + s.writeObject(null); + } + + private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException { + this.map = new PropertyChangeListenerMap(); + + ObjectInputStream.GetField fields = s.readFields(); + + Hashtable children = (Hashtable) fields.get("children", null); + this.source = fields.get("source", null); + fields.get("propertyChangeSupportSerializedDataVersion", 2); + + Object listenerOrNull; + while (null != (listenerOrNull = s.readObject())) { + this.map.add(null, (PropertyChangeListener)listenerOrNull); + } + if (children != null) { + for (Entry entry : children.entrySet()) { + for (PropertyChangeListener listener : entry.getValue().getPropertyChangeListeners()) { + this.map.add(entry.getKey(), listener); + } + } + } + } + + /** + * The object to be provided as the "source" for any generated events. + */ + private Object source; + + /** + * @serialField children Hashtable + * @serialField source Object + * @serialField propertyChangeSupportSerializedDataVersion int + */ + private static final ObjectStreamField[] serialPersistentFields = { + new ObjectStreamField("children", Hashtable.class), + new ObjectStreamField("source", Object.class), + new ObjectStreamField("propertyChangeSupportSerializedDataVersion", Integer.TYPE) + }; + + /** + * Serialization version ID, so we're compatible with JDK 1.1 + */ + static final long serialVersionUID = 6401253773779951803L; + + /** + * This is a {@link ChangeListenerMap ChangeListenerMap} implementation + * that works with {@link PropertyChangeListener PropertyChangeListener} objects. + */ + private static final class PropertyChangeListenerMap extends ChangeListenerMap { + private static final PropertyChangeListener[] EMPTY = {}; + + /** + * Creates an array of {@link PropertyChangeListener PropertyChangeListener} objects. + * This method uses the same instance of the empty array + * when {@code length} equals {@code 0}. + * + * @param length the array length + * @return an array with specified length + */ + @Override + protected PropertyChangeListener[] newArray(int length) { + return (0 < length) + ? new PropertyChangeListener[length] + : EMPTY; + } + + /** + * Creates a {@link PropertyChangeListenerProxy PropertyChangeListenerProxy} + * object for the specified property. + * + * @param name the name of the property to listen on + * @param listener the listener to process events + * @return a {@code PropertyChangeListenerProxy} object + */ + @Override + protected PropertyChangeListener newProxy(String name, PropertyChangeListener listener) { + return new PropertyChangeListenerProxy(name, listener); + } + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/beans/PropertyVetoException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/beans/PropertyVetoException.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.beans; + + +/** + * A PropertyVetoException is thrown when a proposed change to a + * property represents an unacceptable value. + */ + +public +class PropertyVetoException extends Exception { + private static final long serialVersionUID = 129596057694162164L; + + /** + * Constructs a PropertyVetoException with a + * detailed message. + * + * @param mess Descriptive message + * @param evt A PropertyChangeEvent describing the vetoed change. + */ + public PropertyVetoException(String mess, PropertyChangeEvent evt) { + super(mess); + this.evt = evt; + } + + /** + * Gets the vetoed PropertyChangeEvent. + * + * @return A PropertyChangeEvent describing the vetoed change. + */ + public PropertyChangeEvent getPropertyChangeEvent() { + return evt; + } + + /** + * A PropertyChangeEvent describing the vetoed change. + * @serial + */ + private PropertyChangeEvent evt; +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/beans/VetoableChangeListener.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/beans/VetoableChangeListener.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 1996, 1997, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.beans; + +/** + * A VetoableChange event gets fired whenever a bean changes a "constrained" + * property. You can register a VetoableChangeListener with a source bean + * so as to be notified of any constrained property updates. + */ +public interface VetoableChangeListener extends java.util.EventListener { + /** + * This method gets called when a constrained property is changed. + * + * @param evt a PropertyChangeEvent object describing the + * event source and the property that has changed. + * @exception PropertyVetoException if the recipient wishes the property + * change to be rolled back. + */ + void vetoableChange(PropertyChangeEvent evt) + throws PropertyVetoException; +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/beans/VetoableChangeListenerProxy.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/beans/VetoableChangeListenerProxy.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2000, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.beans; + +import java.util.EventListenerProxy; + +/** + * A class which extends the {@code EventListenerProxy} + * specifically for adding a {@code VetoableChangeListener} + * with a "constrained" property. + * Instances of this class can be added + * as {@code VetoableChangeListener}s to a bean + * which supports firing vetoable change events. + *

+ * If the object has a {@code getVetoableChangeListeners} method + * then the array returned could be a mixture of {@code VetoableChangeListener} + * and {@code VetoableChangeListenerProxy} objects. + * + * @see java.util.EventListenerProxy + * @see VetoableChangeSupport#getVetoableChangeListeners + * @since 1.4 + */ +public class VetoableChangeListenerProxy + extends EventListenerProxy + implements VetoableChangeListener { + + private final String propertyName; + + /** + * Constructor which binds the {@code VetoableChangeListener} + * to a specific property. + * + * @param propertyName the name of the property to listen on + * @param listener the listener object + */ + public VetoableChangeListenerProxy(String propertyName, VetoableChangeListener listener) { + super(listener); + this.propertyName = propertyName; + } + + /** + * Forwards the property change event to the listener delegate. + * + * @param event the property change event + * + * @exception PropertyVetoException if the recipient wishes the property + * change to be rolled back + */ + public void vetoableChange(PropertyChangeEvent event) throws PropertyVetoException{ + getListener().vetoableChange(event); + } + + /** + * Returns the name of the named property associated with the listener. + * + * @return the name of the named property associated with the listener + */ + public String getPropertyName() { + return this.propertyName; + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/beans/VetoableChangeSupport.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/beans/VetoableChangeSupport.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,526 @@ +/* + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.beans; + +import java.io.Serializable; +import java.io.ObjectStreamField; +import java.io.ObjectOutputStream; +import java.io.ObjectInputStream; +import java.io.IOException; +import java.util.Hashtable; +import java.util.Map.Entry; +import org.apidesign.bck2brwsr.emul.lang.System; + +/** + * This is a utility class that can be used by beans that support constrained + * properties. It manages a list of listeners and dispatches + * {@link PropertyChangeEvent}s to them. You can use an instance of this class + * as a member field of your bean and delegate these types of work to it. + * The {@link VetoableChangeListener} can be registered for all properties + * or for a property specified by name. + *

+ * Here is an example of {@code VetoableChangeSupport} usage that follows + * the rules and recommendations laid out in the JavaBeans™ specification: + *

+ * public class MyBean {
+ *     private final VetoableChangeSupport vcs = new VetoableChangeSupport(this);
+ *
+ *     public void addVetoableChangeListener(VetoableChangeListener listener) {
+ *         this.vcs.addVetoableChangeListener(listener);
+ *     }
+ *
+ *     public void removeVetoableChangeListener(VetoableChangeListener listener) {
+ *         this.vcs.removeVetoableChangeListener(listener);
+ *     }
+ *
+ *     private String value;
+ *
+ *     public String getValue() {
+ *         return this.value;
+ *     }
+ *
+ *     public void setValue(String newValue) throws PropertyVetoException {
+ *         String oldValue = this.value;
+ *         this.vcs.fireVetoableChange("value", oldValue, newValue);
+ *         this.value = newValue;
+ *     }
+ *
+ *     [...]
+ * }
+ * 
+ *

+ * A {@code VetoableChangeSupport} instance is thread-safe. + *

+ * This class is serializable. When it is serialized it will save + * (and restore) any listeners that are themselves serializable. Any + * non-serializable listeners will be skipped during serialization. + * + * @see PropertyChangeSupport + */ +public class VetoableChangeSupport implements Serializable { + private VetoableChangeListenerMap map = new VetoableChangeListenerMap(); + + /** + * Constructs a VetoableChangeSupport object. + * + * @param sourceBean The bean to be given as the source for any events. + */ + public VetoableChangeSupport(Object sourceBean) { + if (sourceBean == null) { + throw new NullPointerException(); + } + source = sourceBean; + } + + /** + * Add a VetoableChangeListener to the listener list. + * The listener is registered for all properties. + * The same listener object may be added more than once, and will be called + * as many times as it is added. + * If listener is null, no exception is thrown and no action + * is taken. + * + * @param listener The VetoableChangeListener to be added + */ + public void addVetoableChangeListener(VetoableChangeListener listener) { + if (listener == null) { + return; + } + if (listener instanceof VetoableChangeListenerProxy) { + VetoableChangeListenerProxy proxy = + (VetoableChangeListenerProxy)listener; + // Call two argument add method. + addVetoableChangeListener(proxy.getPropertyName(), + proxy.getListener()); + } else { + this.map.add(null, listener); + } + } + + /** + * Remove a VetoableChangeListener from the listener list. + * This removes a VetoableChangeListener that was registered + * for all properties. + * If listener was added more than once to the same event + * source, it will be notified one less time after being removed. + * If listener is null, or was never added, no exception is + * thrown and no action is taken. + * + * @param listener The VetoableChangeListener to be removed + */ + public void removeVetoableChangeListener(VetoableChangeListener listener) { + if (listener == null) { + return; + } + if (listener instanceof VetoableChangeListenerProxy) { + VetoableChangeListenerProxy proxy = + (VetoableChangeListenerProxy)listener; + // Call two argument remove method. + removeVetoableChangeListener(proxy.getPropertyName(), + proxy.getListener()); + } else { + this.map.remove(null, listener); + } + } + + /** + * Returns an array of all the listeners that were added to the + * VetoableChangeSupport object with addVetoableChangeListener(). + *

+ * If some listeners have been added with a named property, then + * the returned array will be a mixture of VetoableChangeListeners + * and VetoableChangeListenerProxys. If the calling + * method is interested in distinguishing the listeners then it must + * test each element to see if it's a + * VetoableChangeListenerProxy, perform the cast, and examine + * the parameter. + * + *

+     * VetoableChangeListener[] listeners = bean.getVetoableChangeListeners();
+     * for (int i = 0; i < listeners.length; i++) {
+     *        if (listeners[i] instanceof VetoableChangeListenerProxy) {
+     *     VetoableChangeListenerProxy proxy =
+     *                    (VetoableChangeListenerProxy)listeners[i];
+     *     if (proxy.getPropertyName().equals("foo")) {
+     *       // proxy is a VetoableChangeListener which was associated
+     *       // with the property named "foo"
+     *     }
+     *   }
+     * }
+     *
+ * + * @see VetoableChangeListenerProxy + * @return all of the VetoableChangeListeners added or an + * empty array if no listeners have been added + * @since 1.4 + */ + public VetoableChangeListener[] getVetoableChangeListeners(){ + return this.map.getListeners(); + } + + /** + * Add a VetoableChangeListener for a specific property. The listener + * will be invoked only when a call on fireVetoableChange names that + * specific property. + * The same listener object may be added more than once. For each + * property, the listener will be invoked the number of times it was added + * for that property. + * If propertyName or listener is null, no + * exception is thrown and no action is taken. + * + * @param propertyName The name of the property to listen on. + * @param listener The VetoableChangeListener to be added + */ + public void addVetoableChangeListener( + String propertyName, + VetoableChangeListener listener) { + if (listener == null || propertyName == null) { + return; + } + listener = this.map.extract(listener); + if (listener != null) { + this.map.add(propertyName, listener); + } + } + + /** + * Remove a VetoableChangeListener for a specific property. + * If listener was added more than once to the same event + * source for the specified property, it will be notified one less time + * after being removed. + * If propertyName is null, no exception is thrown and no + * action is taken. + * If listener is null, or was never added for the specified + * property, no exception is thrown and no action is taken. + * + * @param propertyName The name of the property that was listened on. + * @param listener The VetoableChangeListener to be removed + */ + public void removeVetoableChangeListener( + String propertyName, + VetoableChangeListener listener) { + if (listener == null || propertyName == null) { + return; + } + listener = this.map.extract(listener); + if (listener != null) { + this.map.remove(propertyName, listener); + } + } + + /** + * Returns an array of all the listeners which have been associated + * with the named property. + * + * @param propertyName The name of the property being listened to + * @return all the VetoableChangeListeners associated with + * the named property. If no such listeners have been added, + * or if propertyName is null, an empty array is + * returned. + * @since 1.4 + */ + public VetoableChangeListener[] getVetoableChangeListeners(String propertyName) { + return this.map.getListeners(propertyName); + } + + /** + * Reports a constrained property update to listeners + * that have been registered to track updates of + * all properties or a property with the specified name. + *

+ * Any listener can throw a {@code PropertyVetoException} to veto the update. + * If one of the listeners vetoes the update, this method passes + * a new "undo" {@code PropertyChangeEvent} that reverts to the old value + * to all listeners that already confirmed this update + * and throws the {@code PropertyVetoException} again. + *

+ * No event is fired if old and new values are equal and non-null. + *

+ * This is merely a convenience wrapper around the more general + * {@link #fireVetoableChange(PropertyChangeEvent)} method. + * + * @param propertyName the programmatic name of the property that is about to change + * @param oldValue the old value of the property + * @param newValue the new value of the property + * @throws PropertyVetoException if one of listeners vetoes the property update + */ + public void fireVetoableChange(String propertyName, Object oldValue, Object newValue) + throws PropertyVetoException { + if (oldValue == null || newValue == null || !oldValue.equals(newValue)) { + fireVetoableChange(new PropertyChangeEvent(this.source, propertyName, oldValue, newValue)); + } + } + + /** + * Reports an integer constrained property update to listeners + * that have been registered to track updates of + * all properties or a property with the specified name. + *

+ * Any listener can throw a {@code PropertyVetoException} to veto the update. + * If one of the listeners vetoes the update, this method passes + * a new "undo" {@code PropertyChangeEvent} that reverts to the old value + * to all listeners that already confirmed this update + * and throws the {@code PropertyVetoException} again. + *

+ * No event is fired if old and new values are equal. + *

+ * This is merely a convenience wrapper around the more general + * {@link #fireVetoableChange(String, Object, Object)} method. + * + * @param propertyName the programmatic name of the property that is about to change + * @param oldValue the old value of the property + * @param newValue the new value of the property + * @throws PropertyVetoException if one of listeners vetoes the property update + */ + public void fireVetoableChange(String propertyName, int oldValue, int newValue) + throws PropertyVetoException { + if (oldValue != newValue) { + fireVetoableChange(propertyName, Integer.valueOf(oldValue), Integer.valueOf(newValue)); + } + } + + /** + * Reports a boolean constrained property update to listeners + * that have been registered to track updates of + * all properties or a property with the specified name. + *

+ * Any listener can throw a {@code PropertyVetoException} to veto the update. + * If one of the listeners vetoes the update, this method passes + * a new "undo" {@code PropertyChangeEvent} that reverts to the old value + * to all listeners that already confirmed this update + * and throws the {@code PropertyVetoException} again. + *

+ * No event is fired if old and new values are equal. + *

+ * This is merely a convenience wrapper around the more general + * {@link #fireVetoableChange(String, Object, Object)} method. + * + * @param propertyName the programmatic name of the property that is about to change + * @param oldValue the old value of the property + * @param newValue the new value of the property + * @throws PropertyVetoException if one of listeners vetoes the property update + */ + public void fireVetoableChange(String propertyName, boolean oldValue, boolean newValue) + throws PropertyVetoException { + if (oldValue != newValue) { + fireVetoableChange(propertyName, Boolean.valueOf(oldValue), Boolean.valueOf(newValue)); + } + } + + /** + * Fires a property change event to listeners + * that have been registered to track updates of + * all properties or a property with the specified name. + *

+ * Any listener can throw a {@code PropertyVetoException} to veto the update. + * If one of the listeners vetoes the update, this method passes + * a new "undo" {@code PropertyChangeEvent} that reverts to the old value + * to all listeners that already confirmed this update + * and throws the {@code PropertyVetoException} again. + *

+ * No event is fired if the given event's old and new values are equal and non-null. + * + * @param event the {@code PropertyChangeEvent} to be fired + * @throws PropertyVetoException if one of listeners vetoes the property update + */ + public void fireVetoableChange(PropertyChangeEvent event) + throws PropertyVetoException { + Object oldValue = event.getOldValue(); + Object newValue = event.getNewValue(); + if (oldValue == null || newValue == null || !oldValue.equals(newValue)) { + String name = event.getPropertyName(); + + VetoableChangeListener[] common = this.map.get(null); + VetoableChangeListener[] named = (name != null) + ? this.map.get(name) + : null; + + VetoableChangeListener[] listeners; + if (common == null) { + listeners = named; + } + else if (named == null) { + listeners = common; + } + else { + listeners = new VetoableChangeListener[common.length + named.length]; + System.arraycopy(common, 0, listeners, 0, common.length); + System.arraycopy(named, 0, listeners, common.length, named.length); + } + if (listeners != null) { + int current = 0; + try { + while (current < listeners.length) { + listeners[current].vetoableChange(event); + current++; + } + } + catch (PropertyVetoException veto) { + event = new PropertyChangeEvent(this.source, name, newValue, oldValue); + for (int i = 0; i < current; i++) { + try { + listeners[i].vetoableChange(event); + } + catch (PropertyVetoException exception) { + // ignore exceptions that occur during rolling back + } + } + throw veto; // rethrow the veto exception + } + } + } + } + + /** + * Check if there are any listeners for a specific property, including + * those registered on all properties. If propertyName + * is null, only check for listeners registered on all properties. + * + * @param propertyName the property name. + * @return true if there are one or more listeners for the given property + */ + public boolean hasListeners(String propertyName) { + return this.map.hasListeners(propertyName); + } + + /** + * @serialData Null terminated list of VetoableChangeListeners. + *

+ * At serialization time we skip non-serializable listeners and + * only serialize the serializable listeners. + */ + private void writeObject(ObjectOutputStream s) throws IOException { + Hashtable children = null; + VetoableChangeListener[] listeners = null; + synchronized (this.map) { + for (Entry entry : this.map.getEntries()) { + String property = entry.getKey(); + if (property == null) { + listeners = entry.getValue(); + } else { + if (children == null) { + children = new Hashtable(); + } + VetoableChangeSupport vcs = new VetoableChangeSupport(this.source); + vcs.map.set(null, entry.getValue()); + children.put(property, vcs); + } + } + } + ObjectOutputStream.PutField fields = s.putFields(); + fields.put("children", children); + fields.put("source", this.source); + fields.put("vetoableChangeSupportSerializedDataVersion", 2); + s.writeFields(); + + if (listeners != null) { + for (VetoableChangeListener l : listeners) { + if (l instanceof Serializable) { + s.writeObject(l); + } + } + } + s.writeObject(null); + } + + private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException { + this.map = new VetoableChangeListenerMap(); + + ObjectInputStream.GetField fields = s.readFields(); + + Hashtable children = (Hashtable) fields.get("children", null); + this.source = fields.get("source", null); + fields.get("vetoableChangeSupportSerializedDataVersion", 2); + + Object listenerOrNull; + while (null != (listenerOrNull = s.readObject())) { + this.map.add(null, (VetoableChangeListener)listenerOrNull); + } + if (children != null) { + for (Entry entry : children.entrySet()) { + for (VetoableChangeListener listener : entry.getValue().getVetoableChangeListeners()) { + this.map.add(entry.getKey(), listener); + } + } + } + } + + /** + * The object to be provided as the "source" for any generated events. + */ + private Object source; + + /** + * @serialField children Hashtable + * @serialField source Object + * @serialField vetoableChangeSupportSerializedDataVersion int + */ + private static final ObjectStreamField[] serialPersistentFields = { + new ObjectStreamField("children", Hashtable.class), + new ObjectStreamField("source", Object.class), + new ObjectStreamField("vetoableChangeSupportSerializedDataVersion", Integer.TYPE) + }; + + /** + * Serialization version ID, so we're compatible with JDK 1.1 + */ + static final long serialVersionUID = -5090210921595982017L; + + /** + * This is a {@link ChangeListenerMap ChangeListenerMap} implementation + * that works with {@link VetoableChangeListener VetoableChangeListener} objects. + */ + private static final class VetoableChangeListenerMap extends ChangeListenerMap { + private static final VetoableChangeListener[] EMPTY = {}; + + /** + * Creates an array of {@link VetoableChangeListener VetoableChangeListener} objects. + * This method uses the same instance of the empty array + * when {@code length} equals {@code 0}. + * + * @param length the array length + * @return an array with specified length + */ + @Override + protected VetoableChangeListener[] newArray(int length) { + return (0 < length) + ? new VetoableChangeListener[length] + : EMPTY; + } + + /** + * Creates a {@link VetoableChangeListenerProxy VetoableChangeListenerProxy} + * object for the specified property. + * + * @param name the name of the property to listen on + * @param listener the listener to process events + * @return a {@code VetoableChangeListenerProxy} object + */ + @Override + protected VetoableChangeListener newProxy(String name, VetoableChangeListener listener) { + return new VetoableChangeListenerProxy(name, listener); + } + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/Bits.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/Bits.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +/** + * Utility methods for packing/unpacking primitive values in/out of byte arrays + * using big-endian byte ordering. + */ +class Bits { + + /* + * Methods for unpacking primitive values from byte arrays starting at + * given offsets. + */ + + static boolean getBoolean(byte[] b, int off) { + return b[off] != 0; + } + + static char getChar(byte[] b, int off) { + return (char) ((b[off + 1] & 0xFF) + + (b[off] << 8)); + } + + static short getShort(byte[] b, int off) { + return (short) ((b[off + 1] & 0xFF) + + (b[off] << 8)); + } + + static int getInt(byte[] b, int off) { + return ((b[off + 3] & 0xFF) ) + + ((b[off + 2] & 0xFF) << 8) + + ((b[off + 1] & 0xFF) << 16) + + ((b[off ] ) << 24); + } + + static float getFloat(byte[] b, int off) { + return Float.intBitsToFloat(getInt(b, off)); + } + + static long getLong(byte[] b, int off) { + return ((b[off + 7] & 0xFFL) ) + + ((b[off + 6] & 0xFFL) << 8) + + ((b[off + 5] & 0xFFL) << 16) + + ((b[off + 4] & 0xFFL) << 24) + + ((b[off + 3] & 0xFFL) << 32) + + ((b[off + 2] & 0xFFL) << 40) + + ((b[off + 1] & 0xFFL) << 48) + + (((long) b[off]) << 56); + } + + static double getDouble(byte[] b, int off) { + return Double.longBitsToDouble(getLong(b, off)); + } + + /* + * Methods for packing primitive values into byte arrays starting at given + * offsets. + */ + + static void putBoolean(byte[] b, int off, boolean val) { + b[off] = (byte) (val ? 1 : 0); + } + + static void putChar(byte[] b, int off, char val) { + b[off + 1] = (byte) (val ); + b[off ] = (byte) (val >>> 8); + } + + static void putShort(byte[] b, int off, short val) { + b[off + 1] = (byte) (val ); + b[off ] = (byte) (val >>> 8); + } + + static void putInt(byte[] b, int off, int val) { + b[off + 3] = (byte) (val ); + b[off + 2] = (byte) (val >>> 8); + b[off + 1] = (byte) (val >>> 16); + b[off ] = (byte) (val >>> 24); + } + + static void putFloat(byte[] b, int off, float val) { + putInt(b, off, Float.floatToIntBits(val)); + } + + static void putLong(byte[] b, int off, long val) { + b[off + 7] = (byte) (val ); + b[off + 6] = (byte) (val >>> 8); + b[off + 5] = (byte) (val >>> 16); + b[off + 4] = (byte) (val >>> 24); + b[off + 3] = (byte) (val >>> 32); + b[off + 2] = (byte) (val >>> 40); + b[off + 1] = (byte) (val >>> 48); + b[off ] = (byte) (val >>> 56); + } + + static void putDouble(byte[] b, int off, double val) { + putLong(b, off, Double.doubleToLongBits(val)); + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/BufferedReader.java --- a/emul/compact/src/main/java/java/io/BufferedReader.java Tue Feb 05 16:40:01 2013 +0100 +++ b/emul/compact/src/main/java/java/io/BufferedReader.java Tue Feb 05 17:04:22 2013 +0100 @@ -25,7 +25,6 @@ package java.io; -import org.apidesign.bck2brwsr.emul.lang.System; /** diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/DataOutput.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/DataOutput.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,354 @@ +/* + * Copyright (c) 1995, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +/** + * The DataOutput interface provides + * for converting data from any of the Java + * primitive types to a series of bytes and + * writing these bytes to a binary stream. + * There is also a facility for converting + * a String into + * modified UTF-8 + * format and writing the resulting series + * of bytes. + *

+ * For all the methods in this interface that + * write bytes, it is generally true that if + * a byte cannot be written for any reason, + * an IOException is thrown. + * + * @author Frank Yellin + * @see java.io.DataInput + * @see java.io.DataOutputStream + * @since JDK1.0 + */ +public +interface DataOutput { + /** + * Writes to the output stream the eight + * low-order bits of the argument b. + * The 24 high-order bits of b + * are ignored. + * + * @param b the byte to be written. + * @throws IOException if an I/O error occurs. + */ + void write(int b) throws IOException; + + /** + * Writes to the output stream all the bytes in array b. + * If b is null, + * a NullPointerException is thrown. + * If b.length is zero, then + * no bytes are written. Otherwise, the byte + * b[0] is written first, then + * b[1], and so on; the last byte + * written is b[b.length-1]. + * + * @param b the data. + * @throws IOException if an I/O error occurs. + */ + void write(byte b[]) throws IOException; + + /** + * Writes len bytes from array + * b, in order, to + * the output stream. If b + * is null, a NullPointerException + * is thrown. If off is negative, + * or len is negative, or off+len + * is greater than the length of the array + * b, then an IndexOutOfBoundsException + * is thrown. If len is zero, + * then no bytes are written. Otherwise, the + * byte b[off] is written first, + * then b[off+1], and so on; the + * last byte written is b[off+len-1]. + * + * @param b the data. + * @param off the start offset in the data. + * @param len the number of bytes to write. + * @throws IOException if an I/O error occurs. + */ + void write(byte b[], int off, int len) throws IOException; + + /** + * Writes a boolean value to this output stream. + * If the argument v + * is true, the value (byte)1 + * is written; if v is false, + * the value (byte)0 is written. + * The byte written by this method may + * be read by the readBoolean + * method of interface DataInput, + * which will then return a boolean + * equal to v. + * + * @param v the boolean to be written. + * @throws IOException if an I/O error occurs. + */ + void writeBoolean(boolean v) throws IOException; + + /** + * Writes to the output stream the eight low- + * order bits of the argument v. + * The 24 high-order bits of v + * are ignored. (This means that writeByte + * does exactly the same thing as write + * for an integer argument.) The byte written + * by this method may be read by the readByte + * method of interface DataInput, + * which will then return a byte + * equal to (byte)v. + * + * @param v the byte value to be written. + * @throws IOException if an I/O error occurs. + */ + void writeByte(int v) throws IOException; + + /** + * Writes two bytes to the output + * stream to represent the value of the argument. + * The byte values to be written, in the order + * shown, are:

+ *


+     * (byte)(0xff & (v >> 8))
+     * (byte)(0xff & v)
+     *  

+ * The bytes written by this method may be + * read by the readShort method + * of interface DataInput , which + * will then return a short equal + * to (short)v. + * + * @param v the short value to be written. + * @throws IOException if an I/O error occurs. + */ + void writeShort(int v) throws IOException; + + /** + * Writes a char value, which + * is comprised of two bytes, to the + * output stream. + * The byte values to be written, in the order + * shown, are: + *


+     * (byte)(0xff & (v >> 8))
+     * (byte)(0xff & v)
+     * 

+ * The bytes written by this method may be + * read by the readChar method + * of interface DataInput , which + * will then return a char equal + * to (char)v. + * + * @param v the char value to be written. + * @throws IOException if an I/O error occurs. + */ + void writeChar(int v) throws IOException; + + /** + * Writes an int value, which is + * comprised of four bytes, to the output stream. + * The byte values to be written, in the order + * shown, are: + *


+     * (byte)(0xff & (v >> 24))
+     * (byte)(0xff & (v >> 16))
+     * (byte)(0xff & (v >>    8))
+     * (byte)(0xff & v)
+     * 

+ * The bytes written by this method may be read + * by the readInt method of interface + * DataInput , which will then + * return an int equal to v. + * + * @param v the int value to be written. + * @throws IOException if an I/O error occurs. + */ + void writeInt(int v) throws IOException; + + /** + * Writes a long value, which is + * comprised of eight bytes, to the output stream. + * The byte values to be written, in the order + * shown, are: + *


+     * (byte)(0xff & (v >> 56))
+     * (byte)(0xff & (v >> 48))
+     * (byte)(0xff & (v >> 40))
+     * (byte)(0xff & (v >> 32))
+     * (byte)(0xff & (v >> 24))
+     * (byte)(0xff & (v >> 16))
+     * (byte)(0xff & (v >>  8))
+     * (byte)(0xff & v)
+     * 

+ * The bytes written by this method may be + * read by the readLong method + * of interface DataInput , which + * will then return a long equal + * to v. + * + * @param v the long value to be written. + * @throws IOException if an I/O error occurs. + */ + void writeLong(long v) throws IOException; + + /** + * Writes a float value, + * which is comprised of four bytes, to the output stream. + * It does this as if it first converts this + * float value to an int + * in exactly the manner of the Float.floatToIntBits + * method and then writes the int + * value in exactly the manner of the writeInt + * method. The bytes written by this method + * may be read by the readFloat + * method of interface DataInput, + * which will then return a float + * equal to v. + * + * @param v the float value to be written. + * @throws IOException if an I/O error occurs. + */ + void writeFloat(float v) throws IOException; + + /** + * Writes a double value, + * which is comprised of eight bytes, to the output stream. + * It does this as if it first converts this + * double value to a long + * in exactly the manner of the Double.doubleToLongBits + * method and then writes the long + * value in exactly the manner of the writeLong + * method. The bytes written by this method + * may be read by the readDouble + * method of interface DataInput, + * which will then return a double + * equal to v. + * + * @param v the double value to be written. + * @throws IOException if an I/O error occurs. + */ + void writeDouble(double v) throws IOException; + + /** + * Writes a string to the output stream. + * For every character in the string + * s, taken in order, one byte + * is written to the output stream. If + * s is null, a NullPointerException + * is thrown.

If s.length + * is zero, then no bytes are written. Otherwise, + * the character s[0] is written + * first, then s[1], and so on; + * the last character written is s[s.length-1]. + * For each character, one byte is written, + * the low-order byte, in exactly the manner + * of the writeByte method . The + * high-order eight bits of each character + * in the string are ignored. + * + * @param s the string of bytes to be written. + * @throws IOException if an I/O error occurs. + */ + void writeBytes(String s) throws IOException; + + /** + * Writes every character in the string s, + * to the output stream, in order, + * two bytes per character. If s + * is null, a NullPointerException + * is thrown. If s.length + * is zero, then no characters are written. + * Otherwise, the character s[0] + * is written first, then s[1], + * and so on; the last character written is + * s[s.length-1]. For each character, + * two bytes are actually written, high-order + * byte first, in exactly the manner of the + * writeChar method. + * + * @param s the string value to be written. + * @throws IOException if an I/O error occurs. + */ + void writeChars(String s) throws IOException; + + /** + * Writes two bytes of length information + * to the output stream, followed + * by the + * modified UTF-8 + * representation + * of every character in the string s. + * If s is null, + * a NullPointerException is thrown. + * Each character in the string s + * is converted to a group of one, two, or + * three bytes, depending on the value of the + * character.

+ * If a character c + * is in the range \u0001 through + * \u007f, it is represented + * by one byte:

+ *

(byte)c 

+ * If a character c is \u0000 + * or is in the range \u0080 + * through \u07ff, then it is + * represented by two bytes, to be written + * in the order shown:


+     * (byte)(0xc0 | (0x1f & (c >> 6)))
+     * (byte)(0x80 | (0x3f & c))
+     *  

If a character + * c is in the range \u0800 + * through uffff, then it is + * represented by three bytes, to be written + * in the order shown:


+     * (byte)(0xe0 | (0x0f & (c >> 12)))
+     * (byte)(0x80 | (0x3f & (c >>  6)))
+     * (byte)(0x80 | (0x3f & c))
+     *  

First, + * the total number of bytes needed to represent + * all the characters of s is + * calculated. If this number is larger than + * 65535, then a UTFDataFormatException + * is thrown. Otherwise, this length is written + * to the output stream in exactly the manner + * of the writeShort method; + * after this, the one-, two-, or three-byte + * representation of each character in the + * string s is written.

The + * bytes written by this method may be read + * by the readUTF method of interface + * DataInput , which will then + * return a String equal to s. + * + * @param s the string value to be written. + * @throws IOException if an I/O error occurs. + */ + void writeUTF(String s) throws IOException; +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/DataOutputStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/DataOutputStream.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,416 @@ +/* + * Copyright (c) 1994, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +/** + * A data output stream lets an application write primitive Java data + * types to an output stream in a portable way. An application can + * then use a data input stream to read the data back in. + * + * @author unascribed + * @see java.io.DataInputStream + * @since JDK1.0 + */ +public +class DataOutputStream extends FilterOutputStream implements DataOutput { + /** + * The number of bytes written to the data output stream so far. + * If this counter overflows, it will be wrapped to Integer.MAX_VALUE. + */ + protected int written; + + /** + * bytearr is initialized on demand by writeUTF + */ + private byte[] bytearr = null; + + /** + * Creates a new data output stream to write data to the specified + * underlying output stream. The counter written is + * set to zero. + * + * @param out the underlying output stream, to be saved for later + * use. + * @see java.io.FilterOutputStream#out + */ + public DataOutputStream(OutputStream out) { + super(out); + } + + /** + * Increases the written counter by the specified value + * until it reaches Integer.MAX_VALUE. + */ + private void incCount(int value) { + int temp = written + value; + if (temp < 0) { + temp = Integer.MAX_VALUE; + } + written = temp; + } + + /** + * Writes the specified byte (the low eight bits of the argument + * b) to the underlying output stream. If no exception + * is thrown, the counter written is incremented by + * 1. + *

+ * Implements the write method of OutputStream. + * + * @param b the byte to be written. + * @exception IOException if an I/O error occurs. + * @see java.io.FilterOutputStream#out + */ + public synchronized void write(int b) throws IOException { + out.write(b); + incCount(1); + } + + /** + * Writes len bytes from the specified byte array + * starting at offset off to the underlying output stream. + * If no exception is thrown, the counter written is + * incremented by len. + * + * @param b the data. + * @param off the start offset in the data. + * @param len the number of bytes to write. + * @exception IOException if an I/O error occurs. + * @see java.io.FilterOutputStream#out + */ + public synchronized void write(byte b[], int off, int len) + throws IOException + { + out.write(b, off, len); + incCount(len); + } + + /** + * Flushes this data output stream. This forces any buffered output + * bytes to be written out to the stream. + *

+ * The flush method of DataOutputStream + * calls the flush method of its underlying output stream. + * + * @exception IOException if an I/O error occurs. + * @see java.io.FilterOutputStream#out + * @see java.io.OutputStream#flush() + */ + public void flush() throws IOException { + out.flush(); + } + + /** + * Writes a boolean to the underlying output stream as + * a 1-byte value. The value true is written out as the + * value (byte)1; the value false is + * written out as the value (byte)0. If no exception is + * thrown, the counter written is incremented by + * 1. + * + * @param v a boolean value to be written. + * @exception IOException if an I/O error occurs. + * @see java.io.FilterOutputStream#out + */ + public final void writeBoolean(boolean v) throws IOException { + out.write(v ? 1 : 0); + incCount(1); + } + + /** + * Writes out a byte to the underlying output stream as + * a 1-byte value. If no exception is thrown, the counter + * written is incremented by 1. + * + * @param v a byte value to be written. + * @exception IOException if an I/O error occurs. + * @see java.io.FilterOutputStream#out + */ + public final void writeByte(int v) throws IOException { + out.write(v); + incCount(1); + } + + /** + * Writes a short to the underlying output stream as two + * bytes, high byte first. If no exception is thrown, the counter + * written is incremented by 2. + * + * @param v a short to be written. + * @exception IOException if an I/O error occurs. + * @see java.io.FilterOutputStream#out + */ + public final void writeShort(int v) throws IOException { + out.write((v >>> 8) & 0xFF); + out.write((v >>> 0) & 0xFF); + incCount(2); + } + + /** + * Writes a char to the underlying output stream as a + * 2-byte value, high byte first. If no exception is thrown, the + * counter written is incremented by 2. + * + * @param v a char value to be written. + * @exception IOException if an I/O error occurs. + * @see java.io.FilterOutputStream#out + */ + public final void writeChar(int v) throws IOException { + out.write((v >>> 8) & 0xFF); + out.write((v >>> 0) & 0xFF); + incCount(2); + } + + /** + * Writes an int to the underlying output stream as four + * bytes, high byte first. If no exception is thrown, the counter + * written is incremented by 4. + * + * @param v an int to be written. + * @exception IOException if an I/O error occurs. + * @see java.io.FilterOutputStream#out + */ + public final void writeInt(int v) throws IOException { + out.write((v >>> 24) & 0xFF); + out.write((v >>> 16) & 0xFF); + out.write((v >>> 8) & 0xFF); + out.write((v >>> 0) & 0xFF); + incCount(4); + } + + private byte writeBuffer[] = new byte[8]; + + /** + * Writes a long to the underlying output stream as eight + * bytes, high byte first. In no exception is thrown, the counter + * written is incremented by 8. + * + * @param v a long to be written. + * @exception IOException if an I/O error occurs. + * @see java.io.FilterOutputStream#out + */ + public final void writeLong(long v) throws IOException { + writeBuffer[0] = (byte)(v >>> 56); + writeBuffer[1] = (byte)(v >>> 48); + writeBuffer[2] = (byte)(v >>> 40); + writeBuffer[3] = (byte)(v >>> 32); + writeBuffer[4] = (byte)(v >>> 24); + writeBuffer[5] = (byte)(v >>> 16); + writeBuffer[6] = (byte)(v >>> 8); + writeBuffer[7] = (byte)(v >>> 0); + out.write(writeBuffer, 0, 8); + incCount(8); + } + + /** + * Converts the float argument to an int using the + * floatToIntBits method in class Float, + * and then writes that int value to the underlying + * output stream as a 4-byte quantity, high byte first. If no + * exception is thrown, the counter written is + * incremented by 4. + * + * @param v a float value to be written. + * @exception IOException if an I/O error occurs. + * @see java.io.FilterOutputStream#out + * @see java.lang.Float#floatToIntBits(float) + */ + public final void writeFloat(float v) throws IOException { + writeInt(Float.floatToIntBits(v)); + } + + /** + * Converts the double argument to a long using the + * doubleToLongBits method in class Double, + * and then writes that long value to the underlying + * output stream as an 8-byte quantity, high byte first. If no + * exception is thrown, the counter written is + * incremented by 8. + * + * @param v a double value to be written. + * @exception IOException if an I/O error occurs. + * @see java.io.FilterOutputStream#out + * @see java.lang.Double#doubleToLongBits(double) + */ + public final void writeDouble(double v) throws IOException { + writeLong(Double.doubleToLongBits(v)); + } + + /** + * Writes out the string to the underlying output stream as a + * sequence of bytes. Each character in the string is written out, in + * sequence, by discarding its high eight bits. If no exception is + * thrown, the counter written is incremented by the + * length of s. + * + * @param s a string of bytes to be written. + * @exception IOException if an I/O error occurs. + * @see java.io.FilterOutputStream#out + */ + public final void writeBytes(String s) throws IOException { + int len = s.length(); + for (int i = 0 ; i < len ; i++) { + out.write((byte)s.charAt(i)); + } + incCount(len); + } + + /** + * Writes a string to the underlying output stream as a sequence of + * characters. Each character is written to the data output stream as + * if by the writeChar method. If no exception is + * thrown, the counter written is incremented by twice + * the length of s. + * + * @param s a String value to be written. + * @exception IOException if an I/O error occurs. + * @see java.io.DataOutputStream#writeChar(int) + * @see java.io.FilterOutputStream#out + */ + public final void writeChars(String s) throws IOException { + int len = s.length(); + for (int i = 0 ; i < len ; i++) { + int v = s.charAt(i); + out.write((v >>> 8) & 0xFF); + out.write((v >>> 0) & 0xFF); + } + incCount(len * 2); + } + + /** + * Writes a string to the underlying output stream using + * modified UTF-8 + * encoding in a machine-independent manner. + *

+ * First, two bytes are written to the output stream as if by the + * writeShort method giving the number of bytes to + * follow. This value is the number of bytes actually written out, + * not the length of the string. Following the length, each character + * of the string is output, in sequence, using the modified UTF-8 encoding + * for the character. If no exception is thrown, the counter + * written is incremented by the total number of + * bytes written to the output stream. This will be at least two + * plus the length of str, and at most two plus + * thrice the length of str. + * + * @param str a string to be written. + * @exception IOException if an I/O error occurs. + */ + public final void writeUTF(String str) throws IOException { + writeUTF(str, this); + } + + /** + * Writes a string to the specified DataOutput using + * modified UTF-8 + * encoding in a machine-independent manner. + *

+ * First, two bytes are written to out as if by the writeShort + * method giving the number of bytes to follow. This value is the number of + * bytes actually written out, not the length of the string. Following the + * length, each character of the string is output, in sequence, using the + * modified UTF-8 encoding for the character. If no exception is thrown, the + * counter written is incremented by the total number of + * bytes written to the output stream. This will be at least two + * plus the length of str, and at most two plus + * thrice the length of str. + * + * @param str a string to be written. + * @param out destination to write to + * @return The number of bytes written out. + * @exception IOException if an I/O error occurs. + */ + static int writeUTF(String str, DataOutput out) throws IOException { + int strlen = str.length(); + int utflen = 0; + int c, count = 0; + + /* use charAt instead of copying String to char array */ + for (int i = 0; i < strlen; i++) { + c = str.charAt(i); + if ((c >= 0x0001) && (c <= 0x007F)) { + utflen++; + } else if (c > 0x07FF) { + utflen += 3; + } else { + utflen += 2; + } + } + + if (utflen > 65535) + throw new UTFDataFormatException( + "encoded string too long: " + utflen + " bytes"); + + byte[] bytearr = null; + if (out instanceof DataOutputStream) { + DataOutputStream dos = (DataOutputStream)out; + if(dos.bytearr == null || (dos.bytearr.length < (utflen+2))) + dos.bytearr = new byte[(utflen*2) + 2]; + bytearr = dos.bytearr; + } else { + bytearr = new byte[utflen+2]; + } + + bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF); + bytearr[count++] = (byte) ((utflen >>> 0) & 0xFF); + + int i=0; + for (i=0; i= 0x0001) && (c <= 0x007F))) break; + bytearr[count++] = (byte) c; + } + + for (;i < strlen; i++){ + c = str.charAt(i); + if ((c >= 0x0001) && (c <= 0x007F)) { + bytearr[count++] = (byte) c; + + } else if (c > 0x07FF) { + bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F)); + bytearr[count++] = (byte) (0x80 | ((c >> 6) & 0x3F)); + bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F)); + } else { + bytearr[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F)); + bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F)); + } + } + out.write(bytearr, 0, utflen+2); + return utflen + 2; + } + + /** + * Returns the current value of the counter written, + * the number of bytes written to this data output stream so far. + * If the counter overflows, it will be wrapped to Integer.MAX_VALUE. + * + * @return the value of the written field. + * @see java.io.DataOutputStream#written + */ + public final int size() { + return written; + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/Externalizable.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/Externalizable.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 1996, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +import java.io.ObjectOutput; +import java.io.ObjectInput; + +/** + * Only the identity of the class of an Externalizable instance is + * written in the serialization stream and it is the responsibility + * of the class to save and restore the contents of its instances. + * + * The writeExternal and readExternal methods of the Externalizable + * interface are implemented by a class to give the class complete + * control over the format and contents of the stream for an object + * and its supertypes. These methods must explicitly + * coordinate with the supertype to save its state. These methods supersede + * customized implementations of writeObject and readObject methods.
+ * + * Object Serialization uses the Serializable and Externalizable + * interfaces. Object persistence mechanisms can use them as well. Each + * object to be stored is tested for the Externalizable interface. If + * the object supports Externalizable, the writeExternal method is called. If the + * object does not support Externalizable and does implement + * Serializable, the object is saved using + * ObjectOutputStream.
When an Externalizable object is + * reconstructed, an instance is created using the public no-arg + * constructor, then the readExternal method called. Serializable + * objects are restored by reading them from an ObjectInputStream.
+ * + * An Externalizable instance can designate a substitution object via + * the writeReplace and readResolve methods documented in the Serializable + * interface.
+ * + * @author unascribed + * @see java.io.ObjectOutputStream + * @see java.io.ObjectInputStream + * @see java.io.ObjectOutput + * @see java.io.ObjectInput + * @see java.io.Serializable + * @since JDK1.1 + */ +public interface Externalizable extends java.io.Serializable { + /** + * The object implements the writeExternal method to save its contents + * by calling the methods of DataOutput for its primitive values or + * calling the writeObject method of ObjectOutput for objects, strings, + * and arrays. + * + * @serialData Overriding methods should use this tag to describe + * the data layout of this Externalizable object. + * List the sequence of element types and, if possible, + * relate the element to a public/protected field and/or + * method of this Externalizable class. + * + * @param out the stream to write the object to + * @exception IOException Includes any I/O exceptions that may occur + */ + void writeExternal(ObjectOutput out) throws IOException; + + /** + * The object implements the readExternal method to restore its + * contents by calling the methods of DataInput for primitive + * types and readObject for objects, strings and arrays. The + * readExternal method must read the values in the same sequence + * and with the same types as were written by writeExternal. + * + * @param in the stream to read data from in order to restore the object + * @exception IOException if I/O errors occur + * @exception ClassNotFoundException If the class for an object being + * restored cannot be found. + */ + void readExternal(ObjectInput in) throws IOException, ClassNotFoundException; +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/FilterOutputStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/FilterOutputStream.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,162 @@ +/* + * Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +/** + * This class is the superclass of all classes that filter output + * streams. These streams sit on top of an already existing output + * stream (the underlying output stream) which it uses as its + * basic sink of data, but possibly transforming the data along the + * way or providing additional functionality. + *

+ * The class FilterOutputStream itself simply overrides + * all methods of OutputStream with versions that pass + * all requests to the underlying output stream. Subclasses of + * FilterOutputStream may further override some of these + * methods as well as provide additional methods and fields. + * + * @author Jonathan Payne + * @since JDK1.0 + */ +public +class FilterOutputStream extends OutputStream { + /** + * The underlying output stream to be filtered. + */ + protected OutputStream out; + + /** + * Creates an output stream filter built on top of the specified + * underlying output stream. + * + * @param out the underlying output stream to be assigned to + * the field this.out for later use, or + * null if this instance is to be + * created without an underlying stream. + */ + public FilterOutputStream(OutputStream out) { + this.out = out; + } + + /** + * Writes the specified byte to this output stream. + *

+ * The write method of FilterOutputStream + * calls the write method of its underlying output stream, + * that is, it performs out.write(b). + *

+ * Implements the abstract write method of OutputStream. + * + * @param b the byte. + * @exception IOException if an I/O error occurs. + */ + public void write(int b) throws IOException { + out.write(b); + } + + /** + * Writes b.length bytes to this output stream. + *

+ * The write method of FilterOutputStream + * calls its write method of three arguments with the + * arguments b, 0, and + * b.length. + *

+ * Note that this method does not call the one-argument + * write method of its underlying stream with the single + * argument b. + * + * @param b the data to be written. + * @exception IOException if an I/O error occurs. + * @see java.io.FilterOutputStream#write(byte[], int, int) + */ + public void write(byte b[]) throws IOException { + write(b, 0, b.length); + } + + /** + * Writes len bytes from the specified + * byte array starting at offset off to + * this output stream. + *

+ * The write method of FilterOutputStream + * calls the write method of one argument on each + * byte to output. + *

+ * Note that this method does not call the write method + * of its underlying input stream with the same arguments. Subclasses + * of FilterOutputStream should provide a more efficient + * implementation of this method. + * + * @param b the data. + * @param off the start offset in the data. + * @param len the number of bytes to write. + * @exception IOException if an I/O error occurs. + * @see java.io.FilterOutputStream#write(int) + */ + public void write(byte b[], int off, int len) throws IOException { + if ((off | len | (b.length - (len + off)) | (off + len)) < 0) + throw new IndexOutOfBoundsException(); + + for (int i = 0 ; i < len ; i++) { + write(b[off + i]); + } + } + + /** + * Flushes this output stream and forces any buffered output bytes + * to be written out to the stream. + *

+ * The flush method of FilterOutputStream + * calls the flush method of its underlying output stream. + * + * @exception IOException if an I/O error occurs. + * @see java.io.FilterOutputStream#out + */ + public void flush() throws IOException { + out.flush(); + } + + /** + * Closes this output stream and releases any system resources + * associated with the stream. + *

+ * The close method of FilterOutputStream + * calls its flush method, and then calls the + * close method of its underlying output stream. + * + * @exception IOException if an I/O error occurs. + * @see java.io.FilterOutputStream#flush() + * @see java.io.FilterOutputStream#out + */ + public void close() throws IOException { + try { + flush(); + } catch (IOException ignored) { + } + out.close(); + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/Flushable.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/Flushable.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +import java.io.IOException; + +/** + * A Flushable is a destination of data that can be flushed. The + * flush method is invoked to write any buffered output to the underlying + * stream. + * + * @since 1.5 + */ + +public interface Flushable { + + /** + * Flushes this stream by writing any buffered output to the underlying + * stream. + * + * @throws IOException If an I/O error occurs + */ + void flush() throws IOException; +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/InputStreamReader.java --- a/emul/compact/src/main/java/java/io/InputStreamReader.java Tue Feb 05 16:40:01 2013 +0100 +++ b/emul/compact/src/main/java/java/io/InputStreamReader.java Tue Feb 05 17:04:22 2013 +0100 @@ -156,9 +156,39 @@ * @exception IOException If an I/O error occurs */ public int read() throws IOException { - return ((InputStream)lock).read(); + final InputStream is = (InputStream)lock; + int c = is.read(); + if (c == -1) { + return -1; + } + c = (int) c & 0xff; + switch (c >> 4) { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + /* 0xxxxxxx*/ + return c; + case 12: case 13: { + /* 110x xxxx 10xx xxxx*/ + int char2 = (int) is.read(); + if ((char2 & 0xC0) != 0x80) + throw new UTFDataFormatException("malformed input"); + return (((c & 0x1F) << 6) | (char2 & 0x3F)); + } + case 14: { + /* 1110 xxxx 10xx xxxx 10xx xxxx */ + int char2 = is.read(); + int char3 = is.read(); + if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) + throw new UTFDataFormatException("malformed input"); + return (((c & 0x0F) << 12) | + ((char2 & 0x3F) << 6) | + ((char3 & 0x3F) << 0)); + } + default: + /* 10xx xxxx, 1111 xxxx */ + throw new UTFDataFormatException("malformed input"); + } } - + /** * Reads characters into a portion of an array. * diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/InvalidClassException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/InvalidClassException.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +/** + * Thrown when the Serialization runtime detects one of the following + * problems with a Class. + *

    + *
  • The serial version of the class does not match that of the class + * descriptor read from the stream + *
  • The class contains unknown datatypes + *
  • The class does not have an accessible no-arg constructor + *
+ * + * @author unascribed + * @since JDK1.1 + */ +public class InvalidClassException extends ObjectStreamException { + + private static final long serialVersionUID = -4333316296251054416L; + + /** + * Name of the invalid class. + * + * @serial Name of the invalid class. + */ + public String classname; + + /** + * Report an InvalidClassException for the reason specified. + * + * @param reason String describing the reason for the exception. + */ + public InvalidClassException(String reason) { + super(reason); + } + + /** + * Constructs an InvalidClassException object. + * + * @param cname a String naming the invalid class. + * @param reason a String describing the reason for the exception. + */ + public InvalidClassException(String cname, String reason) { + super(reason); + classname = cname; + } + + /** + * Produce the message and include the classname, if present. + */ + public String getMessage() { + if (classname == null) + return super.getMessage(); + else + return classname + "; " + super.getMessage(); + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/InvalidObjectException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/InvalidObjectException.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +/** + * Indicates that one or more deserialized objects failed validation + * tests. The argument should provide the reason for the failure. + * + * @see ObjectInputValidation + * @since JDK1.1 + * + * @author unascribed + * @since JDK1.1 + */ +public class InvalidObjectException extends ObjectStreamException { + + private static final long serialVersionUID = 3233174318281839583L; + + /** + * Constructs an InvalidObjectException. + * @param reason Detailed message explaining the reason for the failure. + * + * @see ObjectInputValidation + */ + public InvalidObjectException(String reason) { + super(reason); + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/NotActiveException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/NotActiveException.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +/** + * Thrown when serialization or deserialization is not active. + * + * @author unascribed + * @since JDK1.1 + */ +public class NotActiveException extends ObjectStreamException { + + private static final long serialVersionUID = -3893467273049808895L; + + /** + * Constructor to create a new NotActiveException with the reason given. + * + * @param reason a String describing the reason for the exception. + */ + public NotActiveException(String reason) { + super(reason); + } + + /** + * Constructor to create a new NotActiveException without a reason. + */ + public NotActiveException() { + super(); + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/NotSerializableException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/NotSerializableException.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +/** + * Thrown when an instance is required to have a Serializable interface. + * The serialization runtime or the class of the instance can throw + * this exception. The argument should be the name of the class. + * + * @author unascribed + * @since JDK1.1 + */ +public class NotSerializableException extends ObjectStreamException { + + private static final long serialVersionUID = 2906642554793891381L; + + /** + * Constructs a NotSerializableException object with message string. + * + * @param classname Class of the instance being serialized/deserialized. + */ + public NotSerializableException(String classname) { + super(classname); + } + + /** + * Constructs a NotSerializableException object. + */ + public NotSerializableException() { + super(); + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/ObjectInput.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/ObjectInput.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,107 @@ +/* + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +/** + * ObjectInput extends the DataInput interface to include the reading of + * objects. DataInput includes methods for the input of primitive types, + * ObjectInput extends that interface to include objects, arrays, and Strings. + * + * @author unascribed + * @see java.io.InputStream + * @see java.io.ObjectOutputStream + * @see java.io.ObjectInputStream + * @since JDK1.1 + */ +public interface ObjectInput extends DataInput, AutoCloseable { + /** + * Read and return an object. The class that implements this interface + * defines where the object is "read" from. + * + * @return the object read from the stream + * @exception java.lang.ClassNotFoundException If the class of a serialized + * object cannot be found. + * @exception IOException If any of the usual Input/Output + * related exceptions occur. + */ + public Object readObject() + throws ClassNotFoundException, IOException; + + /** + * Reads a byte of data. This method will block if no input is + * available. + * @return the byte read, or -1 if the end of the + * stream is reached. + * @exception IOException If an I/O error has occurred. + */ + public int read() throws IOException; + + /** + * Reads into an array of bytes. This method will + * block until some input is available. + * @param b the buffer into which the data is read + * @return the actual number of bytes read, -1 is + * returned when the end of the stream is reached. + * @exception IOException If an I/O error has occurred. + */ + public int read(byte b[]) throws IOException; + + /** + * Reads into an array of bytes. This method will + * block until some input is available. + * @param b the buffer into which the data is read + * @param off the start offset of the data + * @param len the maximum number of bytes read + * @return the actual number of bytes read, -1 is + * returned when the end of the stream is reached. + * @exception IOException If an I/O error has occurred. + */ + public int read(byte b[], int off, int len) throws IOException; + + /** + * Skips n bytes of input. + * @param n the number of bytes to be skipped + * @return the actual number of bytes skipped. + * @exception IOException If an I/O error has occurred. + */ + public long skip(long n) throws IOException; + + /** + * Returns the number of bytes that can be read + * without blocking. + * @return the number of available bytes. + * @exception IOException If an I/O error has occurred. + */ + public int available() throws IOException; + + /** + * Closes the input stream. Must be called + * to release any resources associated with + * the stream. + * @exception IOException If an I/O error has occurred. + */ + public void close() throws IOException; +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/ObjectInputStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/ObjectInputStream.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,3357 @@ +/* + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +import java.lang.reflect.Array; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; +import java.util.Arrays; +import java.util.HashMap; +import org.apidesign.bck2brwsr.emul.lang.System; + +/** + * An ObjectInputStream deserializes primitive data and objects previously + * written using an ObjectOutputStream. + * + *

ObjectOutputStream and ObjectInputStream can provide an application with + * persistent storage for graphs of objects when used with a FileOutputStream + * and FileInputStream respectively. ObjectInputStream is used to recover + * those objects previously serialized. Other uses include passing objects + * between hosts using a socket stream or for marshaling and unmarshaling + * arguments and parameters in a remote communication system. + * + *

ObjectInputStream ensures that the types of all objects in the graph + * created from the stream match the classes present in the Java Virtual + * Machine. Classes are loaded as required using the standard mechanisms. + * + *

Only objects that support the java.io.Serializable or + * java.io.Externalizable interface can be read from streams. + * + *

The method readObject is used to read an object from the + * stream. Java's safe casting should be used to get the desired type. In + * Java, strings and arrays are objects and are treated as objects during + * serialization. When read they need to be cast to the expected type. + * + *

Primitive data types can be read from the stream using the appropriate + * method on DataInput. + * + *

The default deserialization mechanism for objects restores the contents + * of each field to the value and type it had when it was written. Fields + * declared as transient or static are ignored by the deserialization process. + * References to other objects cause those objects to be read from the stream + * as necessary. Graphs of objects are restored correctly using a reference + * sharing mechanism. New objects are always allocated when deserializing, + * which prevents existing objects from being overwritten. + * + *

Reading an object is analogous to running the constructors of a new + * object. Memory is allocated for the object and initialized to zero (NULL). + * No-arg constructors are invoked for the non-serializable classes and then + * the fields of the serializable classes are restored from the stream starting + * with the serializable class closest to java.lang.object and finishing with + * the object's most specific class. + * + *

For example to read from a stream as written by the example in + * ObjectOutputStream: + *
+ *

+ *      FileInputStream fis = new FileInputStream("t.tmp");
+ *      ObjectInputStream ois = new ObjectInputStream(fis);
+ *
+ *      int i = ois.readInt();
+ *      String today = (String) ois.readObject();
+ *      Date date = (Date) ois.readObject();
+ *
+ *      ois.close();
+ * 
+ * + *

Classes control how they are serialized by implementing either the + * java.io.Serializable or java.io.Externalizable interfaces. + * + *

Implementing the Serializable interface allows object serialization to + * save and restore the entire state of the object and it allows classes to + * evolve between the time the stream is written and the time it is read. It + * automatically traverses references between objects, saving and restoring + * entire graphs. + * + *

Serializable classes that require special handling during the + * serialization and deserialization process should implement the following + * methods:

+ * + *

+ * private void writeObject(java.io.ObjectOutputStream stream)
+ *     throws IOException;
+ * private void readObject(java.io.ObjectInputStream stream)
+ *     throws IOException, ClassNotFoundException;
+ * private void readObjectNoData()
+ *     throws ObjectStreamException;
+ * 
+ * + *

The readObject method is responsible for reading and restoring the state + * of the object for its particular class using data written to the stream by + * the corresponding writeObject method. The method does not need to concern + * itself with the state belonging to its superclasses or subclasses. State is + * restored by reading data from the ObjectInputStream for the individual + * fields and making assignments to the appropriate fields of the object. + * Reading primitive data types is supported by DataInput. + * + *

Any attempt to read object data which exceeds the boundaries of the + * custom data written by the corresponding writeObject method will cause an + * OptionalDataException to be thrown with an eof field value of true. + * Non-object reads which exceed the end of the allotted data will reflect the + * end of data in the same way that they would indicate the end of the stream: + * bytewise reads will return -1 as the byte read or number of bytes read, and + * primitive reads will throw EOFExceptions. If there is no corresponding + * writeObject method, then the end of default serialized data marks the end of + * the allotted data. + * + *

Primitive and object read calls issued from within a readExternal method + * behave in the same manner--if the stream is already positioned at the end of + * data written by the corresponding writeExternal method, object reads will + * throw OptionalDataExceptions with eof set to true, bytewise reads will + * return -1, and primitive reads will throw EOFExceptions. Note that this + * behavior does not hold for streams written with the old + * ObjectStreamConstants.PROTOCOL_VERSION_1 protocol, in which the + * end of data written by writeExternal methods is not demarcated, and hence + * cannot be detected. + * + *

The readObjectNoData method is responsible for initializing the state of + * the object for its particular class in the event that the serialization + * stream does not list the given class as a superclass of the object being + * deserialized. This may occur in cases where the receiving party uses a + * different version of the deserialized instance's class than the sending + * party, and the receiver's version extends classes that are not extended by + * the sender's version. This may also occur if the serialization stream has + * been tampered; hence, readObjectNoData is useful for initializing + * deserialized objects properly despite a "hostile" or incomplete source + * stream. + * + *

Serialization does not read or assign values to the fields of any object + * that does not implement the java.io.Serializable interface. Subclasses of + * Objects that are not serializable can be serializable. In this case the + * non-serializable class must have a no-arg constructor to allow its fields to + * be initialized. In this case it is the responsibility of the subclass to + * save and restore the state of the non-serializable class. It is frequently + * the case that the fields of that class are accessible (public, package, or + * protected) or that there are get and set methods that can be used to restore + * the state. + * + *

Any exception that occurs while deserializing an object will be caught by + * the ObjectInputStream and abort the reading process. + * + *

Implementing the Externalizable interface allows the object to assume + * complete control over the contents and format of the object's serialized + * form. The methods of the Externalizable interface, writeExternal and + * readExternal, are called to save and restore the objects state. When + * implemented by a class they can write and read their own state using all of + * the methods of ObjectOutput and ObjectInput. It is the responsibility of + * the objects to handle any versioning that occurs. + * + *

Enum constants are deserialized differently than ordinary serializable or + * externalizable objects. The serialized form of an enum constant consists + * solely of its name; field values of the constant are not transmitted. To + * deserialize an enum constant, ObjectInputStream reads the constant name from + * the stream; the deserialized constant is then obtained by calling the static + * method Enum.valueOf(Class, String) with the enum constant's + * base type and the received constant name as arguments. Like other + * serializable or externalizable objects, enum constants can function as the + * targets of back references appearing subsequently in the serialization + * stream. The process by which enum constants are deserialized cannot be + * customized: any class-specific readObject, readObjectNoData, and readResolve + * methods defined by enum types are ignored during deserialization. + * Similarly, any serialPersistentFields or serialVersionUID field declarations + * are also ignored--all enum types have a fixed serialVersionUID of 0L. + * + * @author Mike Warres + * @author Roger Riggs + * @see java.io.DataInput + * @see java.io.ObjectOutputStream + * @see java.io.Serializable + * @see Object Serialization Specification, Section 3, Object Input Classes + * @since JDK1.1 + */ +public class ObjectInputStream + extends InputStream implements ObjectInput, ObjectStreamConstants +{ + /** handle value representing null */ + private static final int NULL_HANDLE = -1; + + /** marker for unshared objects in internal handle table */ + private static final Object unsharedMarker = new Object(); + + /** table mapping primitive type names to corresponding class objects */ + private static final HashMap> primClasses + = new HashMap<>(8, 1.0F); + static { + primClasses.put("boolean", boolean.class); + primClasses.put("byte", byte.class); + primClasses.put("char", char.class); + primClasses.put("short", short.class); + primClasses.put("int", int.class); + primClasses.put("long", long.class); + primClasses.put("float", float.class); + primClasses.put("double", double.class); + primClasses.put("void", void.class); + } + + /** filter stream for handling block data conversion */ + private final BlockDataInputStream bin; + /** validation callback list */ + private final ValidationList vlist; + /** recursion depth */ + private int depth; + /** whether stream is closed */ + private boolean closed; + + /** wire handle -> obj/exception map */ + private final HandleTable handles; + /** scratch field for passing handle values up/down call stack */ + private int passHandle = NULL_HANDLE; + /** flag set when at end of field value block with no TC_ENDBLOCKDATA */ + private boolean defaultDataEnd = false; + + /** buffer for reading primitive field values */ + private byte[] primVals; + + /** if true, invoke readObjectOverride() instead of readObject() */ + private final boolean enableOverride; + /** if true, invoke resolveObject() */ + private boolean enableResolve; + + /** + * Context during upcalls to class-defined readObject methods; holds + * object currently being deserialized and descriptor for current class. + * Null when not during readObject upcall. + */ + private Object curContext; + + /** + * Creates an ObjectInputStream that reads from the specified InputStream. + * A serialization stream header is read from the stream and verified. + * This constructor will block until the corresponding ObjectOutputStream + * has written and flushed the header. + * + *

If a security manager is installed, this constructor will check for + * the "enableSubclassImplementation" SerializablePermission when invoked + * directly or indirectly by the constructor of a subclass which overrides + * the ObjectInputStream.readFields or ObjectInputStream.readUnshared + * methods. + * + * @param in input stream to read from + * @throws StreamCorruptedException if the stream header is incorrect + * @throws IOException if an I/O error occurs while reading stream header + * @throws SecurityException if untrusted subclass illegally overrides + * security-sensitive methods + * @throws NullPointerException if in is null + * @see ObjectInputStream#ObjectInputStream() + * @see ObjectInputStream#readFields() + * @see ObjectOutputStream#ObjectOutputStream(OutputStream) + */ + public ObjectInputStream(InputStream in) throws IOException { + verifySubclass(); + bin = new BlockDataInputStream(in); + handles = new HandleTable(10); + vlist = new ValidationList(); + enableOverride = false; + readStreamHeader(); + bin.setBlockDataMode(true); + } + + /** + * Provide a way for subclasses that are completely reimplementing + * ObjectInputStream to not have to allocate private data just used by this + * implementation of ObjectInputStream. + * + *

If there is a security manager installed, this method first calls the + * security manager's checkPermission method with the + * SerializablePermission("enableSubclassImplementation") + * permission to ensure it's ok to enable subclassing. + * + * @throws SecurityException if a security manager exists and its + * checkPermission method denies enabling + * subclassing. + * @see SecurityManager#checkPermission + * @see java.io.SerializablePermission + */ + protected ObjectInputStream() throws IOException, SecurityException { + throw new SecurityException(); + } + + /** + * Read an object from the ObjectInputStream. The class of the object, the + * signature of the class, and the values of the non-transient and + * non-static fields of the class and all of its supertypes are read. + * Default deserializing for a class can be overriden using the writeObject + * and readObject methods. Objects referenced by this object are read + * transitively so that a complete equivalent graph of objects is + * reconstructed by readObject. + * + *

The root object is completely restored when all of its fields and the + * objects it references are completely restored. At this point the object + * validation callbacks are executed in order based on their registered + * priorities. The callbacks are registered by objects (in the readObject + * special methods) as they are individually restored. + * + *

Exceptions are thrown for problems with the InputStream and for + * classes that should not be deserialized. All exceptions are fatal to + * the InputStream and leave it in an indeterminate state; it is up to the + * caller to ignore or recover the stream state. + * + * @throws ClassNotFoundException Class of a serialized object cannot be + * found. + * @throws InvalidClassException Something is wrong with a class used by + * serialization. + * @throws StreamCorruptedException Control information in the + * stream is inconsistent. + * @throws OptionalDataException Primitive data was found in the + * stream instead of objects. + * @throws IOException Any of the usual Input/Output related exceptions. + */ + public final Object readObject() + throws IOException, ClassNotFoundException + { + throw new IOException(); + } + + /** + * This method is called by trusted subclasses of ObjectOutputStream that + * constructed ObjectOutputStream using the protected no-arg constructor. + * The subclass is expected to provide an override method with the modifier + * "final". + * + * @return the Object read from the stream. + * @throws ClassNotFoundException Class definition of a serialized object + * cannot be found. + * @throws OptionalDataException Primitive data was found in the stream + * instead of objects. + * @throws IOException if I/O errors occurred while reading from the + * underlying stream + * @see #ObjectInputStream() + * @see #readObject() + * @since 1.2 + */ + protected Object readObjectOverride() + throws IOException, ClassNotFoundException + { + return null; + } + + /** + * Reads an "unshared" object from the ObjectInputStream. This method is + * identical to readObject, except that it prevents subsequent calls to + * readObject and readUnshared from returning additional references to the + * deserialized instance obtained via this call. Specifically: + *

    + *
  • If readUnshared is called to deserialize a back-reference (the + * stream representation of an object which has been written + * previously to the stream), an ObjectStreamException will be + * thrown. + * + *
  • If readUnshared returns successfully, then any subsequent attempts + * to deserialize back-references to the stream handle deserialized + * by readUnshared will cause an ObjectStreamException to be thrown. + *
+ * Deserializing an object via readUnshared invalidates the stream handle + * associated with the returned object. Note that this in itself does not + * always guarantee that the reference returned by readUnshared is unique; + * the deserialized object may define a readResolve method which returns an + * object visible to other parties, or readUnshared may return a Class + * object or enum constant obtainable elsewhere in the stream or through + * external means. If the deserialized object defines a readResolve method + * and the invocation of that method returns an array, then readUnshared + * returns a shallow clone of that array; this guarantees that the returned + * array object is unique and cannot be obtained a second time from an + * invocation of readObject or readUnshared on the ObjectInputStream, + * even if the underlying data stream has been manipulated. + * + *

ObjectInputStream subclasses which override this method can only be + * constructed in security contexts possessing the + * "enableSubclassImplementation" SerializablePermission; any attempt to + * instantiate such a subclass without this permission will cause a + * SecurityException to be thrown. + * + * @return reference to deserialized object + * @throws ClassNotFoundException if class of an object to deserialize + * cannot be found + * @throws StreamCorruptedException if control information in the stream + * is inconsistent + * @throws ObjectStreamException if object to deserialize has already + * appeared in stream + * @throws OptionalDataException if primitive data is next in stream + * @throws IOException if an I/O error occurs during deserialization + * @since 1.4 + */ + public Object readUnshared() throws IOException, ClassNotFoundException { + // if nested read, passHandle contains handle of enclosing object + int outerHandle = passHandle; + try { + Object obj = readObject0(true); + handles.markDependency(outerHandle, passHandle); + ClassNotFoundException ex = handles.lookupException(passHandle); + if (ex != null) { + throw ex; + } + if (depth == 0) { + vlist.doCallbacks(); + } + return obj; + } finally { + passHandle = outerHandle; + if (closed && depth == 0) { + clear(); + } + } + } + + /** + * Read the non-static and non-transient fields of the current class from + * this stream. This may only be called from the readObject method of the + * class being deserialized. It will throw the NotActiveException if it is + * called otherwise. + * + * @throws ClassNotFoundException if the class of a serialized object + * could not be found. + * @throws IOException if an I/O error occurs. + * @throws NotActiveException if the stream is not currently reading + * objects. + */ + public void defaultReadObject() + throws IOException, ClassNotFoundException + { + if (curContext == null) { + throw new NotActiveException("not in call to readObject"); + } + Object curObj = null; // curContext.getObj(); + ObjectStreamClass curDesc = null; // curContext.getDesc(); + bin.setBlockDataMode(false); + defaultReadFields(curObj, curDesc); + bin.setBlockDataMode(true); + if (!curDesc.hasWriteObjectData()) { + /* + * Fix for 4360508: since stream does not contain terminating + * TC_ENDBLOCKDATA tag, set flag so that reading code elsewhere + * knows to simulate end-of-custom-data behavior. + */ + defaultDataEnd = true; + } + ClassNotFoundException ex = handles.lookupException(passHandle); + if (ex != null) { + throw ex; + } + } + + /** + * Reads the persistent fields from the stream and makes them available by + * name. + * + * @return the GetField object representing the persistent + * fields of the object being deserialized + * @throws ClassNotFoundException if the class of a serialized object + * could not be found. + * @throws IOException if an I/O error occurs. + * @throws NotActiveException if the stream is not currently reading + * objects. + * @since 1.2 + */ + public ObjectInputStream.GetField readFields() + throws IOException, ClassNotFoundException + { + if (curContext == null) { + throw new NotActiveException("not in call to readObject"); + } + Object curObj = null; // curContext.getObj(); + ObjectStreamClass curDesc = null; // curContext.getDesc(); + bin.setBlockDataMode(false); + GetFieldImpl getField = new GetFieldImpl(curDesc); + getField.readFields(); + bin.setBlockDataMode(true); + if (!curDesc.hasWriteObjectData()) { + /* + * Fix for 4360508: since stream does not contain terminating + * TC_ENDBLOCKDATA tag, set flag so that reading code elsewhere + * knows to simulate end-of-custom-data behavior. + */ + defaultDataEnd = true; + } + + return getField; + } + + /** + * Register an object to be validated before the graph is returned. While + * similar to resolveObject these validations are called after the entire + * graph has been reconstituted. Typically, a readObject method will + * register the object with the stream so that when all of the objects are + * restored a final set of validations can be performed. + * + * @param obj the object to receive the validation callback. + * @param prio controls the order of callbacks;zero is a good default. + * Use higher numbers to be called back earlier, lower numbers for + * later callbacks. Within a priority, callbacks are processed in + * no particular order. + * @throws NotActiveException The stream is not currently reading objects + * so it is invalid to register a callback. + * @throws InvalidObjectException The validation object is null. + */ + public void registerValidation(ObjectInputValidation obj, int prio) + throws NotActiveException, InvalidObjectException + { + if (depth == 0) { + throw new NotActiveException("stream inactive"); + } + vlist.register(obj, prio); + } + + /** + * Load the local class equivalent of the specified stream class + * description. Subclasses may implement this method to allow classes to + * be fetched from an alternate source. + * + *

The corresponding method in ObjectOutputStream is + * annotateClass. This method will be invoked only once for + * each unique class in the stream. This method can be implemented by + * subclasses to use an alternate loading mechanism but must return a + * Class object. Once returned, if the class is not an array + * class, its serialVersionUID is compared to the serialVersionUID of the + * serialized class, and if there is a mismatch, the deserialization fails + * and an {@link InvalidClassException} is thrown. + * + *

The default implementation of this method in + * ObjectInputStream returns the result of calling + *

+     *     Class.forName(desc.getName(), false, loader)
+     * 
+ * where loader is determined as follows: if there is a + * method on the current thread's stack whose declaring class was + * defined by a user-defined class loader (and was not a generated to + * implement reflective invocations), then loader is class + * loader corresponding to the closest such method to the currently + * executing frame; otherwise, loader is + * null. If this call results in a + * ClassNotFoundException and the name of the passed + * ObjectStreamClass instance is the Java language keyword + * for a primitive type or void, then the Class object + * representing that primitive type or void will be returned + * (e.g., an ObjectStreamClass with the name + * "int" will be resolved to Integer.TYPE). + * Otherwise, the ClassNotFoundException will be thrown to + * the caller of this method. + * + * @param desc an instance of class ObjectStreamClass + * @return a Class object corresponding to desc + * @throws IOException any of the usual Input/Output exceptions. + * @throws ClassNotFoundException if class of a serialized object cannot + * be found. + */ + protected Class resolveClass(ObjectStreamClass desc) + throws IOException, ClassNotFoundException + { + String name = desc.getName(); + try { + return Class.forName(name, false, latestUserDefinedLoader()); + } catch (ClassNotFoundException ex) { + Class cl = primClasses.get(name); + if (cl != null) { + return cl; + } else { + throw ex; + } + } + } + + /** + * Returns a proxy class that implements the interfaces named in a proxy + * class descriptor; subclasses may implement this method to read custom + * data from the stream along with the descriptors for dynamic proxy + * classes, allowing them to use an alternate loading mechanism for the + * interfaces and the proxy class. + * + *

This method is called exactly once for each unique proxy class + * descriptor in the stream. + * + *

The corresponding method in ObjectOutputStream is + * annotateProxyClass. For a given subclass of + * ObjectInputStream that overrides this method, the + * annotateProxyClass method in the corresponding subclass of + * ObjectOutputStream must write any data or objects read by + * this method. + * + *

The default implementation of this method in + * ObjectInputStream returns the result of calling + * Proxy.getProxyClass with the list of Class + * objects for the interfaces that are named in the interfaces + * parameter. The Class object for each interface name + * i is the value returned by calling + *

+     *     Class.forName(i, false, loader)
+     * 
+ * where loader is that of the first non-null + * class loader up the execution stack, or null if no + * non-null class loaders are on the stack (the same class + * loader choice used by the resolveClass method). Unless any + * of the resolved interfaces are non-public, this same value of + * loader is also the class loader passed to + * Proxy.getProxyClass; if non-public interfaces are present, + * their class loader is passed instead (if more than one non-public + * interface class loader is encountered, an + * IllegalAccessError is thrown). + * If Proxy.getProxyClass throws an + * IllegalArgumentException, resolveProxyClass + * will throw a ClassNotFoundException containing the + * IllegalArgumentException. + * + * @param interfaces the list of interface names that were + * deserialized in the proxy class descriptor + * @return a proxy class for the specified interfaces + * @throws IOException any exception thrown by the underlying + * InputStream + * @throws ClassNotFoundException if the proxy class or any of the + * named interfaces could not be found + * @see ObjectOutputStream#annotateProxyClass(Class) + * @since 1.3 + */ + protected Class resolveProxyClass(String[] interfaces) + throws IOException, ClassNotFoundException + { + ClassLoader latestLoader = latestUserDefinedLoader(); + ClassLoader nonPublicLoader = null; + boolean hasNonPublicInterface = false; + + // define proxy in class loader of non-public interface(s), if any + Class[] classObjs = new Class[interfaces.length]; + for (int i = 0; i < interfaces.length; i++) { + Class cl = Class.forName(interfaces[i], false, latestLoader); + if ((cl.getModifiers() & Modifier.PUBLIC) == 0) { + if (hasNonPublicInterface) { + if (nonPublicLoader != cl.getClassLoader()) { + throw new IllegalAccessError( + "conflicting non-public interface class loaders"); + } + } else { + nonPublicLoader = cl.getClassLoader(); + hasNonPublicInterface = true; + } + } + classObjs[i] = cl; + } + try { + return Proxy.getProxyClass( + hasNonPublicInterface ? nonPublicLoader : latestLoader, + classObjs); + } catch (IllegalArgumentException e) { + throw new ClassNotFoundException(null, e); + } + } + + /** + * This method will allow trusted subclasses of ObjectInputStream to + * substitute one object for another during deserialization. Replacing + * objects is disabled until enableResolveObject is called. The + * enableResolveObject method checks that the stream requesting to resolve + * object can be trusted. Every reference to serializable objects is passed + * to resolveObject. To insure that the private state of objects is not + * unintentionally exposed only trusted streams may use resolveObject. + * + *

This method is called after an object has been read but before it is + * returned from readObject. The default resolveObject method just returns + * the same object. + * + *

When a subclass is replacing objects it must insure that the + * substituted object is compatible with every field where the reference + * will be stored. Objects whose type is not a subclass of the type of the + * field or array element abort the serialization by raising an exception + * and the object is not be stored. + * + *

This method is called only once when each object is first + * encountered. All subsequent references to the object will be redirected + * to the new object. + * + * @param obj object to be substituted + * @return the substituted object + * @throws IOException Any of the usual Input/Output exceptions. + */ + protected Object resolveObject(Object obj) throws IOException { + return obj; + } + + /** + * Enable the stream to allow objects read from the stream to be replaced. + * When enabled, the resolveObject method is called for every object being + * deserialized. + * + *

If enable is true, and there is a security manager installed, + * this method first calls the security manager's + * checkPermission method with the + * SerializablePermission("enableSubstitution") permission to + * ensure it's ok to enable the stream to allow objects read from the + * stream to be replaced. + * + * @param enable true for enabling use of resolveObject for + * every object being deserialized + * @return the previous setting before this method was invoked + * @throws SecurityException if a security manager exists and its + * checkPermission method denies enabling the stream + * to allow objects read from the stream to be replaced. + * @see SecurityManager#checkPermission + * @see java.io.SerializablePermission + */ + protected boolean enableResolveObject(boolean enable) + throws SecurityException + { + throw new SecurityException(); + } + + /** + * The readStreamHeader method is provided to allow subclasses to read and + * verify their own stream headers. It reads and verifies the magic number + * and version number. + * + * @throws IOException if there are I/O errors while reading from the + * underlying InputStream + * @throws StreamCorruptedException if control information in the stream + * is inconsistent + */ + protected void readStreamHeader() + throws IOException, StreamCorruptedException + { + short s0 = bin.readShort(); + short s1 = bin.readShort(); + if (s0 != STREAM_MAGIC || s1 != STREAM_VERSION) { + throw new StreamCorruptedException( + String.format("invalid stream header: %04X%04X", s0, s1)); + } + } + + /** + * Read a class descriptor from the serialization stream. This method is + * called when the ObjectInputStream expects a class descriptor as the next + * item in the serialization stream. Subclasses of ObjectInputStream may + * override this method to read in class descriptors that have been written + * in non-standard formats (by subclasses of ObjectOutputStream which have + * overridden the writeClassDescriptor method). By default, + * this method reads class descriptors according to the format defined in + * the Object Serialization specification. + * + * @return the class descriptor read + * @throws IOException If an I/O error has occurred. + * @throws ClassNotFoundException If the Class of a serialized object used + * in the class descriptor representation cannot be found + * @see java.io.ObjectOutputStream#writeClassDescriptor(java.io.ObjectStreamClass) + * @since 1.3 + */ + protected ObjectStreamClass readClassDescriptor() + throws IOException, ClassNotFoundException + { + ObjectStreamClass desc = new ObjectStreamClass(); + desc.readNonProxy(this); + return desc; + } + + /** + * Reads a byte of data. This method will block if no input is available. + * + * @return the byte read, or -1 if the end of the stream is reached. + * @throws IOException If an I/O error has occurred. + */ + public int read() throws IOException { + return bin.read(); + } + + /** + * Reads into an array of bytes. This method will block until some input + * is available. Consider using java.io.DataInputStream.readFully to read + * exactly 'length' bytes. + * + * @param buf the buffer into which the data is read + * @param off the start offset of the data + * @param len the maximum number of bytes read + * @return the actual number of bytes read, -1 is returned when the end of + * the stream is reached. + * @throws IOException If an I/O error has occurred. + * @see java.io.DataInputStream#readFully(byte[],int,int) + */ + public int read(byte[] buf, int off, int len) throws IOException { + if (buf == null) { + throw new NullPointerException(); + } + int endoff = off + len; + if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) { + throw new IndexOutOfBoundsException(); + } + return bin.read(buf, off, len, false); + } + + /** + * Returns the number of bytes that can be read without blocking. + * + * @return the number of available bytes. + * @throws IOException if there are I/O errors while reading from the + * underlying InputStream + */ + public int available() throws IOException { + return bin.available(); + } + + /** + * Closes the input stream. Must be called to release any resources + * associated with the stream. + * + * @throws IOException If an I/O error has occurred. + */ + public void close() throws IOException { + /* + * Even if stream already closed, propagate redundant close to + * underlying stream to stay consistent with previous implementations. + */ + closed = true; + if (depth == 0) { + clear(); + } + bin.close(); + } + + /** + * Reads in a boolean. + * + * @return the boolean read. + * @throws EOFException If end of file is reached. + * @throws IOException If other I/O error has occurred. + */ + public boolean readBoolean() throws IOException { + return bin.readBoolean(); + } + + /** + * Reads an 8 bit byte. + * + * @return the 8 bit byte read. + * @throws EOFException If end of file is reached. + * @throws IOException If other I/O error has occurred. + */ + public byte readByte() throws IOException { + return bin.readByte(); + } + + /** + * Reads an unsigned 8 bit byte. + * + * @return the 8 bit byte read. + * @throws EOFException If end of file is reached. + * @throws IOException If other I/O error has occurred. + */ + public int readUnsignedByte() throws IOException { + return bin.readUnsignedByte(); + } + + /** + * Reads a 16 bit char. + * + * @return the 16 bit char read. + * @throws EOFException If end of file is reached. + * @throws IOException If other I/O error has occurred. + */ + public char readChar() throws IOException { + return bin.readChar(); + } + + /** + * Reads a 16 bit short. + * + * @return the 16 bit short read. + * @throws EOFException If end of file is reached. + * @throws IOException If other I/O error has occurred. + */ + public short readShort() throws IOException { + return bin.readShort(); + } + + /** + * Reads an unsigned 16 bit short. + * + * @return the 16 bit short read. + * @throws EOFException If end of file is reached. + * @throws IOException If other I/O error has occurred. + */ + public int readUnsignedShort() throws IOException { + return bin.readUnsignedShort(); + } + + /** + * Reads a 32 bit int. + * + * @return the 32 bit integer read. + * @throws EOFException If end of file is reached. + * @throws IOException If other I/O error has occurred. + */ + public int readInt() throws IOException { + return bin.readInt(); + } + + /** + * Reads a 64 bit long. + * + * @return the read 64 bit long. + * @throws EOFException If end of file is reached. + * @throws IOException If other I/O error has occurred. + */ + public long readLong() throws IOException { + return bin.readLong(); + } + + /** + * Reads a 32 bit float. + * + * @return the 32 bit float read. + * @throws EOFException If end of file is reached. + * @throws IOException If other I/O error has occurred. + */ + public float readFloat() throws IOException { + return bin.readFloat(); + } + + /** + * Reads a 64 bit double. + * + * @return the 64 bit double read. + * @throws EOFException If end of file is reached. + * @throws IOException If other I/O error has occurred. + */ + public double readDouble() throws IOException { + return bin.readDouble(); + } + + /** + * Reads bytes, blocking until all bytes are read. + * + * @param buf the buffer into which the data is read + * @throws EOFException If end of file is reached. + * @throws IOException If other I/O error has occurred. + */ + public void readFully(byte[] buf) throws IOException { + bin.readFully(buf, 0, buf.length, false); + } + + /** + * Reads bytes, blocking until all bytes are read. + * + * @param buf the buffer into which the data is read + * @param off the start offset of the data + * @param len the maximum number of bytes to read + * @throws EOFException If end of file is reached. + * @throws IOException If other I/O error has occurred. + */ + public void readFully(byte[] buf, int off, int len) throws IOException { + int endoff = off + len; + if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) { + throw new IndexOutOfBoundsException(); + } + bin.readFully(buf, off, len, false); + } + + /** + * Skips bytes. + * + * @param len the number of bytes to be skipped + * @return the actual number of bytes skipped. + * @throws IOException If an I/O error has occurred. + */ + public int skipBytes(int len) throws IOException { + return bin.skipBytes(len); + } + + /** + * Reads in a line that has been terminated by a \n, \r, \r\n or EOF. + * + * @return a String copy of the line. + * @throws IOException if there are I/O errors while reading from the + * underlying InputStream + * @deprecated This method does not properly convert bytes to characters. + * see DataInputStream for the details and alternatives. + */ + @Deprecated + public String readLine() throws IOException { + return bin.readLine(); + } + + /** + * Reads a String in + * modified UTF-8 + * format. + * + * @return the String. + * @throws IOException if there are I/O errors while reading from the + * underlying InputStream + * @throws UTFDataFormatException if read bytes do not represent a valid + * modified UTF-8 encoding of a string + */ + public String readUTF() throws IOException { + return bin.readUTF(); + } + + /** + * Provide access to the persistent fields read from the input stream. + */ + public static abstract class GetField { + + /** + * Get the ObjectStreamClass that describes the fields in the stream. + * + * @return the descriptor class that describes the serializable fields + */ + public abstract ObjectStreamClass getObjectStreamClass(); + + /** + * Return true if the named field is defaulted and has no value in this + * stream. + * + * @param name the name of the field + * @return true, if and only if the named field is defaulted + * @throws IOException if there are I/O errors while reading from + * the underlying InputStream + * @throws IllegalArgumentException if name does not + * correspond to a serializable field + */ + public abstract boolean defaulted(String name) throws IOException; + + /** + * Get the value of the named boolean field from the persistent field. + * + * @param name the name of the field + * @param val the default value to use if name does not + * have a value + * @return the value of the named boolean field + * @throws IOException if there are I/O errors while reading from the + * underlying InputStream + * @throws IllegalArgumentException if type of name is + * not serializable or if the field type is incorrect + */ + public abstract boolean get(String name, boolean val) + throws IOException; + + /** + * Get the value of the named byte field from the persistent field. + * + * @param name the name of the field + * @param val the default value to use if name does not + * have a value + * @return the value of the named byte field + * @throws IOException if there are I/O errors while reading from the + * underlying InputStream + * @throws IllegalArgumentException if type of name is + * not serializable or if the field type is incorrect + */ + public abstract byte get(String name, byte val) throws IOException; + + /** + * Get the value of the named char field from the persistent field. + * + * @param name the name of the field + * @param val the default value to use if name does not + * have a value + * @return the value of the named char field + * @throws IOException if there are I/O errors while reading from the + * underlying InputStream + * @throws IllegalArgumentException if type of name is + * not serializable or if the field type is incorrect + */ + public abstract char get(String name, char val) throws IOException; + + /** + * Get the value of the named short field from the persistent field. + * + * @param name the name of the field + * @param val the default value to use if name does not + * have a value + * @return the value of the named short field + * @throws IOException if there are I/O errors while reading from the + * underlying InputStream + * @throws IllegalArgumentException if type of name is + * not serializable or if the field type is incorrect + */ + public abstract short get(String name, short val) throws IOException; + + /** + * Get the value of the named int field from the persistent field. + * + * @param name the name of the field + * @param val the default value to use if name does not + * have a value + * @return the value of the named int field + * @throws IOException if there are I/O errors while reading from the + * underlying InputStream + * @throws IllegalArgumentException if type of name is + * not serializable or if the field type is incorrect + */ + public abstract int get(String name, int val) throws IOException; + + /** + * Get the value of the named long field from the persistent field. + * + * @param name the name of the field + * @param val the default value to use if name does not + * have a value + * @return the value of the named long field + * @throws IOException if there are I/O errors while reading from the + * underlying InputStream + * @throws IllegalArgumentException if type of name is + * not serializable or if the field type is incorrect + */ + public abstract long get(String name, long val) throws IOException; + + /** + * Get the value of the named float field from the persistent field. + * + * @param name the name of the field + * @param val the default value to use if name does not + * have a value + * @return the value of the named float field + * @throws IOException if there are I/O errors while reading from the + * underlying InputStream + * @throws IllegalArgumentException if type of name is + * not serializable or if the field type is incorrect + */ + public abstract float get(String name, float val) throws IOException; + + /** + * Get the value of the named double field from the persistent field. + * + * @param name the name of the field + * @param val the default value to use if name does not + * have a value + * @return the value of the named double field + * @throws IOException if there are I/O errors while reading from the + * underlying InputStream + * @throws IllegalArgumentException if type of name is + * not serializable or if the field type is incorrect + */ + public abstract double get(String name, double val) throws IOException; + + /** + * Get the value of the named Object field from the persistent field. + * + * @param name the name of the field + * @param val the default value to use if name does not + * have a value + * @return the value of the named Object field + * @throws IOException if there are I/O errors while reading from the + * underlying InputStream + * @throws IllegalArgumentException if type of name is + * not serializable or if the field type is incorrect + */ + public abstract Object get(String name, Object val) throws IOException; + } + + /** + * Verifies that this (possibly subclass) instance can be constructed + * without violating security constraints: the subclass must not override + * security-sensitive non-final methods, or else the + * "enableSubclassImplementation" SerializablePermission is checked. + */ + private void verifySubclass() { + Class cl = getClass(); + if (cl == ObjectInputStream.class) { + return; + } + throw new SecurityException(); + } + + /** + * Clears internal data structures. + */ + private void clear() { + handles.clear(); + vlist.clear(); + } + + /** + * Underlying readObject implementation. + */ + private Object readObject0(boolean unshared) throws IOException { + boolean oldMode = bin.getBlockDataMode(); + if (oldMode) { + int remain = bin.currentBlockRemaining(); + if (remain > 0) { + throw new OptionalDataException(remain); + } else if (defaultDataEnd) { + /* + * Fix for 4360508: stream is currently at the end of a field + * value block written via default serialization; since there + * is no terminating TC_ENDBLOCKDATA tag, simulate + * end-of-custom-data behavior explicitly. + */ + throw new OptionalDataException(true); + } + bin.setBlockDataMode(false); + } + + byte tc; + while ((tc = bin.peekByte()) == TC_RESET) { + bin.readByte(); + handleReset(); + } + + depth++; + try { + switch (tc) { + case TC_NULL: + return readNull(); + + case TC_REFERENCE: + return readHandle(unshared); + + case TC_CLASS: + return readClass(unshared); + + case TC_CLASSDESC: + case TC_PROXYCLASSDESC: + return readClassDesc(unshared); + + case TC_STRING: + case TC_LONGSTRING: + return checkResolve(readString(unshared)); + + case TC_ARRAY: + return checkResolve(readArray(unshared)); + + case TC_ENUM: + return checkResolve(readEnum(unshared)); + + case TC_OBJECT: + return checkResolve(readOrdinaryObject(unshared)); + + case TC_EXCEPTION: + IOException ex = readFatalException(); + throw new WriteAbortedException("writing aborted", ex); + + case TC_BLOCKDATA: + case TC_BLOCKDATALONG: + if (oldMode) { + bin.setBlockDataMode(true); + bin.peek(); // force header read + throw new OptionalDataException( + bin.currentBlockRemaining()); + } else { + throw new StreamCorruptedException( + "unexpected block data"); + } + + case TC_ENDBLOCKDATA: + if (oldMode) { + throw new OptionalDataException(true); + } else { + throw new StreamCorruptedException( + "unexpected end of block data"); + } + + default: + throw new StreamCorruptedException( + String.format("invalid type code: %02X", tc)); + } + } finally { + depth--; + bin.setBlockDataMode(oldMode); + } + } + + /** + * If resolveObject has been enabled and given object does not have an + * exception associated with it, calls resolveObject to determine + * replacement for object, and updates handle table accordingly. Returns + * replacement object, or echoes provided object if no replacement + * occurred. Expects that passHandle is set to given object's handle prior + * to calling this method. + */ + private Object checkResolve(Object obj) throws IOException { + if (!enableResolve || handles.lookupException(passHandle) != null) { + return obj; + } + Object rep = resolveObject(obj); + if (rep != obj) { + handles.setObject(passHandle, rep); + } + return rep; + } + + /** + * Reads string without allowing it to be replaced in stream. Called from + * within ObjectStreamClass.read(). + */ + String readTypeString() throws IOException { + int oldHandle = passHandle; + try { + byte tc = bin.peekByte(); + switch (tc) { + case TC_NULL: + return (String) readNull(); + + case TC_REFERENCE: + return (String) readHandle(false); + + case TC_STRING: + case TC_LONGSTRING: + return readString(false); + + default: + throw new StreamCorruptedException( + String.format("invalid type code: %02X", tc)); + } + } finally { + passHandle = oldHandle; + } + } + + /** + * Reads in null code, sets passHandle to NULL_HANDLE and returns null. + */ + private Object readNull() throws IOException { + if (bin.readByte() != TC_NULL) { + throw new InternalError(); + } + passHandle = NULL_HANDLE; + return null; + } + + /** + * Reads in object handle, sets passHandle to the read handle, and returns + * object associated with the handle. + */ + private Object readHandle(boolean unshared) throws IOException { + if (bin.readByte() != TC_REFERENCE) { + throw new InternalError(); + } + passHandle = bin.readInt() - baseWireHandle; + if (passHandle < 0 || passHandle >= handles.size()) { + throw new StreamCorruptedException( + String.format("invalid handle value: %08X", passHandle + + baseWireHandle)); + } + if (unshared) { + // REMIND: what type of exception to throw here? + throw new InvalidObjectException( + "cannot read back reference as unshared"); + } + + Object obj = handles.lookupObject(passHandle); + if (obj == unsharedMarker) { + // REMIND: what type of exception to throw here? + throw new InvalidObjectException( + "cannot read back reference to unshared object"); + } + return obj; + } + + /** + * Reads in and returns class object. Sets passHandle to class object's + * assigned handle. Returns null if class is unresolvable (in which case a + * ClassNotFoundException will be associated with the class' handle in the + * handle table). + */ + private Class readClass(boolean unshared) throws IOException { + if (bin.readByte() != TC_CLASS) { + throw new InternalError(); + } + ObjectStreamClass desc = readClassDesc(false); + Class cl = desc.forClass(); + passHandle = handles.assign(unshared ? unsharedMarker : cl); + + ClassNotFoundException resolveEx = desc.getResolveException(); + if (resolveEx != null) { + handles.markException(passHandle, resolveEx); + } + + handles.finish(passHandle); + return cl; + } + + /** + * Reads in and returns (possibly null) class descriptor. Sets passHandle + * to class descriptor's assigned handle. If class descriptor cannot be + * resolved to a class in the local VM, a ClassNotFoundException is + * associated with the class descriptor's handle. + */ + private ObjectStreamClass readClassDesc(boolean unshared) + throws IOException + { + byte tc = bin.peekByte(); + switch (tc) { + case TC_NULL: + return (ObjectStreamClass) readNull(); + + case TC_REFERENCE: + return (ObjectStreamClass) readHandle(unshared); + + case TC_PROXYCLASSDESC: + return readProxyDesc(unshared); + + case TC_CLASSDESC: + return readNonProxyDesc(unshared); + + default: + throw new StreamCorruptedException( + String.format("invalid type code: %02X", tc)); + } + } + + /** + * Reads in and returns class descriptor for a dynamic proxy class. Sets + * passHandle to proxy class descriptor's assigned handle. If proxy class + * descriptor cannot be resolved to a class in the local VM, a + * ClassNotFoundException is associated with the descriptor's handle. + */ + private ObjectStreamClass readProxyDesc(boolean unshared) + throws IOException + { + if (bin.readByte() != TC_PROXYCLASSDESC) { + throw new InternalError(); + } + + ObjectStreamClass desc = new ObjectStreamClass(); + int descHandle = handles.assign(unshared ? unsharedMarker : desc); + passHandle = NULL_HANDLE; + + int numIfaces = bin.readInt(); + String[] ifaces = new String[numIfaces]; + for (int i = 0; i < numIfaces; i++) { + ifaces[i] = bin.readUTF(); + } + + Class cl = null; + ClassNotFoundException resolveEx = null; + bin.setBlockDataMode(true); + try { + if ((cl = resolveProxyClass(ifaces)) == null) { + resolveEx = new ClassNotFoundException("null class"); + } + } catch (ClassNotFoundException ex) { + resolveEx = ex; + } + skipCustomData(); + + desc.initProxy(cl, resolveEx, readClassDesc(false)); + + handles.finish(descHandle); + passHandle = descHandle; + return desc; + } + + /** + * Reads in and returns class descriptor for a class that is not a dynamic + * proxy class. Sets passHandle to class descriptor's assigned handle. If + * class descriptor cannot be resolved to a class in the local VM, a + * ClassNotFoundException is associated with the descriptor's handle. + */ + private ObjectStreamClass readNonProxyDesc(boolean unshared) + throws IOException + { + if (bin.readByte() != TC_CLASSDESC) { + throw new InternalError(); + } + + ObjectStreamClass desc = new ObjectStreamClass(); + int descHandle = handles.assign(unshared ? unsharedMarker : desc); + passHandle = NULL_HANDLE; + + ObjectStreamClass readDesc = null; + try { + readDesc = readClassDescriptor(); + } catch (ClassNotFoundException ex) { + throw (IOException) new InvalidClassException( + "failed to read class descriptor").initCause(ex); + } + + Class cl = null; + ClassNotFoundException resolveEx = null; + bin.setBlockDataMode(true); + try { + if ((cl = resolveClass(readDesc)) == null) { + resolveEx = new ClassNotFoundException("null class"); + } + } catch (ClassNotFoundException ex) { + resolveEx = ex; + } + skipCustomData(); + + desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false)); + + handles.finish(descHandle); + passHandle = descHandle; + return desc; + } + + /** + * Reads in and returns new string. Sets passHandle to new string's + * assigned handle. + */ + private String readString(boolean unshared) throws IOException { + String str; + byte tc = bin.readByte(); + switch (tc) { + case TC_STRING: + str = bin.readUTF(); + break; + + case TC_LONGSTRING: + str = bin.readLongUTF(); + break; + + default: + throw new StreamCorruptedException( + String.format("invalid type code: %02X", tc)); + } + passHandle = handles.assign(unshared ? unsharedMarker : str); + handles.finish(passHandle); + return str; + } + + /** + * Reads in and returns array object, or null if array class is + * unresolvable. Sets passHandle to array's assigned handle. + */ + private Object readArray(boolean unshared) throws IOException { + if (bin.readByte() != TC_ARRAY) { + throw new InternalError(); + } + + ObjectStreamClass desc = readClassDesc(false); + int len = bin.readInt(); + + Object array = null; + Class cl, ccl = null; + if ((cl = desc.forClass()) != null) { + ccl = cl.getComponentType(); + array = Array.newInstance(ccl, len); + } + + int arrayHandle = handles.assign(unshared ? unsharedMarker : array); + ClassNotFoundException resolveEx = desc.getResolveException(); + if (resolveEx != null) { + handles.markException(arrayHandle, resolveEx); + } + + if (ccl == null) { + for (int i = 0; i < len; i++) { + readObject0(false); + } + } else if (ccl.isPrimitive()) { + if (ccl == Integer.TYPE) { + bin.readInts((int[]) array, 0, len); + } else if (ccl == Byte.TYPE) { + bin.readFully((byte[]) array, 0, len, true); + } else if (ccl == Long.TYPE) { + bin.readLongs((long[]) array, 0, len); + } else if (ccl == Float.TYPE) { + bin.readFloats((float[]) array, 0, len); + } else if (ccl == Double.TYPE) { + bin.readDoubles((double[]) array, 0, len); + } else if (ccl == Short.TYPE) { + bin.readShorts((short[]) array, 0, len); + } else if (ccl == Character.TYPE) { + bin.readChars((char[]) array, 0, len); + } else if (ccl == Boolean.TYPE) { + bin.readBooleans((boolean[]) array, 0, len); + } else { + throw new InternalError(); + } + } else { + Object[] oa = (Object[]) array; + for (int i = 0; i < len; i++) { + oa[i] = readObject0(false); + handles.markDependency(arrayHandle, passHandle); + } + } + + handles.finish(arrayHandle); + passHandle = arrayHandle; + return array; + } + + /** + * Reads in and returns enum constant, or null if enum type is + * unresolvable. Sets passHandle to enum constant's assigned handle. + */ + private Enum readEnum(boolean unshared) throws IOException { + if (bin.readByte() != TC_ENUM) { + throw new InternalError(); + } + + ObjectStreamClass desc = readClassDesc(false); + if (!desc.isEnum()) { + throw new InvalidClassException("non-enum class: " + desc); + } + + int enumHandle = handles.assign(unshared ? unsharedMarker : null); + ClassNotFoundException resolveEx = desc.getResolveException(); + if (resolveEx != null) { + handles.markException(enumHandle, resolveEx); + } + + String name = readString(false); + Enum en = null; + Class cl = desc.forClass(); + if (cl != null) { + try { + en = Enum.valueOf(cl, name); + } catch (IllegalArgumentException ex) { + throw (IOException) new InvalidObjectException( + "enum constant " + name + " does not exist in " + + cl).initCause(ex); + } + if (!unshared) { + handles.setObject(enumHandle, en); + } + } + + handles.finish(enumHandle); + passHandle = enumHandle; + return en; + } + + /** + * Reads and returns "ordinary" (i.e., not a String, Class, + * ObjectStreamClass, array, or enum constant) object, or null if object's + * class is unresolvable (in which case a ClassNotFoundException will be + * associated with object's handle). Sets passHandle to object's assigned + * handle. + */ + private Object readOrdinaryObject(boolean unshared) + throws IOException + { + if (bin.readByte() != TC_OBJECT) { + throw new InternalError(); + } + + ObjectStreamClass desc = readClassDesc(false); + desc.checkDeserialize(); + + Object obj; + try { + obj = desc.isInstantiable() ? desc.newInstance() : null; + } catch (Exception ex) { + throw (IOException) new InvalidClassException( + desc.forClass().getName(), + "unable to create instance").initCause(ex); + } + + passHandle = handles.assign(unshared ? unsharedMarker : obj); + ClassNotFoundException resolveEx = desc.getResolveException(); + if (resolveEx != null) { + handles.markException(passHandle, resolveEx); + } + + if (desc.isExternalizable()) { + readExternalData((Externalizable) obj, desc); + } else { + readSerialData(obj, desc); + } + + handles.finish(passHandle); + + if (obj != null && + handles.lookupException(passHandle) == null && + desc.hasReadResolveMethod()) + { + Object rep = desc.invokeReadResolve(obj); + if (unshared && rep.getClass().isArray()) { + rep = cloneArray(rep); + } + if (rep != obj) { + handles.setObject(passHandle, obj = rep); + } + } + + return obj; + } + + /** + * If obj is non-null, reads externalizable data by invoking readExternal() + * method of obj; otherwise, attempts to skip over externalizable data. + * Expects that passHandle is set to obj's handle before this method is + * called. + */ + private void readExternalData(Externalizable obj, ObjectStreamClass desc) + throws IOException + { + Object oldContext = curContext; + curContext = null; + try { + boolean blocked = desc.hasBlockExternalData(); + if (blocked) { + bin.setBlockDataMode(true); + } + if (obj != null) { + try { + obj.readExternal(this); + } catch (ClassNotFoundException ex) { + /* + * In most cases, the handle table has already propagated + * a CNFException to passHandle at this point; this mark + * call is included to address cases where the readExternal + * method has cons'ed and thrown a new CNFException of its + * own. + */ + handles.markException(passHandle, ex); + } + } + if (blocked) { + skipCustomData(); + } + } finally { + curContext = oldContext; + } + /* + * At this point, if the externalizable data was not written in + * block-data form and either the externalizable class doesn't exist + * locally (i.e., obj == null) or readExternal() just threw a + * CNFException, then the stream is probably in an inconsistent state, + * since some (or all) of the externalizable data may not have been + * consumed. Since there's no "correct" action to take in this case, + * we mimic the behavior of past serialization implementations and + * blindly hope that the stream is in sync; if it isn't and additional + * externalizable data remains in the stream, a subsequent read will + * most likely throw a StreamCorruptedException. + */ + } + + /** + * Reads (or attempts to skip, if obj is null or is tagged with a + * ClassNotFoundException) instance data for each serializable class of + * object in stream, from superclass to subclass. Expects that passHandle + * is set to obj's handle before this method is called. + */ + private void readSerialData(Object obj, ObjectStreamClass desc) + throws IOException + { + ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout(); + for (int i = 0; i < slots.length; i++) { + ObjectStreamClass slotDesc = slots[i].desc; + + if (slots[i].hasData) { + if (obj != null && + slotDesc.hasReadObjectMethod() && + handles.lookupException(passHandle) == null) + { + Object oldContext = curContext; + + try { + curContext = null; //new SerialCallbackContext(obj, slotDesc); + + bin.setBlockDataMode(true); + slotDesc.invokeReadObject(obj, this); + } catch (ClassNotFoundException ex) { + /* + * In most cases, the handle table has already + * propagated a CNFException to passHandle at this + * point; this mark call is included to address cases + * where the custom readObject method has cons'ed and + * thrown a new CNFException of its own. + */ + handles.markException(passHandle, ex); + } finally { + //curContext.setUsed(); + curContext = oldContext; + } + + /* + * defaultDataEnd may have been set indirectly by custom + * readObject() method when calling defaultReadObject() or + * readFields(); clear it to restore normal read behavior. + */ + defaultDataEnd = false; + } else { + defaultReadFields(obj, slotDesc); + } + if (slotDesc.hasWriteObjectData()) { + skipCustomData(); + } else { + bin.setBlockDataMode(false); + } + } else { + if (obj != null && + slotDesc.hasReadObjectNoDataMethod() && + handles.lookupException(passHandle) == null) + { + slotDesc.invokeReadObjectNoData(obj); + } + } + } + } + + /** + * Skips over all block data and objects until TC_ENDBLOCKDATA is + * encountered. + */ + private void skipCustomData() throws IOException { + int oldHandle = passHandle; + for (;;) { + if (bin.getBlockDataMode()) { + bin.skipBlockData(); + bin.setBlockDataMode(false); + } + switch (bin.peekByte()) { + case TC_BLOCKDATA: + case TC_BLOCKDATALONG: + bin.setBlockDataMode(true); + break; + + case TC_ENDBLOCKDATA: + bin.readByte(); + passHandle = oldHandle; + return; + + default: + readObject0(false); + break; + } + } + } + + /** + * Reads in values of serializable fields declared by given class + * descriptor. If obj is non-null, sets field values in obj. Expects that + * passHandle is set to obj's handle before this method is called. + */ + private void defaultReadFields(Object obj, ObjectStreamClass desc) + throws IOException + { + // REMIND: is isInstance check necessary? + Class cl = desc.forClass(); + if (cl != null && obj != null && !cl.isInstance(obj)) { + throw new ClassCastException(); + } + + int primDataSize = desc.getPrimDataSize(); + if (primVals == null || primVals.length < primDataSize) { + primVals = new byte[primDataSize]; + } + bin.readFully(primVals, 0, primDataSize, false); + if (obj != null) { + desc.setPrimFieldValues(obj, primVals); + } + + int objHandle = passHandle; + ObjectStreamField[] fields = desc.getFields(false); + Object[] objVals = new Object[desc.getNumObjFields()]; + int numPrimFields = fields.length - objVals.length; + for (int i = 0; i < objVals.length; i++) { + ObjectStreamField f = fields[numPrimFields + i]; + objVals[i] = readObject0(f.isUnshared()); + if (f.getField() != null) { + handles.markDependency(objHandle, passHandle); + } + } + if (obj != null) { + desc.setObjFieldValues(obj, objVals); + } + passHandle = objHandle; + } + + /** + * Reads in and returns IOException that caused serialization to abort. + * All stream state is discarded prior to reading in fatal exception. Sets + * passHandle to fatal exception's handle. + */ + private IOException readFatalException() throws IOException { + if (bin.readByte() != TC_EXCEPTION) { + throw new InternalError(); + } + clear(); + return (IOException) readObject0(false); + } + + /** + * If recursion depth is 0, clears internal data structures; otherwise, + * throws a StreamCorruptedException. This method is called when a + * TC_RESET typecode is encountered. + */ + private void handleReset() throws StreamCorruptedException { + if (depth > 0) { + throw new StreamCorruptedException( + "unexpected reset; recursion depth: " + depth); + } + clear(); + } + + /** + * Converts specified span of bytes into float values. + */ + // REMIND: remove once hotspot inlines Float.intBitsToFloat + private static native void bytesToFloats(byte[] src, int srcpos, + float[] dst, int dstpos, + int nfloats); + + /** + * Converts specified span of bytes into double values. + */ + // REMIND: remove once hotspot inlines Double.longBitsToDouble + private static native void bytesToDoubles(byte[] src, int srcpos, + double[] dst, int dstpos, + int ndoubles); + + /** + * Returns the first non-null class loader (not counting class loaders of + * generated reflection implementation classes) up the execution stack, or + * null if only code from the null class loader is on the stack. This + * method is also called via reflection by the following RMI-IIOP class: + * + * com.sun.corba.se.internal.util.JDKClassLoader + * + * This method should not be removed or its signature changed without + * corresponding modifications to the above class. + */ + // REMIND: change name to something more accurate? + private static native ClassLoader latestUserDefinedLoader(); + + /** + * Default GetField implementation. + */ + private class GetFieldImpl extends GetField { + + /** class descriptor describing serializable fields */ + private final ObjectStreamClass desc; + /** primitive field values */ + private final byte[] primVals; + /** object field values */ + private final Object[] objVals; + /** object field value handles */ + private final int[] objHandles; + + /** + * Creates GetFieldImpl object for reading fields defined in given + * class descriptor. + */ + GetFieldImpl(ObjectStreamClass desc) { + this.desc = desc; + primVals = new byte[desc.getPrimDataSize()]; + objVals = new Object[desc.getNumObjFields()]; + objHandles = new int[objVals.length]; + } + + public ObjectStreamClass getObjectStreamClass() { + return desc; + } + + public boolean defaulted(String name) throws IOException { + return (getFieldOffset(name, null) < 0); + } + + public boolean get(String name, boolean val) throws IOException { + int off = getFieldOffset(name, Boolean.TYPE); + return (off >= 0) ? Bits.getBoolean(primVals, off) : val; + } + + public byte get(String name, byte val) throws IOException { + int off = getFieldOffset(name, Byte.TYPE); + return (off >= 0) ? primVals[off] : val; + } + + public char get(String name, char val) throws IOException { + int off = getFieldOffset(name, Character.TYPE); + return (off >= 0) ? Bits.getChar(primVals, off) : val; + } + + public short get(String name, short val) throws IOException { + int off = getFieldOffset(name, Short.TYPE); + return (off >= 0) ? Bits.getShort(primVals, off) : val; + } + + public int get(String name, int val) throws IOException { + int off = getFieldOffset(name, Integer.TYPE); + return (off >= 0) ? Bits.getInt(primVals, off) : val; + } + + public float get(String name, float val) throws IOException { + int off = getFieldOffset(name, Float.TYPE); + return (off >= 0) ? Bits.getFloat(primVals, off) : val; + } + + public long get(String name, long val) throws IOException { + int off = getFieldOffset(name, Long.TYPE); + return (off >= 0) ? Bits.getLong(primVals, off) : val; + } + + public double get(String name, double val) throws IOException { + int off = getFieldOffset(name, Double.TYPE); + return (off >= 0) ? Bits.getDouble(primVals, off) : val; + } + + public Object get(String name, Object val) throws IOException { + int off = getFieldOffset(name, Object.class); + if (off >= 0) { + int objHandle = objHandles[off]; + handles.markDependency(passHandle, objHandle); + return (handles.lookupException(objHandle) == null) ? + objVals[off] : null; + } else { + return val; + } + } + + /** + * Reads primitive and object field values from stream. + */ + void readFields() throws IOException { + bin.readFully(primVals, 0, primVals.length, false); + + int oldHandle = passHandle; + ObjectStreamField[] fields = desc.getFields(false); + int numPrimFields = fields.length - objVals.length; + for (int i = 0; i < objVals.length; i++) { + objVals[i] = + readObject0(fields[numPrimFields + i].isUnshared()); + objHandles[i] = passHandle; + } + passHandle = oldHandle; + } + + /** + * Returns offset of field with given name and type. A specified type + * of null matches all types, Object.class matches all non-primitive + * types, and any other non-null type matches assignable types only. + * If no matching field is found in the (incoming) class + * descriptor but a matching field is present in the associated local + * class descriptor, returns -1. Throws IllegalArgumentException if + * neither incoming nor local class descriptor contains a match. + */ + private int getFieldOffset(String name, Class type) { + ObjectStreamField field = desc.getField(name, type); + if (field != null) { + return field.getOffset(); + } else if (desc.getLocalDesc().getField(name, type) != null) { + return -1; + } else { + throw new IllegalArgumentException("no such field " + name + + " with type " + type); + } + } + } + + /** + * Prioritized list of callbacks to be performed once object graph has been + * completely deserialized. + */ + private static class ValidationList { + + + /** + * Creates new (empty) ValidationList. + */ + ValidationList() { + } + + /** + * Registers callback. Throws InvalidObjectException if callback + * object is null. + */ + void register(ObjectInputValidation obj, int priority) + throws InvalidObjectException + { + if (obj == null) { + throw new InvalidObjectException("null callback"); + } + throw new InvalidObjectException("Does not work."); + } + + /** + * Invokes all registered callbacks and clears the callback list. + * Callbacks with higher priorities are called first; those with equal + * priorities may be called in any order. If any of the callbacks + * throws an InvalidObjectException, the callback process is terminated + * and the exception propagated upwards. + */ + void doCallbacks() throws InvalidObjectException { + } + + /** + * Resets the callback list to its initial (empty) state. + */ + public void clear() { + } + } + + /** + * Input stream supporting single-byte peek operations. + */ + private static class PeekInputStream extends InputStream { + + /** underlying stream */ + private final InputStream in; + /** peeked byte */ + private int peekb = -1; + + /** + * Creates new PeekInputStream on top of given underlying stream. + */ + PeekInputStream(InputStream in) { + this.in = in; + } + + /** + * Peeks at next byte value in stream. Similar to read(), except + * that it does not consume the read value. + */ + int peek() throws IOException { + return (peekb >= 0) ? peekb : (peekb = in.read()); + } + + public int read() throws IOException { + if (peekb >= 0) { + int v = peekb; + peekb = -1; + return v; + } else { + return in.read(); + } + } + + public int read(byte[] b, int off, int len) throws IOException { + if (len == 0) { + return 0; + } else if (peekb < 0) { + return in.read(b, off, len); + } else { + b[off++] = (byte) peekb; + len--; + peekb = -1; + int n = in.read(b, off, len); + return (n >= 0) ? (n + 1) : 1; + } + } + + void readFully(byte[] b, int off, int len) throws IOException { + int n = 0; + while (n < len) { + int count = read(b, off + n, len - n); + if (count < 0) { + throw new EOFException(); + } + n += count; + } + } + + public long skip(long n) throws IOException { + if (n <= 0) { + return 0; + } + int skipped = 0; + if (peekb >= 0) { + peekb = -1; + skipped++; + n--; + } + return skipped + skip(n); + } + + public int available() throws IOException { + return in.available() + ((peekb >= 0) ? 1 : 0); + } + + public void close() throws IOException { + in.close(); + } + } + + /** + * Input stream with two modes: in default mode, inputs data written in the + * same format as DataOutputStream; in "block data" mode, inputs data + * bracketed by block data markers (see object serialization specification + * for details). Buffering depends on block data mode: when in default + * mode, no data is buffered in advance; when in block data mode, all data + * for the current data block is read in at once (and buffered). + */ + private class BlockDataInputStream + extends InputStream implements DataInput + { + /** maximum data block length */ + private static final int MAX_BLOCK_SIZE = 1024; + /** maximum data block header length */ + private static final int MAX_HEADER_SIZE = 5; + /** (tunable) length of char buffer (for reading strings) */ + private static final int CHAR_BUF_SIZE = 256; + /** readBlockHeader() return value indicating header read may block */ + private static final int HEADER_BLOCKED = -2; + + /** buffer for reading general/block data */ + private final byte[] buf = new byte[MAX_BLOCK_SIZE]; + /** buffer for reading block data headers */ + private final byte[] hbuf = new byte[MAX_HEADER_SIZE]; + /** char buffer for fast string reads */ + private final char[] cbuf = new char[CHAR_BUF_SIZE]; + + /** block data mode */ + private boolean blkmode = false; + + // block data state fields; values meaningful only when blkmode true + /** current offset into buf */ + private int pos = 0; + /** end offset of valid data in buf, or -1 if no more block data */ + private int end = -1; + /** number of bytes in current block yet to be read from stream */ + private int unread = 0; + + /** underlying stream (wrapped in peekable filter stream) */ + private final PeekInputStream in; + /** loopback stream (for data reads that span data blocks) */ + private final DataInputStream din; + + /** + * Creates new BlockDataInputStream on top of given underlying stream. + * Block data mode is turned off by default. + */ + BlockDataInputStream(InputStream in) { + this.in = new PeekInputStream(in); + din = new DataInputStream(this); + } + + /** + * Sets block data mode to the given mode (true == on, false == off) + * and returns the previous mode value. If the new mode is the same as + * the old mode, no action is taken. Throws IllegalStateException if + * block data mode is being switched from on to off while unconsumed + * block data is still present in the stream. + */ + boolean setBlockDataMode(boolean newmode) throws IOException { + if (blkmode == newmode) { + return blkmode; + } + if (newmode) { + pos = 0; + end = 0; + unread = 0; + } else if (pos < end) { + throw new IllegalStateException("unread block data"); + } + blkmode = newmode; + return !blkmode; + } + + /** + * Returns true if the stream is currently in block data mode, false + * otherwise. + */ + boolean getBlockDataMode() { + return blkmode; + } + + /** + * If in block data mode, skips to the end of the current group of data + * blocks (but does not unset block data mode). If not in block data + * mode, throws an IllegalStateException. + */ + void skipBlockData() throws IOException { + if (!blkmode) { + throw new IllegalStateException("not in block data mode"); + } + while (end >= 0) { + refill(); + } + } + + /** + * Attempts to read in the next block data header (if any). If + * canBlock is false and a full header cannot be read without possibly + * blocking, returns HEADER_BLOCKED, else if the next element in the + * stream is a block data header, returns the block data length + * specified by the header, else returns -1. + */ + private int readBlockHeader(boolean canBlock) throws IOException { + if (defaultDataEnd) { + /* + * Fix for 4360508: stream is currently at the end of a field + * value block written via default serialization; since there + * is no terminating TC_ENDBLOCKDATA tag, simulate + * end-of-custom-data behavior explicitly. + */ + return -1; + } + try { + for (;;) { + int avail = canBlock ? Integer.MAX_VALUE : in.available(); + if (avail == 0) { + return HEADER_BLOCKED; + } + + int tc = in.peek(); + switch (tc) { + case TC_BLOCKDATA: + if (avail < 2) { + return HEADER_BLOCKED; + } + in.readFully(hbuf, 0, 2); + return hbuf[1] & 0xFF; + + case TC_BLOCKDATALONG: + if (avail < 5) { + return HEADER_BLOCKED; + } + in.readFully(hbuf, 0, 5); + int len = Bits.getInt(hbuf, 1); + if (len < 0) { + throw new StreamCorruptedException( + "illegal block data header length: " + + len); + } + return len; + + /* + * TC_RESETs may occur in between data blocks. + * Unfortunately, this case must be parsed at a lower + * level than other typecodes, since primitive data + * reads may span data blocks separated by a TC_RESET. + */ + case TC_RESET: + in.read(); + handleReset(); + break; + + default: + if (tc >= 0 && (tc < TC_BASE || tc > TC_MAX)) { + throw new StreamCorruptedException( + String.format("invalid type code: %02X", + tc)); + } + return -1; + } + } + } catch (EOFException ex) { + throw new StreamCorruptedException( + "unexpected EOF while reading block data header"); + } + } + + /** + * Refills internal buffer buf with block data. Any data in buf at the + * time of the call is considered consumed. Sets the pos, end, and + * unread fields to reflect the new amount of available block data; if + * the next element in the stream is not a data block, sets pos and + * unread to 0 and end to -1. + */ + private void refill() throws IOException { + try { + do { + pos = 0; + if (unread > 0) { + int n = + in.read(buf, 0, Math.min(unread, MAX_BLOCK_SIZE)); + if (n >= 0) { + end = n; + unread -= n; + } else { + throw new StreamCorruptedException( + "unexpected EOF in middle of data block"); + } + } else { + int n = readBlockHeader(true); + if (n >= 0) { + end = 0; + unread = n; + } else { + end = -1; + unread = 0; + } + } + } while (pos == end); + } catch (IOException ex) { + pos = 0; + end = -1; + unread = 0; + throw ex; + } + } + + /** + * If in block data mode, returns the number of unconsumed bytes + * remaining in the current data block. If not in block data mode, + * throws an IllegalStateException. + */ + int currentBlockRemaining() { + if (blkmode) { + return (end >= 0) ? (end - pos) + unread : 0; + } else { + throw new IllegalStateException(); + } + } + + /** + * Peeks at (but does not consume) and returns the next byte value in + * the stream, or -1 if the end of the stream/block data (if in block + * data mode) has been reached. + */ + int peek() throws IOException { + if (blkmode) { + if (pos == end) { + refill(); + } + return (end >= 0) ? (buf[pos] & 0xFF) : -1; + } else { + return in.peek(); + } + } + + /** + * Peeks at (but does not consume) and returns the next byte value in + * the stream, or throws EOFException if end of stream/block data has + * been reached. + */ + byte peekByte() throws IOException { + int val = peek(); + if (val < 0) { + throw new EOFException(); + } + return (byte) val; + } + + + /* ----------------- generic input stream methods ------------------ */ + /* + * The following methods are equivalent to their counterparts in + * InputStream, except that they interpret data block boundaries and + * read the requested data from within data blocks when in block data + * mode. + */ + + public int read() throws IOException { + if (blkmode) { + if (pos == end) { + refill(); + } + return (end >= 0) ? (buf[pos++] & 0xFF) : -1; + } else { + return in.read(); + } + } + + public int read(byte[] b, int off, int len) throws IOException { + return read(b, off, len, false); + } + + public long skip(long len) throws IOException { + long remain = len; + while (remain > 0) { + if (blkmode) { + if (pos == end) { + refill(); + } + if (end < 0) { + break; + } + int nread = (int) Math.min(remain, end - pos); + remain -= nread; + pos += nread; + } else { + int nread = (int) Math.min(remain, MAX_BLOCK_SIZE); + if ((nread = in.read(buf, 0, nread)) < 0) { + break; + } + remain -= nread; + } + } + return len - remain; + } + + public int available() throws IOException { + if (blkmode) { + if ((pos == end) && (unread == 0)) { + int n; + while ((n = readBlockHeader(false)) == 0) ; + switch (n) { + case HEADER_BLOCKED: + break; + + case -1: + pos = 0; + end = -1; + break; + + default: + pos = 0; + end = 0; + unread = n; + break; + } + } + // avoid unnecessary call to in.available() if possible + int unreadAvail = (unread > 0) ? + Math.min(in.available(), unread) : 0; + return (end >= 0) ? (end - pos) + unreadAvail : 0; + } else { + return in.available(); + } + } + + public void close() throws IOException { + if (blkmode) { + pos = 0; + end = -1; + unread = 0; + } + in.close(); + } + + /** + * Attempts to read len bytes into byte array b at offset off. Returns + * the number of bytes read, or -1 if the end of stream/block data has + * been reached. If copy is true, reads values into an intermediate + * buffer before copying them to b (to avoid exposing a reference to + * b). + */ + int read(byte[] b, int off, int len, boolean copy) throws IOException { + if (len == 0) { + return 0; + } else if (blkmode) { + if (pos == end) { + refill(); + } + if (end < 0) { + return -1; + } + int nread = Math.min(len, end - pos); + System.arraycopy(buf, pos, b, off, nread); + pos += nread; + return nread; + } else if (copy) { + int nread = in.read(buf, 0, Math.min(len, MAX_BLOCK_SIZE)); + if (nread > 0) { + System.arraycopy(buf, 0, b, off, nread); + } + return nread; + } else { + return in.read(b, off, len); + } + } + + /* ----------------- primitive data input methods ------------------ */ + /* + * The following methods are equivalent to their counterparts in + * DataInputStream, except that they interpret data block boundaries + * and read the requested data from within data blocks when in block + * data mode. + */ + + public void readFully(byte[] b) throws IOException { + readFully(b, 0, b.length, false); + } + + public void readFully(byte[] b, int off, int len) throws IOException { + readFully(b, off, len, false); + } + + public void readFully(byte[] b, int off, int len, boolean copy) + throws IOException + { + while (len > 0) { + int n = read(b, off, len, copy); + if (n < 0) { + throw new EOFException(); + } + off += n; + len -= n; + } + } + + public int skipBytes(int n) throws IOException { + return din.skipBytes(n); + } + + public boolean readBoolean() throws IOException { + int v = read(); + if (v < 0) { + throw new EOFException(); + } + return (v != 0); + } + + public byte readByte() throws IOException { + int v = read(); + if (v < 0) { + throw new EOFException(); + } + return (byte) v; + } + + public int readUnsignedByte() throws IOException { + int v = read(); + if (v < 0) { + throw new EOFException(); + } + return v; + } + + public char readChar() throws IOException { + if (!blkmode) { + pos = 0; + in.readFully(buf, 0, 2); + } else if (end - pos < 2) { + return din.readChar(); + } + char v = Bits.getChar(buf, pos); + pos += 2; + return v; + } + + public short readShort() throws IOException { + if (!blkmode) { + pos = 0; + in.readFully(buf, 0, 2); + } else if (end - pos < 2) { + return din.readShort(); + } + short v = Bits.getShort(buf, pos); + pos += 2; + return v; + } + + public int readUnsignedShort() throws IOException { + if (!blkmode) { + pos = 0; + in.readFully(buf, 0, 2); + } else if (end - pos < 2) { + return din.readUnsignedShort(); + } + int v = Bits.getShort(buf, pos) & 0xFFFF; + pos += 2; + return v; + } + + public int readInt() throws IOException { + if (!blkmode) { + pos = 0; + in.readFully(buf, 0, 4); + } else if (end - pos < 4) { + return din.readInt(); + } + int v = Bits.getInt(buf, pos); + pos += 4; + return v; + } + + public float readFloat() throws IOException { + if (!blkmode) { + pos = 0; + in.readFully(buf, 0, 4); + } else if (end - pos < 4) { + return din.readFloat(); + } + float v = Bits.getFloat(buf, pos); + pos += 4; + return v; + } + + public long readLong() throws IOException { + if (!blkmode) { + pos = 0; + in.readFully(buf, 0, 8); + } else if (end - pos < 8) { + return din.readLong(); + } + long v = Bits.getLong(buf, pos); + pos += 8; + return v; + } + + public double readDouble() throws IOException { + if (!blkmode) { + pos = 0; + in.readFully(buf, 0, 8); + } else if (end - pos < 8) { + return din.readDouble(); + } + double v = Bits.getDouble(buf, pos); + pos += 8; + return v; + } + + public String readUTF() throws IOException { + return readUTFBody(readUnsignedShort()); + } + + public String readLine() throws IOException { + return din.readLine(); // deprecated, not worth optimizing + } + + /* -------------- primitive data array input methods --------------- */ + /* + * The following methods read in spans of primitive data values. + * Though equivalent to calling the corresponding primitive read + * methods repeatedly, these methods are optimized for reading groups + * of primitive data values more efficiently. + */ + + void readBooleans(boolean[] v, int off, int len) throws IOException { + int stop, endoff = off + len; + while (off < endoff) { + if (!blkmode) { + int span = Math.min(endoff - off, MAX_BLOCK_SIZE); + in.readFully(buf, 0, span); + stop = off + span; + pos = 0; + } else if (end - pos < 1) { + v[off++] = din.readBoolean(); + continue; + } else { + stop = Math.min(endoff, off + end - pos); + } + + while (off < stop) { + v[off++] = Bits.getBoolean(buf, pos++); + } + } + } + + void readChars(char[] v, int off, int len) throws IOException { + int stop, endoff = off + len; + while (off < endoff) { + if (!blkmode) { + int span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 1); + in.readFully(buf, 0, span << 1); + stop = off + span; + pos = 0; + } else if (end - pos < 2) { + v[off++] = din.readChar(); + continue; + } else { + stop = Math.min(endoff, off + ((end - pos) >> 1)); + } + + while (off < stop) { + v[off++] = Bits.getChar(buf, pos); + pos += 2; + } + } + } + + void readShorts(short[] v, int off, int len) throws IOException { + int stop, endoff = off + len; + while (off < endoff) { + if (!blkmode) { + int span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 1); + in.readFully(buf, 0, span << 1); + stop = off + span; + pos = 0; + } else if (end - pos < 2) { + v[off++] = din.readShort(); + continue; + } else { + stop = Math.min(endoff, off + ((end - pos) >> 1)); + } + + while (off < stop) { + v[off++] = Bits.getShort(buf, pos); + pos += 2; + } + } + } + + void readInts(int[] v, int off, int len) throws IOException { + int stop, endoff = off + len; + while (off < endoff) { + if (!blkmode) { + int span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 2); + in.readFully(buf, 0, span << 2); + stop = off + span; + pos = 0; + } else if (end - pos < 4) { + v[off++] = din.readInt(); + continue; + } else { + stop = Math.min(endoff, off + ((end - pos) >> 2)); + } + + while (off < stop) { + v[off++] = Bits.getInt(buf, pos); + pos += 4; + } + } + } + + void readFloats(float[] v, int off, int len) throws IOException { + int span, endoff = off + len; + while (off < endoff) { + if (!blkmode) { + span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 2); + in.readFully(buf, 0, span << 2); + pos = 0; + } else if (end - pos < 4) { + v[off++] = din.readFloat(); + continue; + } else { + span = Math.min(endoff - off, ((end - pos) >> 2)); + } + + bytesToFloats(buf, pos, v, off, span); + off += span; + pos += span << 2; + } + } + + void readLongs(long[] v, int off, int len) throws IOException { + int stop, endoff = off + len; + while (off < endoff) { + if (!blkmode) { + int span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 3); + in.readFully(buf, 0, span << 3); + stop = off + span; + pos = 0; + } else if (end - pos < 8) { + v[off++] = din.readLong(); + continue; + } else { + stop = Math.min(endoff, off + ((end - pos) >> 3)); + } + + while (off < stop) { + v[off++] = Bits.getLong(buf, pos); + pos += 8; + } + } + } + + void readDoubles(double[] v, int off, int len) throws IOException { + int span, endoff = off + len; + while (off < endoff) { + if (!blkmode) { + span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 3); + in.readFully(buf, 0, span << 3); + pos = 0; + } else if (end - pos < 8) { + v[off++] = din.readDouble(); + continue; + } else { + span = Math.min(endoff - off, ((end - pos) >> 3)); + } + + bytesToDoubles(buf, pos, v, off, span); + off += span; + pos += span << 3; + } + } + + /** + * Reads in string written in "long" UTF format. "Long" UTF format is + * identical to standard UTF, except that it uses an 8 byte header + * (instead of the standard 2 bytes) to convey the UTF encoding length. + */ + String readLongUTF() throws IOException { + return readUTFBody(readLong()); + } + + /** + * Reads in the "body" (i.e., the UTF representation minus the 2-byte + * or 8-byte length header) of a UTF encoding, which occupies the next + * utflen bytes. + */ + private String readUTFBody(long utflen) throws IOException { + StringBuilder sbuf = new StringBuilder(); + if (!blkmode) { + end = pos = 0; + } + + while (utflen > 0) { + int avail = end - pos; + if (avail >= 3 || (long) avail == utflen) { + utflen -= readUTFSpan(sbuf, utflen); + } else { + if (blkmode) { + // near block boundary, read one byte at a time + utflen -= readUTFChar(sbuf, utflen); + } else { + // shift and refill buffer manually + if (avail > 0) { + System.arraycopy(buf, pos, buf, 0, avail); + } + pos = 0; + end = (int) Math.min(MAX_BLOCK_SIZE, utflen); + in.readFully(buf, avail, end - avail); + } + } + } + + return sbuf.toString(); + } + + /** + * Reads span of UTF-encoded characters out of internal buffer + * (starting at offset pos and ending at or before offset end), + * consuming no more than utflen bytes. Appends read characters to + * sbuf. Returns the number of bytes consumed. + */ + private long readUTFSpan(StringBuilder sbuf, long utflen) + throws IOException + { + int cpos = 0; + int start = pos; + int avail = Math.min(end - pos, CHAR_BUF_SIZE); + // stop short of last char unless all of utf bytes in buffer + int stop = pos + ((utflen > avail) ? avail - 2 : (int) utflen); + boolean outOfBounds = false; + + try { + while (pos < stop) { + int b1, b2, b3; + b1 = buf[pos++] & 0xFF; + switch (b1 >> 4) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: // 1 byte format: 0xxxxxxx + cbuf[cpos++] = (char) b1; + break; + + case 12: + case 13: // 2 byte format: 110xxxxx 10xxxxxx + b2 = buf[pos++]; + if ((b2 & 0xC0) != 0x80) { + throw new UTFDataFormatException(); + } + cbuf[cpos++] = (char) (((b1 & 0x1F) << 6) | + ((b2 & 0x3F) << 0)); + break; + + case 14: // 3 byte format: 1110xxxx 10xxxxxx 10xxxxxx + b3 = buf[pos + 1]; + b2 = buf[pos + 0]; + pos += 2; + if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80) { + throw new UTFDataFormatException(); + } + cbuf[cpos++] = (char) (((b1 & 0x0F) << 12) | + ((b2 & 0x3F) << 6) | + ((b3 & 0x3F) << 0)); + break; + + default: // 10xx xxxx, 1111 xxxx + throw new UTFDataFormatException(); + } + } + } catch (ArrayIndexOutOfBoundsException ex) { + outOfBounds = true; + } finally { + if (outOfBounds || (pos - start) > utflen) { + /* + * Fix for 4450867: if a malformed utf char causes the + * conversion loop to scan past the expected end of the utf + * string, only consume the expected number of utf bytes. + */ + pos = start + (int) utflen; + throw new UTFDataFormatException(); + } + } + + sbuf.append(cbuf, 0, cpos); + return pos - start; + } + + /** + * Reads in single UTF-encoded character one byte at a time, appends + * the character to sbuf, and returns the number of bytes consumed. + * This method is used when reading in UTF strings written in block + * data mode to handle UTF-encoded characters which (potentially) + * straddle block-data boundaries. + */ + private int readUTFChar(StringBuilder sbuf, long utflen) + throws IOException + { + int b1, b2, b3; + b1 = readByte() & 0xFF; + switch (b1 >> 4) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: // 1 byte format: 0xxxxxxx + sbuf.append((char) b1); + return 1; + + case 12: + case 13: // 2 byte format: 110xxxxx 10xxxxxx + if (utflen < 2) { + throw new UTFDataFormatException(); + } + b2 = readByte(); + if ((b2 & 0xC0) != 0x80) { + throw new UTFDataFormatException(); + } + sbuf.append((char) (((b1 & 0x1F) << 6) | + ((b2 & 0x3F) << 0))); + return 2; + + case 14: // 3 byte format: 1110xxxx 10xxxxxx 10xxxxxx + if (utflen < 3) { + if (utflen == 2) { + readByte(); // consume remaining byte + } + throw new UTFDataFormatException(); + } + b2 = readByte(); + b3 = readByte(); + if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80) { + throw new UTFDataFormatException(); + } + sbuf.append((char) (((b1 & 0x0F) << 12) | + ((b2 & 0x3F) << 6) | + ((b3 & 0x3F) << 0))); + return 3; + + default: // 10xx xxxx, 1111 xxxx + throw new UTFDataFormatException(); + } + } + } + + /** + * Unsynchronized table which tracks wire handle to object mappings, as + * well as ClassNotFoundExceptions associated with deserialized objects. + * This class implements an exception-propagation algorithm for + * determining which objects should have ClassNotFoundExceptions associated + * with them, taking into account cycles and discontinuities (e.g., skipped + * fields) in the object graph. + * + *

General use of the table is as follows: during deserialization, a + * given object is first assigned a handle by calling the assign method. + * This method leaves the assigned handle in an "open" state, wherein + * dependencies on the exception status of other handles can be registered + * by calling the markDependency method, or an exception can be directly + * associated with the handle by calling markException. When a handle is + * tagged with an exception, the HandleTable assumes responsibility for + * propagating the exception to any other objects which depend + * (transitively) on the exception-tagged object. + * + *

Once all exception information/dependencies for the handle have been + * registered, the handle should be "closed" by calling the finish method + * on it. The act of finishing a handle allows the exception propagation + * algorithm to aggressively prune dependency links, lessening the + * performance/memory impact of exception tracking. + * + *

Note that the exception propagation algorithm used depends on handles + * being assigned/finished in LIFO order; however, for simplicity as well + * as memory conservation, it does not enforce this constraint. + */ + // REMIND: add full description of exception propagation algorithm? + private static class HandleTable { + + /* status codes indicating whether object has associated exception */ + private static final byte STATUS_OK = 1; + private static final byte STATUS_UNKNOWN = 2; + private static final byte STATUS_EXCEPTION = 3; + + /** array mapping handle -> object status */ + byte[] status; + /** array mapping handle -> object/exception (depending on status) */ + Object[] entries; + /** array mapping handle -> list of dependent handles (if any) */ + HandleList[] deps; + /** lowest unresolved dependency */ + int lowDep = -1; + /** number of handles in table */ + int size = 0; + + /** + * Creates handle table with the given initial capacity. + */ + HandleTable(int initialCapacity) { + status = new byte[initialCapacity]; + entries = new Object[initialCapacity]; + deps = new HandleList[initialCapacity]; + } + + /** + * Assigns next available handle to given object, and returns assigned + * handle. Once object has been completely deserialized (and all + * dependencies on other objects identified), the handle should be + * "closed" by passing it to finish(). + */ + int assign(Object obj) { + if (size >= entries.length) { + grow(); + } + status[size] = STATUS_UNKNOWN; + entries[size] = obj; + return size++; + } + + /** + * Registers a dependency (in exception status) of one handle on + * another. The dependent handle must be "open" (i.e., assigned, but + * not finished yet). No action is taken if either dependent or target + * handle is NULL_HANDLE. + */ + void markDependency(int dependent, int target) { + if (dependent == NULL_HANDLE || target == NULL_HANDLE) { + return; + } + switch (status[dependent]) { + + case STATUS_UNKNOWN: + switch (status[target]) { + case STATUS_OK: + // ignore dependencies on objs with no exception + break; + + case STATUS_EXCEPTION: + // eagerly propagate exception + markException(dependent, + (ClassNotFoundException) entries[target]); + break; + + case STATUS_UNKNOWN: + // add to dependency list of target + if (deps[target] == null) { + deps[target] = new HandleList(); + } + deps[target].add(dependent); + + // remember lowest unresolved target seen + if (lowDep < 0 || lowDep > target) { + lowDep = target; + } + break; + + default: + throw new InternalError(); + } + break; + + case STATUS_EXCEPTION: + break; + + default: + throw new InternalError(); + } + } + + /** + * Associates a ClassNotFoundException (if one not already associated) + * with the currently active handle and propagates it to other + * referencing objects as appropriate. The specified handle must be + * "open" (i.e., assigned, but not finished yet). + */ + void markException(int handle, ClassNotFoundException ex) { + switch (status[handle]) { + case STATUS_UNKNOWN: + status[handle] = STATUS_EXCEPTION; + entries[handle] = ex; + + // propagate exception to dependents + HandleList dlist = deps[handle]; + if (dlist != null) { + int ndeps = dlist.size(); + for (int i = 0; i < ndeps; i++) { + markException(dlist.get(i), ex); + } + deps[handle] = null; + } + break; + + case STATUS_EXCEPTION: + break; + + default: + throw new InternalError(); + } + } + + /** + * Marks given handle as finished, meaning that no new dependencies + * will be marked for handle. Calls to the assign and finish methods + * must occur in LIFO order. + */ + void finish(int handle) { + int end; + if (lowDep < 0) { + // no pending unknowns, only resolve current handle + end = handle + 1; + } else if (lowDep >= handle) { + // pending unknowns now clearable, resolve all upward handles + end = size; + lowDep = -1; + } else { + // unresolved backrefs present, can't resolve anything yet + return; + } + + // change STATUS_UNKNOWN -> STATUS_OK in selected span of handles + for (int i = handle; i < end; i++) { + switch (status[i]) { + case STATUS_UNKNOWN: + status[i] = STATUS_OK; + deps[i] = null; + break; + + case STATUS_OK: + case STATUS_EXCEPTION: + break; + + default: + throw new InternalError(); + } + } + } + + /** + * Assigns a new object to the given handle. The object previously + * associated with the handle is forgotten. This method has no effect + * if the given handle already has an exception associated with it. + * This method may be called at any time after the handle is assigned. + */ + void setObject(int handle, Object obj) { + switch (status[handle]) { + case STATUS_UNKNOWN: + case STATUS_OK: + entries[handle] = obj; + break; + + case STATUS_EXCEPTION: + break; + + default: + throw new InternalError(); + } + } + + /** + * Looks up and returns object associated with the given handle. + * Returns null if the given handle is NULL_HANDLE, or if it has an + * associated ClassNotFoundException. + */ + Object lookupObject(int handle) { + return (handle != NULL_HANDLE && + status[handle] != STATUS_EXCEPTION) ? + entries[handle] : null; + } + + /** + * Looks up and returns ClassNotFoundException associated with the + * given handle. Returns null if the given handle is NULL_HANDLE, or + * if there is no ClassNotFoundException associated with the handle. + */ + ClassNotFoundException lookupException(int handle) { + return (handle != NULL_HANDLE && + status[handle] == STATUS_EXCEPTION) ? + (ClassNotFoundException) entries[handle] : null; + } + + /** + * Resets table to its initial state. + */ + void clear() { + Arrays.fill(status, 0, size, (byte) 0); + Arrays.fill(entries, 0, size, null); + Arrays.fill(deps, 0, size, null); + lowDep = -1; + size = 0; + } + + /** + * Returns number of handles registered in table. + */ + int size() { + return size; + } + + /** + * Expands capacity of internal arrays. + */ + private void grow() { + int newCapacity = (entries.length << 1) + 1; + + byte[] newStatus = new byte[newCapacity]; + Object[] newEntries = new Object[newCapacity]; + HandleList[] newDeps = new HandleList[newCapacity]; + + System.arraycopy(status, 0, newStatus, 0, size); + System.arraycopy(entries, 0, newEntries, 0, size); + System.arraycopy(deps, 0, newDeps, 0, size); + + status = newStatus; + entries = newEntries; + deps = newDeps; + } + + /** + * Simple growable list of (integer) handles. + */ + private static class HandleList { + private int[] list = new int[4]; + private int size = 0; + + public HandleList() { + } + + public void add(int handle) { + if (size >= list.length) { + int[] newList = new int[list.length << 1]; + System.arraycopy(list, 0, newList, 0, list.length); + list = newList; + } + list[size++] = handle; + } + + public int get(int index) { + if (index >= size) { + throw new ArrayIndexOutOfBoundsException(); + } + return list[index]; + } + + public int size() { + return size; + } + } + } + + /** + * Method for cloning arrays in case of using unsharing reading + */ + private static Object cloneArray(Object array) { + if (array instanceof Object[]) { + return ((Object[]) array).clone(); + } else if (array instanceof boolean[]) { + return ((boolean[]) array).clone(); + } else if (array instanceof byte[]) { + return ((byte[]) array).clone(); + } else if (array instanceof char[]) { + return ((char[]) array).clone(); + } else if (array instanceof double[]) { + return ((double[]) array).clone(); + } else if (array instanceof float[]) { + return ((float[]) array).clone(); + } else if (array instanceof int[]) { + return ((int[]) array).clone(); + } else if (array instanceof long[]) { + return ((long[]) array).clone(); + } else if (array instanceof short[]) { + return ((short[]) array).clone(); + } else { + throw new AssertionError(); + } + } + +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/ObjectInputValidation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/ObjectInputValidation.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 1996, 1999, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +/** + * Callback interface to allow validation of objects within a graph. + * Allows an object to be called when a complete graph of objects has + * been deserialized. + * + * @author unascribed + * @see ObjectInputStream + * @see ObjectInputStream#registerValidation(java.io.ObjectInputValidation, int) + * @since JDK1.1 + */ +public interface ObjectInputValidation { + /** + * Validates the object. + * + * @exception InvalidObjectException If the object cannot validate itself. + */ + public void validateObject() throws InvalidObjectException; +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/ObjectOutput.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/ObjectOutput.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +/** + * ObjectOutput extends the DataOutput interface to include writing of objects. + * DataOutput includes methods for output of primitive types, ObjectOutput + * extends that interface to include objects, arrays, and Strings. + * + * @author unascribed + * @see java.io.InputStream + * @see java.io.ObjectOutputStream + * @see java.io.ObjectInputStream + * @since JDK1.1 + */ +public interface ObjectOutput extends DataOutput, AutoCloseable { + /** + * Write an object to the underlying storage or stream. The + * class that implements this interface defines how the object is + * written. + * + * @param obj the object to be written + * @exception IOException Any of the usual Input/Output related exceptions. + */ + public void writeObject(Object obj) + throws IOException; + + /** + * Writes a byte. This method will block until the byte is actually + * written. + * @param b the byte + * @exception IOException If an I/O error has occurred. + */ + public void write(int b) throws IOException; + + /** + * Writes an array of bytes. This method will block until the bytes + * are actually written. + * @param b the data to be written + * @exception IOException If an I/O error has occurred. + */ + public void write(byte b[]) throws IOException; + + /** + * Writes a sub array of bytes. + * @param b the data to be written + * @param off the start offset in the data + * @param len the number of bytes that are written + * @exception IOException If an I/O error has occurred. + */ + public void write(byte b[], int off, int len) throws IOException; + + /** + * Flushes the stream. This will write any buffered + * output bytes. + * @exception IOException If an I/O error has occurred. + */ + public void flush() throws IOException; + + /** + * Closes the stream. This method must be called + * to release any resources associated with the + * stream. + * @exception IOException If an I/O error has occurred. + */ + public void close() throws IOException; +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/ObjectOutputStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/ObjectOutputStream.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,2369 @@ +/* + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.apidesign.bck2brwsr.emul.lang.System; + +/** + * An ObjectOutputStream writes primitive data types and graphs of Java objects + * to an OutputStream. The objects can be read (reconstituted) using an + * ObjectInputStream. Persistent storage of objects can be accomplished by + * using a file for the stream. If the stream is a network socket stream, the + * objects can be reconstituted on another host or in another process. + * + *

Only objects that support the java.io.Serializable interface can be + * written to streams. The class of each serializable object is encoded + * including the class name and signature of the class, the values of the + * object's fields and arrays, and the closure of any other objects referenced + * from the initial objects. + * + *

The method writeObject is used to write an object to the stream. Any + * object, including Strings and arrays, is written with writeObject. Multiple + * objects or primitives can be written to the stream. The objects must be + * read back from the corresponding ObjectInputstream with the same types and + * in the same order as they were written. + * + *

Primitive data types can also be written to the stream using the + * appropriate methods from DataOutput. Strings can also be written using the + * writeUTF method. + * + *

The default serialization mechanism for an object writes the class of the + * object, the class signature, and the values of all non-transient and + * non-static fields. References to other objects (except in transient or + * static fields) cause those objects to be written also. Multiple references + * to a single object are encoded using a reference sharing mechanism so that + * graphs of objects can be restored to the same shape as when the original was + * written. + * + *

For example to write an object that can be read by the example in + * ObjectInputStream: + *
+ *

+ *      FileOutputStream fos = new FileOutputStream("t.tmp");
+ *      ObjectOutputStream oos = new ObjectOutputStream(fos);
+ *
+ *      oos.writeInt(12345);
+ *      oos.writeObject("Today");
+ *      oos.writeObject(new Date());
+ *
+ *      oos.close();
+ * 
+ * + *

Classes that require special handling during the serialization and + * deserialization process must implement special methods with these exact + * signatures: + *
+ *

+ * private void readObject(java.io.ObjectInputStream stream)
+ *     throws IOException, ClassNotFoundException;
+ * private void writeObject(java.io.ObjectOutputStream stream)
+ *     throws IOException
+ * private void readObjectNoData()
+ *     throws ObjectStreamException;
+ * 
+ * + *

The writeObject method is responsible for writing the state of the object + * for its particular class so that the corresponding readObject method can + * restore it. The method does not need to concern itself with the state + * belonging to the object's superclasses or subclasses. State is saved by + * writing the individual fields to the ObjectOutputStream using the + * writeObject method or by using the methods for primitive data types + * supported by DataOutput. + * + *

Serialization does not write out the fields of any object that does not + * implement the java.io.Serializable interface. Subclasses of Objects that + * are not serializable can be serializable. In this case the non-serializable + * class must have a no-arg constructor to allow its fields to be initialized. + * In this case it is the responsibility of the subclass to save and restore + * the state of the non-serializable class. It is frequently the case that the + * fields of that class are accessible (public, package, or protected) or that + * there are get and set methods that can be used to restore the state. + * + *

Serialization of an object can be prevented by implementing writeObject + * and readObject methods that throw the NotSerializableException. The + * exception will be caught by the ObjectOutputStream and abort the + * serialization process. + * + *

Implementing the Externalizable interface allows the object to assume + * complete control over the contents and format of the object's serialized + * form. The methods of the Externalizable interface, writeExternal and + * readExternal, are called to save and restore the objects state. When + * implemented by a class they can write and read their own state using all of + * the methods of ObjectOutput and ObjectInput. It is the responsibility of + * the objects to handle any versioning that occurs. + * + *

Enum constants are serialized differently than ordinary serializable or + * externalizable objects. The serialized form of an enum constant consists + * solely of its name; field values of the constant are not transmitted. To + * serialize an enum constant, ObjectOutputStream writes the string returned by + * the constant's name method. Like other serializable or externalizable + * objects, enum constants can function as the targets of back references + * appearing subsequently in the serialization stream. The process by which + * enum constants are serialized cannot be customized; any class-specific + * writeObject and writeReplace methods defined by enum types are ignored + * during serialization. Similarly, any serialPersistentFields or + * serialVersionUID field declarations are also ignored--all enum types have a + * fixed serialVersionUID of 0L. + * + *

Primitive data, excluding serializable fields and externalizable data, is + * written to the ObjectOutputStream in block-data records. A block data record + * is composed of a header and data. The block data header consists of a marker + * and the number of bytes to follow the header. Consecutive primitive data + * writes are merged into one block-data record. The blocking factor used for + * a block-data record will be 1024 bytes. Each block-data record will be + * filled up to 1024 bytes, or be written whenever there is a termination of + * block-data mode. Calls to the ObjectOutputStream methods writeObject, + * defaultWriteObject and writeFields initially terminate any existing + * block-data record. + * + * @author Mike Warres + * @author Roger Riggs + * @see java.io.DataOutput + * @see java.io.ObjectInputStream + * @see java.io.Serializable + * @see java.io.Externalizable + * @see Object Serialization Specification, Section 2, Object Output Classes + * @since JDK1.1 + */ +public class ObjectOutputStream + extends OutputStream implements ObjectOutput, ObjectStreamConstants +{ + /** filter stream for handling block data conversion */ + private final BlockDataOutputStream bout; + /** obj -> wire handle map */ + private final HandleTable handles; + /** obj -> replacement obj map */ + private final ReplaceTable subs; + /** stream protocol version */ + private int protocol = PROTOCOL_VERSION_2; + /** recursion depth */ + private int depth; + + /** buffer for writing primitive field values */ + private byte[] primVals; + + /** if true, invoke writeObjectOverride() instead of writeObject() */ + private final boolean enableOverride; + /** if true, invoke replaceObject() */ + private boolean enableReplace; + + // values below valid only during upcalls to writeObject()/writeExternal() + /** + * Context during upcalls to class-defined writeObject methods; holds + * object currently being serialized and descriptor for current class. + * Null when not during writeObject upcall. + */ + private Object curContext; + /** current PutField object */ + private PutFieldImpl curPut; + + /** custom storage for debug trace info */ + private final DebugTraceInfoStack debugInfoStack; + + /** + * value of "sun.io.serialization.extendedDebugInfo" property, + * as true or false for extended information about exception's place + */ + private static final boolean extendedDebugInfo = false; + + /** + * Creates an ObjectOutputStream that writes to the specified OutputStream. + * This constructor writes the serialization stream header to the + * underlying stream; callers may wish to flush the stream immediately to + * ensure that constructors for receiving ObjectInputStreams will not block + * when reading the header. + * + *

If a security manager is installed, this constructor will check for + * the "enableSubclassImplementation" SerializablePermission when invoked + * directly or indirectly by the constructor of a subclass which overrides + * the ObjectOutputStream.putFields or ObjectOutputStream.writeUnshared + * methods. + * + * @param out output stream to write to + * @throws IOException if an I/O error occurs while writing stream header + * @throws SecurityException if untrusted subclass illegally overrides + * security-sensitive methods + * @throws NullPointerException if out is null + * @since 1.4 + * @see ObjectOutputStream#ObjectOutputStream() + * @see ObjectOutputStream#putFields() + * @see ObjectInputStream#ObjectInputStream(InputStream) + */ + public ObjectOutputStream(OutputStream out) throws IOException { + verifySubclass(); + bout = new BlockDataOutputStream(out); + handles = new HandleTable(10, (float) 3.00); + subs = new ReplaceTable(10, (float) 3.00); + enableOverride = false; + writeStreamHeader(); + bout.setBlockDataMode(true); + if (extendedDebugInfo) { + debugInfoStack = new DebugTraceInfoStack(); + } else { + debugInfoStack = null; + } + } + + /** + * Provide a way for subclasses that are completely reimplementing + * ObjectOutputStream to not have to allocate private data just used by + * this implementation of ObjectOutputStream. + * + *

If there is a security manager installed, this method first calls the + * security manager's checkPermission method with a + * SerializablePermission("enableSubclassImplementation") + * permission to ensure it's ok to enable subclassing. + * + * @throws SecurityException if a security manager exists and its + * checkPermission method denies enabling + * subclassing. + * @see SecurityManager#checkPermission + * @see java.io.SerializablePermission + */ + protected ObjectOutputStream() throws IOException, SecurityException { + throw new SecurityException(); + } + + /** + * Specify stream protocol version to use when writing the stream. + * + *

This routine provides a hook to enable the current version of + * Serialization to write in a format that is backwards compatible to a + * previous version of the stream format. + * + *

Every effort will be made to avoid introducing additional + * backwards incompatibilities; however, sometimes there is no + * other alternative. + * + * @param version use ProtocolVersion from java.io.ObjectStreamConstants. + * @throws IllegalStateException if called after any objects + * have been serialized. + * @throws IllegalArgumentException if invalid version is passed in. + * @throws IOException if I/O errors occur + * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1 + * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_2 + * @since 1.2 + */ + public void useProtocolVersion(int version) throws IOException { + if (handles.size() != 0) { + // REMIND: implement better check for pristine stream? + throw new IllegalStateException("stream non-empty"); + } + switch (version) { + case PROTOCOL_VERSION_1: + case PROTOCOL_VERSION_2: + protocol = version; + break; + + default: + throw new IllegalArgumentException( + "unknown version: " + version); + } + } + + /** + * Write the specified object to the ObjectOutputStream. The class of the + * object, the signature of the class, and the values of the non-transient + * and non-static fields of the class and all of its supertypes are + * written. Default serialization for a class can be overridden using the + * writeObject and the readObject methods. Objects referenced by this + * object are written transitively so that a complete equivalent graph of + * objects can be reconstructed by an ObjectInputStream. + * + *

Exceptions are thrown for problems with the OutputStream and for + * classes that should not be serialized. All exceptions are fatal to the + * OutputStream, which is left in an indeterminate state, and it is up to + * the caller to ignore or recover the stream state. + * + * @throws InvalidClassException Something is wrong with a class used by + * serialization. + * @throws NotSerializableException Some object to be serialized does not + * implement the java.io.Serializable interface. + * @throws IOException Any exception thrown by the underlying + * OutputStream. + */ + public final void writeObject(Object obj) throws IOException { + if (enableOverride) { + writeObjectOverride(obj); + return; + } + try { + writeObject0(obj, false); + } catch (IOException ex) { + if (depth == 0) { + writeFatalException(ex); + } + throw ex; + } + } + + /** + * Method used by subclasses to override the default writeObject method. + * This method is called by trusted subclasses of ObjectInputStream that + * constructed ObjectInputStream using the protected no-arg constructor. + * The subclass is expected to provide an override method with the modifier + * "final". + * + * @param obj object to be written to the underlying stream + * @throws IOException if there are I/O errors while writing to the + * underlying stream + * @see #ObjectOutputStream() + * @see #writeObject(Object) + * @since 1.2 + */ + protected void writeObjectOverride(Object obj) throws IOException { + } + + /** + * Writes an "unshared" object to the ObjectOutputStream. This method is + * identical to writeObject, except that it always writes the given object + * as a new, unique object in the stream (as opposed to a back-reference + * pointing to a previously serialized instance). Specifically: + *

    + *
  • An object written via writeUnshared is always serialized in the + * same manner as a newly appearing object (an object that has not + * been written to the stream yet), regardless of whether or not the + * object has been written previously. + * + *
  • If writeObject is used to write an object that has been previously + * written with writeUnshared, the previous writeUnshared operation + * is treated as if it were a write of a separate object. In other + * words, ObjectOutputStream will never generate back-references to + * object data written by calls to writeUnshared. + *
+ * While writing an object via writeUnshared does not in itself guarantee a + * unique reference to the object when it is deserialized, it allows a + * single object to be defined multiple times in a stream, so that multiple + * calls to readUnshared by the receiver will not conflict. Note that the + * rules described above only apply to the base-level object written with + * writeUnshared, and not to any transitively referenced sub-objects in the + * object graph to be serialized. + * + *

ObjectOutputStream subclasses which override this method can only be + * constructed in security contexts possessing the + * "enableSubclassImplementation" SerializablePermission; any attempt to + * instantiate such a subclass without this permission will cause a + * SecurityException to be thrown. + * + * @param obj object to write to stream + * @throws NotSerializableException if an object in the graph to be + * serialized does not implement the Serializable interface + * @throws InvalidClassException if a problem exists with the class of an + * object to be serialized + * @throws IOException if an I/O error occurs during serialization + * @since 1.4 + */ + public void writeUnshared(Object obj) throws IOException { + try { + writeObject0(obj, true); + } catch (IOException ex) { + if (depth == 0) { + writeFatalException(ex); + } + throw ex; + } + } + + /** + * Write the non-static and non-transient fields of the current class to + * this stream. This may only be called from the writeObject method of the + * class being serialized. It will throw the NotActiveException if it is + * called otherwise. + * + * @throws IOException if I/O errors occur while writing to the underlying + * OutputStream + */ + public void defaultWriteObject() throws IOException { + if ( curContext == null ) { + throw new NotActiveException("not in call to writeObject"); + } + Object curObj = null; // curContext.getObj(); + ObjectStreamClass curDesc = null; // curContext.getDesc(); + bout.setBlockDataMode(false); + defaultWriteFields(curObj, curDesc); + bout.setBlockDataMode(true); + } + + /** + * Retrieve the object used to buffer persistent fields to be written to + * the stream. The fields will be written to the stream when writeFields + * method is called. + * + * @return an instance of the class Putfield that holds the serializable + * fields + * @throws IOException if I/O errors occur + * @since 1.2 + */ + public ObjectOutputStream.PutField putFields() throws IOException { + if (curPut == null) { + if (curContext == null) { + throw new NotActiveException("not in call to writeObject"); + } + Object curObj = null; // curContext.getObj(); + ObjectStreamClass curDesc = null; // curContext.getDesc(); + curPut = new PutFieldImpl(curDesc); + } + return curPut; + } + + /** + * Write the buffered fields to the stream. + * + * @throws IOException if I/O errors occur while writing to the underlying + * stream + * @throws NotActiveException Called when a classes writeObject method was + * not called to write the state of the object. + * @since 1.2 + */ + public void writeFields() throws IOException { + if (curPut == null) { + throw new NotActiveException("no current PutField object"); + } + bout.setBlockDataMode(false); + curPut.writeFields(); + bout.setBlockDataMode(true); + } + + /** + * Reset will disregard the state of any objects already written to the + * stream. The state is reset to be the same as a new ObjectOutputStream. + * The current point in the stream is marked as reset so the corresponding + * ObjectInputStream will be reset at the same point. Objects previously + * written to the stream will not be refered to as already being in the + * stream. They will be written to the stream again. + * + * @throws IOException if reset() is invoked while serializing an object. + */ + public void reset() throws IOException { + if (depth != 0) { + throw new IOException("stream active"); + } + bout.setBlockDataMode(false); + bout.writeByte(TC_RESET); + clear(); + bout.setBlockDataMode(true); + } + + /** + * Subclasses may implement this method to allow class data to be stored in + * the stream. By default this method does nothing. The corresponding + * method in ObjectInputStream is resolveClass. This method is called + * exactly once for each unique class in the stream. The class name and + * signature will have already been written to the stream. This method may + * make free use of the ObjectOutputStream to save any representation of + * the class it deems suitable (for example, the bytes of the class file). + * The resolveClass method in the corresponding subclass of + * ObjectInputStream must read and use any data or objects written by + * annotateClass. + * + * @param cl the class to annotate custom data for + * @throws IOException Any exception thrown by the underlying + * OutputStream. + */ + protected void annotateClass(Class cl) throws IOException { + } + + /** + * Subclasses may implement this method to store custom data in the stream + * along with descriptors for dynamic proxy classes. + * + *

This method is called exactly once for each unique proxy class + * descriptor in the stream. The default implementation of this method in + * ObjectOutputStream does nothing. + * + *

The corresponding method in ObjectInputStream is + * resolveProxyClass. For a given subclass of + * ObjectOutputStream that overrides this method, the + * resolveProxyClass method in the corresponding subclass of + * ObjectInputStream must read any data or objects written by + * annotateProxyClass. + * + * @param cl the proxy class to annotate custom data for + * @throws IOException any exception thrown by the underlying + * OutputStream + * @see ObjectInputStream#resolveProxyClass(String[]) + * @since 1.3 + */ + protected void annotateProxyClass(Class cl) throws IOException { + } + + /** + * This method will allow trusted subclasses of ObjectOutputStream to + * substitute one object for another during serialization. Replacing + * objects is disabled until enableReplaceObject is called. The + * enableReplaceObject method checks that the stream requesting to do + * replacement can be trusted. The first occurrence of each object written + * into the serialization stream is passed to replaceObject. Subsequent + * references to the object are replaced by the object returned by the + * original call to replaceObject. To ensure that the private state of + * objects is not unintentionally exposed, only trusted streams may use + * replaceObject. + * + *

The ObjectOutputStream.writeObject method takes a parameter of type + * Object (as opposed to type Serializable) to allow for cases where + * non-serializable objects are replaced by serializable ones. + * + *

When a subclass is replacing objects it must insure that either a + * complementary substitution must be made during deserialization or that + * the substituted object is compatible with every field where the + * reference will be stored. Objects whose type is not a subclass of the + * type of the field or array element abort the serialization by raising an + * exception and the object is not be stored. + * + *

This method is called only once when each object is first + * encountered. All subsequent references to the object will be redirected + * to the new object. This method should return the object to be + * substituted or the original object. + * + *

Null can be returned as the object to be substituted, but may cause + * NullReferenceException in classes that contain references to the + * original object since they may be expecting an object instead of + * null. + * + * @param obj the object to be replaced + * @return the alternate object that replaced the specified one + * @throws IOException Any exception thrown by the underlying + * OutputStream. + */ + protected Object replaceObject(Object obj) throws IOException { + return obj; + } + + /** + * Enable the stream to do replacement of objects in the stream. When + * enabled, the replaceObject method is called for every object being + * serialized. + * + *

If enable is true, and there is a security manager + * installed, this method first calls the security manager's + * checkPermission method with a + * SerializablePermission("enableSubstitution") permission to + * ensure it's ok to enable the stream to do replacement of objects in the + * stream. + * + * @param enable boolean parameter to enable replacement of objects + * @return the previous setting before this method was invoked + * @throws SecurityException if a security manager exists and its + * checkPermission method denies enabling the stream + * to do replacement of objects in the stream. + * @see SecurityManager#checkPermission + * @see java.io.SerializablePermission + */ + protected boolean enableReplaceObject(boolean enable) + throws SecurityException + { + throw new SecurityException(); + } + + /** + * The writeStreamHeader method is provided so subclasses can append or + * prepend their own header to the stream. It writes the magic number and + * version to the stream. + * + * @throws IOException if I/O errors occur while writing to the underlying + * stream + */ + protected void writeStreamHeader() throws IOException { + bout.writeShort(STREAM_MAGIC); + bout.writeShort(STREAM_VERSION); + } + + /** + * Write the specified class descriptor to the ObjectOutputStream. Class + * descriptors are used to identify the classes of objects written to the + * stream. Subclasses of ObjectOutputStream may override this method to + * customize the way in which class descriptors are written to the + * serialization stream. The corresponding method in ObjectInputStream, + * readClassDescriptor, should then be overridden to + * reconstitute the class descriptor from its custom stream representation. + * By default, this method writes class descriptors according to the format + * defined in the Object Serialization specification. + * + *

Note that this method will only be called if the ObjectOutputStream + * is not using the old serialization stream format (set by calling + * ObjectOutputStream's useProtocolVersion method). If this + * serialization stream is using the old format + * (PROTOCOL_VERSION_1), the class descriptor will be written + * internally in a manner that cannot be overridden or customized. + * + * @param desc class descriptor to write to the stream + * @throws IOException If an I/O error has occurred. + * @see java.io.ObjectInputStream#readClassDescriptor() + * @see #useProtocolVersion(int) + * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1 + * @since 1.3 + */ + protected void writeClassDescriptor(ObjectStreamClass desc) + throws IOException + { + desc.writeNonProxy(this); + } + + /** + * Writes a byte. This method will block until the byte is actually + * written. + * + * @param val the byte to be written to the stream + * @throws IOException If an I/O error has occurred. + */ + public void write(int val) throws IOException { + bout.write(val); + } + + /** + * Writes an array of bytes. This method will block until the bytes are + * actually written. + * + * @param buf the data to be written + * @throws IOException If an I/O error has occurred. + */ + public void write(byte[] buf) throws IOException { + bout.write(buf, 0, buf.length, false); + } + + /** + * Writes a sub array of bytes. + * + * @param buf the data to be written + * @param off the start offset in the data + * @param len the number of bytes that are written + * @throws IOException If an I/O error has occurred. + */ + public void write(byte[] buf, int off, int len) throws IOException { + if (buf == null) { + throw new NullPointerException(); + } + int endoff = off + len; + if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) { + throw new IndexOutOfBoundsException(); + } + bout.write(buf, off, len, false); + } + + /** + * Flushes the stream. This will write any buffered output bytes and flush + * through to the underlying stream. + * + * @throws IOException If an I/O error has occurred. + */ + public void flush() throws IOException { + bout.flush(); + } + + /** + * Drain any buffered data in ObjectOutputStream. Similar to flush but + * does not propagate the flush to the underlying stream. + * + * @throws IOException if I/O errors occur while writing to the underlying + * stream + */ + protected void drain() throws IOException { + bout.drain(); + } + + /** + * Closes the stream. This method must be called to release any resources + * associated with the stream. + * + * @throws IOException If an I/O error has occurred. + */ + public void close() throws IOException { + flush(); + clear(); + bout.close(); + } + + /** + * Writes a boolean. + * + * @param val the boolean to be written + * @throws IOException if I/O errors occur while writing to the underlying + * stream + */ + public void writeBoolean(boolean val) throws IOException { + bout.writeBoolean(val); + } + + /** + * Writes an 8 bit byte. + * + * @param val the byte value to be written + * @throws IOException if I/O errors occur while writing to the underlying + * stream + */ + public void writeByte(int val) throws IOException { + bout.writeByte(val); + } + + /** + * Writes a 16 bit short. + * + * @param val the short value to be written + * @throws IOException if I/O errors occur while writing to the underlying + * stream + */ + public void writeShort(int val) throws IOException { + bout.writeShort(val); + } + + /** + * Writes a 16 bit char. + * + * @param val the char value to be written + * @throws IOException if I/O errors occur while writing to the underlying + * stream + */ + public void writeChar(int val) throws IOException { + bout.writeChar(val); + } + + /** + * Writes a 32 bit int. + * + * @param val the integer value to be written + * @throws IOException if I/O errors occur while writing to the underlying + * stream + */ + public void writeInt(int val) throws IOException { + bout.writeInt(val); + } + + /** + * Writes a 64 bit long. + * + * @param val the long value to be written + * @throws IOException if I/O errors occur while writing to the underlying + * stream + */ + public void writeLong(long val) throws IOException { + bout.writeLong(val); + } + + /** + * Writes a 32 bit float. + * + * @param val the float value to be written + * @throws IOException if I/O errors occur while writing to the underlying + * stream + */ + public void writeFloat(float val) throws IOException { + bout.writeFloat(val); + } + + /** + * Writes a 64 bit double. + * + * @param val the double value to be written + * @throws IOException if I/O errors occur while writing to the underlying + * stream + */ + public void writeDouble(double val) throws IOException { + bout.writeDouble(val); + } + + /** + * Writes a String as a sequence of bytes. + * + * @param str the String of bytes to be written + * @throws IOException if I/O errors occur while writing to the underlying + * stream + */ + public void writeBytes(String str) throws IOException { + bout.writeBytes(str); + } + + /** + * Writes a String as a sequence of chars. + * + * @param str the String of chars to be written + * @throws IOException if I/O errors occur while writing to the underlying + * stream + */ + public void writeChars(String str) throws IOException { + bout.writeChars(str); + } + + /** + * Primitive data write of this String in + * modified UTF-8 + * format. Note that there is a + * significant difference between writing a String into the stream as + * primitive data or as an Object. A String instance written by writeObject + * is written into the stream as a String initially. Future writeObject() + * calls write references to the string into the stream. + * + * @param str the String to be written + * @throws IOException if I/O errors occur while writing to the underlying + * stream + */ + public void writeUTF(String str) throws IOException { + bout.writeUTF(str); + } + + /** + * Provide programmatic access to the persistent fields to be written + * to ObjectOutput. + * + * @since 1.2 + */ + public static abstract class PutField { + + /** + * Put the value of the named boolean field into the persistent field. + * + * @param name the name of the serializable field + * @param val the value to assign to the field + * @throws IllegalArgumentException if name does not + * match the name of a serializable field for the class whose fields + * are being written, or if the type of the named field is not + * boolean + */ + public abstract void put(String name, boolean val); + + /** + * Put the value of the named byte field into the persistent field. + * + * @param name the name of the serializable field + * @param val the value to assign to the field + * @throws IllegalArgumentException if name does not + * match the name of a serializable field for the class whose fields + * are being written, or if the type of the named field is not + * byte + */ + public abstract void put(String name, byte val); + + /** + * Put the value of the named char field into the persistent field. + * + * @param name the name of the serializable field + * @param val the value to assign to the field + * @throws IllegalArgumentException if name does not + * match the name of a serializable field for the class whose fields + * are being written, or if the type of the named field is not + * char + */ + public abstract void put(String name, char val); + + /** + * Put the value of the named short field into the persistent field. + * + * @param name the name of the serializable field + * @param val the value to assign to the field + * @throws IllegalArgumentException if name does not + * match the name of a serializable field for the class whose fields + * are being written, or if the type of the named field is not + * short + */ + public abstract void put(String name, short val); + + /** + * Put the value of the named int field into the persistent field. + * + * @param name the name of the serializable field + * @param val the value to assign to the field + * @throws IllegalArgumentException if name does not + * match the name of a serializable field for the class whose fields + * are being written, or if the type of the named field is not + * int + */ + public abstract void put(String name, int val); + + /** + * Put the value of the named long field into the persistent field. + * + * @param name the name of the serializable field + * @param val the value to assign to the field + * @throws IllegalArgumentException if name does not + * match the name of a serializable field for the class whose fields + * are being written, or if the type of the named field is not + * long + */ + public abstract void put(String name, long val); + + /** + * Put the value of the named float field into the persistent field. + * + * @param name the name of the serializable field + * @param val the value to assign to the field + * @throws IllegalArgumentException if name does not + * match the name of a serializable field for the class whose fields + * are being written, or if the type of the named field is not + * float + */ + public abstract void put(String name, float val); + + /** + * Put the value of the named double field into the persistent field. + * + * @param name the name of the serializable field + * @param val the value to assign to the field + * @throws IllegalArgumentException if name does not + * match the name of a serializable field for the class whose fields + * are being written, or if the type of the named field is not + * double + */ + public abstract void put(String name, double val); + + /** + * Put the value of the named Object field into the persistent field. + * + * @param name the name of the serializable field + * @param val the value to assign to the field + * (which may be null) + * @throws IllegalArgumentException if name does not + * match the name of a serializable field for the class whose fields + * are being written, or if the type of the named field is not a + * reference type + */ + public abstract void put(String name, Object val); + + /** + * Write the data and fields to the specified ObjectOutput stream, + * which must be the same stream that produced this + * PutField object. + * + * @param out the stream to write the data and fields to + * @throws IOException if I/O errors occur while writing to the + * underlying stream + * @throws IllegalArgumentException if the specified stream is not + * the same stream that produced this PutField + * object + * @deprecated This method does not write the values contained by this + * PutField object in a proper format, and may + * result in corruption of the serialization stream. The + * correct way to write PutField data is by + * calling the {@link java.io.ObjectOutputStream#writeFields()} + * method. + */ + @Deprecated + public abstract void write(ObjectOutput out) throws IOException; + } + + + /** + * Returns protocol version in use. + */ + int getProtocolVersion() { + return protocol; + } + + /** + * Writes string without allowing it to be replaced in stream. Used by + * ObjectStreamClass to write class descriptor type strings. + */ + void writeTypeString(String str) throws IOException { + int handle; + if (str == null) { + writeNull(); + } else if ((handle = handles.lookup(str)) != -1) { + writeHandle(handle); + } else { + writeString(str, false); + } + } + + /** + * Verifies that this (possibly subclass) instance can be constructed + * without violating security constraints: the subclass must not override + * security-sensitive non-final methods, or else the + * "enableSubclassImplementation" SerializablePermission is checked. + */ + private void verifySubclass() { + Class cl = getClass(); + if (cl == ObjectOutputStream.class) { + return; + } + throw new SecurityException(); + } + + /** + * Clears internal data structures. + */ + private void clear() { + subs.clear(); + handles.clear(); + } + + /** + * Underlying writeObject/writeUnshared implementation. + */ + private void writeObject0(Object obj, boolean unshared) + throws IOException + { + boolean oldMode = bout.setBlockDataMode(false); + depth++; + try { + // handle previously written and non-replaceable objects + int h; + if ((obj = subs.lookup(obj)) == null) { + writeNull(); + return; + } else if (!unshared && (h = handles.lookup(obj)) != -1) { + writeHandle(h); + return; + } else if (obj instanceof Class) { + writeClass((Class) obj, unshared); + return; + } else if (obj instanceof ObjectStreamClass) { + writeClassDesc((ObjectStreamClass) obj, unshared); + return; + } + + // check for replacement object + Object orig = obj; + Class cl = obj.getClass(); + ObjectStreamClass desc; + for (;;) { + // REMIND: skip this check for strings/arrays? + Class repCl; + desc = ObjectStreamClass.lookup(cl, true); + if (!desc.hasWriteReplaceMethod() || + (obj = desc.invokeWriteReplace(obj)) == null || + (repCl = obj.getClass()) == cl) + { + break; + } + cl = repCl; + } + if (enableReplace) { + Object rep = replaceObject(obj); + if (rep != obj && rep != null) { + cl = rep.getClass(); + desc = ObjectStreamClass.lookup(cl, true); + } + obj = rep; + } + + // if object replaced, run through original checks a second time + if (obj != orig) { + subs.assign(orig, obj); + if (obj == null) { + writeNull(); + return; + } else if (!unshared && (h = handles.lookup(obj)) != -1) { + writeHandle(h); + return; + } else if (obj instanceof Class) { + writeClass((Class) obj, unshared); + return; + } else if (obj instanceof ObjectStreamClass) { + writeClassDesc((ObjectStreamClass) obj, unshared); + return; + } + } + + // remaining cases + if (obj instanceof String) { + writeString((String) obj, unshared); + } else if (cl.isArray()) { + writeArray(obj, desc, unshared); + } else if (obj instanceof Enum) { + writeEnum((Enum) obj, desc, unshared); + } else if (obj instanceof Serializable) { + writeOrdinaryObject(obj, desc, unshared); + } else { + if (extendedDebugInfo) { + throw new NotSerializableException( + cl.getName() + "\n" + debugInfoStack.toString()); + } else { + throw new NotSerializableException(cl.getName()); + } + } + } finally { + depth--; + bout.setBlockDataMode(oldMode); + } + } + + /** + * Writes null code to stream. + */ + private void writeNull() throws IOException { + bout.writeByte(TC_NULL); + } + + /** + * Writes given object handle to stream. + */ + private void writeHandle(int handle) throws IOException { + bout.writeByte(TC_REFERENCE); + bout.writeInt(baseWireHandle + handle); + } + + /** + * Writes representation of given class to stream. + */ + private void writeClass(Class cl, boolean unshared) throws IOException { + bout.writeByte(TC_CLASS); + writeClassDesc(ObjectStreamClass.lookup(cl, true), false); + handles.assign(unshared ? null : cl); + } + + /** + * Writes representation of given class descriptor to stream. + */ + private void writeClassDesc(ObjectStreamClass desc, boolean unshared) + throws IOException + { + int handle; + if (desc == null) { + writeNull(); + } else if (!unshared && (handle = handles.lookup(desc)) != -1) { + writeHandle(handle); + } else if (desc.isProxy()) { + writeProxyDesc(desc, unshared); + } else { + writeNonProxyDesc(desc, unshared); + } + } + + /** + * Writes class descriptor representing a dynamic proxy class to stream. + */ + private void writeProxyDesc(ObjectStreamClass desc, boolean unshared) + throws IOException + { + bout.writeByte(TC_PROXYCLASSDESC); + handles.assign(unshared ? null : desc); + + Class cl = desc.forClass(); + Class[] ifaces = cl.getInterfaces(); + bout.writeInt(ifaces.length); + for (int i = 0; i < ifaces.length; i++) { + bout.writeUTF(ifaces[i].getName()); + } + + bout.setBlockDataMode(true); + annotateProxyClass(cl); + bout.setBlockDataMode(false); + bout.writeByte(TC_ENDBLOCKDATA); + + writeClassDesc(desc.getSuperDesc(), false); + } + + /** + * Writes class descriptor representing a standard (i.e., not a dynamic + * proxy) class to stream. + */ + private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared) + throws IOException + { + bout.writeByte(TC_CLASSDESC); + handles.assign(unshared ? null : desc); + + if (protocol == PROTOCOL_VERSION_1) { + // do not invoke class descriptor write hook with old protocol + desc.writeNonProxy(this); + } else { + writeClassDescriptor(desc); + } + + Class cl = desc.forClass(); + bout.setBlockDataMode(true); + annotateClass(cl); + bout.setBlockDataMode(false); + bout.writeByte(TC_ENDBLOCKDATA); + + writeClassDesc(desc.getSuperDesc(), false); + } + + /** + * Writes given string to stream, using standard or long UTF format + * depending on string length. + */ + private void writeString(String str, boolean unshared) throws IOException { + handles.assign(unshared ? null : str); + long utflen = bout.getUTFLength(str); + if (utflen <= 0xFFFF) { + bout.writeByte(TC_STRING); + bout.writeUTF(str, utflen); + } else { + bout.writeByte(TC_LONGSTRING); + bout.writeLongUTF(str, utflen); + } + } + + /** + * Writes given array object to stream. + */ + private void writeArray(Object array, + ObjectStreamClass desc, + boolean unshared) + throws IOException + { + bout.writeByte(TC_ARRAY); + writeClassDesc(desc, false); + handles.assign(unshared ? null : array); + + Class ccl = desc.forClass().getComponentType(); + if (ccl.isPrimitive()) { + if (ccl == Integer.TYPE) { + int[] ia = (int[]) array; + bout.writeInt(ia.length); + bout.writeInts(ia, 0, ia.length); + } else if (ccl == Byte.TYPE) { + byte[] ba = (byte[]) array; + bout.writeInt(ba.length); + bout.write(ba, 0, ba.length, true); + } else if (ccl == Long.TYPE) { + long[] ja = (long[]) array; + bout.writeInt(ja.length); + bout.writeLongs(ja, 0, ja.length); + } else if (ccl == Float.TYPE) { + float[] fa = (float[]) array; + bout.writeInt(fa.length); + bout.writeFloats(fa, 0, fa.length); + } else if (ccl == Double.TYPE) { + double[] da = (double[]) array; + bout.writeInt(da.length); + bout.writeDoubles(da, 0, da.length); + } else if (ccl == Short.TYPE) { + short[] sa = (short[]) array; + bout.writeInt(sa.length); + bout.writeShorts(sa, 0, sa.length); + } else if (ccl == Character.TYPE) { + char[] ca = (char[]) array; + bout.writeInt(ca.length); + bout.writeChars(ca, 0, ca.length); + } else if (ccl == Boolean.TYPE) { + boolean[] za = (boolean[]) array; + bout.writeInt(za.length); + bout.writeBooleans(za, 0, za.length); + } else { + throw new InternalError(); + } + } else { + Object[] objs = (Object[]) array; + int len = objs.length; + bout.writeInt(len); + if (extendedDebugInfo) { + debugInfoStack.push( + "array (class \"" + array.getClass().getName() + + "\", size: " + len + ")"); + } + try { + for (int i = 0; i < len; i++) { + if (extendedDebugInfo) { + debugInfoStack.push( + "element of array (index: " + i + ")"); + } + try { + writeObject0(objs[i], false); + } finally { + if (extendedDebugInfo) { + debugInfoStack.pop(); + } + } + } + } finally { + if (extendedDebugInfo) { + debugInfoStack.pop(); + } + } + } + } + + /** + * Writes given enum constant to stream. + */ + private void writeEnum(Enum en, + ObjectStreamClass desc, + boolean unshared) + throws IOException + { + bout.writeByte(TC_ENUM); + ObjectStreamClass sdesc = desc.getSuperDesc(); + writeClassDesc((sdesc.forClass() == Enum.class) ? desc : sdesc, false); + handles.assign(unshared ? null : en); + writeString(en.name(), false); + } + + /** + * Writes representation of a "ordinary" (i.e., not a String, Class, + * ObjectStreamClass, array, or enum constant) serializable object to the + * stream. + */ + private void writeOrdinaryObject(Object obj, + ObjectStreamClass desc, + boolean unshared) + throws IOException + { + if (extendedDebugInfo) { + debugInfoStack.push( + (depth == 1 ? "root " : "") + "object (class \"" + + obj.getClass().getName() + "\", " + obj.toString() + ")"); + } + try { + desc.checkSerialize(); + + bout.writeByte(TC_OBJECT); + writeClassDesc(desc, false); + handles.assign(unshared ? null : obj); + if (desc.isExternalizable() && !desc.isProxy()) { + writeExternalData((Externalizable) obj); + } else { + writeSerialData(obj, desc); + } + } finally { + if (extendedDebugInfo) { + debugInfoStack.pop(); + } + } + } + + /** + * Writes externalizable data of given object by invoking its + * writeExternal() method. + */ + private void writeExternalData(Externalizable obj) throws IOException { + PutFieldImpl oldPut = curPut; + curPut = null; + + if (extendedDebugInfo) { + debugInfoStack.push("writeExternal data"); + } + Object oldContext = curContext; + try { + curContext = null; + if (protocol == PROTOCOL_VERSION_1) { + obj.writeExternal(this); + } else { + bout.setBlockDataMode(true); + obj.writeExternal(this); + bout.setBlockDataMode(false); + bout.writeByte(TC_ENDBLOCKDATA); + } + } finally { + curContext = oldContext; + if (extendedDebugInfo) { + debugInfoStack.pop(); + } + } + + curPut = oldPut; + } + + /** + * Writes instance data for each serializable class of given object, from + * superclass to subclass. + */ + private void writeSerialData(Object obj, ObjectStreamClass desc) + throws IOException + { + ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout(); + for (int i = 0; i < slots.length; i++) { + ObjectStreamClass slotDesc = slots[i].desc; + if (slotDesc.hasWriteObjectMethod()) { + PutFieldImpl oldPut = curPut; + curPut = null; + Object oldContext = curContext; + + if (extendedDebugInfo) { + debugInfoStack.push( + "custom writeObject data (class \"" + + slotDesc.getName() + "\")"); + } + try { + curContext = new Object(); //new SerialCallbackContext(obj, slotDesc); + bout.setBlockDataMode(true); + slotDesc.invokeWriteObject(obj, this); + bout.setBlockDataMode(false); + bout.writeByte(TC_ENDBLOCKDATA); + } finally { + //curContext.setUsed(); + curContext = oldContext; + if (extendedDebugInfo) { + debugInfoStack.pop(); + } + } + + curPut = oldPut; + } else { + defaultWriteFields(obj, slotDesc); + } + } + } + + /** + * Fetches and writes values of serializable fields of given object to + * stream. The given class descriptor specifies which field values to + * write, and in which order they should be written. + */ + private void defaultWriteFields(Object obj, ObjectStreamClass desc) + throws IOException + { + // REMIND: perform conservative isInstance check here? + desc.checkDefaultSerialize(); + + int primDataSize = desc.getPrimDataSize(); + if (primVals == null || primVals.length < primDataSize) { + primVals = new byte[primDataSize]; + } + desc.getPrimFieldValues(obj, primVals); + bout.write(primVals, 0, primDataSize, false); + + ObjectStreamField[] fields = desc.getFields(false); + Object[] objVals = new Object[desc.getNumObjFields()]; + int numPrimFields = fields.length - objVals.length; + desc.getObjFieldValues(obj, objVals); + for (int i = 0; i < objVals.length; i++) { + if (extendedDebugInfo) { + debugInfoStack.push( + "field (class \"" + desc.getName() + "\", name: \"" + + fields[numPrimFields + i].getName() + "\", type: \"" + + fields[numPrimFields + i].getType() + "\")"); + } + try { + writeObject0(objVals[i], + fields[numPrimFields + i].isUnshared()); + } finally { + if (extendedDebugInfo) { + debugInfoStack.pop(); + } + } + } + } + + /** + * Attempts to write to stream fatal IOException that has caused + * serialization to abort. + */ + private void writeFatalException(IOException ex) throws IOException { + /* + * Note: the serialization specification states that if a second + * IOException occurs while attempting to serialize the original fatal + * exception to the stream, then a StreamCorruptedException should be + * thrown (section 2.1). However, due to a bug in previous + * implementations of serialization, StreamCorruptedExceptions were + * rarely (if ever) actually thrown--the "root" exceptions from + * underlying streams were thrown instead. This historical behavior is + * followed here for consistency. + */ + clear(); + boolean oldMode = bout.setBlockDataMode(false); + try { + bout.writeByte(TC_EXCEPTION); + writeObject0(ex, false); + clear(); + } finally { + bout.setBlockDataMode(oldMode); + } + } + + /** + * Converts specified span of float values into byte values. + */ + // REMIND: remove once hotspot inlines Float.floatToIntBits + private static native void floatsToBytes(float[] src, int srcpos, + byte[] dst, int dstpos, + int nfloats); + + /** + * Converts specified span of double values into byte values. + */ + // REMIND: remove once hotspot inlines Double.doubleToLongBits + private static native void doublesToBytes(double[] src, int srcpos, + byte[] dst, int dstpos, + int ndoubles); + + /** + * Default PutField implementation. + */ + private class PutFieldImpl extends PutField { + + /** class descriptor describing serializable fields */ + private final ObjectStreamClass desc; + /** primitive field values */ + private final byte[] primVals; + /** object field values */ + private final Object[] objVals; + + /** + * Creates PutFieldImpl object for writing fields defined in given + * class descriptor. + */ + PutFieldImpl(ObjectStreamClass desc) { + this.desc = desc; + primVals = new byte[desc.getPrimDataSize()]; + objVals = new Object[desc.getNumObjFields()]; + } + + public void put(String name, boolean val) { + Bits.putBoolean(primVals, getFieldOffset(name, Boolean.TYPE), val); + } + + public void put(String name, byte val) { + primVals[getFieldOffset(name, Byte.TYPE)] = val; + } + + public void put(String name, char val) { + Bits.putChar(primVals, getFieldOffset(name, Character.TYPE), val); + } + + public void put(String name, short val) { + Bits.putShort(primVals, getFieldOffset(name, Short.TYPE), val); + } + + public void put(String name, int val) { + Bits.putInt(primVals, getFieldOffset(name, Integer.TYPE), val); + } + + public void put(String name, float val) { + Bits.putFloat(primVals, getFieldOffset(name, Float.TYPE), val); + } + + public void put(String name, long val) { + Bits.putLong(primVals, getFieldOffset(name, Long.TYPE), val); + } + + public void put(String name, double val) { + Bits.putDouble(primVals, getFieldOffset(name, Double.TYPE), val); + } + + public void put(String name, Object val) { + objVals[getFieldOffset(name, Object.class)] = val; + } + + // deprecated in ObjectOutputStream.PutField + public void write(ObjectOutput out) throws IOException { + /* + * Applications should *not* use this method to write PutField + * data, as it will lead to stream corruption if the PutField + * object writes any primitive data (since block data mode is not + * unset/set properly, as is done in OOS.writeFields()). This + * broken implementation is being retained solely for behavioral + * compatibility, in order to support applications which use + * OOS.PutField.write() for writing only non-primitive data. + * + * Serialization of unshared objects is not implemented here since + * it is not necessary for backwards compatibility; also, unshared + * semantics may not be supported by the given ObjectOutput + * instance. Applications which write unshared objects using the + * PutField API must use OOS.writeFields(). + */ + if (ObjectOutputStream.this != out) { + throw new IllegalArgumentException("wrong stream"); + } + out.write(primVals, 0, primVals.length); + + ObjectStreamField[] fields = desc.getFields(false); + int numPrimFields = fields.length - objVals.length; + // REMIND: warn if numPrimFields > 0? + for (int i = 0; i < objVals.length; i++) { + if (fields[numPrimFields + i].isUnshared()) { + throw new IOException("cannot write unshared object"); + } + out.writeObject(objVals[i]); + } + } + + /** + * Writes buffered primitive data and object fields to stream. + */ + void writeFields() throws IOException { + bout.write(primVals, 0, primVals.length, false); + + ObjectStreamField[] fields = desc.getFields(false); + int numPrimFields = fields.length - objVals.length; + for (int i = 0; i < objVals.length; i++) { + if (extendedDebugInfo) { + debugInfoStack.push( + "field (class \"" + desc.getName() + "\", name: \"" + + fields[numPrimFields + i].getName() + "\", type: \"" + + fields[numPrimFields + i].getType() + "\")"); + } + try { + writeObject0(objVals[i], + fields[numPrimFields + i].isUnshared()); + } finally { + if (extendedDebugInfo) { + debugInfoStack.pop(); + } + } + } + } + + /** + * Returns offset of field with given name and type. A specified type + * of null matches all types, Object.class matches all non-primitive + * types, and any other non-null type matches assignable types only. + * Throws IllegalArgumentException if no matching field found. + */ + private int getFieldOffset(String name, Class type) { + ObjectStreamField field = desc.getField(name, type); + if (field == null) { + throw new IllegalArgumentException("no such field " + name + + " with type " + type); + } + return field.getOffset(); + } + } + + /** + * Buffered output stream with two modes: in default mode, outputs data in + * same format as DataOutputStream; in "block data" mode, outputs data + * bracketed by block data markers (see object serialization specification + * for details). + */ + private static class BlockDataOutputStream + extends OutputStream implements DataOutput + { + /** maximum data block length */ + private static final int MAX_BLOCK_SIZE = 1024; + /** maximum data block header length */ + private static final int MAX_HEADER_SIZE = 5; + /** (tunable) length of char buffer (for writing strings) */ + private static final int CHAR_BUF_SIZE = 256; + + /** buffer for writing general/block data */ + private final byte[] buf = new byte[MAX_BLOCK_SIZE]; + /** buffer for writing block data headers */ + private final byte[] hbuf = new byte[MAX_HEADER_SIZE]; + /** char buffer for fast string writes */ + private final char[] cbuf = new char[CHAR_BUF_SIZE]; + + /** block data mode */ + private boolean blkmode = false; + /** current offset into buf */ + private int pos = 0; + + /** underlying output stream */ + private final OutputStream out; + /** loopback stream (for data writes that span data blocks) */ + private final DataOutputStream dout; + + /** + * Creates new BlockDataOutputStream on top of given underlying stream. + * Block data mode is turned off by default. + */ + BlockDataOutputStream(OutputStream out) { + this.out = out; + dout = new DataOutputStream(this); + } + + /** + * Sets block data mode to the given mode (true == on, false == off) + * and returns the previous mode value. If the new mode is the same as + * the old mode, no action is taken. If the new mode differs from the + * old mode, any buffered data is flushed before switching to the new + * mode. + */ + boolean setBlockDataMode(boolean mode) throws IOException { + if (blkmode == mode) { + return blkmode; + } + drain(); + blkmode = mode; + return !blkmode; + } + + /** + * Returns true if the stream is currently in block data mode, false + * otherwise. + */ + boolean getBlockDataMode() { + return blkmode; + } + + /* ----------------- generic output stream methods ----------------- */ + /* + * The following methods are equivalent to their counterparts in + * OutputStream, except that they partition written data into data + * blocks when in block data mode. + */ + + public void write(int b) throws IOException { + if (pos >= MAX_BLOCK_SIZE) { + drain(); + } + buf[pos++] = (byte) b; + } + + public void write(byte[] b) throws IOException { + write(b, 0, b.length, false); + } + + public void write(byte[] b, int off, int len) throws IOException { + write(b, off, len, false); + } + + public void flush() throws IOException { + drain(); + out.flush(); + } + + public void close() throws IOException { + flush(); + out.close(); + } + + /** + * Writes specified span of byte values from given array. If copy is + * true, copies the values to an intermediate buffer before writing + * them to underlying stream (to avoid exposing a reference to the + * original byte array). + */ + void write(byte[] b, int off, int len, boolean copy) + throws IOException + { + if (!(copy || blkmode)) { // write directly + drain(); + out.write(b, off, len); + return; + } + + while (len > 0) { + if (pos >= MAX_BLOCK_SIZE) { + drain(); + } + if (len >= MAX_BLOCK_SIZE && !copy && pos == 0) { + // avoid unnecessary copy + writeBlockHeader(MAX_BLOCK_SIZE); + out.write(b, off, MAX_BLOCK_SIZE); + off += MAX_BLOCK_SIZE; + len -= MAX_BLOCK_SIZE; + } else { + int wlen = Math.min(len, MAX_BLOCK_SIZE - pos); + System.arraycopy(b, off, buf, pos, wlen); + pos += wlen; + off += wlen; + len -= wlen; + } + } + } + + /** + * Writes all buffered data from this stream to the underlying stream, + * but does not flush underlying stream. + */ + void drain() throws IOException { + if (pos == 0) { + return; + } + if (blkmode) { + writeBlockHeader(pos); + } + out.write(buf, 0, pos); + pos = 0; + } + + /** + * Writes block data header. Data blocks shorter than 256 bytes are + * prefixed with a 2-byte header; all others start with a 5-byte + * header. + */ + private void writeBlockHeader(int len) throws IOException { + if (len <= 0xFF) { + hbuf[0] = TC_BLOCKDATA; + hbuf[1] = (byte) len; + out.write(hbuf, 0, 2); + } else { + hbuf[0] = TC_BLOCKDATALONG; + Bits.putInt(hbuf, 1, len); + out.write(hbuf, 0, 5); + } + } + + + /* ----------------- primitive data output methods ----------------- */ + /* + * The following methods are equivalent to their counterparts in + * DataOutputStream, except that they partition written data into data + * blocks when in block data mode. + */ + + public void writeBoolean(boolean v) throws IOException { + if (pos >= MAX_BLOCK_SIZE) { + drain(); + } + Bits.putBoolean(buf, pos++, v); + } + + public void writeByte(int v) throws IOException { + if (pos >= MAX_BLOCK_SIZE) { + drain(); + } + buf[pos++] = (byte) v; + } + + public void writeChar(int v) throws IOException { + if (pos + 2 <= MAX_BLOCK_SIZE) { + Bits.putChar(buf, pos, (char) v); + pos += 2; + } else { + dout.writeChar(v); + } + } + + public void writeShort(int v) throws IOException { + if (pos + 2 <= MAX_BLOCK_SIZE) { + Bits.putShort(buf, pos, (short) v); + pos += 2; + } else { + dout.writeShort(v); + } + } + + public void writeInt(int v) throws IOException { + if (pos + 4 <= MAX_BLOCK_SIZE) { + Bits.putInt(buf, pos, v); + pos += 4; + } else { + dout.writeInt(v); + } + } + + public void writeFloat(float v) throws IOException { + if (pos + 4 <= MAX_BLOCK_SIZE) { + Bits.putFloat(buf, pos, v); + pos += 4; + } else { + dout.writeFloat(v); + } + } + + public void writeLong(long v) throws IOException { + if (pos + 8 <= MAX_BLOCK_SIZE) { + Bits.putLong(buf, pos, v); + pos += 8; + } else { + dout.writeLong(v); + } + } + + public void writeDouble(double v) throws IOException { + if (pos + 8 <= MAX_BLOCK_SIZE) { + Bits.putDouble(buf, pos, v); + pos += 8; + } else { + dout.writeDouble(v); + } + } + + public void writeBytes(String s) throws IOException { + int endoff = s.length(); + int cpos = 0; + int csize = 0; + for (int off = 0; off < endoff; ) { + if (cpos >= csize) { + cpos = 0; + csize = Math.min(endoff - off, CHAR_BUF_SIZE); + s.getChars(off, off + csize, cbuf, 0); + } + if (pos >= MAX_BLOCK_SIZE) { + drain(); + } + int n = Math.min(csize - cpos, MAX_BLOCK_SIZE - pos); + int stop = pos + n; + while (pos < stop) { + buf[pos++] = (byte) cbuf[cpos++]; + } + off += n; + } + } + + public void writeChars(String s) throws IOException { + int endoff = s.length(); + for (int off = 0; off < endoff; ) { + int csize = Math.min(endoff - off, CHAR_BUF_SIZE); + s.getChars(off, off + csize, cbuf, 0); + writeChars(cbuf, 0, csize); + off += csize; + } + } + + public void writeUTF(String s) throws IOException { + writeUTF(s, getUTFLength(s)); + } + + + /* -------------- primitive data array output methods -------------- */ + /* + * The following methods write out spans of primitive data values. + * Though equivalent to calling the corresponding primitive write + * methods repeatedly, these methods are optimized for writing groups + * of primitive data values more efficiently. + */ + + void writeBooleans(boolean[] v, int off, int len) throws IOException { + int endoff = off + len; + while (off < endoff) { + if (pos >= MAX_BLOCK_SIZE) { + drain(); + } + int stop = Math.min(endoff, off + (MAX_BLOCK_SIZE - pos)); + while (off < stop) { + Bits.putBoolean(buf, pos++, v[off++]); + } + } + } + + void writeChars(char[] v, int off, int len) throws IOException { + int limit = MAX_BLOCK_SIZE - 2; + int endoff = off + len; + while (off < endoff) { + if (pos <= limit) { + int avail = (MAX_BLOCK_SIZE - pos) >> 1; + int stop = Math.min(endoff, off + avail); + while (off < stop) { + Bits.putChar(buf, pos, v[off++]); + pos += 2; + } + } else { + dout.writeChar(v[off++]); + } + } + } + + void writeShorts(short[] v, int off, int len) throws IOException { + int limit = MAX_BLOCK_SIZE - 2; + int endoff = off + len; + while (off < endoff) { + if (pos <= limit) { + int avail = (MAX_BLOCK_SIZE - pos) >> 1; + int stop = Math.min(endoff, off + avail); + while (off < stop) { + Bits.putShort(buf, pos, v[off++]); + pos += 2; + } + } else { + dout.writeShort(v[off++]); + } + } + } + + void writeInts(int[] v, int off, int len) throws IOException { + int limit = MAX_BLOCK_SIZE - 4; + int endoff = off + len; + while (off < endoff) { + if (pos <= limit) { + int avail = (MAX_BLOCK_SIZE - pos) >> 2; + int stop = Math.min(endoff, off + avail); + while (off < stop) { + Bits.putInt(buf, pos, v[off++]); + pos += 4; + } + } else { + dout.writeInt(v[off++]); + } + } + } + + void writeFloats(float[] v, int off, int len) throws IOException { + int limit = MAX_BLOCK_SIZE - 4; + int endoff = off + len; + while (off < endoff) { + if (pos <= limit) { + int avail = (MAX_BLOCK_SIZE - pos) >> 2; + int chunklen = Math.min(endoff - off, avail); + floatsToBytes(v, off, buf, pos, chunklen); + off += chunklen; + pos += chunklen << 2; + } else { + dout.writeFloat(v[off++]); + } + } + } + + void writeLongs(long[] v, int off, int len) throws IOException { + int limit = MAX_BLOCK_SIZE - 8; + int endoff = off + len; + while (off < endoff) { + if (pos <= limit) { + int avail = (MAX_BLOCK_SIZE - pos) >> 3; + int stop = Math.min(endoff, off + avail); + while (off < stop) { + Bits.putLong(buf, pos, v[off++]); + pos += 8; + } + } else { + dout.writeLong(v[off++]); + } + } + } + + void writeDoubles(double[] v, int off, int len) throws IOException { + int limit = MAX_BLOCK_SIZE - 8; + int endoff = off + len; + while (off < endoff) { + if (pos <= limit) { + int avail = (MAX_BLOCK_SIZE - pos) >> 3; + int chunklen = Math.min(endoff - off, avail); + doublesToBytes(v, off, buf, pos, chunklen); + off += chunklen; + pos += chunklen << 3; + } else { + dout.writeDouble(v[off++]); + } + } + } + + /** + * Returns the length in bytes of the UTF encoding of the given string. + */ + long getUTFLength(String s) { + int len = s.length(); + long utflen = 0; + for (int off = 0; off < len; ) { + int csize = Math.min(len - off, CHAR_BUF_SIZE); + s.getChars(off, off + csize, cbuf, 0); + for (int cpos = 0; cpos < csize; cpos++) { + char c = cbuf[cpos]; + if (c >= 0x0001 && c <= 0x007F) { + utflen++; + } else if (c > 0x07FF) { + utflen += 3; + } else { + utflen += 2; + } + } + off += csize; + } + return utflen; + } + + /** + * Writes the given string in UTF format. This method is used in + * situations where the UTF encoding length of the string is already + * known; specifying it explicitly avoids a prescan of the string to + * determine its UTF length. + */ + void writeUTF(String s, long utflen) throws IOException { + if (utflen > 0xFFFFL) { + throw new UTFDataFormatException(); + } + writeShort((int) utflen); + if (utflen == (long) s.length()) { + writeBytes(s); + } else { + writeUTFBody(s); + } + } + + /** + * Writes given string in "long" UTF format. "Long" UTF format is + * identical to standard UTF, except that it uses an 8 byte header + * (instead of the standard 2 bytes) to convey the UTF encoding length. + */ + void writeLongUTF(String s) throws IOException { + writeLongUTF(s, getUTFLength(s)); + } + + /** + * Writes given string in "long" UTF format, where the UTF encoding + * length of the string is already known. + */ + void writeLongUTF(String s, long utflen) throws IOException { + writeLong(utflen); + if (utflen == (long) s.length()) { + writeBytes(s); + } else { + writeUTFBody(s); + } + } + + /** + * Writes the "body" (i.e., the UTF representation minus the 2-byte or + * 8-byte length header) of the UTF encoding for the given string. + */ + private void writeUTFBody(String s) throws IOException { + int limit = MAX_BLOCK_SIZE - 3; + int len = s.length(); + for (int off = 0; off < len; ) { + int csize = Math.min(len - off, CHAR_BUF_SIZE); + s.getChars(off, off + csize, cbuf, 0); + for (int cpos = 0; cpos < csize; cpos++) { + char c = cbuf[cpos]; + if (pos <= limit) { + if (c <= 0x007F && c != 0) { + buf[pos++] = (byte) c; + } else if (c > 0x07FF) { + buf[pos + 2] = (byte) (0x80 | ((c >> 0) & 0x3F)); + buf[pos + 1] = (byte) (0x80 | ((c >> 6) & 0x3F)); + buf[pos + 0] = (byte) (0xE0 | ((c >> 12) & 0x0F)); + pos += 3; + } else { + buf[pos + 1] = (byte) (0x80 | ((c >> 0) & 0x3F)); + buf[pos + 0] = (byte) (0xC0 | ((c >> 6) & 0x1F)); + pos += 2; + } + } else { // write one byte at a time to normalize block + if (c <= 0x007F && c != 0) { + write(c); + } else if (c > 0x07FF) { + write(0xE0 | ((c >> 12) & 0x0F)); + write(0x80 | ((c >> 6) & 0x3F)); + write(0x80 | ((c >> 0) & 0x3F)); + } else { + write(0xC0 | ((c >> 6) & 0x1F)); + write(0x80 | ((c >> 0) & 0x3F)); + } + } + } + off += csize; + } + } + } + + /** + * Lightweight identity hash table which maps objects to integer handles, + * assigned in ascending order. + */ + private static class HandleTable { + + /* number of mappings in table/next available handle */ + private int size; + /* size threshold determining when to expand hash spine */ + private int threshold; + /* factor for computing size threshold */ + private final float loadFactor; + /* maps hash value -> candidate handle value */ + private int[] spine; + /* maps handle value -> next candidate handle value */ + private int[] next; + /* maps handle value -> associated object */ + private Object[] objs; + + /** + * Creates new HandleTable with given capacity and load factor. + */ + HandleTable(int initialCapacity, float loadFactor) { + this.loadFactor = loadFactor; + spine = new int[initialCapacity]; + next = new int[initialCapacity]; + objs = new Object[initialCapacity]; + threshold = (int) (initialCapacity * loadFactor); + clear(); + } + + /** + * Assigns next available handle to given object, and returns handle + * value. Handles are assigned in ascending order starting at 0. + */ + int assign(Object obj) { + if (size >= next.length) { + growEntries(); + } + if (size >= threshold) { + growSpine(); + } + insert(obj, size); + return size++; + } + + /** + * Looks up and returns handle associated with given object, or -1 if + * no mapping found. + */ + int lookup(Object obj) { + if (size == 0) { + return -1; + } + int index = hash(obj) % spine.length; + for (int i = spine[index]; i >= 0; i = next[i]) { + if (objs[i] == obj) { + return i; + } + } + return -1; + } + + /** + * Resets table to its initial (empty) state. + */ + void clear() { + Arrays.fill(spine, -1); + Arrays.fill(objs, 0, size, null); + size = 0; + } + + /** + * Returns the number of mappings currently in table. + */ + int size() { + return size; + } + + /** + * Inserts mapping object -> handle mapping into table. Assumes table + * is large enough to accommodate new mapping. + */ + private void insert(Object obj, int handle) { + int index = hash(obj) % spine.length; + objs[handle] = obj; + next[handle] = spine[index]; + spine[index] = handle; + } + + /** + * Expands the hash "spine" -- equivalent to increasing the number of + * buckets in a conventional hash table. + */ + private void growSpine() { + spine = new int[(spine.length << 1) + 1]; + threshold = (int) (spine.length * loadFactor); + Arrays.fill(spine, -1); + for (int i = 0; i < size; i++) { + insert(objs[i], i); + } + } + + /** + * Increases hash table capacity by lengthening entry arrays. + */ + private void growEntries() { + int newLength = (next.length << 1) + 1; + int[] newNext = new int[newLength]; + System.arraycopy(next, 0, newNext, 0, size); + next = newNext; + + Object[] newObjs = new Object[newLength]; + System.arraycopy(objs, 0, newObjs, 0, size); + objs = newObjs; + } + + /** + * Returns hash value for given object. + */ + private int hash(Object obj) { + return System.identityHashCode(obj) & 0x7FFFFFFF; + } + } + + /** + * Lightweight identity hash table which maps objects to replacement + * objects. + */ + private static class ReplaceTable { + + /* maps object -> index */ + private final HandleTable htab; + /* maps index -> replacement object */ + private Object[] reps; + + /** + * Creates new ReplaceTable with given capacity and load factor. + */ + ReplaceTable(int initialCapacity, float loadFactor) { + htab = new HandleTable(initialCapacity, loadFactor); + reps = new Object[initialCapacity]; + } + + /** + * Enters mapping from object to replacement object. + */ + void assign(Object obj, Object rep) { + int index = htab.assign(obj); + while (index >= reps.length) { + grow(); + } + reps[index] = rep; + } + + /** + * Looks up and returns replacement for given object. If no + * replacement is found, returns the lookup object itself. + */ + Object lookup(Object obj) { + int index = htab.lookup(obj); + return (index >= 0) ? reps[index] : obj; + } + + /** + * Resets table to its initial (empty) state. + */ + void clear() { + Arrays.fill(reps, 0, htab.size(), null); + htab.clear(); + } + + /** + * Returns the number of mappings currently in table. + */ + int size() { + return htab.size(); + } + + /** + * Increases table capacity. + */ + private void grow() { + Object[] newReps = new Object[(reps.length << 1) + 1]; + System.arraycopy(reps, 0, newReps, 0, reps.length); + reps = newReps; + } + } + + /** + * Stack to keep debug information about the state of the + * serialization process, for embedding in exception messages. + */ + private static class DebugTraceInfoStack { + private final List stack; + + DebugTraceInfoStack() { + stack = new ArrayList<>(); + } + + /** + * Removes all of the elements from enclosed list. + */ + void clear() { + stack.clear(); + } + + /** + * Removes the object at the top of enclosed list. + */ + void pop() { + stack.remove(stack.size()-1); + } + + /** + * Pushes a String onto the top of enclosed list. + */ + void push(String entry) { + stack.add("\t- " + entry); + } + + /** + * Returns a string representation of this object + */ + public String toString() { + StringBuilder buffer = new StringBuilder(); + if (!stack.isEmpty()) { + for(int i = stack.size(); i > 0; i-- ) { + buffer.append(stack.get(i-1) + ((i != 1) ? "\n" : "")); + } + } + return buffer.toString(); + } + } + +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/ObjectStreamClass.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/ObjectStreamClass.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,1396 @@ +/* + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; + +/** + * Serialization's descriptor for classes. It contains the name and + * serialVersionUID of the class. The ObjectStreamClass for a specific class + * loaded in this Java VM can be found/created using the lookup method. + * + *

The algorithm to compute the SerialVersionUID is described in + * Object + * Serialization Specification, Section 4.6, Stream Unique Identifiers. + * + * @author Mike Warres + * @author Roger Riggs + * @see ObjectStreamField + * @see Object Serialization Specification, Section 4, Class Descriptors + * @since JDK1.1 + */ +public class ObjectStreamClass implements Serializable { + + /** serialPersistentFields value indicating no serializable fields */ + public static final ObjectStreamField[] NO_FIELDS = + new ObjectStreamField[0]; + + private static final long serialVersionUID = -6120832682080437368L; + private static final ObjectStreamField[] serialPersistentFields = + NO_FIELDS; + + + /** class associated with this descriptor (if any) */ + private Class cl; + /** name of class represented by this descriptor */ + private String name; + /** serialVersionUID of represented class (null if not computed yet) */ + private volatile Long suid; + + /** true if represents dynamic proxy class */ + private boolean isProxy; + /** true if represents enum type */ + private boolean isEnum; + /** true if represented class implements Serializable */ + private boolean serializable; + /** true if represented class implements Externalizable */ + private boolean externalizable; + /** true if desc has data written by class-defined writeObject method */ + private boolean hasWriteObjectData; + /** + * true if desc has externalizable data written in block data format; this + * must be true by default to accommodate ObjectInputStream subclasses which + * override readClassDescriptor() to return class descriptors obtained from + * ObjectStreamClass.lookup() (see 4461737) + */ + private boolean hasBlockExternalData = true; + + /** exception (if any) thrown while attempting to resolve class */ + private ClassNotFoundException resolveEx; + /** exception (if any) to throw if non-enum deserialization attempted */ + private InvalidClassException deserializeEx; + /** exception (if any) to throw if non-enum serialization attempted */ + private InvalidClassException serializeEx; + /** exception (if any) to throw if default serialization attempted */ + private InvalidClassException defaultSerializeEx; + + /** serializable fields */ + private ObjectStreamField[] fields; + /** aggregate marshalled size of primitive fields */ + private int primDataSize; + /** number of non-primitive fields */ + private int numObjFields; + /** reflector for setting/getting serializable field values */ +// private FieldReflector fieldRefl; + /** data layout of serialized objects described by this class desc */ + private volatile ClassDataSlot[] dataLayout; + + /** serialization-appropriate constructor, or null if none */ + private Constructor cons; + /** class-defined writeObject method, or null if none */ + private Method writeObjectMethod; + /** class-defined readObject method, or null if none */ + private Method readObjectMethod; + /** class-defined readObjectNoData method, or null if none */ + private Method readObjectNoDataMethod; + /** class-defined writeReplace method, or null if none */ + private Method writeReplaceMethod; + /** class-defined readResolve method, or null if none */ + private Method readResolveMethod; + + /** local class descriptor for represented class (may point to self) */ + private ObjectStreamClass localDesc; + /** superclass descriptor appearing in stream */ + private ObjectStreamClass superDesc; + + /** + * Initializes native code. + */ + private static native void initNative(); + static { + initNative(); + } + + /** + * Find the descriptor for a class that can be serialized. Creates an + * ObjectStreamClass instance if one does not exist yet for class. Null is + * returned if the specified class does not implement java.io.Serializable + * or java.io.Externalizable. + * + * @param cl class for which to get the descriptor + * @return the class descriptor for the specified class + */ + public static ObjectStreamClass lookup(Class cl) { + return lookup(cl, false); + } + + /** + * Returns the descriptor for any class, regardless of whether it + * implements {@link Serializable}. + * + * @param cl class for which to get the descriptor + * @return the class descriptor for the specified class + * @since 1.6 + */ + public static ObjectStreamClass lookupAny(Class cl) { + return lookup(cl, true); + } + + /** + * Returns the name of the class described by this descriptor. + * This method returns the name of the class in the format that + * is used by the {@link Class#getName} method. + * + * @return a string representing the name of the class + */ + public String getName() { + return name; + } + + /** + * Return the serialVersionUID for this class. The serialVersionUID + * defines a set of classes all with the same name that have evolved from a + * common root class and agree to be serialized and deserialized using a + * common format. NonSerializable classes have a serialVersionUID of 0L. + * + * @return the SUID of the class described by this descriptor + */ + public long getSerialVersionUID() { + // REMIND: synchronize instead of relying on volatile? + if (suid == null) { + return computeDefaultSUID(cl); + } + return suid.longValue(); + } + + /** + * Return the class in the local VM that this version is mapped to. Null + * is returned if there is no corresponding local class. + * + * @return the Class instance that this descriptor represents + */ + public Class forClass() { + return cl; + } + + /** + * Return an array of the fields of this serializable class. + * + * @return an array containing an element for each persistent field of + * this class. Returns an array of length zero if there are no + * fields. + * @since 1.2 + */ + public ObjectStreamField[] getFields() { + return getFields(true); + } + + /** + * Get the field of this class by name. + * + * @param name the name of the data field to look for + * @return The ObjectStreamField object of the named field or null if + * there is no such named field. + */ + public ObjectStreamField getField(String name) { + return getField(name, null); + } + + /** + * Return a string describing this ObjectStreamClass. + */ + public String toString() { + return name + ": static final long serialVersionUID = " + + getSerialVersionUID() + "L;"; + } + + /** + * Looks up and returns class descriptor for given class, or null if class + * is non-serializable and "all" is set to false. + * + * @param cl class to look up + * @param all if true, return descriptors for all classes; if false, only + * return descriptors for serializable classes + */ + static ObjectStreamClass lookup(Class cl, boolean all) { + if (!(all || Serializable.class.isAssignableFrom(cl))) { + return null; + } + Object entry = null; + EntryFuture future = null; + if (entry == null) { + EntryFuture newEntry = new EntryFuture(); + Reference newRef = new SoftReference<>(newEntry); + if (entry == null) { + future = newEntry; + } + } + + if (entry instanceof ObjectStreamClass) { // check common case first + return (ObjectStreamClass) entry; + } + if (entry instanceof EntryFuture) { + future = (EntryFuture) entry; + if (true) { + /* + * Handle nested call situation described by 4803747: waiting + * for future value to be set by a lookup() call further up the + * stack will result in deadlock, so calculate and set the + * future value here instead. + */ + entry = null; + } else { + entry = future.get(); + } + } + if (entry == null) { + try { + entry = new ObjectStreamClass(cl); + } catch (Throwable th) { + entry = th; + } + // nested lookup call already set future + entry = future.get(); + } + + if (entry instanceof ObjectStreamClass) { + return (ObjectStreamClass) entry; + } else if (entry instanceof RuntimeException) { + throw (RuntimeException) entry; + } else if (entry instanceof Error) { + throw (Error) entry; + } else { + throw new InternalError("unexpected entry: " + entry); + } + } + + /** + * Placeholder used in class descriptor and field reflector lookup tables + * for an entry in the process of being initialized. (Internal) callers + * which receive an EntryFuture belonging to another thread as the result + * of a lookup should call the get() method of the EntryFuture; this will + * return the actual entry once it is ready for use and has been set(). To + * conserve objects, EntryFutures synchronize on themselves. + */ + private static class EntryFuture { + + private static final Object unset = new Object(); + private Object entry = unset; + + /** + * Attempts to set the value contained by this EntryFuture. If the + * EntryFuture's value has not been set already, then the value is + * saved, any callers blocked in the get() method are notified, and + * true is returned. If the value has already been set, then no saving + * or notification occurs, and false is returned. + */ + synchronized boolean set(Object entry) { + if (this.entry != unset) { + return false; + } + this.entry = entry; + notifyAll(); + return true; + } + + /** + * Returns the value contained by this EntryFuture, blocking if + * necessary until a value is set. + */ + synchronized Object get() { + boolean interrupted = false; + while (entry == unset) { + try { + wait(); + } catch (InterruptedException ex) { + interrupted = true; + } + } + return entry; + } + } + + /** + * Creates local class descriptor representing given class. + */ + private ObjectStreamClass(final Class cl) { + this.cl = cl; + name = cl.getName(); + isProxy = Proxy.isProxyClass(cl); + isEnum = Enum.class.isAssignableFrom(cl); + serializable = Serializable.class.isAssignableFrom(cl); + externalizable = Externalizable.class.isAssignableFrom(cl); + + Class superCl = cl.getSuperclass(); + superDesc = (superCl != null) ? lookup(superCl, false) : null; + localDesc = this; + + suid = Long.valueOf(0); + fields = NO_FIELDS; + + + if (deserializeEx == null) { + if (isEnum) { + deserializeEx = new InvalidClassException(name, "enum type"); + } else if (cons == null) { + deserializeEx = new InvalidClassException( + name, "no valid constructor"); + } + } + for (int i = 0; i < fields.length; i++) { + if (fields[i].getField() == null) { + defaultSerializeEx = new InvalidClassException( + name, "unmatched serializable field(s) declared"); + } + } + } + + /** + * Creates blank class descriptor which should be initialized via a + * subsequent call to initProxy(), initNonProxy() or readNonProxy(). + */ + ObjectStreamClass() { + } + + /** + * Initializes class descriptor representing a proxy class. + */ + void initProxy(Class cl, + ClassNotFoundException resolveEx, + ObjectStreamClass superDesc) + throws InvalidClassException + { + this.cl = cl; + this.resolveEx = resolveEx; + this.superDesc = superDesc; + isProxy = true; + serializable = true; + suid = Long.valueOf(0); + fields = NO_FIELDS; + + if (cl != null) { + localDesc = lookup(cl, true); + if (!localDesc.isProxy) { + throw new InvalidClassException( + "cannot bind proxy descriptor to a non-proxy class"); + } + name = localDesc.name; + externalizable = localDesc.externalizable; + cons = localDesc.cons; + writeReplaceMethod = localDesc.writeReplaceMethod; + readResolveMethod = localDesc.readResolveMethod; + deserializeEx = localDesc.deserializeEx; + } + } + + /** + * Initializes class descriptor representing a non-proxy class. + */ + void initNonProxy(ObjectStreamClass model, + Class cl, + ClassNotFoundException resolveEx, + ObjectStreamClass superDesc) + throws InvalidClassException + { + this.cl = cl; + this.resolveEx = resolveEx; + this.superDesc = superDesc; + name = model.name; + suid = Long.valueOf(model.getSerialVersionUID()); + isProxy = false; + isEnum = model.isEnum; + serializable = model.serializable; + externalizable = model.externalizable; + hasBlockExternalData = model.hasBlockExternalData; + hasWriteObjectData = model.hasWriteObjectData; + fields = model.fields; + primDataSize = model.primDataSize; + numObjFields = model.numObjFields; + + if (cl != null) { + localDesc = lookup(cl, true); + if (localDesc.isProxy) { + throw new InvalidClassException( + "cannot bind non-proxy descriptor to a proxy class"); + } + if (isEnum != localDesc.isEnum) { + throw new InvalidClassException(isEnum ? + "cannot bind enum descriptor to a non-enum class" : + "cannot bind non-enum descriptor to an enum class"); + } + + if (serializable == localDesc.serializable && + !cl.isArray() && + suid.longValue() != localDesc.getSerialVersionUID()) + { + throw new InvalidClassException(localDesc.name, + "local class incompatible: " + + "stream classdesc serialVersionUID = " + suid + + ", local class serialVersionUID = " + + localDesc.getSerialVersionUID()); + } + + if (!classNamesEqual(name, localDesc.name)) { + throw new InvalidClassException(localDesc.name, + "local class name incompatible with stream class " + + "name \"" + name + "\""); + } + + if (!isEnum) { + if ((serializable == localDesc.serializable) && + (externalizable != localDesc.externalizable)) + { + throw new InvalidClassException(localDesc.name, + "Serializable incompatible with Externalizable"); + } + + if ((serializable != localDesc.serializable) || + (externalizable != localDesc.externalizable) || + !(serializable || externalizable)) + { + deserializeEx = new InvalidClassException(localDesc.name, + "class invalid for deserialization"); + } + } + + cons = localDesc.cons; + writeObjectMethod = localDesc.writeObjectMethod; + readObjectMethod = localDesc.readObjectMethod; + readObjectNoDataMethod = localDesc.readObjectNoDataMethod; + writeReplaceMethod = localDesc.writeReplaceMethod; + readResolveMethod = localDesc.readResolveMethod; + if (deserializeEx == null) { + deserializeEx = localDesc.deserializeEx; + } + } + // reassign to matched fields so as to reflect local unshared settings + fields = null; + } + + /** + * Reads non-proxy class descriptor information from given input stream. + * The resulting class descriptor is not fully functional; it can only be + * used as input to the ObjectInputStream.resolveClass() and + * ObjectStreamClass.initNonProxy() methods. + */ + void readNonProxy(ObjectInputStream in) + throws IOException, ClassNotFoundException + { + name = in.readUTF(); + suid = Long.valueOf(in.readLong()); + isProxy = false; + + byte flags = in.readByte(); + hasWriteObjectData = + ((flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0); + hasBlockExternalData = + ((flags & ObjectStreamConstants.SC_BLOCK_DATA) != 0); + externalizable = + ((flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0); + boolean sflag = + ((flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0); + if (externalizable && sflag) { + throw new InvalidClassException( + name, "serializable and externalizable flags conflict"); + } + serializable = externalizable || sflag; + isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0); + if (isEnum && suid.longValue() != 0L) { + throw new InvalidClassException(name, + "enum descriptor has non-zero serialVersionUID: " + suid); + } + + int numFields = in.readShort(); + if (isEnum && numFields != 0) { + throw new InvalidClassException(name, + "enum descriptor has non-zero field count: " + numFields); + } + fields = (numFields > 0) ? + new ObjectStreamField[numFields] : NO_FIELDS; + for (int i = 0; i < numFields; i++) { + char tcode = (char) in.readByte(); + String fname = in.readUTF(); + String signature = ((tcode == 'L') || (tcode == '[')) ? + in.readTypeString() : new String(new char[] { tcode }); + try { + fields[i] = new ObjectStreamField(fname, signature, false); + } catch (RuntimeException e) { + throw (IOException) new InvalidClassException(name, + "invalid descriptor for field " + fname).initCause(e); + } + } + computeFieldOffsets(); + } + + /** + * Writes non-proxy class descriptor information to given output stream. + */ + void writeNonProxy(ObjectOutputStream out) throws IOException { + out.writeUTF(name); + out.writeLong(getSerialVersionUID()); + + byte flags = 0; + if (externalizable) { + flags |= ObjectStreamConstants.SC_EXTERNALIZABLE; + int protocol = out.getProtocolVersion(); + if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) { + flags |= ObjectStreamConstants.SC_BLOCK_DATA; + } + } else if (serializable) { + flags |= ObjectStreamConstants.SC_SERIALIZABLE; + } + if (hasWriteObjectData) { + flags |= ObjectStreamConstants.SC_WRITE_METHOD; + } + if (isEnum) { + flags |= ObjectStreamConstants.SC_ENUM; + } + out.writeByte(flags); + + out.writeShort(fields.length); + for (int i = 0; i < fields.length; i++) { + ObjectStreamField f = fields[i]; + out.writeByte(f.getTypeCode()); + out.writeUTF(f.getName()); + if (!f.isPrimitive()) { + out.writeTypeString(f.getTypeString()); + } + } + } + + /** + * Returns ClassNotFoundException (if any) thrown while attempting to + * resolve local class corresponding to this class descriptor. + */ + ClassNotFoundException getResolveException() { + return resolveEx; + } + + /** + * Throws an InvalidClassException if object instances referencing this + * class descriptor should not be allowed to deserialize. This method does + * not apply to deserialization of enum constants. + */ + void checkDeserialize() throws InvalidClassException { + if (deserializeEx != null) { + InvalidClassException ice = + new InvalidClassException(deserializeEx.classname, + deserializeEx.getMessage()); + ice.initCause(deserializeEx); + throw ice; + } + } + + /** + * Throws an InvalidClassException if objects whose class is represented by + * this descriptor should not be allowed to serialize. This method does + * not apply to serialization of enum constants. + */ + void checkSerialize() throws InvalidClassException { + if (serializeEx != null) { + InvalidClassException ice = + new InvalidClassException(serializeEx.classname, + serializeEx.getMessage()); + ice.initCause(serializeEx); + throw ice; + } + } + + /** + * Throws an InvalidClassException if objects whose class is represented by + * this descriptor should not be permitted to use default serialization + * (e.g., if the class declares serializable fields that do not correspond + * to actual fields, and hence must use the GetField API). This method + * does not apply to deserialization of enum constants. + */ + void checkDefaultSerialize() throws InvalidClassException { + if (defaultSerializeEx != null) { + InvalidClassException ice = + new InvalidClassException(defaultSerializeEx.classname, + defaultSerializeEx.getMessage()); + ice.initCause(defaultSerializeEx); + throw ice; + } + } + + /** + * Returns superclass descriptor. Note that on the receiving side, the + * superclass descriptor may be bound to a class that is not a superclass + * of the subclass descriptor's bound class. + */ + ObjectStreamClass getSuperDesc() { + return superDesc; + } + + /** + * Returns the "local" class descriptor for the class associated with this + * class descriptor (i.e., the result of + * ObjectStreamClass.lookup(this.forClass())) or null if there is no class + * associated with this descriptor. + */ + ObjectStreamClass getLocalDesc() { + return localDesc; + } + + /** + * Returns arrays of ObjectStreamFields representing the serializable + * fields of the represented class. If copy is true, a clone of this class + * descriptor's field array is returned, otherwise the array itself is + * returned. + */ + ObjectStreamField[] getFields(boolean copy) { + return copy ? fields.clone() : fields; + } + + /** + * Looks up a serializable field of the represented class by name and type. + * A specified type of null matches all types, Object.class matches all + * non-primitive types, and any other non-null type matches assignable + * types only. Returns matching field, or null if no match found. + */ + ObjectStreamField getField(String name, Class type) { + for (int i = 0; i < fields.length; i++) { + ObjectStreamField f = fields[i]; + if (f.getName().equals(name)) { + if (type == null || + (type == Object.class && !f.isPrimitive())) + { + return f; + } + Class ftype = f.getType(); + if (ftype != null && type.isAssignableFrom(ftype)) { + return f; + } + } + } + return null; + } + + /** + * Returns true if class descriptor represents a dynamic proxy class, false + * otherwise. + */ + boolean isProxy() { + return isProxy; + } + + /** + * Returns true if class descriptor represents an enum type, false + * otherwise. + */ + boolean isEnum() { + return isEnum; + } + + /** + * Returns true if represented class implements Externalizable, false + * otherwise. + */ + boolean isExternalizable() { + return externalizable; + } + + /** + * Returns true if represented class implements Serializable, false + * otherwise. + */ + boolean isSerializable() { + return serializable; + } + + /** + * Returns true if class descriptor represents externalizable class that + * has written its data in 1.2 (block data) format, false otherwise. + */ + boolean hasBlockExternalData() { + return hasBlockExternalData; + } + + /** + * Returns true if class descriptor represents serializable (but not + * externalizable) class which has written its data via a custom + * writeObject() method, false otherwise. + */ + boolean hasWriteObjectData() { + return hasWriteObjectData; + } + + /** + * Returns true if represented class is serializable/externalizable and can + * be instantiated by the serialization runtime--i.e., if it is + * externalizable and defines a public no-arg constructor, or if it is + * non-externalizable and its first non-serializable superclass defines an + * accessible no-arg constructor. Otherwise, returns false. + */ + boolean isInstantiable() { + return (cons != null); + } + + /** + * Returns true if represented class is serializable (but not + * externalizable) and defines a conformant writeObject method. Otherwise, + * returns false. + */ + boolean hasWriteObjectMethod() { + return (writeObjectMethod != null); + } + + /** + * Returns true if represented class is serializable (but not + * externalizable) and defines a conformant readObject method. Otherwise, + * returns false. + */ + boolean hasReadObjectMethod() { + return (readObjectMethod != null); + } + + /** + * Returns true if represented class is serializable (but not + * externalizable) and defines a conformant readObjectNoData method. + * Otherwise, returns false. + */ + boolean hasReadObjectNoDataMethod() { + return (readObjectNoDataMethod != null); + } + + /** + * Returns true if represented class is serializable or externalizable and + * defines a conformant writeReplace method. Otherwise, returns false. + */ + boolean hasWriteReplaceMethod() { + return (writeReplaceMethod != null); + } + + /** + * Returns true if represented class is serializable or externalizable and + * defines a conformant readResolve method. Otherwise, returns false. + */ + boolean hasReadResolveMethod() { + return (readResolveMethod != null); + } + + /** + * Creates a new instance of the represented class. If the class is + * externalizable, invokes its public no-arg constructor; otherwise, if the + * class is serializable, invokes the no-arg constructor of the first + * non-serializable superclass. Throws UnsupportedOperationException if + * this class descriptor is not associated with a class, if the associated + * class is non-serializable or if the appropriate no-arg constructor is + * inaccessible/unavailable. + */ + Object newInstance() + throws InstantiationException, InvocationTargetException, + UnsupportedOperationException + { + if (cons != null) { + try { + return cons.newInstance(); + } catch (IllegalAccessException ex) { + // should not occur, as access checks have been suppressed + throw new InternalError(); + } + } else { + throw new UnsupportedOperationException(); + } + } + + /** + * Invokes the writeObject method of the represented serializable class. + * Throws UnsupportedOperationException if this class descriptor is not + * associated with a class, or if the class is externalizable, + * non-serializable or does not define writeObject. + */ + void invokeWriteObject(Object obj, ObjectOutputStream out) + throws IOException, UnsupportedOperationException + { + if (writeObjectMethod != null) { + try { + writeObjectMethod.invoke(obj, new Object[]{ out }); + } catch (InvocationTargetException ex) { + Throwable th = ex.getTargetException(); + if (th instanceof IOException) { + throw (IOException) th; + } else { + throwMiscException(th); + } + } catch (IllegalAccessException ex) { + // should not occur, as access checks have been suppressed + throw new InternalError(); + } + } else { + throw new UnsupportedOperationException(); + } + } + + /** + * Invokes the readObject method of the represented serializable class. + * Throws UnsupportedOperationException if this class descriptor is not + * associated with a class, or if the class is externalizable, + * non-serializable or does not define readObject. + */ + void invokeReadObject(Object obj, ObjectInputStream in) + throws ClassNotFoundException, IOException, + UnsupportedOperationException + { + if (readObjectMethod != null) { + try { + readObjectMethod.invoke(obj, new Object[]{ in }); + } catch (InvocationTargetException ex) { + Throwable th = ex.getTargetException(); + if (th instanceof ClassNotFoundException) { + throw (ClassNotFoundException) th; + } else if (th instanceof IOException) { + throw (IOException) th; + } else { + throwMiscException(th); + } + } catch (IllegalAccessException ex) { + // should not occur, as access checks have been suppressed + throw new InternalError(); + } + } else { + throw new UnsupportedOperationException(); + } + } + + /** + * Invokes the readObjectNoData method of the represented serializable + * class. Throws UnsupportedOperationException if this class descriptor is + * not associated with a class, or if the class is externalizable, + * non-serializable or does not define readObjectNoData. + */ + void invokeReadObjectNoData(Object obj) + throws IOException, UnsupportedOperationException + { + if (readObjectNoDataMethod != null) { + try { + readObjectNoDataMethod.invoke(obj, (Object[]) null); + } catch (InvocationTargetException ex) { + Throwable th = ex.getTargetException(); + if (th instanceof ObjectStreamException) { + throw (ObjectStreamException) th; + } else { + throwMiscException(th); + } + } catch (IllegalAccessException ex) { + // should not occur, as access checks have been suppressed + throw new InternalError(); + } + } else { + throw new UnsupportedOperationException(); + } + } + + /** + * Invokes the writeReplace method of the represented serializable class and + * returns the result. Throws UnsupportedOperationException if this class + * descriptor is not associated with a class, or if the class is + * non-serializable or does not define writeReplace. + */ + Object invokeWriteReplace(Object obj) + throws IOException, UnsupportedOperationException + { + if (writeReplaceMethod != null) { + try { + return writeReplaceMethod.invoke(obj, (Object[]) null); + } catch (InvocationTargetException ex) { + Throwable th = ex.getTargetException(); + if (th instanceof ObjectStreamException) { + throw (ObjectStreamException) th; + } else { + throwMiscException(th); + throw new InternalError(); // never reached + } + } catch (IllegalAccessException ex) { + // should not occur, as access checks have been suppressed + throw new InternalError(); + } + } else { + throw new UnsupportedOperationException(); + } + } + + /** + * Invokes the readResolve method of the represented serializable class and + * returns the result. Throws UnsupportedOperationException if this class + * descriptor is not associated with a class, or if the class is + * non-serializable or does not define readResolve. + */ + Object invokeReadResolve(Object obj) + throws IOException, UnsupportedOperationException + { + if (readResolveMethod != null) { + try { + return readResolveMethod.invoke(obj, (Object[]) null); + } catch (InvocationTargetException ex) { + Throwable th = ex.getTargetException(); + if (th instanceof ObjectStreamException) { + throw (ObjectStreamException) th; + } else { + throwMiscException(th); + throw new InternalError(); // never reached + } + } catch (IllegalAccessException ex) { + // should not occur, as access checks have been suppressed + throw new InternalError(); + } + } else { + throw new UnsupportedOperationException(); + } + } + + /** + * Class representing the portion of an object's serialized form allotted + * to data described by a given class descriptor. If "hasData" is false, + * the object's serialized form does not contain data associated with the + * class descriptor. + */ + static class ClassDataSlot { + + /** class descriptor "occupying" this slot */ + final ObjectStreamClass desc; + /** true if serialized form includes data for this slot's descriptor */ + final boolean hasData; + + ClassDataSlot(ObjectStreamClass desc, boolean hasData) { + this.desc = desc; + this.hasData = hasData; + } + } + + /** + * Returns array of ClassDataSlot instances representing the data layout + * (including superclass data) for serialized objects described by this + * class descriptor. ClassDataSlots are ordered by inheritance with those + * containing "higher" superclasses appearing first. The final + * ClassDataSlot contains a reference to this descriptor. + */ + ClassDataSlot[] getClassDataLayout() throws InvalidClassException { + // REMIND: synchronize instead of relying on volatile? + if (dataLayout == null) { + dataLayout = getClassDataLayout0(); + } + return dataLayout; + } + + private ClassDataSlot[] getClassDataLayout0() + throws InvalidClassException + { + ArrayList slots = new ArrayList<>(); + Class start = cl, end = cl; + + // locate closest non-serializable superclass + while (end != null && Serializable.class.isAssignableFrom(end)) { + end = end.getSuperclass(); + } + + for (ObjectStreamClass d = this; d != null; d = d.superDesc) { + + // search up inheritance hierarchy for class with matching name + String searchName = (d.cl != null) ? d.cl.getName() : d.name; + Class match = null; + for (Class c = start; c != end; c = c.getSuperclass()) { + if (searchName.equals(c.getName())) { + match = c; + break; + } + } + + // add "no data" slot for each unmatched class below match + if (match != null) { + for (Class c = start; c != match; c = c.getSuperclass()) { + slots.add(new ClassDataSlot( + ObjectStreamClass.lookup(c, true), false)); + } + start = match.getSuperclass(); + } + + // record descriptor/class pairing + slots.add(new ClassDataSlot(d.getVariantFor(match), true)); + } + + // add "no data" slot for any leftover unmatched classes + for (Class c = start; c != end; c = c.getSuperclass()) { + slots.add(new ClassDataSlot( + ObjectStreamClass.lookup(c, true), false)); + } + + // order slots from superclass -> subclass + Collections.reverse(slots); + return slots.toArray(new ClassDataSlot[slots.size()]); + } + + /** + * Returns aggregate size (in bytes) of marshalled primitive field values + * for represented class. + */ + int getPrimDataSize() { + return primDataSize; + } + + /** + * Returns number of non-primitive serializable fields of represented + * class. + */ + int getNumObjFields() { + return numObjFields; + } + + /** + * Fetches the serializable primitive field values of object obj and + * marshals them into byte array buf starting at offset 0. It is the + * responsibility of the caller to ensure that obj is of the proper type if + * non-null. + */ + void getPrimFieldValues(Object obj, byte[] buf) { + } + + /** + * Sets the serializable primitive fields of object obj using values + * unmarshalled from byte array buf starting at offset 0. It is the + * responsibility of the caller to ensure that obj is of the proper type if + * non-null. + */ + void setPrimFieldValues(Object obj, byte[] buf) { + } + + /** + * Fetches the serializable object field values of object obj and stores + * them in array vals starting at offset 0. It is the responsibility of + * the caller to ensure that obj is of the proper type if non-null. + */ + void getObjFieldValues(Object obj, Object[] vals) { + } + + /** + * Sets the serializable object fields of object obj using values from + * array vals starting at offset 0. It is the responsibility of the caller + * to ensure that obj is of the proper type if non-null. + */ + void setObjFieldValues(Object obj, Object[] vals) { + } + + /** + * Calculates and sets serializable field offsets, as well as primitive + * data size and object field count totals. Throws InvalidClassException + * if fields are illegally ordered. + */ + private void computeFieldOffsets() throws InvalidClassException { + primDataSize = 0; + numObjFields = 0; + int firstObjIndex = -1; + + for (int i = 0; i < fields.length; i++) { + ObjectStreamField f = fields[i]; + switch (f.getTypeCode()) { + case 'Z': + case 'B': + f.setOffset(primDataSize++); + break; + + case 'C': + case 'S': + f.setOffset(primDataSize); + primDataSize += 2; + break; + + case 'I': + case 'F': + f.setOffset(primDataSize); + primDataSize += 4; + break; + + case 'J': + case 'D': + f.setOffset(primDataSize); + primDataSize += 8; + break; + + case '[': + case 'L': + f.setOffset(numObjFields++); + if (firstObjIndex == -1) { + firstObjIndex = i; + } + break; + + default: + throw new InternalError(); + } + } + if (firstObjIndex != -1 && + firstObjIndex + numObjFields != fields.length) + { + throw new InvalidClassException(name, "illegal field order"); + } + } + + /** + * If given class is the same as the class associated with this class + * descriptor, returns reference to this class descriptor. Otherwise, + * returns variant of this class descriptor bound to given class. + */ + private ObjectStreamClass getVariantFor(Class cl) + throws InvalidClassException + { + if (this.cl == cl) { + return this; + } + ObjectStreamClass desc = new ObjectStreamClass(); + if (isProxy) { + desc.initProxy(cl, null, superDesc); + } else { + desc.initNonProxy(this, cl, null, superDesc); + } + return desc; + } + + /** + * Returns public no-arg constructor of given class, or null if none found. + * Access checks are disabled on the returned constructor (if any), since + * the defining class may still be non-public. + */ + private static Constructor getExternalizableConstructor(Class cl) { + throw new SecurityException(); + } + + /** + * Returns subclass-accessible no-arg constructor of first non-serializable + * superclass, or null if none found. Access checks are disabled on the + * returned constructor (if any). + */ + private static Constructor getSerializableConstructor(Class cl) { + Class initCl = cl; + while (Serializable.class.isAssignableFrom(initCl)) { + if ((initCl = initCl.getSuperclass()) == null) { + return null; + } + } + throw new SecurityException(); + } + + /** + * Returns non-static, non-abstract method with given signature provided it + * is defined by or accessible (via inheritance) by the given class, or + * null if no match found. Access checks are disabled on the returned + * method (if any). + */ + private static Method getInheritableMethod(Class cl, String name, + Class[] argTypes, + Class returnType) + { + throw new SecurityException(); + } + + /** + * Returns non-static private method with given signature defined by given + * class, or null if none found. Access checks are disabled on the + * returned method (if any). + */ + private static Method getPrivateMethod(Class cl, String name, + Class[] argTypes, + Class returnType) + { + throw new SecurityException(); + } + + /** + * Returns true if classes are defined in the same runtime package, false + * otherwise. + */ + private static boolean packageEquals(Class cl1, Class cl2) { + return (cl1.getClassLoader() == cl2.getClassLoader() && + getPackageName(cl1).equals(getPackageName(cl2))); + } + + /** + * Returns package name of given class. + */ + private static String getPackageName(Class cl) { + String s = cl.getName(); + int i = s.lastIndexOf('['); + if (i >= 0) { + s = s.substring(i + 2); + } + i = s.lastIndexOf('.'); + return (i >= 0) ? s.substring(0, i) : ""; + } + + /** + * Compares class names for equality, ignoring package names. Returns true + * if class names equal, false otherwise. + */ + private static boolean classNamesEqual(String name1, String name2) { + name1 = name1.substring(name1.lastIndexOf('.') + 1); + name2 = name2.substring(name2.lastIndexOf('.') + 1); + return name1.equals(name2); + } + + /** + * Returns JVM type signature for given class. + */ + private static String getClassSignature(Class cl) { + StringBuilder sbuf = new StringBuilder(); + while (cl.isArray()) { + sbuf.append('['); + cl = cl.getComponentType(); + } + if (cl.isPrimitive()) { + if (cl == Integer.TYPE) { + sbuf.append('I'); + } else if (cl == Byte.TYPE) { + sbuf.append('B'); + } else if (cl == Long.TYPE) { + sbuf.append('J'); + } else if (cl == Float.TYPE) { + sbuf.append('F'); + } else if (cl == Double.TYPE) { + sbuf.append('D'); + } else if (cl == Short.TYPE) { + sbuf.append('S'); + } else if (cl == Character.TYPE) { + sbuf.append('C'); + } else if (cl == Boolean.TYPE) { + sbuf.append('Z'); + } else if (cl == Void.TYPE) { + sbuf.append('V'); + } else { + throw new InternalError(); + } + } else { + sbuf.append('L' + cl.getName().replace('.', '/') + ';'); + } + return sbuf.toString(); + } + + /** + * Returns JVM type signature for given list of parameters and return type. + */ + private static String getMethodSignature(Class[] paramTypes, + Class retType) + { + StringBuilder sbuf = new StringBuilder(); + sbuf.append('('); + for (int i = 0; i < paramTypes.length; i++) { + sbuf.append(getClassSignature(paramTypes[i])); + } + sbuf.append(')'); + sbuf.append(getClassSignature(retType)); + return sbuf.toString(); + } + + /** + * Convenience method for throwing an exception that is either a + * RuntimeException, Error, or of some unexpected type (in which case it is + * wrapped inside an IOException). + */ + private static void throwMiscException(Throwable th) throws IOException { + if (th instanceof RuntimeException) { + throw (RuntimeException) th; + } else if (th instanceof Error) { + throw (Error) th; + } else { + IOException ex = new IOException("unexpected exception type"); + ex.initCause(th); + throw ex; + } + } + + /** + * Returns ObjectStreamField array describing the serializable fields of + * the given class. Serializable fields backed by an actual field of the + * class are represented by ObjectStreamFields with corresponding non-null + * Field objects. Throws InvalidClassException if the (explicitly + * declared) serializable fields are invalid. + */ + private static ObjectStreamField[] getSerialFields(Class cl) + throws InvalidClassException + { + ObjectStreamField[] fields; + if (Serializable.class.isAssignableFrom(cl) && + !Externalizable.class.isAssignableFrom(cl) && + !Proxy.isProxyClass(cl) && + !cl.isInterface()) + { + if ((fields = getDeclaredSerialFields(cl)) == null) { + fields = getDefaultSerialFields(cl); + } + Arrays.sort(fields); + } else { + fields = NO_FIELDS; + } + return fields; + } + + /** + * Returns serializable fields of given class as defined explicitly by a + * "serialPersistentFields" field, or null if no appropriate + * "serialPersistentFields" field is defined. Serializable fields backed + * by an actual field of the class are represented by ObjectStreamFields + * with corresponding non-null Field objects. For compatibility with past + * releases, a "serialPersistentFields" field with a null value is + * considered equivalent to not declaring "serialPersistentFields". Throws + * InvalidClassException if the declared serializable fields are + * invalid--e.g., if multiple fields share the same name. + */ + private static ObjectStreamField[] getDeclaredSerialFields(Class cl) + throws InvalidClassException + { + throw new SecurityException(); + } + + /** + * Returns array of ObjectStreamFields corresponding to all non-static + * non-transient fields declared by given class. Each ObjectStreamField + * contains a Field object for the field it represents. If no default + * serializable fields exist, NO_FIELDS is returned. + */ + private static ObjectStreamField[] getDefaultSerialFields(Class cl) { + throw new SecurityException(); + } + + /** + * Returns explicit serial version UID value declared by given class, or + * null if none. + */ + private static Long getDeclaredSUID(Class cl) { + return null; + } + + /** + * Computes the default serial version UID value for the given class. + */ + private static long computeDefaultSUID(Class cl) { + throw new SecurityException(); + } + +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/ObjectStreamConstants.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/ObjectStreamConstants.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,215 @@ +/* + * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +/** + * Constants written into the Object Serialization Stream. + * + * @author unascribed + * @since JDK 1.1 + */ +public interface ObjectStreamConstants { + + /** + * Magic number that is written to the stream header. + */ + final static short STREAM_MAGIC = (short)0xaced; + + /** + * Version number that is written to the stream header. + */ + final static short STREAM_VERSION = 5; + + /* Each item in the stream is preceded by a tag + */ + + /** + * First tag value. + */ + final static byte TC_BASE = 0x70; + + /** + * Null object reference. + */ + final static byte TC_NULL = (byte)0x70; + + /** + * Reference to an object already written into the stream. + */ + final static byte TC_REFERENCE = (byte)0x71; + + /** + * new Class Descriptor. + */ + final static byte TC_CLASSDESC = (byte)0x72; + + /** + * new Object. + */ + final static byte TC_OBJECT = (byte)0x73; + + /** + * new String. + */ + final static byte TC_STRING = (byte)0x74; + + /** + * new Array. + */ + final static byte TC_ARRAY = (byte)0x75; + + /** + * Reference to Class. + */ + final static byte TC_CLASS = (byte)0x76; + + /** + * Block of optional data. Byte following tag indicates number + * of bytes in this block data. + */ + final static byte TC_BLOCKDATA = (byte)0x77; + + /** + * End of optional block data blocks for an object. + */ + final static byte TC_ENDBLOCKDATA = (byte)0x78; + + /** + * Reset stream context. All handles written into stream are reset. + */ + final static byte TC_RESET = (byte)0x79; + + /** + * long Block data. The long following the tag indicates the + * number of bytes in this block data. + */ + final static byte TC_BLOCKDATALONG= (byte)0x7A; + + /** + * Exception during write. + */ + final static byte TC_EXCEPTION = (byte)0x7B; + + /** + * Long string. + */ + final static byte TC_LONGSTRING = (byte)0x7C; + + /** + * new Proxy Class Descriptor. + */ + final static byte TC_PROXYCLASSDESC = (byte)0x7D; + + /** + * new Enum constant. + * @since 1.5 + */ + final static byte TC_ENUM = (byte)0x7E; + + /** + * Last tag value. + */ + final static byte TC_MAX = (byte)0x7E; + + /** + * First wire handle to be assigned. + */ + final static int baseWireHandle = 0x7e0000; + + + /******************************************************/ + /* Bit masks for ObjectStreamClass flag.*/ + + /** + * Bit mask for ObjectStreamClass flag. Indicates a Serializable class + * defines its own writeObject method. + */ + final static byte SC_WRITE_METHOD = 0x01; + + /** + * Bit mask for ObjectStreamClass flag. Indicates Externalizable data + * written in Block Data mode. + * Added for PROTOCOL_VERSION_2. + * + * @see #PROTOCOL_VERSION_2 + * @since 1.2 + */ + final static byte SC_BLOCK_DATA = 0x08; + + /** + * Bit mask for ObjectStreamClass flag. Indicates class is Serializable. + */ + final static byte SC_SERIALIZABLE = 0x02; + + /** + * Bit mask for ObjectStreamClass flag. Indicates class is Externalizable. + */ + final static byte SC_EXTERNALIZABLE = 0x04; + + /** + * Bit mask for ObjectStreamClass flag. Indicates class is an enum type. + * @since 1.5 + */ + final static byte SC_ENUM = 0x10; + + + /* *******************************************************************/ + /* Security permissions */ + + /** + * A Stream Protocol Version.

+ * + * All externalizable data is written in JDK 1.1 external data + * format after calling this method. This version is needed to write + * streams containing Externalizable data that can be read by + * pre-JDK 1.1.6 JVMs. + * + * @see java.io.ObjectOutputStream#useProtocolVersion(int) + * @since 1.2 + */ + public final static int PROTOCOL_VERSION_1 = 1; + + + /** + * A Stream Protocol Version.

+ * + * This protocol is written by JVM 1.2. + * + * Externalizable data is written in block data mode and is + * terminated with TC_ENDBLOCKDATA. Externalizable classdescriptor + * flags has SC_BLOCK_DATA enabled. JVM 1.1.6 and greater can + * read this format change. + * + * Enables writing a nonSerializable class descriptor into the + * stream. The serialVersionUID of a nonSerializable class is + * set to 0L. + * + * @see java.io.ObjectOutputStream#useProtocolVersion(int) + * @see #SC_BLOCK_DATA + * @since 1.2 + */ + public final static int PROTOCOL_VERSION_2 = 2; +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/ObjectStreamException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/ObjectStreamException.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +/** + * Superclass of all exceptions specific to Object Stream classes. + * + * @author unascribed + * @since JDK1.1 + */ +public abstract class ObjectStreamException extends IOException { + + private static final long serialVersionUID = 7260898174833392607L; + + /** + * Create an ObjectStreamException with the specified argument. + * + * @param classname the detailed message for the exception + */ + protected ObjectStreamException(String classname) { + super(classname); + } + + /** + * Create an ObjectStreamException. + */ + protected ObjectStreamException() { + super(); + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/ObjectStreamField.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/ObjectStreamField.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,314 @@ +/* + * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +import java.lang.reflect.Field; + +/** + * A description of a Serializable field from a Serializable class. An array + * of ObjectStreamFields is used to declare the Serializable fields of a class. + * + * @author Mike Warres + * @author Roger Riggs + * @see ObjectStreamClass + * @since 1.2 + */ +public class ObjectStreamField + implements Comparable +{ + + /** field name */ + private final String name; + /** canonical JVM signature of field type */ + private final String signature; + /** field type (Object.class if unknown non-primitive type) */ + private final Class type; + /** whether or not to (de)serialize field values as unshared */ + private final boolean unshared; + /** corresponding reflective field object, if any */ + private final Field field; + /** offset of field value in enclosing field group */ + private int offset = 0; + + /** + * Create a Serializable field with the specified type. This field should + * be documented with a serialField tag. + * + * @param name the name of the serializable field + * @param type the Class object of the serializable field + */ + public ObjectStreamField(String name, Class type) { + this(name, type, false); + } + + /** + * Creates an ObjectStreamField representing a serializable field with the + * given name and type. If unshared is false, values of the represented + * field are serialized and deserialized in the default manner--if the + * field is non-primitive, object values are serialized and deserialized as + * if they had been written and read by calls to writeObject and + * readObject. If unshared is true, values of the represented field are + * serialized and deserialized as if they had been written and read by + * calls to writeUnshared and readUnshared. + * + * @param name field name + * @param type field type + * @param unshared if false, write/read field values in the same manner + * as writeObject/readObject; if true, write/read in the same + * manner as writeUnshared/readUnshared + * @since 1.4 + */ + public ObjectStreamField(String name, Class type, boolean unshared) { + if (name == null) { + throw new NullPointerException(); + } + this.name = name; + this.type = type; + this.unshared = unshared; + signature = getClassSignature(type).intern(); + field = null; + } + + /** + * Creates an ObjectStreamField representing a field with the given name, + * signature and unshared setting. + */ + ObjectStreamField(String name, String signature, boolean unshared) { + if (name == null) { + throw new NullPointerException(); + } + this.name = name; + this.signature = signature.intern(); + this.unshared = unshared; + field = null; + + switch (signature.charAt(0)) { + case 'Z': type = Boolean.TYPE; break; + case 'B': type = Byte.TYPE; break; + case 'C': type = Character.TYPE; break; + case 'S': type = Short.TYPE; break; + case 'I': type = Integer.TYPE; break; + case 'J': type = Long.TYPE; break; + case 'F': type = Float.TYPE; break; + case 'D': type = Double.TYPE; break; + case 'L': + case '[': type = Object.class; break; + default: throw new IllegalArgumentException("illegal signature"); + } + } + + /** + * Creates an ObjectStreamField representing the given field with the + * specified unshared setting. For compatibility with the behavior of + * earlier serialization implementations, a "showType" parameter is + * necessary to govern whether or not a getType() call on this + * ObjectStreamField (if non-primitive) will return Object.class (as + * opposed to a more specific reference type). + */ + ObjectStreamField(Field field, boolean unshared, boolean showType) { + this.field = field; + this.unshared = unshared; + name = field.getName(); + Class ftype = field.getType(); + type = (showType || ftype.isPrimitive()) ? ftype : Object.class; + signature = getClassSignature(ftype).intern(); + } + + /** + * Get the name of this field. + * + * @return a String representing the name of the serializable + * field + */ + public String getName() { + return name; + } + + /** + * Get the type of the field. If the type is non-primitive and this + * ObjectStreamField was obtained from a deserialized {@link + * ObjectStreamClass} instance, then Object.class is returned. + * Otherwise, the Class object for the type of the field is + * returned. + * + * @return a Class object representing the type of the + * serializable field + */ + public Class getType() { + return type; + } + + /** + * Returns character encoding of field type. The encoding is as follows: + *
+     * B            byte
+     * C            char
+     * D            double
+     * F            float
+     * I            int
+     * J            long
+     * L            class or interface
+     * S            short
+     * Z            boolean
+     * [            array
+     * 
+ * + * @return the typecode of the serializable field + */ + // REMIND: deprecate? + public char getTypeCode() { + return signature.charAt(0); + } + + /** + * Return the JVM type signature. + * + * @return null if this field has a primitive type. + */ + // REMIND: deprecate? + public String getTypeString() { + return isPrimitive() ? null : signature; + } + + /** + * Offset of field within instance data. + * + * @return the offset of this field + * @see #setOffset + */ + // REMIND: deprecate? + public int getOffset() { + return offset; + } + + /** + * Offset within instance data. + * + * @param offset the offset of the field + * @see #getOffset + */ + // REMIND: deprecate? + protected void setOffset(int offset) { + this.offset = offset; + } + + /** + * Return true if this field has a primitive type. + * + * @return true if and only if this field corresponds to a primitive type + */ + // REMIND: deprecate? + public boolean isPrimitive() { + char tcode = signature.charAt(0); + return ((tcode != 'L') && (tcode != '[')); + } + + /** + * Returns boolean value indicating whether or not the serializable field + * represented by this ObjectStreamField instance is unshared. + * + * @since 1.4 + */ + public boolean isUnshared() { + return unshared; + } + + /** + * Compare this field with another ObjectStreamField. Return + * -1 if this is smaller, 0 if equal, 1 if greater. Types that are + * primitives are "smaller" than object types. If equal, the field names + * are compared. + */ + // REMIND: deprecate? + public int compareTo(Object obj) { + ObjectStreamField other = (ObjectStreamField) obj; + boolean isPrim = isPrimitive(); + if (isPrim != other.isPrimitive()) { + return isPrim ? -1 : 1; + } + return name.compareTo(other.name); + } + + /** + * Return a string that describes this field. + */ + public String toString() { + return signature + ' ' + name; + } + + /** + * Returns field represented by this ObjectStreamField, or null if + * ObjectStreamField is not associated with an actual field. + */ + Field getField() { + return field; + } + + /** + * Returns JVM type signature of field (similar to getTypeString, except + * that signature strings are returned for primitive fields as well). + */ + String getSignature() { + return signature; + } + + /** + * Returns JVM type signature for given class. + */ + private static String getClassSignature(Class cl) { + StringBuilder sbuf = new StringBuilder(); + while (cl.isArray()) { + sbuf.append('['); + cl = cl.getComponentType(); + } + if (cl.isPrimitive()) { + if (cl == Integer.TYPE) { + sbuf.append('I'); + } else if (cl == Byte.TYPE) { + sbuf.append('B'); + } else if (cl == Long.TYPE) { + sbuf.append('J'); + } else if (cl == Float.TYPE) { + sbuf.append('F'); + } else if (cl == Double.TYPE) { + sbuf.append('D'); + } else if (cl == Short.TYPE) { + sbuf.append('S'); + } else if (cl == Character.TYPE) { + sbuf.append('C'); + } else if (cl == Boolean.TYPE) { + sbuf.append('Z'); + } else if (cl == Void.TYPE) { + sbuf.append('V'); + } else { + throw new InternalError(); + } + } else { + sbuf.append('L' + cl.getName().replace('.', '/') + ';'); + } + return sbuf.toString(); + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/OptionalDataException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/OptionalDataException.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.io; + +/** + * Exception indicating the failure of an object read operation due to + * unread primitive data, or the end of data belonging to a serialized + * object in the stream. This exception may be thrown in two cases: + * + *
    + *
  • An attempt was made to read an object when the next element in the + * stream is primitive data. In this case, the OptionalDataException's + * length field is set to the number of bytes of primitive data + * immediately readable from the stream, and the eof field is set to + * false. + * + *
  • An attempt was made to read past the end of data consumable by a + * class-defined readObject or readExternal method. In this case, the + * OptionalDataException's eof field is set to true, and the length field + * is set to 0. + *
+ * + * @author unascribed + * @since JDK1.1 + */ +public class OptionalDataException extends ObjectStreamException { + + private static final long serialVersionUID = -8011121865681257820L; + + /* + * Create an OptionalDataException with a length. + */ + OptionalDataException(int len) { + eof = false; + length = len; + } + + /* + * Create an OptionalDataException signifying no + * more primitive data is available. + */ + OptionalDataException(boolean end) { + length = 0; + eof = end; + } + + /** + * The number of bytes of primitive data available to be read + * in the current buffer. + * + * @serial + */ + public int length; + + /** + * True if there is no more data in the buffered part of the stream. + * + * @serial + */ + public boolean eof; +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/OutputStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/OutputStream.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,154 @@ +/* + * Copyright (c) 1994, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +/** + * This abstract class is the superclass of all classes representing + * an output stream of bytes. An output stream accepts output bytes + * and sends them to some sink. + *

+ * Applications that need to define a subclass of + * OutputStream must always provide at least a method + * that writes one byte of output. + * + * @author Arthur van Hoff + * @see java.io.BufferedOutputStream + * @see java.io.ByteArrayOutputStream + * @see java.io.DataOutputStream + * @see java.io.FilterOutputStream + * @see java.io.InputStream + * @see java.io.OutputStream#write(int) + * @since JDK1.0 + */ +public abstract class OutputStream implements Closeable, Flushable { + /** + * Writes the specified byte to this output stream. The general + * contract for write is that one byte is written + * to the output stream. The byte to be written is the eight + * low-order bits of the argument b. The 24 + * high-order bits of b are ignored. + *

+ * Subclasses of OutputStream must provide an + * implementation for this method. + * + * @param b the byte. + * @exception IOException if an I/O error occurs. In particular, + * an IOException may be thrown if the + * output stream has been closed. + */ + public abstract void write(int b) throws IOException; + + /** + * Writes b.length bytes from the specified byte array + * to this output stream. The general contract for write(b) + * is that it should have exactly the same effect as the call + * write(b, 0, b.length). + * + * @param b the data. + * @exception IOException if an I/O error occurs. + * @see java.io.OutputStream#write(byte[], int, int) + */ + public void write(byte b[]) throws IOException { + write(b, 0, b.length); + } + + /** + * Writes len bytes from the specified byte array + * starting at offset off to this output stream. + * The general contract for write(b, off, len) is that + * some of the bytes in the array b are written to the + * output stream in order; element b[off] is the first + * byte written and b[off+len-1] is the last byte written + * by this operation. + *

+ * The write method of OutputStream calls + * the write method of one argument on each of the bytes to be + * written out. Subclasses are encouraged to override this method and + * provide a more efficient implementation. + *

+ * If b is null, a + * NullPointerException is thrown. + *

+ * If off is negative, or len is negative, or + * off+len is greater than the length of the array + * b, then an IndexOutOfBoundsException is thrown. + * + * @param b the data. + * @param off the start offset in the data. + * @param len the number of bytes to write. + * @exception IOException if an I/O error occurs. In particular, + * an IOException is thrown if the output + * stream is closed. + */ + public void write(byte b[], int off, int len) throws IOException { + if (b == null) { + throw new NullPointerException(); + } else if ((off < 0) || (off > b.length) || (len < 0) || + ((off + len) > b.length) || ((off + len) < 0)) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return; + } + for (int i = 0 ; i < len ; i++) { + write(b[off + i]); + } + } + + /** + * Flushes this output stream and forces any buffered output bytes + * to be written out. The general contract of flush is + * that calling it is an indication that, if any bytes previously + * written have been buffered by the implementation of the output + * stream, such bytes should immediately be written to their + * intended destination. + *

+ * If the intended destination of this stream is an abstraction provided by + * the underlying operating system, for example a file, then flushing the + * stream guarantees only that bytes previously written to the stream are + * passed to the operating system for writing; it does not guarantee that + * they are actually written to a physical device such as a disk drive. + *

+ * The flush method of OutputStream does nothing. + * + * @exception IOException if an I/O error occurs. + */ + public void flush() throws IOException { + } + + /** + * Closes this output stream and releases any system resources + * associated with this stream. The general contract of close + * is that it closes the output stream. A closed stream cannot perform + * output operations and cannot be reopened. + *

+ * The close method of OutputStream does nothing. + * + * @exception IOException if an I/O error occurs. + */ + public void close() throws IOException { + } + +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/StreamCorruptedException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/StreamCorruptedException.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +/** + * Thrown when control information that was read from an object stream + * violates internal consistency checks. + * + * @author unascribed + * @since JDK1.1 + */ +public class StreamCorruptedException extends ObjectStreamException { + + private static final long serialVersionUID = 8983558202217591746L; + + /** + * Create a StreamCorruptedException and list a reason why thrown. + * + * @param reason String describing the reason for the exception. + */ + public StreamCorruptedException(String reason) { + super(reason); + } + + /** + * Create a StreamCorruptedException and list no reason why thrown. + */ + public StreamCorruptedException() { + super(); + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/io/WriteAbortedException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/WriteAbortedException.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +/** + * Signals that one of the ObjectStreamExceptions was thrown during a + * write operation. Thrown during a read operation when one of the + * ObjectStreamExceptions was thrown during a write operation. The + * exception that terminated the write can be found in the detail + * field. The stream is reset to it's initial state and all references + * to objects already deserialized are discarded. + * + *

As of release 1.4, this exception has been retrofitted to conform to + * the general purpose exception-chaining mechanism. The "exception causing + * the abort" that is provided at construction time and + * accessed via the public {@link #detail} field is now known as the + * cause, and may be accessed via the {@link Throwable#getCause()} + * method, as well as the aforementioned "legacy field." + * + * @author unascribed + * @since JDK1.1 + */ +public class WriteAbortedException extends ObjectStreamException { + private static final long serialVersionUID = -3326426625597282442L; + + /** + * Exception that was caught while writing the ObjectStream. + * + *

This field predates the general-purpose exception chaining facility. + * The {@link Throwable#getCause()} method is now the preferred means of + * obtaining this information. + * + * @serial + */ + public Exception detail; + + /** + * Constructs a WriteAbortedException with a string describing + * the exception and the exception causing the abort. + * @param s String describing the exception. + * @param ex Exception causing the abort. + */ + public WriteAbortedException(String s, Exception ex) { + super(s); + initCause(null); // Disallow subsequent initCause + detail = ex; + } + + /** + * Produce the message and include the message from the nested + * exception, if there is one. + */ + public String getMessage() { + if (detail == null) + return super.getMessage(); + else + return super.getMessage() + "; " + detail.toString(); + } + + /** + * Returns the exception that terminated the operation (the cause). + * + * @return the exception that terminated the operation (the cause), + * which may be null. + * @since 1.4 + */ + public Throwable getCause() { + return detail; + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/lang/AbstractMethodError.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/lang/AbstractMethodError.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 1994, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * Thrown when an application tries to call an abstract method. + * Normally, this error is caught by the compiler; this error can + * only occur at run time if the definition of some class has + * incompatibly changed since the currently executing method was last + * compiled. + * + * @author unascribed + * @since JDK1.0 + */ +public +class AbstractMethodError extends IncompatibleClassChangeError { + private static final long serialVersionUID = -1654391082989018462L; + + /** + * Constructs an AbstractMethodError with no detail message. + */ + public AbstractMethodError() { + super(); + } + + /** + * Constructs an AbstractMethodError with the specified + * detail message. + * + * @param s the detail message. + */ + public AbstractMethodError(String s) { + super(s); + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/lang/IncompatibleClassChangeError.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/lang/IncompatibleClassChangeError.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 1994, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * Thrown when an incompatible class change has occurred to some class + * definition. The definition of some class, on which the currently + * executing method depends, has since changed. + * + * @author unascribed + * @since JDK1.0 + */ +public +class IncompatibleClassChangeError extends LinkageError { + private static final long serialVersionUID = -4914975503642802119L; + + /** + * Constructs an IncompatibleClassChangeError with no + * detail message. + */ + public IncompatibleClassChangeError () { + super(); + } + + /** + * Constructs an IncompatibleClassChangeError with the + * specified detail message. + * + * @param s the detail message. + */ + public IncompatibleClassChangeError(String s) { + super(s); + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/lang/NoSuchFieldError.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/lang/NoSuchFieldError.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * Thrown if an application tries to access or modify a specified + * field of an object, and that object no longer has that field. + *

+ * Normally, this error is caught by the compiler; this error can + * only occur at run time if the definition of a class has + * incompatibly changed. + * + * @author unascribed + * @since JDK1.0 + */ +public +class NoSuchFieldError extends IncompatibleClassChangeError { + private static final long serialVersionUID = -3456430195886129035L; + + /** + * Constructs a NoSuchFieldError with no detail message. + */ + public NoSuchFieldError() { + super(); + } + + /** + * Constructs a NoSuchFieldError with the specified + * detail message. + * + * @param s the detail message. + */ + public NoSuchFieldError(String s) { + super(s); + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/lang/System.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/lang/System.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,36 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program 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, version 2 of the License. + * + * This program 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 this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package java.lang; + +/** Poor man's re-implementation of most important System methods. + * + * @author Jaroslav Tulach + */ +public class System { + private System() { + } + + public static void arraycopy(Object value, int srcBegin, Object dst, int dstBegin, int count) { + org.apidesign.bck2brwsr.emul.lang.System.arraycopy(value, srcBegin, dst, dstBegin, count); + } + + public static long currentTimeMillis() { + return org.apidesign.bck2brwsr.emul.lang.System.currentTimeMillis(); + } + +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/lang/ref/PhantomReference.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/lang/ref/PhantomReference.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.ref; + + +/** + * Phantom reference objects, which are enqueued after the collector + * determines that their referents may otherwise be reclaimed. Phantom + * references are most often used for scheduling pre-mortem cleanup actions in + * a more flexible way than is possible with the Java finalization mechanism. + * + *

If the garbage collector determines at a certain point in time that the + * referent of a phantom reference is phantom reachable, then at that + * time or at some later time it will enqueue the reference. + * + *

In order to ensure that a reclaimable object remains so, the referent of + * a phantom reference may not be retrieved: The get method of a + * phantom reference always returns null. + * + *

Unlike soft and weak references, phantom references are not + * automatically cleared by the garbage collector as they are enqueued. An + * object that is reachable via phantom references will remain so until all + * such references are cleared or themselves become unreachable. + * + * @author Mark Reinhold + * @since 1.2 + */ + +public class PhantomReference extends Reference { + + /** + * Returns this reference object's referent. Because the referent of a + * phantom reference is always inaccessible, this method always returns + * null. + * + * @return null + */ + public T get() { + return null; + } + + /** + * Creates a new phantom reference that refers to the given object and + * is registered with the given queue. + * + *

It is possible to create a phantom reference with a null + * queue, but such a reference is completely useless: Its get + * method will always return null and, since it does not have a queue, it + * will never be enqueued. + * + * @param referent the object the new phantom reference will refer to + * @param q the queue with which the reference is to be registered, + * or null if registration is not required + */ + public PhantomReference(T referent, ReferenceQueue q) { + super(referent, q); + } + +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/lang/ref/Reference.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/lang/ref/Reference.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,185 @@ +/* + * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.ref; + + +/** + * Abstract base class for reference objects. This class defines the + * operations common to all reference objects. Because reference objects are + * implemented in close cooperation with the garbage collector, this class may + * not be subclassed directly. + * + * @author Mark Reinhold + * @since 1.2 + */ + +public abstract class Reference { + + /* A Reference instance is in one of four possible internal states: + * + * Active: Subject to special treatment by the garbage collector. Some + * time after the collector detects that the reachability of the + * referent has changed to the appropriate state, it changes the + * instance's state to either Pending or Inactive, depending upon + * whether or not the instance was registered with a queue when it was + * created. In the former case it also adds the instance to the + * pending-Reference list. Newly-created instances are Active. + * + * Pending: An element of the pending-Reference list, waiting to be + * enqueued by the Reference-handler thread. Unregistered instances + * are never in this state. + * + * Enqueued: An element of the queue with which the instance was + * registered when it was created. When an instance is removed from + * its ReferenceQueue, it is made Inactive. Unregistered instances are + * never in this state. + * + * Inactive: Nothing more to do. Once an instance becomes Inactive its + * state will never change again. + * + * The state is encoded in the queue and next fields as follows: + * + * Active: queue = ReferenceQueue with which instance is registered, or + * ReferenceQueue.NULL if it was not registered with a queue; next = + * null. + * + * Pending: queue = ReferenceQueue with which instance is registered; + * next = Following instance in queue, or this if at end of list. + * + * Enqueued: queue = ReferenceQueue.ENQUEUED; next = Following instance + * in queue, or this if at end of list. + * + * Inactive: queue = ReferenceQueue.NULL; next = this. + * + * With this scheme the collector need only examine the next field in order + * to determine whether a Reference instance requires special treatment: If + * the next field is null then the instance is active; if it is non-null, + * then the collector should treat the instance normally. + * + * To ensure that concurrent collector can discover active Reference + * objects without interfering with application threads that may apply + * the enqueue() method to those objects, collectors should link + * discovered objects through the discovered field. + */ + + private T referent; /* Treated specially by GC */ + + ReferenceQueue queue; + + Reference next; + transient private Reference discovered; /* used by VM */ + + + /* Object used to synchronize with the garbage collector. The collector + * must acquire this lock at the beginning of each collection cycle. It is + * therefore critical that any code holding this lock complete as quickly + * as possible, allocate no new objects, and avoid calling user code. + */ + static private class Lock { }; + private static Lock lock = new Lock(); + + + /* List of References waiting to be enqueued. The collector adds + * References to this list, while the Reference-handler thread removes + * them. This list is protected by the above lock object. + */ + private static Reference pending = null; + + + + /* -- Referent accessor and setters -- */ + + /** + * Returns this reference object's referent. If this reference object has + * been cleared, either by the program or by the garbage collector, then + * this method returns null. + * + * @return The object to which this reference refers, or + * null if this reference object has been cleared + */ + public T get() { + return this.referent; + } + + /** + * Clears this reference object. Invoking this method will not cause this + * object to be enqueued. + * + *

This method is invoked only by Java code; when the garbage collector + * clears references it does so directly, without invoking this method. + */ + public void clear() { + this.referent = null; + } + + + /* -- Queue operations -- */ + + /** + * Tells whether or not this reference object has been enqueued, either by + * the program or by the garbage collector. If this reference object was + * not registered with a queue when it was created, then this method will + * always return false. + * + * @return true if and only if this reference object has + * been enqueued + */ + public boolean isEnqueued() { + /* In terms of the internal states, this predicate actually tests + whether the instance is either Pending or Enqueued */ + synchronized (this) { + return (this.queue != ReferenceQueue.NULL) && (this.next != null); + } + } + + /** + * Adds this reference object to the queue with which it is registered, + * if any. + * + *

This method is invoked only by Java code; when the garbage collector + * enqueues references it does so directly, without invoking this method. + * + * @return true if this reference object was successfully + * enqueued; false if it was already enqueued or if + * it was not registered with a queue when it was created + */ + public boolean enqueue() { + return this.queue.enqueue(this); + } + + + /* -- Constructors -- */ + + Reference(T referent) { + this(referent, null); + } + + Reference(T referent, ReferenceQueue queue) { + this.referent = referent; + this.queue = (queue == null) ? ReferenceQueue.NULL : queue; + } + +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/lang/ref/ReferenceQueue.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/lang/ref/ReferenceQueue.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,148 @@ +/* + * Copyright (c) 1997, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.ref; + +/** + * Reference queues, to which registered reference objects are appended by the + * garbage collector after the appropriate reachability changes are detected. + * + * @author Mark Reinhold + * @since 1.2 + */ + +public class ReferenceQueue { + + /** + * Constructs a new reference-object queue. + */ + public ReferenceQueue() { } + + private static class Null extends ReferenceQueue { + boolean enqueue(Reference r) { + return false; + } + } + + static ReferenceQueue NULL = new Null(); + static ReferenceQueue ENQUEUED = new Null(); + + static private class Lock { }; + private Lock lock = new Lock(); + private volatile Reference head = null; + private long queueLength = 0; + + boolean enqueue(Reference r) { /* Called only by Reference class */ + synchronized (r) { + if (r.queue == ENQUEUED) return false; + synchronized (lock) { + r.queue = ENQUEUED; + r.next = (head == null) ? r : head; + head = r; + queueLength++; + lock.notifyAll(); + return true; + } + } + } + + private Reference reallyPoll() { /* Must hold lock */ + if (head != null) { + Reference r = head; + head = (r.next == r) ? null : r.next; + r.queue = NULL; + r.next = r; + queueLength--; + return r; + } + return null; + } + + /** + * Polls this queue to see if a reference object is available. If one is + * available without further delay then it is removed from the queue and + * returned. Otherwise this method immediately returns null. + * + * @return A reference object, if one was immediately available, + * otherwise null + */ + public Reference poll() { + if (head == null) + return null; + synchronized (lock) { + return reallyPoll(); + } + } + + /** + * Removes the next reference object in this queue, blocking until either + * one becomes available or the given timeout period expires. + * + *

This method does not offer real-time guarantees: It schedules the + * timeout as if by invoking the {@link Object#wait(long)} method. + * + * @param timeout If positive, block for up to timeout + * milliseconds while waiting for a reference to be + * added to this queue. If zero, block indefinitely. + * + * @return A reference object, if one was available within the specified + * timeout period, otherwise null + * + * @throws IllegalArgumentException + * If the value of the timeout argument is negative + * + * @throws InterruptedException + * If the timeout wait is interrupted + */ + public Reference remove(long timeout) + throws IllegalArgumentException, InterruptedException + { + if (timeout < 0) { + throw new IllegalArgumentException("Negative timeout value"); + } + synchronized (lock) { + Reference r = reallyPoll(); + if (r != null) return r; + for (;;) { + lock.wait(timeout); + r = reallyPoll(); + if (r != null) return r; + if (timeout != 0) return null; + } + } + } + + /** + * Removes the next reference object in this queue, blocking until one + * becomes available. + * + * @return A reference object, blocking until one becomes available + * @throws InterruptedException If the wait is interrupted + */ + public Reference remove() throws InterruptedException { + return remove(0); + } + +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/lang/ref/SoftReference.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/lang/ref/SoftReference.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,118 @@ +/* + * Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.ref; + + +/** + * Soft reference objects, which are cleared at the discretion of the garbage + * collector in response to memory demand. Soft references are most often used + * to implement memory-sensitive caches. + * + *

Suppose that the garbage collector determines at a certain point in time + * that an object is softly + * reachable. At that time it may choose to clear atomically all soft + * references to that object and all soft references to any other + * softly-reachable objects from which that object is reachable through a chain + * of strong references. At the same time or at some later time it will + * enqueue those newly-cleared soft references that are registered with + * reference queues. + * + *

All soft references to softly-reachable objects are guaranteed to have + * been cleared before the virtual machine throws an + * OutOfMemoryError. Otherwise no constraints are placed upon the + * time at which a soft reference will be cleared or the order in which a set + * of such references to different objects will be cleared. Virtual machine + * implementations are, however, encouraged to bias against clearing + * recently-created or recently-used soft references. + * + *

Direct instances of this class may be used to implement simple caches; + * this class or derived subclasses may also be used in larger data structures + * to implement more sophisticated caches. As long as the referent of a soft + * reference is strongly reachable, that is, is actually in use, the soft + * reference will not be cleared. Thus a sophisticated cache can, for example, + * prevent its most recently used entries from being discarded by keeping + * strong referents to those entries, leaving the remaining entries to be + * discarded at the discretion of the garbage collector. + * + * @author Mark Reinhold + * @since 1.2 + */ + +public class SoftReference extends Reference { + + /** + * Timestamp clock, updated by the garbage collector + */ + static private long clock; + + /** + * Timestamp updated by each invocation of the get method. The VM may use + * this field when selecting soft references to be cleared, but it is not + * required to do so. + */ + private long timestamp; + + /** + * Creates a new soft reference that refers to the given object. The new + * reference is not registered with any queue. + * + * @param referent object the new soft reference will refer to + */ + public SoftReference(T referent) { + super(referent); + this.timestamp = clock; + } + + /** + * Creates a new soft reference that refers to the given object and is + * registered with the given queue. + * + * @param referent object the new soft reference will refer to + * @param q the queue with which the reference is to be registered, + * or null if registration is not required + * + */ + public SoftReference(T referent, ReferenceQueue q) { + super(referent, q); + this.timestamp = clock; + } + + /** + * Returns this reference object's referent. If this reference object has + * been cleared, either by the program or by the garbage collector, then + * this method returns null. + * + * @return The object to which this reference refers, or + * null if this reference object has been cleared + */ + public T get() { + T o = super.get(); + if (o != null && this.timestamp != clock) + this.timestamp = clock; + return o; + } + +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/lang/ref/WeakReference.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/lang/ref/WeakReference.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.ref; + + +/** + * Weak reference objects, which do not prevent their referents from being + * made finalizable, finalized, and then reclaimed. Weak references are most + * often used to implement canonicalizing mappings. + * + *

Suppose that the garbage collector determines at a certain point in time + * that an object is weakly + * reachable. At that time it will atomically clear all weak references to + * that object and all weak references to any other weakly-reachable objects + * from which that object is reachable through a chain of strong and soft + * references. At the same time it will declare all of the formerly + * weakly-reachable objects to be finalizable. At the same time or at some + * later time it will enqueue those newly-cleared weak references that are + * registered with reference queues. + * + * @author Mark Reinhold + * @since 1.2 + */ + +public class WeakReference extends Reference { + + /** + * Creates a new weak reference that refers to the given object. The new + * reference is not registered with any queue. + * + * @param referent object the new weak reference will refer to + */ + public WeakReference(T referent) { + super(referent); + } + + /** + * Creates a new weak reference that refers to the given object and is + * registered with the given queue. + * + * @param referent object the new weak reference will refer to + * @param q the queue with which the reference is to be registered, + * or null if registration is not required + */ + public WeakReference(T referent, ReferenceQueue q) { + super(referent, q); + } + +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/lang/ref/package.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/lang/ref/package.html Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,147 @@ + + + + + + + +Provides reference-object classes, which support a limited degree of +interaction with the garbage collector. A program may use a reference object +to maintain a reference to some other object in such a way that the latter +object may still be reclaimed by the collector. A program may also arrange to +be notified some time after the collector has determined that the reachability +of a given object has changed. + + +

Package Specification

+ +A reference object encapsulates a reference to some other object so +that the reference itself may be examined and manipulated like any other +object. Three types of reference objects are provided, each weaker than the +last: soft, weak, and phantom. Each type +corresponds to a different level of reachability, as defined below. Soft +references are for implementing memory-sensitive caches, weak references are +for implementing canonicalizing mappings that do not prevent their keys (or +values) from being reclaimed, and phantom references are for scheduling +pre-mortem cleanup actions in a more flexible way than is possible with the +Java finalization mechanism. + +

Each reference-object type is implemented by a subclass of the abstract +base {@link java.lang.ref.Reference} class. An instance of one of +these subclasses encapsulates a single reference to a particular object, called +the referent. Every reference object provides methods for getting and +clearing the reference. Aside from the clearing operation reference objects +are otherwise immutable, so no set operation is provided. A +program may further subclass these subclasses, adding whatever fields and +methods are required for its purposes, or it may use these subclasses without +change. + + +

Notification

+ +A program may request to be notified of changes in an object's reachability by +registering an appropriate reference object with a reference +queue at the time the reference object is created. Some time after the +garbage collector determines that the reachability of the referent has changed +to the value corresponding to the type of the reference, it will add the +reference to the associated queue. At this point, the reference is considered +to be enqueued. The program may remove references from a queue either +by polling or by blocking until a reference becomes available. Reference +queues are implemented by the {@link java.lang.ref.ReferenceQueue} +class. + +

The relationship between a registered reference object and its queue is +one-sided. That is, a queue does not keep track of the references that are +registered with it. If a registered reference becomes unreachable itself, then +it will never be enqueued. It is the responsibility of the program using +reference objects to ensure that the objects remain reachable for as long as +the program is interested in their referents. + +

While some programs will choose to dedicate a thread to removing reference +objects from one or more queues and processing them, this is by no means +necessary. A tactic that often works well is to examine a reference queue in +the course of performing some other fairly-frequent action. For example, a +hashtable that uses weak references to implement weak keys could poll its +reference queue each time the table is accessed. This is how the {@link +java.util.WeakHashMap} class works. Because the {@link +java.lang.ref.ReferenceQueue#poll ReferenceQueue.poll} method simply +checks an internal data structure, this check will add little overhead to the +hashtable access methods. + + +

Automatically-cleared references

+ +Soft and weak references are automatically cleared by the collector before +being added to the queues with which they are registered, if any. Therefore +soft and weak references need not be registered with a queue in order to be +useful, while phantom references do. An object that is reachable via phantom +references will remain so until all such references are cleared or themselves +become unreachable. + + + +

Reachability

+ +Going from strongest to weakest, the different levels of reachability reflect +the life cycle of an object. They are operationally defined as follows: + +
    + +
  • An object is strongly reachable if it can be reached by some +thread without traversing any reference objects. A newly-created object is +strongly reachable by the thread that created it. + +
  • An object is softly reachable if it is not strongly reachable but +can be reached by traversing a soft reference. + +
  • An object is weakly reachable if it is neither strongly nor +softly reachable but can be reached by traversing a weak reference. When the +weak references to a weakly-reachable object are cleared, the object becomes +eligible for finalization. + +
  • An object is phantom reachable if it is neither strongly, softly, +nor weakly reachable, it has been finalized, and some phantom reference refers +to it. + +
  • Finally, an object is unreachable, and therefore eligible for +reclamation, when it is not reachable in any of the above ways. + +
+ + +@author Mark Reinhold +@since 1.2 + + + + diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/AbstractQueue.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/AbstractQueue.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,192 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +package java.util; + +/** + * This class provides skeletal implementations of some {@link Queue} + * operations. The implementations in this class are appropriate when + * the base implementation does not allow null + * elements. Methods {@link #add add}, {@link #remove remove}, and + * {@link #element element} are based on {@link #offer offer}, {@link + * #poll poll}, and {@link #peek peek}, respectively, but throw + * exceptions instead of indicating failure via false or + * null returns. + * + *

A Queue implementation that extends this class must + * minimally define a method {@link Queue#offer} which does not permit + * insertion of null elements, along with methods {@link + * Queue#peek}, {@link Queue#poll}, {@link Collection#size}, and + * {@link Collection#iterator}. Typically, additional methods will be + * overridden as well. If these requirements cannot be met, consider + * instead subclassing {@link AbstractCollection}. + * + *

This class is a member of the + * + * Java Collections Framework. + * + * @since 1.5 + * @author Doug Lea + * @param the type of elements held in this collection + */ +public abstract class AbstractQueue + extends AbstractCollection + implements Queue { + + /** + * Constructor for use by subclasses. + */ + protected AbstractQueue() { + } + + /** + * Inserts the specified element into this queue if it is possible to do so + * immediately without violating capacity restrictions, returning + * true upon success and throwing an IllegalStateException + * if no space is currently available. + * + *

This implementation returns true if offer succeeds, + * else throws an IllegalStateException. + * + * @param e the element to add + * @return true (as specified by {@link Collection#add}) + * @throws IllegalStateException if the element cannot be added at this + * time due to capacity restrictions + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this queue + * @throws NullPointerException if the specified element is null and + * this queue does not permit null elements + * @throws IllegalArgumentException if some property of this element + * prevents it from being added to this queue + */ + public boolean add(E e) { + if (offer(e)) + return true; + else + throw new IllegalStateException("Queue full"); + } + + /** + * Retrieves and removes the head of this queue. This method differs + * from {@link #poll poll} only in that it throws an exception if this + * queue is empty. + * + *

This implementation returns the result of poll + * unless the queue is empty. + * + * @return the head of this queue + * @throws NoSuchElementException if this queue is empty + */ + public E remove() { + E x = poll(); + if (x != null) + return x; + else + throw new NoSuchElementException(); + } + + /** + * Retrieves, but does not remove, the head of this queue. This method + * differs from {@link #peek peek} only in that it throws an exception if + * this queue is empty. + * + *

This implementation returns the result of peek + * unless the queue is empty. + * + * @return the head of this queue + * @throws NoSuchElementException if this queue is empty + */ + public E element() { + E x = peek(); + if (x != null) + return x; + else + throw new NoSuchElementException(); + } + + /** + * Removes all of the elements from this queue. + * The queue will be empty after this call returns. + * + *

This implementation repeatedly invokes {@link #poll poll} until it + * returns null. + */ + public void clear() { + while (poll() != null) + ; + } + + /** + * Adds all of the elements in the specified collection to this + * queue. Attempts to addAll of a queue to itself result in + * IllegalArgumentException. Further, the behavior of + * this operation is undefined if the specified collection is + * modified while the operation is in progress. + * + *

This implementation iterates over the specified collection, + * and adds each element returned by the iterator to this + * queue, in turn. A runtime exception encountered while + * trying to add an element (including, in particular, a + * null element) may result in only some of the elements + * having been successfully added when the associated exception is + * thrown. + * + * @param c collection containing elements to be added to this queue + * @return true if this queue changed as a result of the call + * @throws ClassCastException if the class of an element of the specified + * collection prevents it from being added to this queue + * @throws NullPointerException if the specified collection contains a + * null element and this queue does not permit null elements, + * or if the specified collection is null + * @throws IllegalArgumentException if some property of an element of the + * specified collection prevents it from being added to this + * queue, or if the specified collection is this queue + * @throws IllegalStateException if not all the elements can be added at + * this time due to insertion restrictions + * @see #add(Object) + */ + public boolean addAll(Collection c) { + if (c == null) + throw new NullPointerException(); + if (c == this) + throw new IllegalArgumentException(); + boolean modified = false; + for (E e : c) + if (add(e)) + modified = true; + return modified; + } + +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/AbstractSequentialList.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/AbstractSequentialList.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,253 @@ +/* + * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util; + +/** + * This class provides a skeletal implementation of the List + * interface to minimize the effort required to implement this interface + * backed by a "sequential access" data store (such as a linked list). For + * random access data (such as an array), AbstractList should be used + * in preference to this class.

+ * + * This class is the opposite of the AbstractList class in the sense + * that it implements the "random access" methods (get(int index), + * set(int index, E element), add(int index, E element) and + * remove(int index)) on top of the list's list iterator, instead of + * the other way around.

+ * + * To implement a list the programmer needs only to extend this class and + * provide implementations for the listIterator and size + * methods. For an unmodifiable list, the programmer need only implement the + * list iterator's hasNext, next, hasPrevious, + * previous and index methods.

+ * + * For a modifiable list the programmer should additionally implement the list + * iterator's set method. For a variable-size list the programmer + * should additionally implement the list iterator's remove and + * add methods.

+ * + * The programmer should generally provide a void (no argument) and collection + * constructor, as per the recommendation in the Collection interface + * specification.

+ * + * This class is a member of the + * + * Java Collections Framework. + * + * @author Josh Bloch + * @author Neal Gafter + * @see Collection + * @see List + * @see AbstractList + * @see AbstractCollection + * @since 1.2 + */ + +public abstract class AbstractSequentialList extends AbstractList { + /** + * Sole constructor. (For invocation by subclass constructors, typically + * implicit.) + */ + protected AbstractSequentialList() { + } + + /** + * Returns the element at the specified position in this list. + * + *

This implementation first gets a list iterator pointing to the + * indexed element (with listIterator(index)). Then, it gets + * the element using ListIterator.next and returns it. + * + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public E get(int index) { + try { + return listIterator(index).next(); + } catch (NoSuchElementException exc) { + throw new IndexOutOfBoundsException("Index: "+index); + } + } + + /** + * Replaces the element at the specified position in this list with the + * specified element (optional operation). + * + *

This implementation first gets a list iterator pointing to the + * indexed element (with listIterator(index)). Then, it gets + * the current element using ListIterator.next and replaces it + * with ListIterator.set. + * + *

Note that this implementation will throw an + * UnsupportedOperationException if the list iterator does not + * implement the set operation. + * + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public E set(int index, E element) { + try { + ListIterator e = listIterator(index); + E oldVal = e.next(); + e.set(element); + return oldVal; + } catch (NoSuchElementException exc) { + throw new IndexOutOfBoundsException("Index: "+index); + } + } + + /** + * Inserts the specified element at the specified position in this list + * (optional operation). Shifts the element currently at that position + * (if any) and any subsequent elements to the right (adds one to their + * indices). + * + *

This implementation first gets a list iterator pointing to the + * indexed element (with listIterator(index)). Then, it + * inserts the specified element with ListIterator.add. + * + *

Note that this implementation will throw an + * UnsupportedOperationException if the list iterator does not + * implement the add operation. + * + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public void add(int index, E element) { + try { + listIterator(index).add(element); + } catch (NoSuchElementException exc) { + throw new IndexOutOfBoundsException("Index: "+index); + } + } + + /** + * Removes the element at the specified position in this list (optional + * operation). Shifts any subsequent elements to the left (subtracts one + * from their indices). Returns the element that was removed from the + * list. + * + *

This implementation first gets a list iterator pointing to the + * indexed element (with listIterator(index)). Then, it removes + * the element with ListIterator.remove. + * + *

Note that this implementation will throw an + * UnsupportedOperationException if the list iterator does not + * implement the remove operation. + * + * @throws UnsupportedOperationException {@inheritDoc} + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public E remove(int index) { + try { + ListIterator e = listIterator(index); + E outCast = e.next(); + e.remove(); + return outCast; + } catch (NoSuchElementException exc) { + throw new IndexOutOfBoundsException("Index: "+index); + } + } + + + // Bulk Operations + + /** + * Inserts all of the elements in the specified collection into this + * list at the specified position (optional operation). Shifts the + * element currently at that position (if any) and any subsequent + * elements to the right (increases their indices). The new elements + * will appear in this list in the order that they are returned by the + * specified collection's iterator. The behavior of this operation is + * undefined if the specified collection is modified while the + * operation is in progress. (Note that this will occur if the specified + * collection is this list, and it's nonempty.) + * + *

This implementation gets an iterator over the specified collection and + * a list iterator over this list pointing to the indexed element (with + * listIterator(index)). Then, it iterates over the specified + * collection, inserting the elements obtained from the iterator into this + * list, one at a time, using ListIterator.add followed by + * ListIterator.next (to skip over the added element). + * + *

Note that this implementation will throw an + * UnsupportedOperationException if the list iterator returned by + * the listIterator method does not implement the add + * operation. + * + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public boolean addAll(int index, Collection c) { + try { + boolean modified = false; + ListIterator e1 = listIterator(index); + Iterator e2 = c.iterator(); + while (e2.hasNext()) { + e1.add(e2.next()); + modified = true; + } + return modified; + } catch (NoSuchElementException exc) { + throw new IndexOutOfBoundsException("Index: "+index); + } + } + + + // Iterators + + /** + * Returns an iterator over the elements in this list (in proper + * sequence).

+ * + * This implementation merely returns a list iterator over the list. + * + * @return an iterator over the elements in this list (in proper sequence) + */ + public Iterator iterator() { + return listIterator(); + } + + /** + * Returns a list iterator over the elements in this list (in proper + * sequence). + * + * @param index index of first element to be returned from the list + * iterator (by a call to the next method) + * @return a list iterator over the elements in this list (in proper + * sequence) + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public abstract ListIterator listIterator(int index); +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/ArrayDeque.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/ArrayDeque.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,830 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Josh Bloch of Google Inc. and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/. + */ + +package java.util; +import java.io.*; + +/** + * Resizable-array implementation of the {@link Deque} interface. Array + * deques have no capacity restrictions; they grow as necessary to support + * usage. They are not thread-safe; in the absence of external + * synchronization, they do not support concurrent access by multiple threads. + * Null elements are prohibited. This class is likely to be faster than + * {@link Stack} when used as a stack, and faster than {@link LinkedList} + * when used as a queue. + * + *

Most ArrayDeque operations run in amortized constant time. + * Exceptions include {@link #remove(Object) remove}, {@link + * #removeFirstOccurrence removeFirstOccurrence}, {@link #removeLastOccurrence + * removeLastOccurrence}, {@link #contains contains}, {@link #iterator + * iterator.remove()}, and the bulk operations, all of which run in linear + * time. + * + *

The iterators returned by this class's iterator method are + * fail-fast: If the deque is modified at any time after the iterator + * is created, in any way except through the iterator's own remove + * method, the iterator will generally throw a {@link + * ConcurrentModificationException}. Thus, in the face of concurrent + * modification, the iterator fails quickly and cleanly, rather than risking + * arbitrary, non-deterministic behavior at an undetermined time in the + * future. + * + *

Note that the fail-fast behavior of an iterator cannot be guaranteed + * as it is, generally speaking, impossible to make any hard guarantees in the + * presence of unsynchronized concurrent modification. Fail-fast iterators + * throw ConcurrentModificationException on a best-effort basis. + * Therefore, it would be wrong to write a program that depended on this + * exception for its correctness: the fail-fast behavior of iterators + * should be used only to detect bugs. + * + *

This class and its iterator implement all of the + * optional methods of the {@link Collection} and {@link + * Iterator} interfaces. + * + *

This class is a member of the + * + * Java Collections Framework. + * + * @author Josh Bloch and Doug Lea + * @since 1.6 + * @param the type of elements held in this collection + */ +public class ArrayDeque extends AbstractCollection + implements Deque, Cloneable, Serializable +{ + /** + * The array in which the elements of the deque are stored. + * The capacity of the deque is the length of this array, which is + * always a power of two. The array is never allowed to become + * full, except transiently within an addX method where it is + * resized (see doubleCapacity) immediately upon becoming full, + * thus avoiding head and tail wrapping around to equal each + * other. We also guarantee that all array cells not holding + * deque elements are always null. + */ + private transient E[] elements; + + /** + * The index of the element at the head of the deque (which is the + * element that would be removed by remove() or pop()); or an + * arbitrary number equal to tail if the deque is empty. + */ + private transient int head; + + /** + * The index at which the next element would be added to the tail + * of the deque (via addLast(E), add(E), or push(E)). + */ + private transient int tail; + + /** + * The minimum capacity that we'll use for a newly created deque. + * Must be a power of 2. + */ + private static final int MIN_INITIAL_CAPACITY = 8; + + // ****** Array allocation and resizing utilities ****** + + /** + * Allocate empty array to hold the given number of elements. + * + * @param numElements the number of elements to hold + */ + private void allocateElements(int numElements) { + int initialCapacity = MIN_INITIAL_CAPACITY; + // Find the best power of two to hold elements. + // Tests "<=" because arrays aren't kept full. + if (numElements >= initialCapacity) { + initialCapacity = numElements; + initialCapacity |= (initialCapacity >>> 1); + initialCapacity |= (initialCapacity >>> 2); + initialCapacity |= (initialCapacity >>> 4); + initialCapacity |= (initialCapacity >>> 8); + initialCapacity |= (initialCapacity >>> 16); + initialCapacity++; + + if (initialCapacity < 0) // Too many elements, must back off + initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements + } + elements = (E[]) new Object[initialCapacity]; + } + + /** + * Double the capacity of this deque. Call only when full, i.e., + * when head and tail have wrapped around to become equal. + */ + private void doubleCapacity() { + assert head == tail; + int p = head; + int n = elements.length; + int r = n - p; // number of elements to the right of p + int newCapacity = n << 1; + if (newCapacity < 0) + throw new IllegalStateException("Sorry, deque too big"); + Object[] a = new Object[newCapacity]; + System.arraycopy(elements, p, a, 0, r); + System.arraycopy(elements, 0, a, r, p); + elements = (E[])a; + head = 0; + tail = n; + } + + /** + * Copies the elements from our element array into the specified array, + * in order (from first to last element in the deque). It is assumed + * that the array is large enough to hold all elements in the deque. + * + * @return its argument + */ + private T[] copyElements(T[] a) { + if (head < tail) { + System.arraycopy(elements, head, a, 0, size()); + } else if (head > tail) { + int headPortionLen = elements.length - head; + System.arraycopy(elements, head, a, 0, headPortionLen); + System.arraycopy(elements, 0, a, headPortionLen, tail); + } + return a; + } + + /** + * Constructs an empty array deque with an initial capacity + * sufficient to hold 16 elements. + */ + public ArrayDeque() { + elements = (E[]) new Object[16]; + } + + /** + * Constructs an empty array deque with an initial capacity + * sufficient to hold the specified number of elements. + * + * @param numElements lower bound on initial capacity of the deque + */ + public ArrayDeque(int numElements) { + allocateElements(numElements); + } + + /** + * Constructs a deque containing the elements of the specified + * collection, in the order they are returned by the collection's + * iterator. (The first element returned by the collection's + * iterator becomes the first element, or front of the + * deque.) + * + * @param c the collection whose elements are to be placed into the deque + * @throws NullPointerException if the specified collection is null + */ + public ArrayDeque(Collection c) { + allocateElements(c.size()); + addAll(c); + } + + // The main insertion and extraction methods are addFirst, + // addLast, pollFirst, pollLast. The other methods are defined in + // terms of these. + + /** + * Inserts the specified element at the front of this deque. + * + * @param e the element to add + * @throws NullPointerException if the specified element is null + */ + public void addFirst(E e) { + if (e == null) + throw new NullPointerException(); + elements[head = (head - 1) & (elements.length - 1)] = e; + if (head == tail) + doubleCapacity(); + } + + /** + * Inserts the specified element at the end of this deque. + * + *

This method is equivalent to {@link #add}. + * + * @param e the element to add + * @throws NullPointerException if the specified element is null + */ + public void addLast(E e) { + if (e == null) + throw new NullPointerException(); + elements[tail] = e; + if ( (tail = (tail + 1) & (elements.length - 1)) == head) + doubleCapacity(); + } + + /** + * Inserts the specified element at the front of this deque. + * + * @param e the element to add + * @return true (as specified by {@link Deque#offerFirst}) + * @throws NullPointerException if the specified element is null + */ + public boolean offerFirst(E e) { + addFirst(e); + return true; + } + + /** + * Inserts the specified element at the end of this deque. + * + * @param e the element to add + * @return true (as specified by {@link Deque#offerLast}) + * @throws NullPointerException if the specified element is null + */ + public boolean offerLast(E e) { + addLast(e); + return true; + } + + /** + * @throws NoSuchElementException {@inheritDoc} + */ + public E removeFirst() { + E x = pollFirst(); + if (x == null) + throw new NoSuchElementException(); + return x; + } + + /** + * @throws NoSuchElementException {@inheritDoc} + */ + public E removeLast() { + E x = pollLast(); + if (x == null) + throw new NoSuchElementException(); + return x; + } + + public E pollFirst() { + int h = head; + E result = elements[h]; // Element is null if deque empty + if (result == null) + return null; + elements[h] = null; // Must null out slot + head = (h + 1) & (elements.length - 1); + return result; + } + + public E pollLast() { + int t = (tail - 1) & (elements.length - 1); + E result = elements[t]; + if (result == null) + return null; + elements[t] = null; + tail = t; + return result; + } + + /** + * @throws NoSuchElementException {@inheritDoc} + */ + public E getFirst() { + E x = elements[head]; + if (x == null) + throw new NoSuchElementException(); + return x; + } + + /** + * @throws NoSuchElementException {@inheritDoc} + */ + public E getLast() { + E x = elements[(tail - 1) & (elements.length - 1)]; + if (x == null) + throw new NoSuchElementException(); + return x; + } + + public E peekFirst() { + return elements[head]; // elements[head] is null if deque empty + } + + public E peekLast() { + return elements[(tail - 1) & (elements.length - 1)]; + } + + /** + * Removes the first occurrence of the specified element in this + * deque (when traversing the deque from head to tail). + * If the deque does not contain the element, it is unchanged. + * More formally, removes the first element e such that + * o.equals(e) (if such an element exists). + * Returns true if this deque contained the specified element + * (or equivalently, if this deque changed as a result of the call). + * + * @param o element to be removed from this deque, if present + * @return true if the deque contained the specified element + */ + public boolean removeFirstOccurrence(Object o) { + if (o == null) + return false; + int mask = elements.length - 1; + int i = head; + E x; + while ( (x = elements[i]) != null) { + if (o.equals(x)) { + delete(i); + return true; + } + i = (i + 1) & mask; + } + return false; + } + + /** + * Removes the last occurrence of the specified element in this + * deque (when traversing the deque from head to tail). + * If the deque does not contain the element, it is unchanged. + * More formally, removes the last element e such that + * o.equals(e) (if such an element exists). + * Returns true if this deque contained the specified element + * (or equivalently, if this deque changed as a result of the call). + * + * @param o element to be removed from this deque, if present + * @return true if the deque contained the specified element + */ + public boolean removeLastOccurrence(Object o) { + if (o == null) + return false; + int mask = elements.length - 1; + int i = (tail - 1) & mask; + E x; + while ( (x = elements[i]) != null) { + if (o.equals(x)) { + delete(i); + return true; + } + i = (i - 1) & mask; + } + return false; + } + + // *** Queue methods *** + + /** + * Inserts the specified element at the end of this deque. + * + *

This method is equivalent to {@link #addLast}. + * + * @param e the element to add + * @return true (as specified by {@link Collection#add}) + * @throws NullPointerException if the specified element is null + */ + public boolean add(E e) { + addLast(e); + return true; + } + + /** + * Inserts the specified element at the end of this deque. + * + *

This method is equivalent to {@link #offerLast}. + * + * @param e the element to add + * @return true (as specified by {@link Queue#offer}) + * @throws NullPointerException if the specified element is null + */ + public boolean offer(E e) { + return offerLast(e); + } + + /** + * Retrieves and removes the head of the queue represented by this deque. + * + * This method differs from {@link #poll poll} only in that it throws an + * exception if this deque is empty. + * + *

This method is equivalent to {@link #removeFirst}. + * + * @return the head of the queue represented by this deque + * @throws NoSuchElementException {@inheritDoc} + */ + public E remove() { + return removeFirst(); + } + + /** + * Retrieves and removes the head of the queue represented by this deque + * (in other words, the first element of this deque), or returns + * null if this deque is empty. + * + *

This method is equivalent to {@link #pollFirst}. + * + * @return the head of the queue represented by this deque, or + * null if this deque is empty + */ + public E poll() { + return pollFirst(); + } + + /** + * Retrieves, but does not remove, the head of the queue represented by + * this deque. This method differs from {@link #peek peek} only in + * that it throws an exception if this deque is empty. + * + *

This method is equivalent to {@link #getFirst}. + * + * @return the head of the queue represented by this deque + * @throws NoSuchElementException {@inheritDoc} + */ + public E element() { + return getFirst(); + } + + /** + * Retrieves, but does not remove, the head of the queue represented by + * this deque, or returns null if this deque is empty. + * + *

This method is equivalent to {@link #peekFirst}. + * + * @return the head of the queue represented by this deque, or + * null if this deque is empty + */ + public E peek() { + return peekFirst(); + } + + // *** Stack methods *** + + /** + * Pushes an element onto the stack represented by this deque. In other + * words, inserts the element at the front of this deque. + * + *

This method is equivalent to {@link #addFirst}. + * + * @param e the element to push + * @throws NullPointerException if the specified element is null + */ + public void push(E e) { + addFirst(e); + } + + /** + * Pops an element from the stack represented by this deque. In other + * words, removes and returns the first element of this deque. + * + *

This method is equivalent to {@link #removeFirst()}. + * + * @return the element at the front of this deque (which is the top + * of the stack represented by this deque) + * @throws NoSuchElementException {@inheritDoc} + */ + public E pop() { + return removeFirst(); + } + + private void checkInvariants() { + assert elements[tail] == null; + assert head == tail ? elements[head] == null : + (elements[head] != null && + elements[(tail - 1) & (elements.length - 1)] != null); + assert elements[(head - 1) & (elements.length - 1)] == null; + } + + /** + * Removes the element at the specified position in the elements array, + * adjusting head and tail as necessary. This can result in motion of + * elements backwards or forwards in the array. + * + *

This method is called delete rather than remove to emphasize + * that its semantics differ from those of {@link List#remove(int)}. + * + * @return true if elements moved backwards + */ + private boolean delete(int i) { + checkInvariants(); + final E[] elements = this.elements; + final int mask = elements.length - 1; + final int h = head; + final int t = tail; + final int front = (i - h) & mask; + final int back = (t - i) & mask; + + // Invariant: head <= i < tail mod circularity + if (front >= ((t - h) & mask)) + throw new ConcurrentModificationException(); + + // Optimize for least element motion + if (front < back) { + if (h <= i) { + System.arraycopy(elements, h, elements, h + 1, front); + } else { // Wrap around + System.arraycopy(elements, 0, elements, 1, i); + elements[0] = elements[mask]; + System.arraycopy(elements, h, elements, h + 1, mask - h); + } + elements[h] = null; + head = (h + 1) & mask; + return false; + } else { + if (i < t) { // Copy the null tail as well + System.arraycopy(elements, i + 1, elements, i, back); + tail = t - 1; + } else { // Wrap around + System.arraycopy(elements, i + 1, elements, i, mask - i); + elements[mask] = elements[0]; + System.arraycopy(elements, 1, elements, 0, t); + tail = (t - 1) & mask; + } + return true; + } + } + + // *** Collection Methods *** + + /** + * Returns the number of elements in this deque. + * + * @return the number of elements in this deque + */ + public int size() { + return (tail - head) & (elements.length - 1); + } + + /** + * Returns true if this deque contains no elements. + * + * @return true if this deque contains no elements + */ + public boolean isEmpty() { + return head == tail; + } + + /** + * Returns an iterator over the elements in this deque. The elements + * will be ordered from first (head) to last (tail). This is the same + * order that elements would be dequeued (via successive calls to + * {@link #remove} or popped (via successive calls to {@link #pop}). + * + * @return an iterator over the elements in this deque + */ + public Iterator iterator() { + return new DeqIterator(); + } + + public Iterator descendingIterator() { + return new DescendingIterator(); + } + + private class DeqIterator implements Iterator { + /** + * Index of element to be returned by subsequent call to next. + */ + private int cursor = head; + + /** + * Tail recorded at construction (also in remove), to stop + * iterator and also to check for comodification. + */ + private int fence = tail; + + /** + * Index of element returned by most recent call to next. + * Reset to -1 if element is deleted by a call to remove. + */ + private int lastRet = -1; + + public boolean hasNext() { + return cursor != fence; + } + + public E next() { + if (cursor == fence) + throw new NoSuchElementException(); + E result = elements[cursor]; + // This check doesn't catch all possible comodifications, + // but does catch the ones that corrupt traversal + if (tail != fence || result == null) + throw new ConcurrentModificationException(); + lastRet = cursor; + cursor = (cursor + 1) & (elements.length - 1); + return result; + } + + public void remove() { + if (lastRet < 0) + throw new IllegalStateException(); + if (delete(lastRet)) { // if left-shifted, undo increment in next() + cursor = (cursor - 1) & (elements.length - 1); + fence = tail; + } + lastRet = -1; + } + } + + private class DescendingIterator implements Iterator { + /* + * This class is nearly a mirror-image of DeqIterator, using + * tail instead of head for initial cursor, and head instead of + * tail for fence. + */ + private int cursor = tail; + private int fence = head; + private int lastRet = -1; + + public boolean hasNext() { + return cursor != fence; + } + + public E next() { + if (cursor == fence) + throw new NoSuchElementException(); + cursor = (cursor - 1) & (elements.length - 1); + E result = elements[cursor]; + if (head != fence || result == null) + throw new ConcurrentModificationException(); + lastRet = cursor; + return result; + } + + public void remove() { + if (lastRet < 0) + throw new IllegalStateException(); + if (!delete(lastRet)) { + cursor = (cursor + 1) & (elements.length - 1); + fence = head; + } + lastRet = -1; + } + } + + /** + * Returns true if this deque contains the specified element. + * More formally, returns true if and only if this deque contains + * at least one element e such that o.equals(e). + * + * @param o object to be checked for containment in this deque + * @return true if this deque contains the specified element + */ + public boolean contains(Object o) { + if (o == null) + return false; + int mask = elements.length - 1; + int i = head; + E x; + while ( (x = elements[i]) != null) { + if (o.equals(x)) + return true; + i = (i + 1) & mask; + } + return false; + } + + /** + * Removes a single instance of the specified element from this deque. + * If the deque does not contain the element, it is unchanged. + * More formally, removes the first element e such that + * o.equals(e) (if such an element exists). + * Returns true if this deque contained the specified element + * (or equivalently, if this deque changed as a result of the call). + * + *

This method is equivalent to {@link #removeFirstOccurrence}. + * + * @param o element to be removed from this deque, if present + * @return true if this deque contained the specified element + */ + public boolean remove(Object o) { + return removeFirstOccurrence(o); + } + + /** + * Removes all of the elements from this deque. + * The deque will be empty after this call returns. + */ + public void clear() { + int h = head; + int t = tail; + if (h != t) { // clear all cells + head = tail = 0; + int i = h; + int mask = elements.length - 1; + do { + elements[i] = null; + i = (i + 1) & mask; + } while (i != t); + } + } + + /** + * Returns an array containing all of the elements in this deque + * in proper sequence (from first to last element). + * + *

The returned array will be "safe" in that no references to it are + * maintained by this deque. (In other words, this method must allocate + * a new array). The caller is thus free to modify the returned array. + * + *

This method acts as bridge between array-based and collection-based + * APIs. + * + * @return an array containing all of the elements in this deque + */ + public Object[] toArray() { + return copyElements(new Object[size()]); + } + + /** + * Returns an array containing all of the elements in this deque in + * proper sequence (from first to last element); the runtime type of the + * returned array is that of the specified array. If the deque fits in + * the specified array, it is returned therein. Otherwise, a new array + * is allocated with the runtime type of the specified array and the + * size of this deque. + * + *

If this deque fits in the specified array with room to spare + * (i.e., the array has more elements than this deque), the element in + * the array immediately following the end of the deque is set to + * null. + * + *

Like the {@link #toArray()} method, this method acts as bridge between + * array-based and collection-based APIs. Further, this method allows + * precise control over the runtime type of the output array, and may, + * under certain circumstances, be used to save allocation costs. + * + *

Suppose x is a deque known to contain only strings. + * The following code can be used to dump the deque into a newly + * allocated array of String: + * + *

+     *     String[] y = x.toArray(new String[0]);
+ * + * Note that toArray(new Object[0]) is identical in function to + * toArray(). + * + * @param a the array into which the elements of the deque are to + * be stored, if it is big enough; otherwise, a new array of the + * same runtime type is allocated for this purpose + * @return an array containing all of the elements in this deque + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in + * this deque + * @throws NullPointerException if the specified array is null + */ + public T[] toArray(T[] a) { + int size = size(); + if (a.length < size) + a = (T[])java.lang.reflect.Array.newInstance( + a.getClass().getComponentType(), size); + copyElements(a); + if (a.length > size) + a[size] = null; + return a; + } + + // *** Object methods *** + + /** + * Returns a copy of this deque. + * + * @return a copy of this deque + */ + public ArrayDeque clone() { + try { + ArrayDeque result = (ArrayDeque) super.clone(); + result.elements = Arrays.copyOf(elements, elements.length); + return result; + + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } + + /** + * Appease the serialization gods. + */ + private static final long serialVersionUID = 2340985798034038923L; + +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/ArrayList.java --- a/emul/compact/src/main/java/java/util/ArrayList.java Tue Feb 05 16:40:01 2013 +0100 +++ b/emul/compact/src/main/java/java/util/ArrayList.java Tue Feb 05 17:04:22 2013 +0100 @@ -25,7 +25,6 @@ package java.util; -import org.apidesign.bck2brwsr.emul.lang.System; /** * Resizable-array implementation of the List interface. Implements diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/Arrays.java --- a/emul/compact/src/main/java/java/util/Arrays.java Tue Feb 05 16:40:01 2013 +0100 +++ b/emul/compact/src/main/java/java/util/Arrays.java Tue Feb 05 17:04:22 2013 +0100 @@ -26,7 +26,6 @@ package java.util; import java.lang.reflect.*; -import org.apidesign.bck2brwsr.emul.lang.System; /** * This class contains various methods for manipulating arrays (such as diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/Collections.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/Collections.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,3953 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util; +import java.io.Serializable; +import java.io.IOException; +import java.lang.reflect.Array; + +/** + * This class consists exclusively of static methods that operate on or return + * collections. It contains polymorphic algorithms that operate on + * collections, "wrappers", which return a new collection backed by a + * specified collection, and a few other odds and ends. + * + *

The methods of this class all throw a NullPointerException + * if the collections or class objects provided to them are null. + * + *

The documentation for the polymorphic algorithms contained in this class + * generally includes a brief description of the implementation. Such + * descriptions should be regarded as implementation notes, rather than + * parts of the specification. Implementors should feel free to + * substitute other algorithms, so long as the specification itself is adhered + * to. (For example, the algorithm used by sort does not have to be + * a mergesort, but it does have to be stable.) + * + *

The "destructive" algorithms contained in this class, that is, the + * algorithms that modify the collection on which they operate, are specified + * to throw UnsupportedOperationException if the collection does not + * support the appropriate mutation primitive(s), such as the set + * method. These algorithms may, but are not required to, throw this + * exception if an invocation would have no effect on the collection. For + * example, invoking the sort method on an unmodifiable list that is + * already sorted may or may not throw UnsupportedOperationException. + * + *

This class is a member of the + * + * Java Collections Framework. + * + * @author Josh Bloch + * @author Neal Gafter + * @see Collection + * @see Set + * @see List + * @see Map + * @since 1.2 + */ + +public class Collections { + // Suppresses default constructor, ensuring non-instantiability. + private Collections() { + } + + // Algorithms + + /* + * Tuning parameters for algorithms - Many of the List algorithms have + * two implementations, one of which is appropriate for RandomAccess + * lists, the other for "sequential." Often, the random access variant + * yields better performance on small sequential access lists. The + * tuning parameters below determine the cutoff point for what constitutes + * a "small" sequential access list for each algorithm. The values below + * were empirically determined to work well for LinkedList. Hopefully + * they should be reasonable for other sequential access List + * implementations. Those doing performance work on this code would + * do well to validate the values of these parameters from time to time. + * (The first word of each tuning parameter name is the algorithm to which + * it applies.) + */ + private static final int BINARYSEARCH_THRESHOLD = 5000; + private static final int REVERSE_THRESHOLD = 18; + private static final int SHUFFLE_THRESHOLD = 5; + private static final int FILL_THRESHOLD = 25; + private static final int ROTATE_THRESHOLD = 100; + private static final int COPY_THRESHOLD = 10; + private static final int REPLACEALL_THRESHOLD = 11; + private static final int INDEXOFSUBLIST_THRESHOLD = 35; + + /** + * Sorts the specified list into ascending order, according to the + * {@linkplain Comparable natural ordering} of its elements. + * All elements in the list must implement the {@link Comparable} + * interface. Furthermore, all elements in the list must be + * mutually comparable (that is, {@code e1.compareTo(e2)} + * must not throw a {@code ClassCastException} for any elements + * {@code e1} and {@code e2} in the list). + * + *

This sort is guaranteed to be stable: equal elements will + * not be reordered as a result of the sort. + * + *

The specified list must be modifiable, but need not be resizable. + * + *

Implementation note: This implementation is a stable, adaptive, + * iterative mergesort that requires far fewer than n lg(n) comparisons + * when the input array is partially sorted, while offering the + * performance of a traditional mergesort when the input array is + * randomly ordered. If the input array is nearly sorted, the + * implementation requires approximately n comparisons. Temporary + * storage requirements vary from a small constant for nearly sorted + * input arrays to n/2 object references for randomly ordered input + * arrays. + * + *

The implementation takes equal advantage of ascending and + * descending order in its input array, and can take advantage of + * ascending and descending order in different parts of the same + * input array. It is well-suited to merging two or more sorted arrays: + * simply concatenate the arrays and sort the resulting array. + * + *

The implementation was adapted from Tim Peters's list sort for Python + * ( + * TimSort). It uses techiques from Peter McIlroy's "Optimistic + * Sorting and Information Theoretic Complexity", in Proceedings of the + * Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474, + * January 1993. + * + *

This implementation dumps the specified list into an array, sorts + * the array, and iterates over the list resetting each element + * from the corresponding position in the array. This avoids the + * n2 log(n) performance that would result from attempting + * to sort a linked list in place. + * + * @param list the list to be sorted. + * @throws ClassCastException if the list contains elements that are not + * mutually comparable (for example, strings and integers). + * @throws UnsupportedOperationException if the specified list's + * list-iterator does not support the {@code set} operation. + * @throws IllegalArgumentException (optional) if the implementation + * detects that the natural ordering of the list elements is + * found to violate the {@link Comparable} contract + */ + public static > void sort(List list) { + Object[] a = list.toArray(); + Arrays.sort(a); + ListIterator i = list.listIterator(); + for (int j=0; jmutually + * comparable using the specified comparator (that is, + * {@code c.compare(e1, e2)} must not throw a {@code ClassCastException} + * for any elements {@code e1} and {@code e2} in the list). + * + *

This sort is guaranteed to be stable: equal elements will + * not be reordered as a result of the sort. + * + *

The specified list must be modifiable, but need not be resizable. + * + *

Implementation note: This implementation is a stable, adaptive, + * iterative mergesort that requires far fewer than n lg(n) comparisons + * when the input array is partially sorted, while offering the + * performance of a traditional mergesort when the input array is + * randomly ordered. If the input array is nearly sorted, the + * implementation requires approximately n comparisons. Temporary + * storage requirements vary from a small constant for nearly sorted + * input arrays to n/2 object references for randomly ordered input + * arrays. + * + *

The implementation takes equal advantage of ascending and + * descending order in its input array, and can take advantage of + * ascending and descending order in different parts of the same + * input array. It is well-suited to merging two or more sorted arrays: + * simply concatenate the arrays and sort the resulting array. + * + *

The implementation was adapted from Tim Peters's list sort for Python + * ( + * TimSort). It uses techiques from Peter McIlroy's "Optimistic + * Sorting and Information Theoretic Complexity", in Proceedings of the + * Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474, + * January 1993. + * + *

This implementation dumps the specified list into an array, sorts + * the array, and iterates over the list resetting each element + * from the corresponding position in the array. This avoids the + * n2 log(n) performance that would result from attempting + * to sort a linked list in place. + * + * @param list the list to be sorted. + * @param c the comparator to determine the order of the list. A + * {@code null} value indicates that the elements' natural + * ordering should be used. + * @throws ClassCastException if the list contains elements that are not + * mutually comparable using the specified comparator. + * @throws UnsupportedOperationException if the specified list's + * list-iterator does not support the {@code set} operation. + * @throws IllegalArgumentException (optional) if the comparator is + * found to violate the {@link Comparator} contract + */ + public static void sort(List list, Comparator c) { + Object[] a = list.toArray(); + Arrays.sort(a, (Comparator)c); + ListIterator i = list.listIterator(); + for (int j=0; jThis method runs in log(n) time for a "random access" list (which + * provides near-constant-time positional access). If the specified list + * does not implement the {@link RandomAccess} interface and is large, + * this method will do an iterator-based binary search that performs + * O(n) link traversals and O(log n) element comparisons. + * + * @param list the list to be searched. + * @param key the key to be searched for. + * @return the index of the search key, if it is contained in the list; + * otherwise, (-(insertion point) - 1). The + * insertion point is defined as the point at which the + * key would be inserted into the list: the index of the first + * element greater than the key, or list.size() if all + * elements in the list are less than the specified key. Note + * that this guarantees that the return value will be >= 0 if + * and only if the key is found. + * @throws ClassCastException if the list contains elements that are not + * mutually comparable (for example, strings and + * integers), or the search key is not mutually comparable + * with the elements of the list. + */ + public static + int binarySearch(List> list, T key) { + if (list instanceof RandomAccess || list.size() + int indexedBinarySearch(List> list, T key) + { + int low = 0; + int high = list.size()-1; + + while (low <= high) { + int mid = (low + high) >>> 1; + Comparable midVal = list.get(mid); + int cmp = midVal.compareTo(key); + + if (cmp < 0) + low = mid + 1; + else if (cmp > 0) + high = mid - 1; + else + return mid; // key found + } + return -(low + 1); // key not found + } + + private static + int iteratorBinarySearch(List> list, T key) + { + int low = 0; + int high = list.size()-1; + ListIterator> i = list.listIterator(); + + while (low <= high) { + int mid = (low + high) >>> 1; + Comparable midVal = get(i, mid); + int cmp = midVal.compareTo(key); + + if (cmp < 0) + low = mid + 1; + else if (cmp > 0) + high = mid - 1; + else + return mid; // key found + } + return -(low + 1); // key not found + } + + /** + * Gets the ith element from the given list by repositioning the specified + * list listIterator. + */ + private static T get(ListIterator i, int index) { + T obj = null; + int pos = i.nextIndex(); + if (pos <= index) { + do { + obj = i.next(); + } while (pos++ < index); + } else { + do { + obj = i.previous(); + } while (--pos > index); + } + return obj; + } + + /** + * Searches the specified list for the specified object using the binary + * search algorithm. The list must be sorted into ascending order + * according to the specified comparator (as by the + * {@link #sort(List, Comparator) sort(List, Comparator)} + * method), prior to making this call. If it is + * not sorted, the results are undefined. If the list contains multiple + * elements equal to the specified object, there is no guarantee which one + * will be found. + * + *

This method runs in log(n) time for a "random access" list (which + * provides near-constant-time positional access). If the specified list + * does not implement the {@link RandomAccess} interface and is large, + * this method will do an iterator-based binary search that performs + * O(n) link traversals and O(log n) element comparisons. + * + * @param list the list to be searched. + * @param key the key to be searched for. + * @param c the comparator by which the list is ordered. + * A null value indicates that the elements' + * {@linkplain Comparable natural ordering} should be used. + * @return the index of the search key, if it is contained in the list; + * otherwise, (-(insertion point) - 1). The + * insertion point is defined as the point at which the + * key would be inserted into the list: the index of the first + * element greater than the key, or list.size() if all + * elements in the list are less than the specified key. Note + * that this guarantees that the return value will be >= 0 if + * and only if the key is found. + * @throws ClassCastException if the list contains elements that are not + * mutually comparable using the specified comparator, + * or the search key is not mutually comparable with the + * elements of the list using this comparator. + */ + public static int binarySearch(List list, T key, Comparator c) { + if (c==null) + return binarySearch((List) list, key); + + if (list instanceof RandomAccess || list.size() int indexedBinarySearch(List l, T key, Comparator c) { + int low = 0; + int high = l.size()-1; + + while (low <= high) { + int mid = (low + high) >>> 1; + T midVal = l.get(mid); + int cmp = c.compare(midVal, key); + + if (cmp < 0) + low = mid + 1; + else if (cmp > 0) + high = mid - 1; + else + return mid; // key found + } + return -(low + 1); // key not found + } + + private static int iteratorBinarySearch(List l, T key, Comparator c) { + int low = 0; + int high = l.size()-1; + ListIterator i = l.listIterator(); + + while (low <= high) { + int mid = (low + high) >>> 1; + T midVal = get(i, mid); + int cmp = c.compare(midVal, key); + + if (cmp < 0) + low = mid + 1; + else if (cmp > 0) + high = mid - 1; + else + return mid; // key found + } + return -(low + 1); // key not found + } + + private interface SelfComparable extends Comparable {} + + + /** + * Reverses the order of the elements in the specified list.

+ * + * This method runs in linear time. + * + * @param list the list whose elements are to be reversed. + * @throws UnsupportedOperationException if the specified list or + * its list-iterator does not support the set operation. + */ + public static void reverse(List list) { + int size = list.size(); + if (size < REVERSE_THRESHOLD || list instanceof RandomAccess) { + for (int i=0, mid=size>>1, j=size-1; i>1; i + * + * The hedge "approximately" is used in the foregoing description because + * default source of randomness is only approximately an unbiased source + * of independently chosen bits. If it were a perfect source of randomly + * chosen bits, then the algorithm would choose permutations with perfect + * uniformity.

+ * + * This implementation traverses the list backwards, from the last element + * up to the second, repeatedly swapping a randomly selected element into + * the "current position". Elements are randomly selected from the + * portion of the list that runs from the first element to the current + * position, inclusive.

+ * + * This method runs in linear time. If the specified list does not + * implement the {@link RandomAccess} interface and is large, this + * implementation dumps the specified list into an array before shuffling + * it, and dumps the shuffled array back into the list. This avoids the + * quadratic behavior that would result from shuffling a "sequential + * access" list in place. + * + * @param list the list to be shuffled. + * @throws UnsupportedOperationException if the specified list or + * its list-iterator does not support the set operation. + */ + public static void shuffle(List list) { + Random rnd = r; + if (rnd == null) + r = rnd = new Random(); + shuffle(list, rnd); + } + private static Random r; + + /** + * Randomly permute the specified list using the specified source of + * randomness. All permutations occur with equal likelihood + * assuming that the source of randomness is fair.

+ * + * This implementation traverses the list backwards, from the last element + * up to the second, repeatedly swapping a randomly selected element into + * the "current position". Elements are randomly selected from the + * portion of the list that runs from the first element to the current + * position, inclusive.

+ * + * This method runs in linear time. If the specified list does not + * implement the {@link RandomAccess} interface and is large, this + * implementation dumps the specified list into an array before shuffling + * it, and dumps the shuffled array back into the list. This avoids the + * quadratic behavior that would result from shuffling a "sequential + * access" list in place. + * + * @param list the list to be shuffled. + * @param rnd the source of randomness to use to shuffle the list. + * @throws UnsupportedOperationException if the specified list or its + * list-iterator does not support the set operation. + */ + public static void shuffle(List list, Random rnd) { + int size = list.size(); + if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) { + for (int i=size; i>1; i--) + swap(list, i-1, rnd.nextInt(i)); + } else { + Object arr[] = list.toArray(); + + // Shuffle array + for (int i=size; i>1; i--) + swap(arr, i-1, rnd.nextInt(i)); + + // Dump array back into list + ListIterator it = list.listIterator(); + for (int i=0; ii or j + * is out of range (i < 0 || i >= list.size() + * || j < 0 || j >= list.size()). + * @since 1.4 + */ + public static void swap(List list, int i, int j) { + final List l = list; + l.set(i, l.set(j, l.get(i))); + } + + /** + * Swaps the two specified elements in the specified array. + */ + private static void swap(Object[] arr, int i, int j) { + Object tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + /** + * Replaces all of the elements of the specified list with the specified + * element.

+ * + * This method runs in linear time. + * + * @param list the list to be filled with the specified element. + * @param obj The element with which to fill the specified list. + * @throws UnsupportedOperationException if the specified list or its + * list-iterator does not support the set operation. + */ + public static void fill(List list, T obj) { + int size = list.size(); + + if (size < FILL_THRESHOLD || list instanceof RandomAccess) { + for (int i=0; i itr = list.listIterator(); + for (int i=0; i + * + * This method runs in linear time. + * + * @param dest The destination list. + * @param src The source list. + * @throws IndexOutOfBoundsException if the destination list is too small + * to contain the entire source List. + * @throws UnsupportedOperationException if the destination list's + * list-iterator does not support the set operation. + */ + public static void copy(List dest, List src) { + int srcSize = src.size(); + if (srcSize > dest.size()) + throw new IndexOutOfBoundsException("Source does not fit in dest"); + + if (srcSize < COPY_THRESHOLD || + (src instanceof RandomAccess && dest instanceof RandomAccess)) { + for (int i=0; i di=dest.listIterator(); + ListIterator si=src.listIterator(); + for (int i=0; inatural ordering of its elements. All elements in the + * collection must implement the Comparable interface. + * Furthermore, all elements in the collection must be mutually + * comparable (that is, e1.compareTo(e2) must not throw a + * ClassCastException for any elements e1 and + * e2 in the collection).

+ * + * This method iterates over the entire collection, hence it requires + * time proportional to the size of the collection. + * + * @param coll the collection whose minimum element is to be determined. + * @return the minimum element of the given collection, according + * to the natural ordering of its elements. + * @throws ClassCastException if the collection contains elements that are + * not mutually comparable (for example, strings and + * integers). + * @throws NoSuchElementException if the collection is empty. + * @see Comparable + */ + public static > T min(Collection coll) { + Iterator i = coll.iterator(); + T candidate = i.next(); + + while (i.hasNext()) { + T next = i.next(); + if (next.compareTo(candidate) < 0) + candidate = next; + } + return candidate; + } + + /** + * Returns the minimum element of the given collection, according to the + * order induced by the specified comparator. All elements in the + * collection must be mutually comparable by the specified + * comparator (that is, comp.compare(e1, e2) must not throw a + * ClassCastException for any elements e1 and + * e2 in the collection).

+ * + * This method iterates over the entire collection, hence it requires + * time proportional to the size of the collection. + * + * @param coll the collection whose minimum element is to be determined. + * @param comp the comparator with which to determine the minimum element. + * A null value indicates that the elements' natural + * ordering should be used. + * @return the minimum element of the given collection, according + * to the specified comparator. + * @throws ClassCastException if the collection contains elements that are + * not mutually comparable using the specified comparator. + * @throws NoSuchElementException if the collection is empty. + * @see Comparable + */ + public static T min(Collection coll, Comparator comp) { + if (comp==null) + return (T)min((Collection) (Collection) coll); + + Iterator i = coll.iterator(); + T candidate = i.next(); + + while (i.hasNext()) { + T next = i.next(); + if (comp.compare(next, candidate) < 0) + candidate = next; + } + return candidate; + } + + /** + * Returns the maximum element of the given collection, according to the + * natural ordering of its elements. All elements in the + * collection must implement the Comparable interface. + * Furthermore, all elements in the collection must be mutually + * comparable (that is, e1.compareTo(e2) must not throw a + * ClassCastException for any elements e1 and + * e2 in the collection).

+ * + * This method iterates over the entire collection, hence it requires + * time proportional to the size of the collection. + * + * @param coll the collection whose maximum element is to be determined. + * @return the maximum element of the given collection, according + * to the natural ordering of its elements. + * @throws ClassCastException if the collection contains elements that are + * not mutually comparable (for example, strings and + * integers). + * @throws NoSuchElementException if the collection is empty. + * @see Comparable + */ + public static > T max(Collection coll) { + Iterator i = coll.iterator(); + T candidate = i.next(); + + while (i.hasNext()) { + T next = i.next(); + if (next.compareTo(candidate) > 0) + candidate = next; + } + return candidate; + } + + /** + * Returns the maximum element of the given collection, according to the + * order induced by the specified comparator. All elements in the + * collection must be mutually comparable by the specified + * comparator (that is, comp.compare(e1, e2) must not throw a + * ClassCastException for any elements e1 and + * e2 in the collection).

+ * + * This method iterates over the entire collection, hence it requires + * time proportional to the size of the collection. + * + * @param coll the collection whose maximum element is to be determined. + * @param comp the comparator with which to determine the maximum element. + * A null value indicates that the elements' natural + * ordering should be used. + * @return the maximum element of the given collection, according + * to the specified comparator. + * @throws ClassCastException if the collection contains elements that are + * not mutually comparable using the specified comparator. + * @throws NoSuchElementException if the collection is empty. + * @see Comparable + */ + public static T max(Collection coll, Comparator comp) { + if (comp==null) + return (T)max((Collection) (Collection) coll); + + Iterator i = coll.iterator(); + T candidate = i.next(); + + while (i.hasNext()) { + T next = i.next(); + if (comp.compare(next, candidate) > 0) + candidate = next; + } + return candidate; + } + + /** + * Rotates the elements in the specified list by the specified distance. + * After calling this method, the element at index i will be + * the element previously at index (i - distance) mod + * list.size(), for all values of i between 0 + * and list.size()-1, inclusive. (This method has no effect on + * the size of the list.) + * + *

For example, suppose list comprises [t, a, n, k, s]. + * After invoking Collections.rotate(list, 1) (or + * Collections.rotate(list, -4)), list will comprise + * [s, t, a, n, k]. + * + *

Note that this method can usefully be applied to sublists to + * move one or more elements within a list while preserving the + * order of the remaining elements. For example, the following idiom + * moves the element at index j forward to position + * k (which must be greater than or equal to j): + *

+     *     Collections.rotate(list.subList(j, k+1), -1);
+     * 
+ * To make this concrete, suppose list comprises + * [a, b, c, d, e]. To move the element at index 1 + * (b) forward two positions, perform the following invocation: + *
+     *     Collections.rotate(l.subList(1, 4), -1);
+     * 
+ * The resulting list is [a, c, d, b, e]. + * + *

To move more than one element forward, increase the absolute value + * of the rotation distance. To move elements backward, use a positive + * shift distance. + * + *

If the specified list is small or implements the {@link + * RandomAccess} interface, this implementation exchanges the first + * element into the location it should go, and then repeatedly exchanges + * the displaced element into the location it should go until a displaced + * element is swapped into the first element. If necessary, the process + * is repeated on the second and successive elements, until the rotation + * is complete. If the specified list is large and doesn't implement the + * RandomAccess interface, this implementation breaks the + * list into two sublist views around index -distance mod size. + * Then the {@link #reverse(List)} method is invoked on each sublist view, + * and finally it is invoked on the entire list. For a more complete + * description of both algorithms, see Section 2.3 of Jon Bentley's + * Programming Pearls (Addison-Wesley, 1986). + * + * @param list the list to be rotated. + * @param distance the distance to rotate the list. There are no + * constraints on this value; it may be zero, negative, or + * greater than list.size(). + * @throws UnsupportedOperationException if the specified list or + * its list-iterator does not support the set operation. + * @since 1.4 + */ + public static void rotate(List list, int distance) { + if (list instanceof RandomAccess || list.size() < ROTATE_THRESHOLD) + rotate1(list, distance); + else + rotate2(list, distance); + } + + private static void rotate1(List list, int distance) { + int size = list.size(); + if (size == 0) + return; + distance = distance % size; + if (distance < 0) + distance += size; + if (distance == 0) + return; + + for (int cycleStart = 0, nMoved = 0; nMoved != size; cycleStart++) { + T displaced = list.get(cycleStart); + int i = cycleStart; + do { + i += distance; + if (i >= size) + i -= size; + displaced = list.set(i, displaced); + nMoved ++; + } while (i != cycleStart); + } + } + + private static void rotate2(List list, int distance) { + int size = list.size(); + if (size == 0) + return; + int mid = -distance % size; + if (mid < 0) + mid += size; + if (mid == 0) + return; + + reverse(list.subList(0, mid)); + reverse(list.subList(mid, size)); + reverse(list); + } + + /** + * Replaces all occurrences of one specified value in a list with another. + * More formally, replaces with newVal each element e + * in list such that + * (oldVal==null ? e==null : oldVal.equals(e)). + * (This method has no effect on the size of the list.) + * + * @param list the list in which replacement is to occur. + * @param oldVal the old value to be replaced. + * @param newVal the new value with which oldVal is to be + * replaced. + * @return true if list contained one or more elements + * e such that + * (oldVal==null ? e==null : oldVal.equals(e)). + * @throws UnsupportedOperationException if the specified list or + * its list-iterator does not support the set operation. + * @since 1.4 + */ + public static boolean replaceAll(List list, T oldVal, T newVal) { + boolean result = false; + int size = list.size(); + if (size < REPLACEALL_THRESHOLD || list instanceof RandomAccess) { + if (oldVal==null) { + for (int i=0; i itr=list.listIterator(); + if (oldVal==null) { + for (int i=0; ii + * such that source.subList(i, i+target.size()).equals(target), + * or -1 if there is no such index. (Returns -1 if + * target.size() > source.size().) + * + *

This implementation uses the "brute force" technique of scanning + * over the source list, looking for a match with the target at each + * location in turn. + * + * @param source the list in which to search for the first occurrence + * of target. + * @param target the list to search for as a subList of source. + * @return the starting position of the first occurrence of the specified + * target list within the specified source list, or -1 if there + * is no such occurrence. + * @since 1.4 + */ + public static int indexOfSubList(List source, List target) { + int sourceSize = source.size(); + int targetSize = target.size(); + int maxCandidate = sourceSize - targetSize; + + if (sourceSize < INDEXOFSUBLIST_THRESHOLD || + (source instanceof RandomAccess&&target instanceof RandomAccess)) { + nextCand: + for (int candidate = 0; candidate <= maxCandidate; candidate++) { + for (int i=0, j=candidate; i si = source.listIterator(); + nextCand: + for (int candidate = 0; candidate <= maxCandidate; candidate++) { + ListIterator ti = target.listIterator(); + for (int i=0; ii + * such that source.subList(i, i+target.size()).equals(target), + * or -1 if there is no such index. (Returns -1 if + * target.size() > source.size().) + * + *

This implementation uses the "brute force" technique of iterating + * over the source list, looking for a match with the target at each + * location in turn. + * + * @param source the list in which to search for the last occurrence + * of target. + * @param target the list to search for as a subList of source. + * @return the starting position of the last occurrence of the specified + * target list within the specified source list, or -1 if there + * is no such occurrence. + * @since 1.4 + */ + public static int lastIndexOfSubList(List source, List target) { + int sourceSize = source.size(); + int targetSize = target.size(); + int maxCandidate = sourceSize - targetSize; + + if (sourceSize < INDEXOFSUBLIST_THRESHOLD || + source instanceof RandomAccess) { // Index access version + nextCand: + for (int candidate = maxCandidate; candidate >= 0; candidate--) { + for (int i=0, j=candidate; i si = source.listIterator(maxCandidate); + nextCand: + for (int candidate = maxCandidate; candidate >= 0; candidate--) { + ListIterator ti = target.listIterator(); + for (int i=0; iUnsupportedOperationException.

+ * + * The returned collection does not pass the hashCode and equals + * operations through to the backing collection, but relies on + * Object's equals and hashCode methods. This + * is necessary to preserve the contracts of these operations in the case + * that the backing collection is a set or a list.

+ * + * The returned collection will be serializable if the specified collection + * is serializable. + * + * @param c the collection for which an unmodifiable view is to be + * returned. + * @return an unmodifiable view of the specified collection. + */ + public static Collection unmodifiableCollection(Collection c) { + return new UnmodifiableCollection<>(c); + } + + /** + * @serial include + */ + static class UnmodifiableCollection implements Collection, Serializable { + private static final long serialVersionUID = 1820017752578914078L; + + final Collection c; + + UnmodifiableCollection(Collection c) { + if (c==null) + throw new NullPointerException(); + this.c = c; + } + + public int size() {return c.size();} + public boolean isEmpty() {return c.isEmpty();} + public boolean contains(Object o) {return c.contains(o);} + public Object[] toArray() {return c.toArray();} + public T[] toArray(T[] a) {return c.toArray(a);} + public String toString() {return c.toString();} + + public Iterator iterator() { + return new Iterator() { + private final Iterator i = c.iterator(); + + public boolean hasNext() {return i.hasNext();} + public E next() {return i.next();} + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + public boolean add(E e) { + throw new UnsupportedOperationException(); + } + public boolean remove(Object o) { + throw new UnsupportedOperationException(); + } + + public boolean containsAll(Collection coll) { + return c.containsAll(coll); + } + public boolean addAll(Collection coll) { + throw new UnsupportedOperationException(); + } + public boolean removeAll(Collection coll) { + throw new UnsupportedOperationException(); + } + public boolean retainAll(Collection coll) { + throw new UnsupportedOperationException(); + } + public void clear() { + throw new UnsupportedOperationException(); + } + } + + /** + * Returns an unmodifiable view of the specified set. This method allows + * modules to provide users with "read-only" access to internal sets. + * Query operations on the returned set "read through" to the specified + * set, and attempts to modify the returned set, whether direct or via its + * iterator, result in an UnsupportedOperationException.

+ * + * The returned set will be serializable if the specified set + * is serializable. + * + * @param s the set for which an unmodifiable view is to be returned. + * @return an unmodifiable view of the specified set. + */ + public static Set unmodifiableSet(Set s) { + return new UnmodifiableSet<>(s); + } + + /** + * @serial include + */ + static class UnmodifiableSet extends UnmodifiableCollection + implements Set, Serializable { + private static final long serialVersionUID = -9215047833775013803L; + + UnmodifiableSet(Set s) {super(s);} + public boolean equals(Object o) {return o == this || c.equals(o);} + public int hashCode() {return c.hashCode();} + } + + /** + * Returns an unmodifiable view of the specified sorted set. This method + * allows modules to provide users with "read-only" access to internal + * sorted sets. Query operations on the returned sorted set "read + * through" to the specified sorted set. Attempts to modify the returned + * sorted set, whether direct, via its iterator, or via its + * subSet, headSet, or tailSet views, result in + * an UnsupportedOperationException.

+ * + * The returned sorted set will be serializable if the specified sorted set + * is serializable. + * + * @param s the sorted set for which an unmodifiable view is to be + * returned. + * @return an unmodifiable view of the specified sorted set. + */ + public static SortedSet unmodifiableSortedSet(SortedSet s) { + return new UnmodifiableSortedSet<>(s); + } + + /** + * @serial include + */ + static class UnmodifiableSortedSet + extends UnmodifiableSet + implements SortedSet, Serializable { + private static final long serialVersionUID = -4929149591599911165L; + private final SortedSet ss; + + UnmodifiableSortedSet(SortedSet s) {super(s); ss = s;} + + public Comparator comparator() {return ss.comparator();} + + public SortedSet subSet(E fromElement, E toElement) { + return new UnmodifiableSortedSet<>(ss.subSet(fromElement,toElement)); + } + public SortedSet headSet(E toElement) { + return new UnmodifiableSortedSet<>(ss.headSet(toElement)); + } + public SortedSet tailSet(E fromElement) { + return new UnmodifiableSortedSet<>(ss.tailSet(fromElement)); + } + + public E first() {return ss.first();} + public E last() {return ss.last();} + } + + /** + * Returns an unmodifiable view of the specified list. This method allows + * modules to provide users with "read-only" access to internal + * lists. Query operations on the returned list "read through" to the + * specified list, and attempts to modify the returned list, whether + * direct or via its iterator, result in an + * UnsupportedOperationException.

+ * + * The returned list will be serializable if the specified list + * is serializable. Similarly, the returned list will implement + * {@link RandomAccess} if the specified list does. + * + * @param list the list for which an unmodifiable view is to be returned. + * @return an unmodifiable view of the specified list. + */ + public static List unmodifiableList(List list) { + return (list instanceof RandomAccess ? + new UnmodifiableRandomAccessList<>(list) : + new UnmodifiableList<>(list)); + } + + /** + * @serial include + */ + static class UnmodifiableList extends UnmodifiableCollection + implements List { + private static final long serialVersionUID = -283967356065247728L; + final List list; + + UnmodifiableList(List list) { + super(list); + this.list = list; + } + + public boolean equals(Object o) {return o == this || list.equals(o);} + public int hashCode() {return list.hashCode();} + + public E get(int index) {return list.get(index);} + public E set(int index, E element) { + throw new UnsupportedOperationException(); + } + public void add(int index, E element) { + throw new UnsupportedOperationException(); + } + public E remove(int index) { + throw new UnsupportedOperationException(); + } + public int indexOf(Object o) {return list.indexOf(o);} + public int lastIndexOf(Object o) {return list.lastIndexOf(o);} + public boolean addAll(int index, Collection c) { + throw new UnsupportedOperationException(); + } + public ListIterator listIterator() {return listIterator(0);} + + public ListIterator listIterator(final int index) { + return new ListIterator() { + private final ListIterator i + = list.listIterator(index); + + public boolean hasNext() {return i.hasNext();} + public E next() {return i.next();} + public boolean hasPrevious() {return i.hasPrevious();} + public E previous() {return i.previous();} + public int nextIndex() {return i.nextIndex();} + public int previousIndex() {return i.previousIndex();} + + public void remove() { + throw new UnsupportedOperationException(); + } + public void set(E e) { + throw new UnsupportedOperationException(); + } + public void add(E e) { + throw new UnsupportedOperationException(); + } + }; + } + + public List subList(int fromIndex, int toIndex) { + return new UnmodifiableList<>(list.subList(fromIndex, toIndex)); + } + + /** + * UnmodifiableRandomAccessList instances are serialized as + * UnmodifiableList instances to allow them to be deserialized + * in pre-1.4 JREs (which do not have UnmodifiableRandomAccessList). + * This method inverts the transformation. As a beneficial + * side-effect, it also grafts the RandomAccess marker onto + * UnmodifiableList instances that were serialized in pre-1.4 JREs. + * + * Note: Unfortunately, UnmodifiableRandomAccessList instances + * serialized in 1.4.1 and deserialized in 1.4 will become + * UnmodifiableList instances, as this method was missing in 1.4. + */ + private Object readResolve() { + return (list instanceof RandomAccess + ? new UnmodifiableRandomAccessList<>(list) + : this); + } + } + + /** + * @serial include + */ + static class UnmodifiableRandomAccessList extends UnmodifiableList + implements RandomAccess + { + UnmodifiableRandomAccessList(List list) { + super(list); + } + + public List subList(int fromIndex, int toIndex) { + return new UnmodifiableRandomAccessList<>( + list.subList(fromIndex, toIndex)); + } + + private static final long serialVersionUID = -2542308836966382001L; + + /** + * Allows instances to be deserialized in pre-1.4 JREs (which do + * not have UnmodifiableRandomAccessList). UnmodifiableList has + * a readResolve method that inverts this transformation upon + * deserialization. + */ + private Object writeReplace() { + return new UnmodifiableList<>(list); + } + } + + /** + * Returns an unmodifiable view of the specified map. This method + * allows modules to provide users with "read-only" access to internal + * maps. Query operations on the returned map "read through" + * to the specified map, and attempts to modify the returned + * map, whether direct or via its collection views, result in an + * UnsupportedOperationException.

+ * + * The returned map will be serializable if the specified map + * is serializable. + * + * @param m the map for which an unmodifiable view is to be returned. + * @return an unmodifiable view of the specified map. + */ + public static Map unmodifiableMap(Map m) { + return new UnmodifiableMap<>(m); + } + + /** + * @serial include + */ + private static class UnmodifiableMap implements Map, Serializable { + private static final long serialVersionUID = -1034234728574286014L; + + private final Map m; + + UnmodifiableMap(Map m) { + if (m==null) + throw new NullPointerException(); + this.m = m; + } + + public int size() {return m.size();} + public boolean isEmpty() {return m.isEmpty();} + public boolean containsKey(Object key) {return m.containsKey(key);} + public boolean containsValue(Object val) {return m.containsValue(val);} + public V get(Object key) {return m.get(key);} + + public V put(K key, V value) { + throw new UnsupportedOperationException(); + } + public V remove(Object key) { + throw new UnsupportedOperationException(); + } + public void putAll(Map m) { + throw new UnsupportedOperationException(); + } + public void clear() { + throw new UnsupportedOperationException(); + } + + private transient Set keySet = null; + private transient Set> entrySet = null; + private transient Collection values = null; + + public Set keySet() { + if (keySet==null) + keySet = unmodifiableSet(m.keySet()); + return keySet; + } + + public Set> entrySet() { + if (entrySet==null) + entrySet = new UnmodifiableEntrySet<>(m.entrySet()); + return entrySet; + } + + public Collection values() { + if (values==null) + values = unmodifiableCollection(m.values()); + return values; + } + + public boolean equals(Object o) {return o == this || m.equals(o);} + public int hashCode() {return m.hashCode();} + public String toString() {return m.toString();} + + /** + * We need this class in addition to UnmodifiableSet as + * Map.Entries themselves permit modification of the backing Map + * via their setValue operation. This class is subtle: there are + * many possible attacks that must be thwarted. + * + * @serial include + */ + static class UnmodifiableEntrySet + extends UnmodifiableSet> { + private static final long serialVersionUID = 7854390611657943733L; + + UnmodifiableEntrySet(Set> s) { + super((Set)s); + } + public Iterator> iterator() { + return new Iterator>() { + private final Iterator> i = c.iterator(); + + public boolean hasNext() { + return i.hasNext(); + } + public Map.Entry next() { + return new UnmodifiableEntry<>(i.next()); + } + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + public Object[] toArray() { + Object[] a = c.toArray(); + for (int i=0; i((Map.Entry)a[i]); + return a; + } + + public T[] toArray(T[] a) { + // We don't pass a to c.toArray, to avoid window of + // vulnerability wherein an unscrupulous multithreaded client + // could get his hands on raw (unwrapped) Entries from c. + Object[] arr = c.toArray(a.length==0 ? a : Arrays.copyOf(a, 0)); + + for (int i=0; i((Map.Entry)arr[i]); + + if (arr.length > a.length) + return (T[])arr; + + System.arraycopy(arr, 0, a, 0, arr.length); + if (a.length > arr.length) + a[arr.length] = null; + return a; + } + + /** + * This method is overridden to protect the backing set against + * an object with a nefarious equals function that senses + * that the equality-candidate is Map.Entry and calls its + * setValue method. + */ + public boolean contains(Object o) { + if (!(o instanceof Map.Entry)) + return false; + return c.contains( + new UnmodifiableEntry<>((Map.Entry) o)); + } + + /** + * The next two methods are overridden to protect against + * an unscrupulous List whose contains(Object o) method senses + * when o is a Map.Entry, and calls o.setValue. + */ + public boolean containsAll(Collection coll) { + for (Object e : coll) { + if (!contains(e)) // Invokes safe contains() above + return false; + } + return true; + } + public boolean equals(Object o) { + if (o == this) + return true; + + if (!(o instanceof Set)) + return false; + Set s = (Set) o; + if (s.size() != c.size()) + return false; + return containsAll(s); // Invokes safe containsAll() above + } + + /** + * This "wrapper class" serves two purposes: it prevents + * the client from modifying the backing Map, by short-circuiting + * the setValue method, and it protects the backing Map against + * an ill-behaved Map.Entry that attempts to modify another + * Map Entry when asked to perform an equality check. + */ + private static class UnmodifiableEntry implements Map.Entry { + private Map.Entry e; + + UnmodifiableEntry(Map.Entry e) {this.e = e;} + + public K getKey() {return e.getKey();} + public V getValue() {return e.getValue();} + public V setValue(V value) { + throw new UnsupportedOperationException(); + } + public int hashCode() {return e.hashCode();} + public boolean equals(Object o) { + if (!(o instanceof Map.Entry)) + return false; + Map.Entry t = (Map.Entry)o; + return eq(e.getKey(), t.getKey()) && + eq(e.getValue(), t.getValue()); + } + public String toString() {return e.toString();} + } + } + } + + /** + * Returns an unmodifiable view of the specified sorted map. This method + * allows modules to provide users with "read-only" access to internal + * sorted maps. Query operations on the returned sorted map "read through" + * to the specified sorted map. Attempts to modify the returned + * sorted map, whether direct, via its collection views, or via its + * subMap, headMap, or tailMap views, result in + * an UnsupportedOperationException.

+ * + * The returned sorted map will be serializable if the specified sorted map + * is serializable. + * + * @param m the sorted map for which an unmodifiable view is to be + * returned. + * @return an unmodifiable view of the specified sorted map. + */ + public static SortedMap unmodifiableSortedMap(SortedMap m) { + return new UnmodifiableSortedMap<>(m); + } + + /** + * @serial include + */ + static class UnmodifiableSortedMap + extends UnmodifiableMap + implements SortedMap, Serializable { + private static final long serialVersionUID = -8806743815996713206L; + + private final SortedMap sm; + + UnmodifiableSortedMap(SortedMap m) {super(m); sm = m;} + + public Comparator comparator() {return sm.comparator();} + + public SortedMap subMap(K fromKey, K toKey) { + return new UnmodifiableSortedMap<>(sm.subMap(fromKey, toKey)); + } + public SortedMap headMap(K toKey) { + return new UnmodifiableSortedMap<>(sm.headMap(toKey)); + } + public SortedMap tailMap(K fromKey) { + return new UnmodifiableSortedMap<>(sm.tailMap(fromKey)); + } + + public K firstKey() {return sm.firstKey();} + public K lastKey() {return sm.lastKey();} + } + + + // Synch Wrappers + + /** + * Returns a synchronized (thread-safe) collection backed by the specified + * collection. In order to guarantee serial access, it is critical that + * all access to the backing collection is accomplished + * through the returned collection.

+ * + * It is imperative that the user manually synchronize on the returned + * collection when iterating over it: + *

+     *  Collection c = Collections.synchronizedCollection(myCollection);
+     *     ...
+     *  synchronized (c) {
+     *      Iterator i = c.iterator(); // Must be in the synchronized block
+     *      while (i.hasNext())
+     *         foo(i.next());
+     *  }
+     * 
+ * Failure to follow this advice may result in non-deterministic behavior. + * + *

The returned collection does not pass the hashCode + * and equals operations through to the backing collection, but + * relies on Object's equals and hashCode methods. This is + * necessary to preserve the contracts of these operations in the case + * that the backing collection is a set or a list.

+ * + * The returned collection will be serializable if the specified collection + * is serializable. + * + * @param c the collection to be "wrapped" in a synchronized collection. + * @return a synchronized view of the specified collection. + */ + public static Collection synchronizedCollection(Collection c) { + return new SynchronizedCollection<>(c); + } + + static Collection synchronizedCollection(Collection c, Object mutex) { + return new SynchronizedCollection<>(c, mutex); + } + + /** + * @serial include + */ + static class SynchronizedCollection implements Collection, Serializable { + private static final long serialVersionUID = 3053995032091335093L; + + final Collection c; // Backing Collection + final Object mutex; // Object on which to synchronize + + SynchronizedCollection(Collection c) { + if (c==null) + throw new NullPointerException(); + this.c = c; + mutex = this; + } + SynchronizedCollection(Collection c, Object mutex) { + this.c = c; + this.mutex = mutex; + } + + public int size() { + synchronized (mutex) {return c.size();} + } + public boolean isEmpty() { + synchronized (mutex) {return c.isEmpty();} + } + public boolean contains(Object o) { + synchronized (mutex) {return c.contains(o);} + } + public Object[] toArray() { + synchronized (mutex) {return c.toArray();} + } + public T[] toArray(T[] a) { + synchronized (mutex) {return c.toArray(a);} + } + + public Iterator iterator() { + return c.iterator(); // Must be manually synched by user! + } + + public boolean add(E e) { + synchronized (mutex) {return c.add(e);} + } + public boolean remove(Object o) { + synchronized (mutex) {return c.remove(o);} + } + + public boolean containsAll(Collection coll) { + synchronized (mutex) {return c.containsAll(coll);} + } + public boolean addAll(Collection coll) { + synchronized (mutex) {return c.addAll(coll);} + } + public boolean removeAll(Collection coll) { + synchronized (mutex) {return c.removeAll(coll);} + } + public boolean retainAll(Collection coll) { + synchronized (mutex) {return c.retainAll(coll);} + } + public void clear() { + synchronized (mutex) {c.clear();} + } + public String toString() { + synchronized (mutex) {return c.toString();} + } + } + + /** + * Returns a synchronized (thread-safe) set backed by the specified + * set. In order to guarantee serial access, it is critical that + * all access to the backing set is accomplished + * through the returned set.

+ * + * It is imperative that the user manually synchronize on the returned + * set when iterating over it: + *

+     *  Set s = Collections.synchronizedSet(new HashSet());
+     *      ...
+     *  synchronized (s) {
+     *      Iterator i = s.iterator(); // Must be in the synchronized block
+     *      while (i.hasNext())
+     *          foo(i.next());
+     *  }
+     * 
+ * Failure to follow this advice may result in non-deterministic behavior. + * + *

The returned set will be serializable if the specified set is + * serializable. + * + * @param s the set to be "wrapped" in a synchronized set. + * @return a synchronized view of the specified set. + */ + public static Set synchronizedSet(Set s) { + return new SynchronizedSet<>(s); + } + + static Set synchronizedSet(Set s, Object mutex) { + return new SynchronizedSet<>(s, mutex); + } + + /** + * @serial include + */ + static class SynchronizedSet + extends SynchronizedCollection + implements Set { + private static final long serialVersionUID = 487447009682186044L; + + SynchronizedSet(Set s) { + super(s); + } + SynchronizedSet(Set s, Object mutex) { + super(s, mutex); + } + + public boolean equals(Object o) { + synchronized (mutex) {return c.equals(o);} + } + public int hashCode() { + synchronized (mutex) {return c.hashCode();} + } + } + + /** + * Returns a synchronized (thread-safe) sorted set backed by the specified + * sorted set. In order to guarantee serial access, it is critical that + * all access to the backing sorted set is accomplished + * through the returned sorted set (or its views).

+ * + * It is imperative that the user manually synchronize on the returned + * sorted set when iterating over it or any of its subSet, + * headSet, or tailSet views. + *

+     *  SortedSet s = Collections.synchronizedSortedSet(new TreeSet());
+     *      ...
+     *  synchronized (s) {
+     *      Iterator i = s.iterator(); // Must be in the synchronized block
+     *      while (i.hasNext())
+     *          foo(i.next());
+     *  }
+     * 
+ * or: + *
+     *  SortedSet s = Collections.synchronizedSortedSet(new TreeSet());
+     *  SortedSet s2 = s.headSet(foo);
+     *      ...
+     *  synchronized (s) {  // Note: s, not s2!!!
+     *      Iterator i = s2.iterator(); // Must be in the synchronized block
+     *      while (i.hasNext())
+     *          foo(i.next());
+     *  }
+     * 
+ * Failure to follow this advice may result in non-deterministic behavior. + * + *

The returned sorted set will be serializable if the specified + * sorted set is serializable. + * + * @param s the sorted set to be "wrapped" in a synchronized sorted set. + * @return a synchronized view of the specified sorted set. + */ + public static SortedSet synchronizedSortedSet(SortedSet s) { + return new SynchronizedSortedSet<>(s); + } + + /** + * @serial include + */ + static class SynchronizedSortedSet + extends SynchronizedSet + implements SortedSet + { + private static final long serialVersionUID = 8695801310862127406L; + + private final SortedSet ss; + + SynchronizedSortedSet(SortedSet s) { + super(s); + ss = s; + } + SynchronizedSortedSet(SortedSet s, Object mutex) { + super(s, mutex); + ss = s; + } + + public Comparator comparator() { + synchronized (mutex) {return ss.comparator();} + } + + public SortedSet subSet(E fromElement, E toElement) { + synchronized (mutex) { + return new SynchronizedSortedSet<>( + ss.subSet(fromElement, toElement), mutex); + } + } + public SortedSet headSet(E toElement) { + synchronized (mutex) { + return new SynchronizedSortedSet<>(ss.headSet(toElement), mutex); + } + } + public SortedSet tailSet(E fromElement) { + synchronized (mutex) { + return new SynchronizedSortedSet<>(ss.tailSet(fromElement),mutex); + } + } + + public E first() { + synchronized (mutex) {return ss.first();} + } + public E last() { + synchronized (mutex) {return ss.last();} + } + } + + /** + * Returns a synchronized (thread-safe) list backed by the specified + * list. In order to guarantee serial access, it is critical that + * all access to the backing list is accomplished + * through the returned list.

+ * + * It is imperative that the user manually synchronize on the returned + * list when iterating over it: + *

+     *  List list = Collections.synchronizedList(new ArrayList());
+     *      ...
+     *  synchronized (list) {
+     *      Iterator i = list.iterator(); // Must be in synchronized block
+     *      while (i.hasNext())
+     *          foo(i.next());
+     *  }
+     * 
+ * Failure to follow this advice may result in non-deterministic behavior. + * + *

The returned list will be serializable if the specified list is + * serializable. + * + * @param list the list to be "wrapped" in a synchronized list. + * @return a synchronized view of the specified list. + */ + public static List synchronizedList(List list) { + return (list instanceof RandomAccess ? + new SynchronizedRandomAccessList<>(list) : + new SynchronizedList<>(list)); + } + + static List synchronizedList(List list, Object mutex) { + return (list instanceof RandomAccess ? + new SynchronizedRandomAccessList<>(list, mutex) : + new SynchronizedList<>(list, mutex)); + } + + /** + * @serial include + */ + static class SynchronizedList + extends SynchronizedCollection + implements List { + private static final long serialVersionUID = -7754090372962971524L; + + final List list; + + SynchronizedList(List list) { + super(list); + this.list = list; + } + SynchronizedList(List list, Object mutex) { + super(list, mutex); + this.list = list; + } + + public boolean equals(Object o) { + synchronized (mutex) {return list.equals(o);} + } + public int hashCode() { + synchronized (mutex) {return list.hashCode();} + } + + public E get(int index) { + synchronized (mutex) {return list.get(index);} + } + public E set(int index, E element) { + synchronized (mutex) {return list.set(index, element);} + } + public void add(int index, E element) { + synchronized (mutex) {list.add(index, element);} + } + public E remove(int index) { + synchronized (mutex) {return list.remove(index);} + } + + public int indexOf(Object o) { + synchronized (mutex) {return list.indexOf(o);} + } + public int lastIndexOf(Object o) { + synchronized (mutex) {return list.lastIndexOf(o);} + } + + public boolean addAll(int index, Collection c) { + synchronized (mutex) {return list.addAll(index, c);} + } + + public ListIterator listIterator() { + return list.listIterator(); // Must be manually synched by user + } + + public ListIterator listIterator(int index) { + return list.listIterator(index); // Must be manually synched by user + } + + public List subList(int fromIndex, int toIndex) { + synchronized (mutex) { + return new SynchronizedList<>(list.subList(fromIndex, toIndex), + mutex); + } + } + + /** + * SynchronizedRandomAccessList instances are serialized as + * SynchronizedList instances to allow them to be deserialized + * in pre-1.4 JREs (which do not have SynchronizedRandomAccessList). + * This method inverts the transformation. As a beneficial + * side-effect, it also grafts the RandomAccess marker onto + * SynchronizedList instances that were serialized in pre-1.4 JREs. + * + * Note: Unfortunately, SynchronizedRandomAccessList instances + * serialized in 1.4.1 and deserialized in 1.4 will become + * SynchronizedList instances, as this method was missing in 1.4. + */ + private Object readResolve() { + return (list instanceof RandomAccess + ? new SynchronizedRandomAccessList<>(list) + : this); + } + } + + /** + * @serial include + */ + static class SynchronizedRandomAccessList + extends SynchronizedList + implements RandomAccess { + + SynchronizedRandomAccessList(List list) { + super(list); + } + + SynchronizedRandomAccessList(List list, Object mutex) { + super(list, mutex); + } + + public List subList(int fromIndex, int toIndex) { + synchronized (mutex) { + return new SynchronizedRandomAccessList<>( + list.subList(fromIndex, toIndex), mutex); + } + } + + private static final long serialVersionUID = 1530674583602358482L; + + /** + * Allows instances to be deserialized in pre-1.4 JREs (which do + * not have SynchronizedRandomAccessList). SynchronizedList has + * a readResolve method that inverts this transformation upon + * deserialization. + */ + private Object writeReplace() { + return new SynchronizedList<>(list); + } + } + + /** + * Returns a synchronized (thread-safe) map backed by the specified + * map. In order to guarantee serial access, it is critical that + * all access to the backing map is accomplished + * through the returned map.

+ * + * It is imperative that the user manually synchronize on the returned + * map when iterating over any of its collection views: + *

+     *  Map m = Collections.synchronizedMap(new HashMap());
+     *      ...
+     *  Set s = m.keySet();  // Needn't be in synchronized block
+     *      ...
+     *  synchronized (m) {  // Synchronizing on m, not s!
+     *      Iterator i = s.iterator(); // Must be in synchronized block
+     *      while (i.hasNext())
+     *          foo(i.next());
+     *  }
+     * 
+ * Failure to follow this advice may result in non-deterministic behavior. + * + *

The returned map will be serializable if the specified map is + * serializable. + * + * @param m the map to be "wrapped" in a synchronized map. + * @return a synchronized view of the specified map. + */ + public static Map synchronizedMap(Map m) { + return new SynchronizedMap<>(m); + } + + /** + * @serial include + */ + private static class SynchronizedMap + implements Map, Serializable { + private static final long serialVersionUID = 1978198479659022715L; + + private final Map m; // Backing Map + final Object mutex; // Object on which to synchronize + + SynchronizedMap(Map m) { + if (m==null) + throw new NullPointerException(); + this.m = m; + mutex = this; + } + + SynchronizedMap(Map m, Object mutex) { + this.m = m; + this.mutex = mutex; + } + + public int size() { + synchronized (mutex) {return m.size();} + } + public boolean isEmpty() { + synchronized (mutex) {return m.isEmpty();} + } + public boolean containsKey(Object key) { + synchronized (mutex) {return m.containsKey(key);} + } + public boolean containsValue(Object value) { + synchronized (mutex) {return m.containsValue(value);} + } + public V get(Object key) { + synchronized (mutex) {return m.get(key);} + } + + public V put(K key, V value) { + synchronized (mutex) {return m.put(key, value);} + } + public V remove(Object key) { + synchronized (mutex) {return m.remove(key);} + } + public void putAll(Map map) { + synchronized (mutex) {m.putAll(map);} + } + public void clear() { + synchronized (mutex) {m.clear();} + } + + private transient Set keySet = null; + private transient Set> entrySet = null; + private transient Collection values = null; + + public Set keySet() { + synchronized (mutex) { + if (keySet==null) + keySet = new SynchronizedSet<>(m.keySet(), mutex); + return keySet; + } + } + + public Set> entrySet() { + synchronized (mutex) { + if (entrySet==null) + entrySet = new SynchronizedSet<>(m.entrySet(), mutex); + return entrySet; + } + } + + public Collection values() { + synchronized (mutex) { + if (values==null) + values = new SynchronizedCollection<>(m.values(), mutex); + return values; + } + } + + public boolean equals(Object o) { + synchronized (mutex) {return m.equals(o);} + } + public int hashCode() { + synchronized (mutex) {return m.hashCode();} + } + public String toString() { + synchronized (mutex) {return m.toString();} + } + } + + /** + * Returns a synchronized (thread-safe) sorted map backed by the specified + * sorted map. In order to guarantee serial access, it is critical that + * all access to the backing sorted map is accomplished + * through the returned sorted map (or its views).

+ * + * It is imperative that the user manually synchronize on the returned + * sorted map when iterating over any of its collection views, or the + * collections views of any of its subMap, headMap or + * tailMap views. + *

+     *  SortedMap m = Collections.synchronizedSortedMap(new TreeMap());
+     *      ...
+     *  Set s = m.keySet();  // Needn't be in synchronized block
+     *      ...
+     *  synchronized (m) {  // Synchronizing on m, not s!
+     *      Iterator i = s.iterator(); // Must be in synchronized block
+     *      while (i.hasNext())
+     *          foo(i.next());
+     *  }
+     * 
+ * or: + *
+     *  SortedMap m = Collections.synchronizedSortedMap(new TreeMap());
+     *  SortedMap m2 = m.subMap(foo, bar);
+     *      ...
+     *  Set s2 = m2.keySet();  // Needn't be in synchronized block
+     *      ...
+     *  synchronized (m) {  // Synchronizing on m, not m2 or s2!
+     *      Iterator i = s.iterator(); // Must be in synchronized block
+     *      while (i.hasNext())
+     *          foo(i.next());
+     *  }
+     * 
+ * Failure to follow this advice may result in non-deterministic behavior. + * + *

The returned sorted map will be serializable if the specified + * sorted map is serializable. + * + * @param m the sorted map to be "wrapped" in a synchronized sorted map. + * @return a synchronized view of the specified sorted map. + */ + public static SortedMap synchronizedSortedMap(SortedMap m) { + return new SynchronizedSortedMap<>(m); + } + + + /** + * @serial include + */ + static class SynchronizedSortedMap + extends SynchronizedMap + implements SortedMap + { + private static final long serialVersionUID = -8798146769416483793L; + + private final SortedMap sm; + + SynchronizedSortedMap(SortedMap m) { + super(m); + sm = m; + } + SynchronizedSortedMap(SortedMap m, Object mutex) { + super(m, mutex); + sm = m; + } + + public Comparator comparator() { + synchronized (mutex) {return sm.comparator();} + } + + public SortedMap subMap(K fromKey, K toKey) { + synchronized (mutex) { + return new SynchronizedSortedMap<>( + sm.subMap(fromKey, toKey), mutex); + } + } + public SortedMap headMap(K toKey) { + synchronized (mutex) { + return new SynchronizedSortedMap<>(sm.headMap(toKey), mutex); + } + } + public SortedMap tailMap(K fromKey) { + synchronized (mutex) { + return new SynchronizedSortedMap<>(sm.tailMap(fromKey),mutex); + } + } + + public K firstKey() { + synchronized (mutex) {return sm.firstKey();} + } + public K lastKey() { + synchronized (mutex) {return sm.lastKey();} + } + } + + // Dynamically typesafe collection wrappers + + /** + * Returns a dynamically typesafe view of the specified collection. + * Any attempt to insert an element of the wrong type will result in an + * immediate {@link ClassCastException}. Assuming a collection + * contains no incorrectly typed elements prior to the time a + * dynamically typesafe view is generated, and that all subsequent + * access to the collection takes place through the view, it is + * guaranteed that the collection cannot contain an incorrectly + * typed element. + * + *

The generics mechanism in the language provides compile-time + * (static) type checking, but it is possible to defeat this mechanism + * with unchecked casts. Usually this is not a problem, as the compiler + * issues warnings on all such unchecked operations. There are, however, + * times when static type checking alone is not sufficient. For example, + * suppose a collection is passed to a third-party library and it is + * imperative that the library code not corrupt the collection by + * inserting an element of the wrong type. + * + *

Another use of dynamically typesafe views is debugging. Suppose a + * program fails with a {@code ClassCastException}, indicating that an + * incorrectly typed element was put into a parameterized collection. + * Unfortunately, the exception can occur at any time after the erroneous + * element is inserted, so it typically provides little or no information + * as to the real source of the problem. If the problem is reproducible, + * one can quickly determine its source by temporarily modifying the + * program to wrap the collection with a dynamically typesafe view. + * For example, this declaration: + *

 {@code
+     *     Collection c = new HashSet();
+     * }
+ * may be replaced temporarily by this one: + *
 {@code
+     *     Collection c = Collections.checkedCollection(
+     *         new HashSet(), String.class);
+     * }
+ * Running the program again will cause it to fail at the point where + * an incorrectly typed element is inserted into the collection, clearly + * identifying the source of the problem. Once the problem is fixed, the + * modified declaration may be reverted back to the original. + * + *

The returned collection does not pass the hashCode and equals + * operations through to the backing collection, but relies on + * {@code Object}'s {@code equals} and {@code hashCode} methods. This + * is necessary to preserve the contracts of these operations in the case + * that the backing collection is a set or a list. + * + *

The returned collection will be serializable if the specified + * collection is serializable. + * + *

Since {@code null} is considered to be a value of any reference + * type, the returned collection permits insertion of null elements + * whenever the backing collection does. + * + * @param c the collection for which a dynamically typesafe view is to be + * returned + * @param type the type of element that {@code c} is permitted to hold + * @return a dynamically typesafe view of the specified collection + * @since 1.5 + */ + public static Collection checkedCollection(Collection c, + Class type) { + return new CheckedCollection<>(c, type); + } + + @SuppressWarnings("unchecked") + static T[] zeroLengthArray(Class type) { + return (T[]) Array.newInstance(type, 0); + } + + /** + * @serial include + */ + static class CheckedCollection implements Collection, Serializable { + private static final long serialVersionUID = 1578914078182001775L; + + final Collection c; + final Class type; + + void typeCheck(Object o) { + if (o != null && !type.isInstance(o)) + throw new ClassCastException(badElementMsg(o)); + } + + private String badElementMsg(Object o) { + return "Attempt to insert " + o.getClass() + + " element into collection with element type " + type; + } + + CheckedCollection(Collection c, Class type) { + if (c==null || type == null) + throw new NullPointerException(); + this.c = c; + this.type = type; + } + + public int size() { return c.size(); } + public boolean isEmpty() { return c.isEmpty(); } + public boolean contains(Object o) { return c.contains(o); } + public Object[] toArray() { return c.toArray(); } + public T[] toArray(T[] a) { return c.toArray(a); } + public String toString() { return c.toString(); } + public boolean remove(Object o) { return c.remove(o); } + public void clear() { c.clear(); } + + public boolean containsAll(Collection coll) { + return c.containsAll(coll); + } + public boolean removeAll(Collection coll) { + return c.removeAll(coll); + } + public boolean retainAll(Collection coll) { + return c.retainAll(coll); + } + + public Iterator iterator() { + final Iterator it = c.iterator(); + return new Iterator() { + public boolean hasNext() { return it.hasNext(); } + public E next() { return it.next(); } + public void remove() { it.remove(); }}; + } + + public boolean add(E e) { + typeCheck(e); + return c.add(e); + } + + private E[] zeroLengthElementArray = null; // Lazily initialized + + private E[] zeroLengthElementArray() { + return zeroLengthElementArray != null ? zeroLengthElementArray : + (zeroLengthElementArray = zeroLengthArray(type)); + } + + @SuppressWarnings("unchecked") + Collection checkedCopyOf(Collection coll) { + Object[] a = null; + try { + E[] z = zeroLengthElementArray(); + a = coll.toArray(z); + // Defend against coll violating the toArray contract + if (a.getClass() != z.getClass()) + a = Arrays.copyOf(a, a.length, z.getClass()); + } catch (ArrayStoreException ignore) { + // To get better and consistent diagnostics, + // we call typeCheck explicitly on each element. + // We call clone() to defend against coll retaining a + // reference to the returned array and storing a bad + // element into it after it has been type checked. + a = coll.toArray().clone(); + for (Object o : a) + typeCheck(o); + } + // A slight abuse of the type system, but safe here. + return (Collection) Arrays.asList(a); + } + + public boolean addAll(Collection coll) { + // Doing things this way insulates us from concurrent changes + // in the contents of coll and provides all-or-nothing + // semantics (which we wouldn't get if we type-checked each + // element as we added it) + return c.addAll(checkedCopyOf(coll)); + } + } + + /** + * Returns a dynamically typesafe view of the specified set. + * Any attempt to insert an element of the wrong type will result in + * an immediate {@link ClassCastException}. Assuming a set contains + * no incorrectly typed elements prior to the time a dynamically typesafe + * view is generated, and that all subsequent access to the set + * takes place through the view, it is guaranteed that the + * set cannot contain an incorrectly typed element. + * + *

A discussion of the use of dynamically typesafe views may be + * found in the documentation for the {@link #checkedCollection + * checkedCollection} method. + * + *

The returned set will be serializable if the specified set is + * serializable. + * + *

Since {@code null} is considered to be a value of any reference + * type, the returned set permits insertion of null elements whenever + * the backing set does. + * + * @param s the set for which a dynamically typesafe view is to be + * returned + * @param type the type of element that {@code s} is permitted to hold + * @return a dynamically typesafe view of the specified set + * @since 1.5 + */ + public static Set checkedSet(Set s, Class type) { + return new CheckedSet<>(s, type); + } + + /** + * @serial include + */ + static class CheckedSet extends CheckedCollection + implements Set, Serializable + { + private static final long serialVersionUID = 4694047833775013803L; + + CheckedSet(Set s, Class elementType) { super(s, elementType); } + + public boolean equals(Object o) { return o == this || c.equals(o); } + public int hashCode() { return c.hashCode(); } + } + + /** + * Returns a dynamically typesafe view of the specified sorted set. + * Any attempt to insert an element of the wrong type will result in an + * immediate {@link ClassCastException}. Assuming a sorted set + * contains no incorrectly typed elements prior to the time a + * dynamically typesafe view is generated, and that all subsequent + * access to the sorted set takes place through the view, it is + * guaranteed that the sorted set cannot contain an incorrectly + * typed element. + * + *

A discussion of the use of dynamically typesafe views may be + * found in the documentation for the {@link #checkedCollection + * checkedCollection} method. + * + *

The returned sorted set will be serializable if the specified sorted + * set is serializable. + * + *

Since {@code null} is considered to be a value of any reference + * type, the returned sorted set permits insertion of null elements + * whenever the backing sorted set does. + * + * @param s the sorted set for which a dynamically typesafe view is to be + * returned + * @param type the type of element that {@code s} is permitted to hold + * @return a dynamically typesafe view of the specified sorted set + * @since 1.5 + */ + public static SortedSet checkedSortedSet(SortedSet s, + Class type) { + return new CheckedSortedSet<>(s, type); + } + + /** + * @serial include + */ + static class CheckedSortedSet extends CheckedSet + implements SortedSet, Serializable + { + private static final long serialVersionUID = 1599911165492914959L; + private final SortedSet ss; + + CheckedSortedSet(SortedSet s, Class type) { + super(s, type); + ss = s; + } + + public Comparator comparator() { return ss.comparator(); } + public E first() { return ss.first(); } + public E last() { return ss.last(); } + + public SortedSet subSet(E fromElement, E toElement) { + return checkedSortedSet(ss.subSet(fromElement,toElement), type); + } + public SortedSet headSet(E toElement) { + return checkedSortedSet(ss.headSet(toElement), type); + } + public SortedSet tailSet(E fromElement) { + return checkedSortedSet(ss.tailSet(fromElement), type); + } + } + + /** + * Returns a dynamically typesafe view of the specified list. + * Any attempt to insert an element of the wrong type will result in + * an immediate {@link ClassCastException}. Assuming a list contains + * no incorrectly typed elements prior to the time a dynamically typesafe + * view is generated, and that all subsequent access to the list + * takes place through the view, it is guaranteed that the + * list cannot contain an incorrectly typed element. + * + *

A discussion of the use of dynamically typesafe views may be + * found in the documentation for the {@link #checkedCollection + * checkedCollection} method. + * + *

The returned list will be serializable if the specified list + * is serializable. + * + *

Since {@code null} is considered to be a value of any reference + * type, the returned list permits insertion of null elements whenever + * the backing list does. + * + * @param list the list for which a dynamically typesafe view is to be + * returned + * @param type the type of element that {@code list} is permitted to hold + * @return a dynamically typesafe view of the specified list + * @since 1.5 + */ + public static List checkedList(List list, Class type) { + return (list instanceof RandomAccess ? + new CheckedRandomAccessList<>(list, type) : + new CheckedList<>(list, type)); + } + + /** + * @serial include + */ + static class CheckedList + extends CheckedCollection + implements List + { + private static final long serialVersionUID = 65247728283967356L; + final List list; + + CheckedList(List list, Class type) { + super(list, type); + this.list = list; + } + + public boolean equals(Object o) { return o == this || list.equals(o); } + public int hashCode() { return list.hashCode(); } + public E get(int index) { return list.get(index); } + public E remove(int index) { return list.remove(index); } + public int indexOf(Object o) { return list.indexOf(o); } + public int lastIndexOf(Object o) { return list.lastIndexOf(o); } + + public E set(int index, E element) { + typeCheck(element); + return list.set(index, element); + } + + public void add(int index, E element) { + typeCheck(element); + list.add(index, element); + } + + public boolean addAll(int index, Collection c) { + return list.addAll(index, checkedCopyOf(c)); + } + public ListIterator listIterator() { return listIterator(0); } + + public ListIterator listIterator(final int index) { + final ListIterator i = list.listIterator(index); + + return new ListIterator() { + public boolean hasNext() { return i.hasNext(); } + public E next() { return i.next(); } + public boolean hasPrevious() { return i.hasPrevious(); } + public E previous() { return i.previous(); } + public int nextIndex() { return i.nextIndex(); } + public int previousIndex() { return i.previousIndex(); } + public void remove() { i.remove(); } + + public void set(E e) { + typeCheck(e); + i.set(e); + } + + public void add(E e) { + typeCheck(e); + i.add(e); + } + }; + } + + public List subList(int fromIndex, int toIndex) { + return new CheckedList<>(list.subList(fromIndex, toIndex), type); + } + } + + /** + * @serial include + */ + static class CheckedRandomAccessList extends CheckedList + implements RandomAccess + { + private static final long serialVersionUID = 1638200125423088369L; + + CheckedRandomAccessList(List list, Class type) { + super(list, type); + } + + public List subList(int fromIndex, int toIndex) { + return new CheckedRandomAccessList<>( + list.subList(fromIndex, toIndex), type); + } + } + + /** + * Returns a dynamically typesafe view of the specified map. + * Any attempt to insert a mapping whose key or value have the wrong + * type will result in an immediate {@link ClassCastException}. + * Similarly, any attempt to modify the value currently associated with + * a key will result in an immediate {@link ClassCastException}, + * whether the modification is attempted directly through the map + * itself, or through a {@link Map.Entry} instance obtained from the + * map's {@link Map#entrySet() entry set} view. + * + *

Assuming a map contains no incorrectly typed keys or values + * prior to the time a dynamically typesafe view is generated, and + * that all subsequent access to the map takes place through the view + * (or one of its collection views), it is guaranteed that the + * map cannot contain an incorrectly typed key or value. + * + *

A discussion of the use of dynamically typesafe views may be + * found in the documentation for the {@link #checkedCollection + * checkedCollection} method. + * + *

The returned map will be serializable if the specified map is + * serializable. + * + *

Since {@code null} is considered to be a value of any reference + * type, the returned map permits insertion of null keys or values + * whenever the backing map does. + * + * @param m the map for which a dynamically typesafe view is to be + * returned + * @param keyType the type of key that {@code m} is permitted to hold + * @param valueType the type of value that {@code m} is permitted to hold + * @return a dynamically typesafe view of the specified map + * @since 1.5 + */ + public static Map checkedMap(Map m, + Class keyType, + Class valueType) { + return new CheckedMap<>(m, keyType, valueType); + } + + + /** + * @serial include + */ + private static class CheckedMap + implements Map, Serializable + { + private static final long serialVersionUID = 5742860141034234728L; + + private final Map m; + final Class keyType; + final Class valueType; + + private void typeCheck(Object key, Object value) { + if (key != null && !keyType.isInstance(key)) + throw new ClassCastException(badKeyMsg(key)); + + if (value != null && !valueType.isInstance(value)) + throw new ClassCastException(badValueMsg(value)); + } + + private String badKeyMsg(Object key) { + return "Attempt to insert " + key.getClass() + + " key into map with key type " + keyType; + } + + private String badValueMsg(Object value) { + return "Attempt to insert " + value.getClass() + + " value into map with value type " + valueType; + } + + CheckedMap(Map m, Class keyType, Class valueType) { + if (m == null || keyType == null || valueType == null) + throw new NullPointerException(); + this.m = m; + this.keyType = keyType; + this.valueType = valueType; + } + + public int size() { return m.size(); } + public boolean isEmpty() { return m.isEmpty(); } + public boolean containsKey(Object key) { return m.containsKey(key); } + public boolean containsValue(Object v) { return m.containsValue(v); } + public V get(Object key) { return m.get(key); } + public V remove(Object key) { return m.remove(key); } + public void clear() { m.clear(); } + public Set keySet() { return m.keySet(); } + public Collection values() { return m.values(); } + public boolean equals(Object o) { return o == this || m.equals(o); } + public int hashCode() { return m.hashCode(); } + public String toString() { return m.toString(); } + + public V put(K key, V value) { + typeCheck(key, value); + return m.put(key, value); + } + + @SuppressWarnings("unchecked") + public void putAll(Map t) { + // Satisfy the following goals: + // - good diagnostics in case of type mismatch + // - all-or-nothing semantics + // - protection from malicious t + // - correct behavior if t is a concurrent map + Object[] entries = t.entrySet().toArray(); + List> checked = new ArrayList<>(entries.length); + for (Object o : entries) { + Map.Entry e = (Map.Entry) o; + Object k = e.getKey(); + Object v = e.getValue(); + typeCheck(k, v); + checked.add( + new AbstractMap.SimpleImmutableEntry<>((K) k, (V) v)); + } + for (Map.Entry e : checked) + m.put(e.getKey(), e.getValue()); + } + + private transient Set> entrySet = null; + + public Set> entrySet() { + if (entrySet==null) + entrySet = new CheckedEntrySet<>(m.entrySet(), valueType); + return entrySet; + } + + /** + * We need this class in addition to CheckedSet as Map.Entry permits + * modification of the backing Map via the setValue operation. This + * class is subtle: there are many possible attacks that must be + * thwarted. + * + * @serial exclude + */ + static class CheckedEntrySet implements Set> { + private final Set> s; + private final Class valueType; + + CheckedEntrySet(Set> s, Class valueType) { + this.s = s; + this.valueType = valueType; + } + + public int size() { return s.size(); } + public boolean isEmpty() { return s.isEmpty(); } + public String toString() { return s.toString(); } + public int hashCode() { return s.hashCode(); } + public void clear() { s.clear(); } + + public boolean add(Map.Entry e) { + throw new UnsupportedOperationException(); + } + public boolean addAll(Collection> coll) { + throw new UnsupportedOperationException(); + } + + public Iterator> iterator() { + final Iterator> i = s.iterator(); + final Class valueType = this.valueType; + + return new Iterator>() { + public boolean hasNext() { return i.hasNext(); } + public void remove() { i.remove(); } + + public Map.Entry next() { + return checkedEntry(i.next(), valueType); + } + }; + } + + @SuppressWarnings("unchecked") + public Object[] toArray() { + Object[] source = s.toArray(); + + /* + * Ensure that we don't get an ArrayStoreException even if + * s.toArray returns an array of something other than Object + */ + Object[] dest = (CheckedEntry.class.isInstance( + source.getClass().getComponentType()) ? source : + new Object[source.length]); + + for (int i = 0; i < source.length; i++) + dest[i] = checkedEntry((Map.Entry)source[i], + valueType); + return dest; + } + + @SuppressWarnings("unchecked") + public T[] toArray(T[] a) { + // We don't pass a to s.toArray, to avoid window of + // vulnerability wherein an unscrupulous multithreaded client + // could get his hands on raw (unwrapped) Entries from s. + T[] arr = s.toArray(a.length==0 ? a : Arrays.copyOf(a, 0)); + + for (int i=0; i)arr[i], + valueType); + if (arr.length > a.length) + return arr; + + System.arraycopy(arr, 0, a, 0, arr.length); + if (a.length > arr.length) + a[arr.length] = null; + return a; + } + + /** + * This method is overridden to protect the backing set against + * an object with a nefarious equals function that senses + * that the equality-candidate is Map.Entry and calls its + * setValue method. + */ + public boolean contains(Object o) { + if (!(o instanceof Map.Entry)) + return false; + Map.Entry e = (Map.Entry) o; + return s.contains( + (e instanceof CheckedEntry) ? e : checkedEntry(e, valueType)); + } + + /** + * The bulk collection methods are overridden to protect + * against an unscrupulous collection whose contains(Object o) + * method senses when o is a Map.Entry, and calls o.setValue. + */ + public boolean containsAll(Collection c) { + for (Object o : c) + if (!contains(o)) // Invokes safe contains() above + return false; + return true; + } + + public boolean remove(Object o) { + if (!(o instanceof Map.Entry)) + return false; + return s.remove(new AbstractMap.SimpleImmutableEntry + <>((Map.Entry)o)); + } + + public boolean removeAll(Collection c) { + return batchRemove(c, false); + } + public boolean retainAll(Collection c) { + return batchRemove(c, true); + } + private boolean batchRemove(Collection c, boolean complement) { + boolean modified = false; + Iterator> it = iterator(); + while (it.hasNext()) { + if (c.contains(it.next()) != complement) { + it.remove(); + modified = true; + } + } + return modified; + } + + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof Set)) + return false; + Set that = (Set) o; + return that.size() == s.size() + && containsAll(that); // Invokes safe containsAll() above + } + + static CheckedEntry checkedEntry(Map.Entry e, + Class valueType) { + return new CheckedEntry<>(e, valueType); + } + + /** + * This "wrapper class" serves two purposes: it prevents + * the client from modifying the backing Map, by short-circuiting + * the setValue method, and it protects the backing Map against + * an ill-behaved Map.Entry that attempts to modify another + * Map.Entry when asked to perform an equality check. + */ + private static class CheckedEntry implements Map.Entry { + private final Map.Entry e; + private final Class valueType; + + CheckedEntry(Map.Entry e, Class valueType) { + this.e = e; + this.valueType = valueType; + } + + public K getKey() { return e.getKey(); } + public V getValue() { return e.getValue(); } + public int hashCode() { return e.hashCode(); } + public String toString() { return e.toString(); } + + public V setValue(V value) { + if (value != null && !valueType.isInstance(value)) + throw new ClassCastException(badValueMsg(value)); + return e.setValue(value); + } + + private String badValueMsg(Object value) { + return "Attempt to insert " + value.getClass() + + " value into map with value type " + valueType; + } + + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof Map.Entry)) + return false; + return e.equals(new AbstractMap.SimpleImmutableEntry + <>((Map.Entry)o)); + } + } + } + } + + /** + * Returns a dynamically typesafe view of the specified sorted map. + * Any attempt to insert a mapping whose key or value have the wrong + * type will result in an immediate {@link ClassCastException}. + * Similarly, any attempt to modify the value currently associated with + * a key will result in an immediate {@link ClassCastException}, + * whether the modification is attempted directly through the map + * itself, or through a {@link Map.Entry} instance obtained from the + * map's {@link Map#entrySet() entry set} view. + * + *

Assuming a map contains no incorrectly typed keys or values + * prior to the time a dynamically typesafe view is generated, and + * that all subsequent access to the map takes place through the view + * (or one of its collection views), it is guaranteed that the + * map cannot contain an incorrectly typed key or value. + * + *

A discussion of the use of dynamically typesafe views may be + * found in the documentation for the {@link #checkedCollection + * checkedCollection} method. + * + *

The returned map will be serializable if the specified map is + * serializable. + * + *

Since {@code null} is considered to be a value of any reference + * type, the returned map permits insertion of null keys or values + * whenever the backing map does. + * + * @param m the map for which a dynamically typesafe view is to be + * returned + * @param keyType the type of key that {@code m} is permitted to hold + * @param valueType the type of value that {@code m} is permitted to hold + * @return a dynamically typesafe view of the specified map + * @since 1.5 + */ + public static SortedMap checkedSortedMap(SortedMap m, + Class keyType, + Class valueType) { + return new CheckedSortedMap<>(m, keyType, valueType); + } + + /** + * @serial include + */ + static class CheckedSortedMap extends CheckedMap + implements SortedMap, Serializable + { + private static final long serialVersionUID = 1599671320688067438L; + + private final SortedMap sm; + + CheckedSortedMap(SortedMap m, + Class keyType, Class valueType) { + super(m, keyType, valueType); + sm = m; + } + + public Comparator comparator() { return sm.comparator(); } + public K firstKey() { return sm.firstKey(); } + public K lastKey() { return sm.lastKey(); } + + public SortedMap subMap(K fromKey, K toKey) { + return checkedSortedMap(sm.subMap(fromKey, toKey), + keyType, valueType); + } + public SortedMap headMap(K toKey) { + return checkedSortedMap(sm.headMap(toKey), keyType, valueType); + } + public SortedMap tailMap(K fromKey) { + return checkedSortedMap(sm.tailMap(fromKey), keyType, valueType); + } + } + + // Empty collections + + /** + * Returns an iterator that has no elements. More precisely, + * + *

    + * + *
  • {@link Iterator#hasNext hasNext} always returns {@code + * false}. + * + *
  • {@link Iterator#next next} always throws {@link + * NoSuchElementException}. + * + *
  • {@link Iterator#remove remove} always throws {@link + * IllegalStateException}. + * + *
+ * + *

Implementations of this method are permitted, but not + * required, to return the same object from multiple invocations. + * + * @return an empty iterator + * @since 1.7 + */ + @SuppressWarnings("unchecked") + public static Iterator emptyIterator() { + return (Iterator) EmptyIterator.EMPTY_ITERATOR; + } + + private static class EmptyIterator implements Iterator { + static final EmptyIterator EMPTY_ITERATOR + = new EmptyIterator<>(); + + public boolean hasNext() { return false; } + public E next() { throw new NoSuchElementException(); } + public void remove() { throw new IllegalStateException(); } + } + + /** + * Returns a list iterator that has no elements. More precisely, + * + *
    + * + *
  • {@link Iterator#hasNext hasNext} and {@link + * ListIterator#hasPrevious hasPrevious} always return {@code + * false}. + * + *
  • {@link Iterator#next next} and {@link ListIterator#previous + * previous} always throw {@link NoSuchElementException}. + * + *
  • {@link Iterator#remove remove} and {@link ListIterator#set + * set} always throw {@link IllegalStateException}. + * + *
  • {@link ListIterator#add add} always throws {@link + * UnsupportedOperationException}. + * + *
  • {@link ListIterator#nextIndex nextIndex} always returns + * {@code 0} . + * + *
  • {@link ListIterator#previousIndex previousIndex} always + * returns {@code -1}. + * + *
+ * + *

Implementations of this method are permitted, but not + * required, to return the same object from multiple invocations. + * + * @return an empty list iterator + * @since 1.7 + */ + @SuppressWarnings("unchecked") + public static ListIterator emptyListIterator() { + return (ListIterator) EmptyListIterator.EMPTY_ITERATOR; + } + + private static class EmptyListIterator + extends EmptyIterator + implements ListIterator + { + static final EmptyListIterator EMPTY_ITERATOR + = new EmptyListIterator<>(); + + public boolean hasPrevious() { return false; } + public E previous() { throw new NoSuchElementException(); } + public int nextIndex() { return 0; } + public int previousIndex() { return -1; } + public void set(E e) { throw new IllegalStateException(); } + public void add(E e) { throw new UnsupportedOperationException(); } + } + + /** + * Returns an enumeration that has no elements. More precisely, + * + *
    + * + *
  • {@link Enumeration#hasMoreElements hasMoreElements} always + * returns {@code false}. + * + *
  • {@link Enumeration#nextElement nextElement} always throws + * {@link NoSuchElementException}. + * + *
+ * + *

Implementations of this method are permitted, but not + * required, to return the same object from multiple invocations. + * + * @return an empty enumeration + * @since 1.7 + */ + @SuppressWarnings("unchecked") + public static Enumeration emptyEnumeration() { + return (Enumeration) EmptyEnumeration.EMPTY_ENUMERATION; + } + + private static class EmptyEnumeration implements Enumeration { + static final EmptyEnumeration EMPTY_ENUMERATION + = new EmptyEnumeration<>(); + + public boolean hasMoreElements() { return false; } + public E nextElement() { throw new NoSuchElementException(); } + } + + /** + * The empty set (immutable). This set is serializable. + * + * @see #emptySet() + */ + @SuppressWarnings("unchecked") + public static final Set EMPTY_SET = new EmptySet<>(); + + /** + * Returns the empty set (immutable). This set is serializable. + * Unlike the like-named field, this method is parameterized. + * + *

This example illustrates the type-safe way to obtain an empty set: + *

+     *     Set<String> s = Collections.emptySet();
+     * 
+ * Implementation note: Implementations of this method need not + * create a separate Set object for each call. Using this + * method is likely to have comparable cost to using the like-named + * field. (Unlike this method, the field does not provide type safety.) + * + * @see #EMPTY_SET + * @since 1.5 + */ + @SuppressWarnings("unchecked") + public static final Set emptySet() { + return (Set) EMPTY_SET; + } + + /** + * @serial include + */ + private static class EmptySet + extends AbstractSet + implements Serializable + { + private static final long serialVersionUID = 1582296315990362920L; + + public Iterator iterator() { return emptyIterator(); } + + public int size() {return 0;} + public boolean isEmpty() {return true;} + + public boolean contains(Object obj) {return false;} + public boolean containsAll(Collection c) { return c.isEmpty(); } + + public Object[] toArray() { return new Object[0]; } + + public T[] toArray(T[] a) { + if (a.length > 0) + a[0] = null; + return a; + } + + // Preserves singleton property + private Object readResolve() { + return EMPTY_SET; + } + } + + /** + * The empty list (immutable). This list is serializable. + * + * @see #emptyList() + */ + @SuppressWarnings("unchecked") + public static final List EMPTY_LIST = new EmptyList<>(); + + /** + * Returns the empty list (immutable). This list is serializable. + * + *

This example illustrates the type-safe way to obtain an empty list: + *

+     *     List<String> s = Collections.emptyList();
+     * 
+ * Implementation note: Implementations of this method need not + * create a separate List object for each call. Using this + * method is likely to have comparable cost to using the like-named + * field. (Unlike this method, the field does not provide type safety.) + * + * @see #EMPTY_LIST + * @since 1.5 + */ + @SuppressWarnings("unchecked") + public static final List emptyList() { + return (List) EMPTY_LIST; + } + + /** + * @serial include + */ + private static class EmptyList + extends AbstractList + implements RandomAccess, Serializable { + private static final long serialVersionUID = 8842843931221139166L; + + public Iterator iterator() { + return emptyIterator(); + } + public ListIterator listIterator() { + return emptyListIterator(); + } + + public int size() {return 0;} + public boolean isEmpty() {return true;} + + public boolean contains(Object obj) {return false;} + public boolean containsAll(Collection c) { return c.isEmpty(); } + + public Object[] toArray() { return new Object[0]; } + + public T[] toArray(T[] a) { + if (a.length > 0) + a[0] = null; + return a; + } + + public E get(int index) { + throw new IndexOutOfBoundsException("Index: "+index); + } + + public boolean equals(Object o) { + return (o instanceof List) && ((List)o).isEmpty(); + } + + public int hashCode() { return 1; } + + // Preserves singleton property + private Object readResolve() { + return EMPTY_LIST; + } + } + + /** + * The empty map (immutable). This map is serializable. + * + * @see #emptyMap() + * @since 1.3 + */ + @SuppressWarnings("unchecked") + public static final Map EMPTY_MAP = new EmptyMap<>(); + + /** + * Returns the empty map (immutable). This map is serializable. + * + *

This example illustrates the type-safe way to obtain an empty set: + *

+     *     Map<String, Date> s = Collections.emptyMap();
+     * 
+ * Implementation note: Implementations of this method need not + * create a separate Map object for each call. Using this + * method is likely to have comparable cost to using the like-named + * field. (Unlike this method, the field does not provide type safety.) + * + * @see #EMPTY_MAP + * @since 1.5 + */ + @SuppressWarnings("unchecked") + public static final Map emptyMap() { + return (Map) EMPTY_MAP; + } + + /** + * @serial include + */ + private static class EmptyMap + extends AbstractMap + implements Serializable + { + private static final long serialVersionUID = 6428348081105594320L; + + public int size() {return 0;} + public boolean isEmpty() {return true;} + public boolean containsKey(Object key) {return false;} + public boolean containsValue(Object value) {return false;} + public V get(Object key) {return null;} + public Set keySet() {return emptySet();} + public Collection values() {return emptySet();} + public Set> entrySet() {return emptySet();} + + public boolean equals(Object o) { + return (o instanceof Map) && ((Map)o).isEmpty(); + } + + public int hashCode() {return 0;} + + // Preserves singleton property + private Object readResolve() { + return EMPTY_MAP; + } + } + + // Singleton collections + + /** + * Returns an immutable set containing only the specified object. + * The returned set is serializable. + * + * @param o the sole object to be stored in the returned set. + * @return an immutable set containing only the specified object. + */ + public static Set singleton(T o) { + return new SingletonSet<>(o); + } + + static Iterator singletonIterator(final E e) { + return new Iterator() { + private boolean hasNext = true; + public boolean hasNext() { + return hasNext; + } + public E next() { + if (hasNext) { + hasNext = false; + return e; + } + throw new NoSuchElementException(); + } + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * @serial include + */ + private static class SingletonSet + extends AbstractSet + implements Serializable + { + private static final long serialVersionUID = 3193687207550431679L; + + private final E element; + + SingletonSet(E e) {element = e;} + + public Iterator iterator() { + return singletonIterator(element); + } + + public int size() {return 1;} + + public boolean contains(Object o) {return eq(o, element);} + } + + /** + * Returns an immutable list containing only the specified object. + * The returned list is serializable. + * + * @param o the sole object to be stored in the returned list. + * @return an immutable list containing only the specified object. + * @since 1.3 + */ + public static List singletonList(T o) { + return new SingletonList<>(o); + } + + /** + * @serial include + */ + private static class SingletonList + extends AbstractList + implements RandomAccess, Serializable { + + private static final long serialVersionUID = 3093736618740652951L; + + private final E element; + + SingletonList(E obj) {element = obj;} + + public Iterator iterator() { + return singletonIterator(element); + } + + public int size() {return 1;} + + public boolean contains(Object obj) {return eq(obj, element);} + + public E get(int index) { + if (index != 0) + throw new IndexOutOfBoundsException("Index: "+index+", Size: 1"); + return element; + } + } + + /** + * Returns an immutable map, mapping only the specified key to the + * specified value. The returned map is serializable. + * + * @param key the sole key to be stored in the returned map. + * @param value the value to which the returned map maps key. + * @return an immutable map containing only the specified key-value + * mapping. + * @since 1.3 + */ + public static Map singletonMap(K key, V value) { + return new SingletonMap<>(key, value); + } + + /** + * @serial include + */ + private static class SingletonMap + extends AbstractMap + implements Serializable { + private static final long serialVersionUID = -6979724477215052911L; + + private final K k; + private final V v; + + SingletonMap(K key, V value) { + k = key; + v = value; + } + + public int size() {return 1;} + + public boolean isEmpty() {return false;} + + public boolean containsKey(Object key) {return eq(key, k);} + + public boolean containsValue(Object value) {return eq(value, v);} + + public V get(Object key) {return (eq(key, k) ? v : null);} + + private transient Set keySet = null; + private transient Set> entrySet = null; + private transient Collection values = null; + + public Set keySet() { + if (keySet==null) + keySet = singleton(k); + return keySet; + } + + public Set> entrySet() { + if (entrySet==null) + entrySet = Collections.>singleton( + new SimpleImmutableEntry<>(k, v)); + return entrySet; + } + + public Collection values() { + if (values==null) + values = singleton(v); + return values; + } + + } + + // Miscellaneous + + /** + * Returns an immutable list consisting of n copies of the + * specified object. The newly allocated data object is tiny (it contains + * a single reference to the data object). This method is useful in + * combination with the List.addAll method to grow lists. + * The returned list is serializable. + * + * @param n the number of elements in the returned list. + * @param o the element to appear repeatedly in the returned list. + * @return an immutable list consisting of n copies of the + * specified object. + * @throws IllegalArgumentException if {@code n < 0} + * @see List#addAll(Collection) + * @see List#addAll(int, Collection) + */ + public static List nCopies(int n, T o) { + if (n < 0) + throw new IllegalArgumentException("List length = " + n); + return new CopiesList<>(n, o); + } + + /** + * @serial include + */ + private static class CopiesList + extends AbstractList + implements RandomAccess, Serializable + { + private static final long serialVersionUID = 2739099268398711800L; + + final int n; + final E element; + + CopiesList(int n, E e) { + assert n >= 0; + this.n = n; + element = e; + } + + public int size() { + return n; + } + + public boolean contains(Object obj) { + return n != 0 && eq(obj, element); + } + + public int indexOf(Object o) { + return contains(o) ? 0 : -1; + } + + public int lastIndexOf(Object o) { + return contains(o) ? n - 1 : -1; + } + + public E get(int index) { + if (index < 0 || index >= n) + throw new IndexOutOfBoundsException("Index: "+index+ + ", Size: "+n); + return element; + } + + public Object[] toArray() { + final Object[] a = new Object[n]; + if (element != null) + Arrays.fill(a, 0, n, element); + return a; + } + + public T[] toArray(T[] a) { + final int n = this.n; + if (a.length < n) { + a = (T[])java.lang.reflect.Array + .newInstance(a.getClass().getComponentType(), n); + if (element != null) + Arrays.fill(a, 0, n, element); + } else { + Arrays.fill(a, 0, n, element); + if (a.length > n) + a[n] = null; + } + return a; + } + + public List subList(int fromIndex, int toIndex) { + if (fromIndex < 0) + throw new IndexOutOfBoundsException("fromIndex = " + fromIndex); + if (toIndex > n) + throw new IndexOutOfBoundsException("toIndex = " + toIndex); + if (fromIndex > toIndex) + throw new IllegalArgumentException("fromIndex(" + fromIndex + + ") > toIndex(" + toIndex + ")"); + return new CopiesList<>(toIndex - fromIndex, element); + } + } + + /** + * Returns a comparator that imposes the reverse of the natural + * ordering on a collection of objects that implement the + * {@code Comparable} interface. (The natural ordering is the ordering + * imposed by the objects' own {@code compareTo} method.) This enables a + * simple idiom for sorting (or maintaining) collections (or arrays) of + * objects that implement the {@code Comparable} interface in + * reverse-natural-order. For example, suppose {@code a} is an array of + * strings. Then:
+     *          Arrays.sort(a, Collections.reverseOrder());
+     * 
sorts the array in reverse-lexicographic (alphabetical) order.

+ * + * The returned comparator is serializable. + * + * @return A comparator that imposes the reverse of the natural + * ordering on a collection of objects that implement + * the Comparable interface. + * @see Comparable + */ + public static Comparator reverseOrder() { + return (Comparator) ReverseComparator.REVERSE_ORDER; + } + + /** + * @serial include + */ + private static class ReverseComparator + implements Comparator>, Serializable { + + private static final long serialVersionUID = 7207038068494060240L; + + static final ReverseComparator REVERSE_ORDER + = new ReverseComparator(); + + public int compare(Comparable c1, Comparable c2) { + return c2.compareTo(c1); + } + + private Object readResolve() { return reverseOrder(); } + } + + /** + * Returns a comparator that imposes the reverse ordering of the specified + * comparator. If the specified comparator is {@code null}, this method is + * equivalent to {@link #reverseOrder()} (in other words, it returns a + * comparator that imposes the reverse of the natural ordering on + * a collection of objects that implement the Comparable interface). + * + *

The returned comparator is serializable (assuming the specified + * comparator is also serializable or {@code null}). + * + * @param cmp a comparator who's ordering is to be reversed by the returned + * comparator or {@code null} + * @return A comparator that imposes the reverse ordering of the + * specified comparator. + * @since 1.5 + */ + public static Comparator reverseOrder(Comparator cmp) { + if (cmp == null) + return reverseOrder(); + + if (cmp instanceof ReverseComparator2) + return ((ReverseComparator2)cmp).cmp; + + return new ReverseComparator2<>(cmp); + } + + /** + * @serial include + */ + private static class ReverseComparator2 implements Comparator, + Serializable + { + private static final long serialVersionUID = 4374092139857L; + + /** + * The comparator specified in the static factory. This will never + * be null, as the static factory returns a ReverseComparator + * instance if its argument is null. + * + * @serial + */ + final Comparator cmp; + + ReverseComparator2(Comparator cmp) { + assert cmp != null; + this.cmp = cmp; + } + + public int compare(T t1, T t2) { + return cmp.compare(t2, t1); + } + + public boolean equals(Object o) { + return (o == this) || + (o instanceof ReverseComparator2 && + cmp.equals(((ReverseComparator2)o).cmp)); + } + + public int hashCode() { + return cmp.hashCode() ^ Integer.MIN_VALUE; + } + } + + /** + * Returns an enumeration over the specified collection. This provides + * interoperability with legacy APIs that require an enumeration + * as input. + * + * @param c the collection for which an enumeration is to be returned. + * @return an enumeration over the specified collection. + * @see Enumeration + */ + public static Enumeration enumeration(final Collection c) { + return new Enumeration() { + private final Iterator i = c.iterator(); + + public boolean hasMoreElements() { + return i.hasNext(); + } + + public T nextElement() { + return i.next(); + } + }; + } + + /** + * Returns an array list containing the elements returned by the + * specified enumeration in the order they are returned by the + * enumeration. This method provides interoperability between + * legacy APIs that return enumerations and new APIs that require + * collections. + * + * @param e enumeration providing elements for the returned + * array list + * @return an array list containing the elements returned + * by the specified enumeration. + * @since 1.4 + * @see Enumeration + * @see ArrayList + */ + public static ArrayList list(Enumeration e) { + ArrayList l = new ArrayList<>(); + while (e.hasMoreElements()) + l.add(e.nextElement()); + return l; + } + + /** + * Returns true if the specified arguments are equal, or both null. + */ + static boolean eq(Object o1, Object o2) { + return o1==null ? o2==null : o1.equals(o2); + } + + /** + * Returns the number of elements in the specified collection equal to the + * specified object. More formally, returns the number of elements + * e in the collection such that + * (o == null ? e == null : o.equals(e)). + * + * @param c the collection in which to determine the frequency + * of o + * @param o the object whose frequency is to be determined + * @throws NullPointerException if c is null + * @since 1.5 + */ + public static int frequency(Collection c, Object o) { + int result = 0; + if (o == null) { + for (Object e : c) + if (e == null) + result++; + } else { + for (Object e : c) + if (o.equals(e)) + result++; + } + return result; + } + + /** + * Returns {@code true} if the two specified collections have no + * elements in common. + * + *

Care must be exercised if this method is used on collections that + * do not comply with the general contract for {@code Collection}. + * Implementations may elect to iterate over either collection and test + * for containment in the other collection (or to perform any equivalent + * computation). If either collection uses a nonstandard equality test + * (as does a {@link SortedSet} whose ordering is not compatible with + * equals, or the key set of an {@link IdentityHashMap}), both + * collections must use the same nonstandard equality test, or the + * result of this method is undefined. + * + *

Care must also be exercised when using collections that have + * restrictions on the elements that they may contain. Collection + * implementations are allowed to throw exceptions for any operation + * involving elements they deem ineligible. For absolute safety the + * specified collections should contain only elements which are + * eligible elements for both collections. + * + *

Note that it is permissible to pass the same collection in both + * parameters, in which case the method will return {@code true} if and + * only if the collection is empty. + * + * @param c1 a collection + * @param c2 a collection + * @return {@code true} if the two specified collections have no + * elements in common. + * @throws NullPointerException if either collection is {@code null}. + * @throws NullPointerException if one collection contains a {@code null} + * element and {@code null} is not an eligible element for the other collection. + * (optional) + * @throws ClassCastException if one collection contains an element that is + * of a type which is ineligible for the other collection. + * (optional) + * @since 1.5 + */ + public static boolean disjoint(Collection c1, Collection c2) { + // The collection to be used for contains(). Preference is given to + // the collection who's contains() has lower O() complexity. + Collection contains = c2; + // The collection to be iterated. If the collections' contains() impl + // are of different O() complexity, the collection with slower + // contains() will be used for iteration. For collections who's + // contains() are of the same complexity then best performance is + // achieved by iterating the smaller collection. + Collection iterate = c1; + + // Performance optimization cases. The heuristics: + // 1. Generally iterate over c1. + // 2. If c1 is a Set then iterate over c2. + // 3. If either collection is empty then result is always true. + // 4. Iterate over the smaller Collection. + if (c1 instanceof Set) { + // Use c1 for contains as a Set's contains() is expected to perform + // better than O(N/2) + iterate = c2; + contains = c1; + } else if (!(c2 instanceof Set)) { + // Both are mere Collections. Iterate over smaller collection. + // Example: If c1 contains 3 elements and c2 contains 50 elements and + // assuming contains() requires ceiling(N/2) comparisons then + // checking for all c1 elements in c2 would require 75 comparisons + // (3 * ceiling(50/2)) vs. checking all c2 elements in c1 requiring + // 100 comparisons (50 * ceiling(3/2)). + int c1size = c1.size(); + int c2size = c2.size(); + if (c1size == 0 || c2size == 0) { + // At least one collection is empty. Nothing will match. + return true; + } + + if (c1size > c2size) { + iterate = c2; + contains = c1; + } + } + + for (Object e : iterate) { + if (contains.contains(e)) { + // Found a common element. Collections are not disjoint. + return false; + } + } + + // No common elements were found. + return true; + } + + /** + * Adds all of the specified elements to the specified collection. + * Elements to be added may be specified individually or as an array. + * The behavior of this convenience method is identical to that of + * c.addAll(Arrays.asList(elements)), but this method is likely + * to run significantly faster under most implementations. + * + *

When elements are specified individually, this method provides a + * convenient way to add a few elements to an existing collection: + *

+     *     Collections.addAll(flavors, "Peaches 'n Plutonium", "Rocky Racoon");
+     * 
+ * + * @param c the collection into which elements are to be inserted + * @param elements the elements to insert into c + * @return true if the collection changed as a result of the call + * @throws UnsupportedOperationException if c does not support + * the add operation + * @throws NullPointerException if elements contains one or more + * null values and c does not permit null elements, or + * if c or elements are null + * @throws IllegalArgumentException if some property of a value in + * elements prevents it from being added to c + * @see Collection#addAll(Collection) + * @since 1.5 + */ + @SafeVarargs + public static boolean addAll(Collection c, T... elements) { + boolean result = false; + for (T element : elements) + result |= c.add(element); + return result; + } + + /** + * Returns a set backed by the specified map. The resulting set displays + * the same ordering, concurrency, and performance characteristics as the + * backing map. In essence, this factory method provides a {@link Set} + * implementation corresponding to any {@link Map} implementation. There + * is no need to use this method on a {@link Map} implementation that + * already has a corresponding {@link Set} implementation (such as {@link + * HashMap} or {@link TreeMap}). + * + *

Each method invocation on the set returned by this method results in + * exactly one method invocation on the backing map or its keySet + * view, with one exception. The addAll method is implemented + * as a sequence of put invocations on the backing map. + * + *

The specified map must be empty at the time this method is invoked, + * and should not be accessed directly after this method returns. These + * conditions are ensured if the map is created empty, passed directly + * to this method, and no reference to the map is retained, as illustrated + * in the following code fragment: + *

+     *    Set<Object> weakHashSet = Collections.newSetFromMap(
+     *        new WeakHashMap<Object, Boolean>());
+     * 
+ * + * @param map the backing map + * @return the set backed by the map + * @throws IllegalArgumentException if map is not empty + * @since 1.6 + */ + public static Set newSetFromMap(Map map) { + return new SetFromMap<>(map); + } + + /** + * @serial include + */ + private static class SetFromMap extends AbstractSet + implements Set, Serializable + { + private final Map m; // The backing map + private transient Set s; // Its keySet + + SetFromMap(Map map) { + if (!map.isEmpty()) + throw new IllegalArgumentException("Map is non-empty"); + m = map; + s = map.keySet(); + } + + public void clear() { m.clear(); } + public int size() { return m.size(); } + public boolean isEmpty() { return m.isEmpty(); } + public boolean contains(Object o) { return m.containsKey(o); } + public boolean remove(Object o) { return m.remove(o) != null; } + public boolean add(E e) { return m.put(e, Boolean.TRUE) == null; } + public Iterator iterator() { return s.iterator(); } + public Object[] toArray() { return s.toArray(); } + public T[] toArray(T[] a) { return s.toArray(a); } + public String toString() { return s.toString(); } + public int hashCode() { return s.hashCode(); } + public boolean equals(Object o) { return o == this || s.equals(o); } + public boolean containsAll(Collection c) {return s.containsAll(c);} + public boolean removeAll(Collection c) {return s.removeAll(c);} + public boolean retainAll(Collection c) {return s.retainAll(c);} + // addAll is the only inherited implementation + + private static final long serialVersionUID = 2454657854757543876L; + + } + + /** + * Returns a view of a {@link Deque} as a Last-in-first-out (Lifo) + * {@link Queue}. Method add is mapped to push, + * remove is mapped to pop and so on. This + * view can be useful when you would like to use a method + * requiring a Queue but you need Lifo ordering. + * + *

Each method invocation on the queue returned by this method + * results in exactly one method invocation on the backing deque, with + * one exception. The {@link Queue#addAll addAll} method is + * implemented as a sequence of {@link Deque#addFirst addFirst} + * invocations on the backing deque. + * + * @param deque the deque + * @return the queue + * @since 1.6 + */ + public static Queue asLifoQueue(Deque deque) { + return new AsLIFOQueue<>(deque); + } + + /** + * @serial include + */ + static class AsLIFOQueue extends AbstractQueue + implements Queue, Serializable { + private static final long serialVersionUID = 1802017725587941708L; + private final Deque q; + AsLIFOQueue(Deque q) { this.q = q; } + public boolean add(E e) { q.addFirst(e); return true; } + public boolean offer(E e) { return q.offerFirst(e); } + public E poll() { return q.pollFirst(); } + public E remove() { return q.removeFirst(); } + public E peek() { return q.peekFirst(); } + public E element() { return q.getFirst(); } + public void clear() { q.clear(); } + public int size() { return q.size(); } + public boolean isEmpty() { return q.isEmpty(); } + public boolean contains(Object o) { return q.contains(o); } + public boolean remove(Object o) { return q.remove(o); } + public Iterator iterator() { return q.iterator(); } + public Object[] toArray() { return q.toArray(); } + public T[] toArray(T[] a) { return q.toArray(a); } + public String toString() { return q.toString(); } + public boolean containsAll(Collection c) {return q.containsAll(c);} + public boolean removeAll(Collection c) {return q.removeAll(c);} + public boolean retainAll(Collection c) {return q.retainAll(c);} + // We use inherited addAll; forwarding addAll would be wrong + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/ComparableTimSort.java --- a/emul/compact/src/main/java/java/util/ComparableTimSort.java Tue Feb 05 16:40:01 2013 +0100 +++ b/emul/compact/src/main/java/java/util/ComparableTimSort.java Tue Feb 05 17:04:22 2013 +0100 @@ -25,7 +25,6 @@ package java.util; -import org.apidesign.bck2brwsr.emul.lang.System; /** * This is a near duplicate of {@link TimSort}, modified for use with diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/Deque.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/Deque.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,584 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea and Josh Bloch with assistance from members of + * JCP JSR-166 Expert Group and released to the public domain, as explained + * at http://creativecommons.org/publicdomain/zero/1.0/ + */ + +package java.util; + +/** + * A linear collection that supports element insertion and removal at + * both ends. The name deque is short for "double ended queue" + * and is usually pronounced "deck". Most Deque + * implementations place no fixed limits on the number of elements + * they may contain, but this interface supports capacity-restricted + * deques as well as those with no fixed size limit. + * + *

This interface defines methods to access the elements at both + * ends of the deque. Methods are provided to insert, remove, and + * examine the element. Each of these methods exists in two forms: + * one throws an exception if the operation fails, the other returns a + * special value (either null or false, depending on + * the operation). The latter form of the insert operation is + * designed specifically for use with capacity-restricted + * Deque implementations; in most implementations, insert + * operations cannot fail. + * + *

The twelve methods described above are summarized in the + * following table: + * + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
First Element (Head) Last Element (Tail)
Throws exceptionSpecial valueThrows exceptionSpecial value
Insert{@link #addFirst addFirst(e)}{@link #offerFirst offerFirst(e)}{@link #addLast addLast(e)}{@link #offerLast offerLast(e)}
Remove{@link #removeFirst removeFirst()}{@link #pollFirst pollFirst()}{@link #removeLast removeLast()}{@link #pollLast pollLast()}
Examine{@link #getFirst getFirst()}{@link #peekFirst peekFirst()}{@link #getLast getLast()}{@link #peekLast peekLast()}
+ * + *

This interface extends the {@link Queue} interface. When a deque is + * used as a queue, FIFO (First-In-First-Out) behavior results. Elements are + * added at the end of the deque and removed from the beginning. The methods + * inherited from the Queue interface are precisely equivalent to + * Deque methods as indicated in the following table: + * + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Queue Method Equivalent Deque Method
{@link java.util.Queue#add add(e)}{@link #addLast addLast(e)}
{@link java.util.Queue#offer offer(e)}{@link #offerLast offerLast(e)}
{@link java.util.Queue#remove remove()}{@link #removeFirst removeFirst()}
{@link java.util.Queue#poll poll()}{@link #pollFirst pollFirst()}
{@link java.util.Queue#element element()}{@link #getFirst getFirst()}
{@link java.util.Queue#peek peek()}{@link #peek peekFirst()}
+ * + *

Deques can also be used as LIFO (Last-In-First-Out) stacks. This + * interface should be used in preference to the legacy {@link Stack} class. + * When a deque is used as a stack, elements are pushed and popped from the + * beginning of the deque. Stack methods are precisely equivalent to + * Deque methods as indicated in the table below: + * + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Stack Method Equivalent Deque Method
{@link #push push(e)}{@link #addFirst addFirst(e)}
{@link #pop pop()}{@link #removeFirst removeFirst()}
{@link #peek peek()}{@link #peekFirst peekFirst()}
+ * + *

Note that the {@link #peek peek} method works equally well when + * a deque is used as a queue or a stack; in either case, elements are + * drawn from the beginning of the deque. + * + *

This interface provides two methods to remove interior + * elements, {@link #removeFirstOccurrence removeFirstOccurrence} and + * {@link #removeLastOccurrence removeLastOccurrence}. + * + *

Unlike the {@link List} interface, this interface does not + * provide support for indexed access to elements. + * + *

While Deque implementations are not strictly required + * to prohibit the insertion of null elements, they are strongly + * encouraged to do so. Users of any Deque implementations + * that do allow null elements are strongly encouraged not to + * take advantage of the ability to insert nulls. This is so because + * null is used as a special return value by various methods + * to indicated that the deque is empty. + * + *

Deque implementations generally do not define + * element-based versions of the equals and hashCode + * methods, but instead inherit the identity-based versions from class + * Object. + * + *

This interface is a member of the Java Collections + * Framework. + * + * @author Doug Lea + * @author Josh Bloch + * @since 1.6 + * @param the type of elements held in this collection + */ + +public interface Deque extends Queue { + /** + * Inserts the specified element at the front of this deque if it is + * possible to do so immediately without violating capacity restrictions. + * When using a capacity-restricted deque, it is generally preferable to + * use method {@link #offerFirst}. + * + * @param e the element to add + * @throws IllegalStateException if the element cannot be added at this + * time due to capacity restrictions + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this deque + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + void addFirst(E e); + + /** + * Inserts the specified element at the end of this deque if it is + * possible to do so immediately without violating capacity restrictions. + * When using a capacity-restricted deque, it is generally preferable to + * use method {@link #offerLast}. + * + *

This method is equivalent to {@link #add}. + * + * @param e the element to add + * @throws IllegalStateException if the element cannot be added at this + * time due to capacity restrictions + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this deque + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + void addLast(E e); + + /** + * Inserts the specified element at the front of this deque unless it would + * violate capacity restrictions. When using a capacity-restricted deque, + * this method is generally preferable to the {@link #addFirst} method, + * which can fail to insert an element only by throwing an exception. + * + * @param e the element to add + * @return true if the element was added to this deque, else + * false + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this deque + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + boolean offerFirst(E e); + + /** + * Inserts the specified element at the end of this deque unless it would + * violate capacity restrictions. When using a capacity-restricted deque, + * this method is generally preferable to the {@link #addLast} method, + * which can fail to insert an element only by throwing an exception. + * + * @param e the element to add + * @return true if the element was added to this deque, else + * false + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this deque + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + boolean offerLast(E e); + + /** + * Retrieves and removes the first element of this deque. This method + * differs from {@link #pollFirst pollFirst} only in that it throws an + * exception if this deque is empty. + * + * @return the head of this deque + * @throws NoSuchElementException if this deque is empty + */ + E removeFirst(); + + /** + * Retrieves and removes the last element of this deque. This method + * differs from {@link #pollLast pollLast} only in that it throws an + * exception if this deque is empty. + * + * @return the tail of this deque + * @throws NoSuchElementException if this deque is empty + */ + E removeLast(); + + /** + * Retrieves and removes the first element of this deque, + * or returns null if this deque is empty. + * + * @return the head of this deque, or null if this deque is empty + */ + E pollFirst(); + + /** + * Retrieves and removes the last element of this deque, + * or returns null if this deque is empty. + * + * @return the tail of this deque, or null if this deque is empty + */ + E pollLast(); + + /** + * Retrieves, but does not remove, the first element of this deque. + * + * This method differs from {@link #peekFirst peekFirst} only in that it + * throws an exception if this deque is empty. + * + * @return the head of this deque + * @throws NoSuchElementException if this deque is empty + */ + E getFirst(); + + /** + * Retrieves, but does not remove, the last element of this deque. + * This method differs from {@link #peekLast peekLast} only in that it + * throws an exception if this deque is empty. + * + * @return the tail of this deque + * @throws NoSuchElementException if this deque is empty + */ + E getLast(); + + /** + * Retrieves, but does not remove, the first element of this deque, + * or returns null if this deque is empty. + * + * @return the head of this deque, or null if this deque is empty + */ + E peekFirst(); + + /** + * Retrieves, but does not remove, the last element of this deque, + * or returns null if this deque is empty. + * + * @return the tail of this deque, or null if this deque is empty + */ + E peekLast(); + + /** + * Removes the first occurrence of the specified element from this deque. + * If the deque does not contain the element, it is unchanged. + * More formally, removes the first element e such that + * (o==null ? e==null : o.equals(e)) + * (if such an element exists). + * Returns true if this deque contained the specified element + * (or equivalently, if this deque changed as a result of the call). + * + * @param o element to be removed from this deque, if present + * @return true if an element was removed as a result of this call + * @throws ClassCastException if the class of the specified element + * is incompatible with this deque + * (optional) + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * (optional) + */ + boolean removeFirstOccurrence(Object o); + + /** + * Removes the last occurrence of the specified element from this deque. + * If the deque does not contain the element, it is unchanged. + * More formally, removes the last element e such that + * (o==null ? e==null : o.equals(e)) + * (if such an element exists). + * Returns true if this deque contained the specified element + * (or equivalently, if this deque changed as a result of the call). + * + * @param o element to be removed from this deque, if present + * @return true if an element was removed as a result of this call + * @throws ClassCastException if the class of the specified element + * is incompatible with this deque + * (optional) + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * (optional) + */ + boolean removeLastOccurrence(Object o); + + // *** Queue methods *** + + /** + * Inserts the specified element into the queue represented by this deque + * (in other words, at the tail of this deque) if it is possible to do so + * immediately without violating capacity restrictions, returning + * true upon success and throwing an + * IllegalStateException if no space is currently available. + * When using a capacity-restricted deque, it is generally preferable to + * use {@link #offer(Object) offer}. + * + *

This method is equivalent to {@link #addLast}. + * + * @param e the element to add + * @return true (as specified by {@link Collection#add}) + * @throws IllegalStateException if the element cannot be added at this + * time due to capacity restrictions + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this deque + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + boolean add(E e); + + /** + * Inserts the specified element into the queue represented by this deque + * (in other words, at the tail of this deque) if it is possible to do so + * immediately without violating capacity restrictions, returning + * true upon success and false if no space is currently + * available. When using a capacity-restricted deque, this method is + * generally preferable to the {@link #add} method, which can fail to + * insert an element only by throwing an exception. + * + *

This method is equivalent to {@link #offerLast}. + * + * @param e the element to add + * @return true if the element was added to this deque, else + * false + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this deque + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + boolean offer(E e); + + /** + * Retrieves and removes the head of the queue represented by this deque + * (in other words, the first element of this deque). + * This method differs from {@link #poll poll} only in that it throws an + * exception if this deque is empty. + * + *

This method is equivalent to {@link #removeFirst()}. + * + * @return the head of the queue represented by this deque + * @throws NoSuchElementException if this deque is empty + */ + E remove(); + + /** + * Retrieves and removes the head of the queue represented by this deque + * (in other words, the first element of this deque), or returns + * null if this deque is empty. + * + *

This method is equivalent to {@link #pollFirst()}. + * + * @return the first element of this deque, or null if + * this deque is empty + */ + E poll(); + + /** + * Retrieves, but does not remove, the head of the queue represented by + * this deque (in other words, the first element of this deque). + * This method differs from {@link #peek peek} only in that it throws an + * exception if this deque is empty. + * + *

This method is equivalent to {@link #getFirst()}. + * + * @return the head of the queue represented by this deque + * @throws NoSuchElementException if this deque is empty + */ + E element(); + + /** + * Retrieves, but does not remove, the head of the queue represented by + * this deque (in other words, the first element of this deque), or + * returns null if this deque is empty. + * + *

This method is equivalent to {@link #peekFirst()}. + * + * @return the head of the queue represented by this deque, or + * null if this deque is empty + */ + E peek(); + + + // *** Stack methods *** + + /** + * Pushes an element onto the stack represented by this deque (in other + * words, at the head of this deque) if it is possible to do so + * immediately without violating capacity restrictions, returning + * true upon success and throwing an + * IllegalStateException if no space is currently available. + * + *

This method is equivalent to {@link #addFirst}. + * + * @param e the element to push + * @throws IllegalStateException if the element cannot be added at this + * time due to capacity restrictions + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this deque + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + void push(E e); + + /** + * Pops an element from the stack represented by this deque. In other + * words, removes and returns the first element of this deque. + * + *

This method is equivalent to {@link #removeFirst()}. + * + * @return the element at the front of this deque (which is the top + * of the stack represented by this deque) + * @throws NoSuchElementException if this deque is empty + */ + E pop(); + + + // *** Collection methods *** + + /** + * Removes the first occurrence of the specified element from this deque. + * If the deque does not contain the element, it is unchanged. + * More formally, removes the first element e such that + * (o==null ? e==null : o.equals(e)) + * (if such an element exists). + * Returns true if this deque contained the specified element + * (or equivalently, if this deque changed as a result of the call). + * + *

This method is equivalent to {@link #removeFirstOccurrence}. + * + * @param o element to be removed from this deque, if present + * @return true if an element was removed as a result of this call + * @throws ClassCastException if the class of the specified element + * is incompatible with this deque + * (optional) + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * (optional) + */ + boolean remove(Object o); + + /** + * Returns true if this deque contains the specified element. + * More formally, returns true if and only if this deque contains + * at least one element e such that + * (o==null ? e==null : o.equals(e)). + * + * @param o element whose presence in this deque is to be tested + * @return true if this deque contains the specified element + * @throws ClassCastException if the type of the specified element + * is incompatible with this deque + * (optional) + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * (optional) + */ + boolean contains(Object o); + + /** + * Returns the number of elements in this deque. + * + * @return the number of elements in this deque + */ + public int size(); + + /** + * Returns an iterator over the elements in this deque in proper sequence. + * The elements will be returned in order from first (head) to last (tail). + * + * @return an iterator over the elements in this deque in proper sequence + */ + Iterator iterator(); + + /** + * Returns an iterator over the elements in this deque in reverse + * sequential order. The elements will be returned in order from + * last (tail) to first (head). + * + * @return an iterator over the elements in this deque in reverse + * sequence + */ + Iterator descendingIterator(); + +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/Dictionary.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/Dictionary.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,155 @@ +/* + * Copyright (c) 1995, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util; + +/** + * The Dictionary class is the abstract parent of any + * class, such as Hashtable, which maps keys to values. + * Every key and every value is an object. In any one Dictionary + * object, every key is associated with at most one value. Given a + * Dictionary and a key, the associated element can be looked up. + * Any non-null object can be used as a key and as a value. + *

+ * As a rule, the equals method should be used by + * implementations of this class to decide if two keys are the same. + *

+ * NOTE: This class is obsolete. New implementations should + * implement the Map interface, rather than extending this class. + * + * @author unascribed + * @see java.util.Map + * @see java.lang.Object#equals(java.lang.Object) + * @see java.lang.Object#hashCode() + * @see java.util.Hashtable + * @since JDK1.0 + */ +public abstract +class Dictionary { + /** + * Sole constructor. (For invocation by subclass constructors, typically + * implicit.) + */ + public Dictionary() { + } + + /** + * Returns the number of entries (distinct keys) in this dictionary. + * + * @return the number of keys in this dictionary. + */ + abstract public int size(); + + /** + * Tests if this dictionary maps no keys to value. The general contract + * for the isEmpty method is that the result is true if and only + * if this dictionary contains no entries. + * + * @return true if this dictionary maps no keys to values; + * false otherwise. + */ + abstract public boolean isEmpty(); + + /** + * Returns an enumeration of the keys in this dictionary. The general + * contract for the keys method is that an Enumeration object + * is returned that will generate all the keys for which this dictionary + * contains entries. + * + * @return an enumeration of the keys in this dictionary. + * @see java.util.Dictionary#elements() + * @see java.util.Enumeration + */ + abstract public Enumeration keys(); + + /** + * Returns an enumeration of the values in this dictionary. The general + * contract for the elements method is that an + * Enumeration is returned that will generate all the elements + * contained in entries in this dictionary. + * + * @return an enumeration of the values in this dictionary. + * @see java.util.Dictionary#keys() + * @see java.util.Enumeration + */ + abstract public Enumeration elements(); + + /** + * Returns the value to which the key is mapped in this dictionary. + * The general contract for the isEmpty method is that if this + * dictionary contains an entry for the specified key, the associated + * value is returned; otherwise, null is returned. + * + * @return the value to which the key is mapped in this dictionary; + * @param key a key in this dictionary. + * null if the key is not mapped to any value in + * this dictionary. + * @exception NullPointerException if the key is null. + * @see java.util.Dictionary#put(java.lang.Object, java.lang.Object) + */ + abstract public V get(Object key); + + /** + * Maps the specified key to the specified + * value in this dictionary. Neither the key nor the + * value can be null. + *

+ * If this dictionary already contains an entry for the specified + * key, the value already in this dictionary for that + * key is returned, after modifying the entry to contain the + * new element.

If this dictionary does not already have an entry + * for the specified key, an entry is created for the + * specified key and value, and null is + * returned. + *

+ * The value can be retrieved by calling the + * get method with a key that is equal to + * the original key. + * + * @param key the hashtable key. + * @param value the value. + * @return the previous value to which the key was mapped + * in this dictionary, or null if the key did not + * have a previous mapping. + * @exception NullPointerException if the key or + * value is null. + * @see java.lang.Object#equals(java.lang.Object) + * @see java.util.Dictionary#get(java.lang.Object) + */ + abstract public V put(K key, V value); + + /** + * Removes the key (and its corresponding + * value) from this dictionary. This method does nothing + * if the key is not in this dictionary. + * + * @param key the key that needs to be removed. + * @return the value to which the key had been mapped in this + * dictionary, or null if the key did not have a + * mapping. + * @exception NullPointerException if key is null. + */ + abstract public V remove(Object key); +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/EmptyStackException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/EmptyStackException.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 1994, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util; + +/** + * Thrown by methods in the Stack class to indicate + * that the stack is empty. + * + * @author Jonathan Payne + * @see java.util.Stack + * @since JDK1.0 + */ +public +class EmptyStackException extends RuntimeException { + private static final long serialVersionUID = 5084686378493302095L; + + /** + * Constructs a new EmptyStackException with null + * as its error message string. + */ + public EmptyStackException() { + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/EventListener.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/EventListener.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 1996, 1999, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util; + +/** + * A tagging interface that all event listener interfaces must extend. + * @since JDK1.1 + */ +public interface EventListener { +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/EventListenerProxy.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/EventListenerProxy.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2000, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util; + +/** + * An abstract wrapper class for an {@code EventListener} class + * which associates a set of additional parameters with the listener. + * Subclasses must provide the storage and accessor methods + * for the additional arguments or parameters. + *

+ * For example, a bean which supports named properties + * would have a two argument method signature for adding + * a {@code PropertyChangeListener} for a property: + *

+ * public void addPropertyChangeListener(String propertyName,
+ *                                       PropertyChangeListener listener)
+ * 
+ * If the bean also implemented the zero argument get listener method: + *
+ * public PropertyChangeListener[] getPropertyChangeListeners()
+ * 
+ * then the array may contain inner {@code PropertyChangeListeners} + * which are also {@code PropertyChangeListenerProxy} objects. + *

+ * If the calling method is interested in retrieving the named property + * then it would have to test the element to see if it is a proxy class. + * + * @since 1.4 + */ +public abstract class EventListenerProxy + implements EventListener { + + private final T listener; + + /** + * Creates a proxy for the specified listener. + * + * @param listener the listener object + */ + public EventListenerProxy(T listener) { + this.listener = listener; + } + + /** + * Returns the listener associated with the proxy. + * + * @return the listener associated with the proxy + */ + public T getListener() { + return this.listener; + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/EventObject.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/EventObject.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 1996, 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util; + +/** + *

+ * The root class from which all event state objects shall be derived. + *

+ * All Events are constructed with a reference to the object, the "source", + * that is logically deemed to be the object upon which the Event in question + * initially occurred upon. + * + * @since JDK1.1 + */ + +public class EventObject implements java.io.Serializable { + + private static final long serialVersionUID = 5516075349620653480L; + + /** + * The object on which the Event initially occurred. + */ + protected transient Object source; + + /** + * Constructs a prototypical Event. + * + * @param source The object on which the Event initially occurred. + * @exception IllegalArgumentException if source is null. + */ + public EventObject(Object source) { + if (source == null) + throw new IllegalArgumentException("null source"); + + this.source = source; + } + + /** + * The object on which the Event initially occurred. + * + * @return The object on which the Event initially occurred. + */ + public Object getSource() { + return source; + } + + /** + * Returns a String representation of this EventObject. + * + * @return A a String representation of this EventObject. + */ + public String toString() { + return getClass().getName() + "[source=" + source + "]"; + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/Hashtable.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/Hashtable.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,1005 @@ +/* + * Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util; +import java.io.*; + +/** + * This class implements a hash table, which maps keys to values. Any + * non-null object can be used as a key or as a value.

+ * + * To successfully store and retrieve objects from a hashtable, the + * objects used as keys must implement the hashCode + * method and the equals method.

+ * + * An instance of Hashtable has two parameters that affect its + * performance: initial capacity and load factor. The + * capacity is the number of buckets in the hash table, and the + * initial capacity is simply the capacity at the time the hash table + * is created. Note that the hash table is open: in the case of a "hash + * collision", a single bucket stores multiple entries, which must be searched + * sequentially. The load factor is a measure of how full the hash + * table is allowed to get before its capacity is automatically increased. + * The initial capacity and load factor parameters are merely hints to + * the implementation. The exact details as to when and whether the rehash + * method is invoked are implementation-dependent.

+ * + * Generally, the default load factor (.75) offers a good tradeoff between + * time and space costs. Higher values decrease the space overhead but + * increase the time cost to look up an entry (which is reflected in most + * Hashtable operations, including get and put).

+ * + * The initial capacity controls a tradeoff between wasted space and the + * need for rehash operations, which are time-consuming. + * No rehash operations will ever occur if the initial + * capacity is greater than the maximum number of entries the + * Hashtable will contain divided by its load factor. However, + * setting the initial capacity too high can waste space.

+ * + * If many entries are to be made into a Hashtable, + * creating it with a sufficiently large capacity may allow the + * entries to be inserted more efficiently than letting it perform + * automatic rehashing as needed to grow the table.

+ * + * This example creates a hashtable of numbers. It uses the names of + * the numbers as keys: + *

   {@code
+ *   Hashtable numbers
+ *     = new Hashtable();
+ *   numbers.put("one", 1);
+ *   numbers.put("two", 2);
+ *   numbers.put("three", 3);}
+ * + *

To retrieve a number, use the following code: + *

   {@code
+ *   Integer n = numbers.get("two");
+ *   if (n != null) {
+ *     System.out.println("two = " + n);
+ *   }}
+ * + *

The iterators returned by the iterator method of the collections + * returned by all of this class's "collection view methods" are + * fail-fast: if the Hashtable is structurally modified at any time + * after the iterator is created, in any way except through the iterator's own + * remove method, the iterator will throw a {@link + * ConcurrentModificationException}. Thus, in the face of concurrent + * modification, the iterator fails quickly and cleanly, rather than risking + * arbitrary, non-deterministic behavior at an undetermined time in the future. + * The Enumerations returned by Hashtable's keys and elements methods are + * not fail-fast. + * + *

Note that the fail-fast behavior of an iterator cannot be guaranteed + * as it is, generally speaking, impossible to make any hard guarantees in the + * presence of unsynchronized concurrent modification. Fail-fast iterators + * throw ConcurrentModificationException on a best-effort basis. + * Therefore, it would be wrong to write a program that depended on this + * exception for its correctness: the fail-fast behavior of iterators + * should be used only to detect bugs. + * + *

As of the Java 2 platform v1.2, this class was retrofitted to + * implement the {@link Map} interface, making it a member of the + * + * + * Java Collections Framework. Unlike the new collection + * implementations, {@code Hashtable} is synchronized. If a + * thread-safe implementation is not needed, it is recommended to use + * {@link HashMap} in place of {@code Hashtable}. If a thread-safe + * highly-concurrent implementation is desired, then it is recommended + * to use {@link java.util.concurrent.ConcurrentHashMap} in place of + * {@code Hashtable}. + * + * @author Arthur van Hoff + * @author Josh Bloch + * @author Neal Gafter + * @see Object#equals(java.lang.Object) + * @see Object#hashCode() + * @see Hashtable#rehash() + * @see Collection + * @see Map + * @see HashMap + * @see TreeMap + * @since JDK1.0 + */ +public class Hashtable + extends Dictionary + implements Map, Cloneable, java.io.Serializable { + + /** + * The hash table data. + */ + private transient Entry[] table; + + /** + * The total number of entries in the hash table. + */ + private transient int count; + + /** + * The table is rehashed when its size exceeds this threshold. (The + * value of this field is (int)(capacity * loadFactor).) + * + * @serial + */ + private int threshold; + + /** + * The load factor for the hashtable. + * + * @serial + */ + private float loadFactor; + + /** + * The number of times this Hashtable has been structurally modified + * Structural modifications are those that change the number of entries in + * the Hashtable or otherwise modify its internal structure (e.g., + * rehash). This field is used to make iterators on Collection-views of + * the Hashtable fail-fast. (See ConcurrentModificationException). + */ + private transient int modCount = 0; + + /** use serialVersionUID from JDK 1.0.2 for interoperability */ + private static final long serialVersionUID = 1421746759512286392L; + + /** + * Constructs a new, empty hashtable with the specified initial + * capacity and the specified load factor. + * + * @param initialCapacity the initial capacity of the hashtable. + * @param loadFactor the load factor of the hashtable. + * @exception IllegalArgumentException if the initial capacity is less + * than zero, or if the load factor is nonpositive. + */ + public Hashtable(int initialCapacity, float loadFactor) { + if (initialCapacity < 0) + throw new IllegalArgumentException("Illegal Capacity: "+ + initialCapacity); + if (loadFactor <= 0 || Float.isNaN(loadFactor)) + throw new IllegalArgumentException("Illegal Load: "+loadFactor); + + if (initialCapacity==0) + initialCapacity = 1; + this.loadFactor = loadFactor; + table = new Entry[initialCapacity]; + threshold = (int)(initialCapacity * loadFactor); + } + + /** + * Constructs a new, empty hashtable with the specified initial capacity + * and default load factor (0.75). + * + * @param initialCapacity the initial capacity of the hashtable. + * @exception IllegalArgumentException if the initial capacity is less + * than zero. + */ + public Hashtable(int initialCapacity) { + this(initialCapacity, 0.75f); + } + + /** + * Constructs a new, empty hashtable with a default initial capacity (11) + * and load factor (0.75). + */ + public Hashtable() { + this(11, 0.75f); + } + + /** + * Constructs a new hashtable with the same mappings as the given + * Map. The hashtable is created with an initial capacity sufficient to + * hold the mappings in the given Map and a default load factor (0.75). + * + * @param t the map whose mappings are to be placed in this map. + * @throws NullPointerException if the specified map is null. + * @since 1.2 + */ + public Hashtable(Map t) { + this(Math.max(2*t.size(), 11), 0.75f); + putAll(t); + } + + /** + * Returns the number of keys in this hashtable. + * + * @return the number of keys in this hashtable. + */ + public synchronized int size() { + return count; + } + + /** + * Tests if this hashtable maps no keys to values. + * + * @return true if this hashtable maps no keys to values; + * false otherwise. + */ + public synchronized boolean isEmpty() { + return count == 0; + } + + /** + * Returns an enumeration of the keys in this hashtable. + * + * @return an enumeration of the keys in this hashtable. + * @see Enumeration + * @see #elements() + * @see #keySet() + * @see Map + */ + public synchronized Enumeration keys() { + return this.getEnumeration(KEYS); + } + + /** + * Returns an enumeration of the values in this hashtable. + * Use the Enumeration methods on the returned object to fetch the elements + * sequentially. + * + * @return an enumeration of the values in this hashtable. + * @see java.util.Enumeration + * @see #keys() + * @see #values() + * @see Map + */ + public synchronized Enumeration elements() { + return this.getEnumeration(VALUES); + } + + /** + * Tests if some key maps into the specified value in this hashtable. + * This operation is more expensive than the {@link #containsKey + * containsKey} method. + * + *

Note that this method is identical in functionality to + * {@link #containsValue containsValue}, (which is part of the + * {@link Map} interface in the collections framework). + * + * @param value a value to search for + * @return true if and only if some key maps to the + * value argument in this hashtable as + * determined by the equals method; + * false otherwise. + * @exception NullPointerException if the value is null + */ + public synchronized boolean contains(Object value) { + if (value == null) { + throw new NullPointerException(); + } + + Entry tab[] = table; + for (int i = tab.length ; i-- > 0 ;) { + for (Entry e = tab[i] ; e != null ; e = e.next) { + if (e.value.equals(value)) { + return true; + } + } + } + return false; + } + + /** + * Returns true if this hashtable maps one or more keys to this value. + * + *

Note that this method is identical in functionality to {@link + * #contains contains} (which predates the {@link Map} interface). + * + * @param value value whose presence in this hashtable is to be tested + * @return true if this map maps one or more keys to the + * specified value + * @throws NullPointerException if the value is null + * @since 1.2 + */ + public boolean containsValue(Object value) { + return contains(value); + } + + /** + * Tests if the specified object is a key in this hashtable. + * + * @param key possible key + * @return true if and only if the specified object + * is a key in this hashtable, as determined by the + * equals method; false otherwise. + * @throws NullPointerException if the key is null + * @see #contains(Object) + */ + public synchronized boolean containsKey(Object key) { + Entry tab[] = table; + int hash = key.hashCode(); + int index = (hash & 0x7FFFFFFF) % tab.length; + for (Entry e = tab[index] ; e != null ; e = e.next) { + if ((e.hash == hash) && e.key.equals(key)) { + return true; + } + } + return false; + } + + /** + * Returns the value to which the specified key is mapped, + * or {@code null} if this map contains no mapping for the key. + * + *

More formally, if this map contains a mapping from a key + * {@code k} to a value {@code v} such that {@code (key.equals(k))}, + * then this method returns {@code v}; otherwise it returns + * {@code null}. (There can be at most one such mapping.) + * + * @param key the key whose associated value is to be returned + * @return the value to which the specified key is mapped, or + * {@code null} if this map contains no mapping for the key + * @throws NullPointerException if the specified key is null + * @see #put(Object, Object) + */ + public synchronized V get(Object key) { + Entry tab[] = table; + int hash = key.hashCode(); + int index = (hash & 0x7FFFFFFF) % tab.length; + for (Entry e = tab[index] ; e != null ; e = e.next) { + if ((e.hash == hash) && e.key.equals(key)) { + return e.value; + } + } + return null; + } + + /** + * The maximum size of array to allocate. + * Some VMs reserve some header words in an array. + * Attempts to allocate larger arrays may result in + * OutOfMemoryError: Requested array size exceeds VM limit + */ + private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + + /** + * Increases the capacity of and internally reorganizes this + * hashtable, in order to accommodate and access its entries more + * efficiently. This method is called automatically when the + * number of keys in the hashtable exceeds this hashtable's capacity + * and load factor. + */ + protected void rehash() { + int oldCapacity = table.length; + Entry[] oldMap = table; + + // overflow-conscious code + int newCapacity = (oldCapacity << 1) + 1; + if (newCapacity - MAX_ARRAY_SIZE > 0) { + if (oldCapacity == MAX_ARRAY_SIZE) + // Keep running with MAX_ARRAY_SIZE buckets + return; + newCapacity = MAX_ARRAY_SIZE; + } + Entry[] newMap = new Entry[newCapacity]; + + modCount++; + threshold = (int)(newCapacity * loadFactor); + table = newMap; + + for (int i = oldCapacity ; i-- > 0 ;) { + for (Entry old = oldMap[i] ; old != null ; ) { + Entry e = old; + old = old.next; + + int index = (e.hash & 0x7FFFFFFF) % newCapacity; + e.next = newMap[index]; + newMap[index] = e; + } + } + } + + /** + * Maps the specified key to the specified + * value in this hashtable. Neither the key nor the + * value can be null.

+ * + * The value can be retrieved by calling the get method + * with a key that is equal to the original key. + * + * @param key the hashtable key + * @param value the value + * @return the previous value of the specified key in this hashtable, + * or null if it did not have one + * @exception NullPointerException if the key or value is + * null + * @see Object#equals(Object) + * @see #get(Object) + */ + public synchronized V put(K key, V value) { + // Make sure the value is not null + if (value == null) { + throw new NullPointerException(); + } + + // Makes sure the key is not already in the hashtable. + Entry tab[] = table; + int hash = key.hashCode(); + int index = (hash & 0x7FFFFFFF) % tab.length; + for (Entry e = tab[index] ; e != null ; e = e.next) { + if ((e.hash == hash) && e.key.equals(key)) { + V old = e.value; + e.value = value; + return old; + } + } + + modCount++; + if (count >= threshold) { + // Rehash the table if the threshold is exceeded + rehash(); + + tab = table; + index = (hash & 0x7FFFFFFF) % tab.length; + } + + // Creates the new entry. + Entry e = tab[index]; + tab[index] = new Entry<>(hash, key, value, e); + count++; + return null; + } + + /** + * Removes the key (and its corresponding value) from this + * hashtable. This method does nothing if the key is not in the hashtable. + * + * @param key the key that needs to be removed + * @return the value to which the key had been mapped in this hashtable, + * or null if the key did not have a mapping + * @throws NullPointerException if the key is null + */ + public synchronized V remove(Object key) { + Entry tab[] = table; + int hash = key.hashCode(); + int index = (hash & 0x7FFFFFFF) % tab.length; + for (Entry e = tab[index], prev = null ; e != null ; prev = e, e = e.next) { + if ((e.hash == hash) && e.key.equals(key)) { + modCount++; + if (prev != null) { + prev.next = e.next; + } else { + tab[index] = e.next; + } + count--; + V oldValue = e.value; + e.value = null; + return oldValue; + } + } + return null; + } + + /** + * Copies all of the mappings from the specified map to this hashtable. + * These mappings will replace any mappings that this hashtable had for any + * of the keys currently in the specified map. + * + * @param t mappings to be stored in this map + * @throws NullPointerException if the specified map is null + * @since 1.2 + */ + public synchronized void putAll(Map t) { + for (Map.Entry e : t.entrySet()) + put(e.getKey(), e.getValue()); + } + + /** + * Clears this hashtable so that it contains no keys. + */ + public synchronized void clear() { + Entry tab[] = table; + modCount++; + for (int index = tab.length; --index >= 0; ) + tab[index] = null; + count = 0; + } + + /** + * Creates a shallow copy of this hashtable. All the structure of the + * hashtable itself is copied, but the keys and values are not cloned. + * This is a relatively expensive operation. + * + * @return a clone of the hashtable + */ + public synchronized Object clone() { + try { + Hashtable t = (Hashtable) super.clone(); + t.table = new Entry[table.length]; + for (int i = table.length ; i-- > 0 ; ) { + t.table[i] = (table[i] != null) + ? (Entry) table[i].clone() : null; + } + t.keySet = null; + t.entrySet = null; + t.values = null; + t.modCount = 0; + return t; + } catch (CloneNotSupportedException e) { + // this shouldn't happen, since we are Cloneable + throw new InternalError(); + } + } + + /** + * Returns a string representation of this Hashtable object + * in the form of a set of entries, enclosed in braces and separated + * by the ASCII characters "" (comma and space). Each + * entry is rendered as the key, an equals sign =, and the + * associated element, where the toString method is used to + * convert the key and element to strings. + * + * @return a string representation of this hashtable + */ + public synchronized String toString() { + int max = size() - 1; + if (max == -1) + return "{}"; + + StringBuilder sb = new StringBuilder(); + Iterator> it = entrySet().iterator(); + + sb.append('{'); + for (int i = 0; ; i++) { + Map.Entry e = it.next(); + K key = e.getKey(); + V value = e.getValue(); + sb.append(key == this ? "(this Map)" : key.toString()); + sb.append('='); + sb.append(value == this ? "(this Map)" : value.toString()); + + if (i == max) + return sb.append('}').toString(); + sb.append(", "); + } + } + + + private Enumeration getEnumeration(int type) { + if (count == 0) { + return Collections.emptyEnumeration(); + } else { + return new Enumerator<>(type, false); + } + } + + private Iterator getIterator(int type) { + if (count == 0) { + return Collections.emptyIterator(); + } else { + return new Enumerator<>(type, true); + } + } + + // Views + + /** + * Each of these fields are initialized to contain an instance of the + * appropriate view the first time this view is requested. The views are + * stateless, so there's no reason to create more than one of each. + */ + private transient volatile Set keySet = null; + private transient volatile Set> entrySet = null; + private transient volatile Collection values = null; + + /** + * Returns a {@link Set} view of the keys contained in this map. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. If the map is modified + * while an iteration over the set is in progress (except through + * the iterator's own remove operation), the results of + * the iteration are undefined. The set supports element removal, + * which removes the corresponding mapping from the map, via the + * Iterator.remove, Set.remove, + * removeAll, retainAll, and clear + * operations. It does not support the add or addAll + * operations. + * + * @since 1.2 + */ + public Set keySet() { + if (keySet == null) + keySet = Collections.synchronizedSet(new KeySet(), this); + return keySet; + } + + private class KeySet extends AbstractSet { + public Iterator iterator() { + return getIterator(KEYS); + } + public int size() { + return count; + } + public boolean contains(Object o) { + return containsKey(o); + } + public boolean remove(Object o) { + return Hashtable.this.remove(o) != null; + } + public void clear() { + Hashtable.this.clear(); + } + } + + /** + * Returns a {@link Set} view of the mappings contained in this map. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. If the map is modified + * while an iteration over the set is in progress (except through + * the iterator's own remove operation, or through the + * setValue operation on a map entry returned by the + * iterator) the results of the iteration are undefined. The set + * supports element removal, which removes the corresponding + * mapping from the map, via the Iterator.remove, + * Set.remove, removeAll, retainAll and + * clear operations. It does not support the + * add or addAll operations. + * + * @since 1.2 + */ + public Set> entrySet() { + if (entrySet==null) + entrySet = Collections.synchronizedSet(new EntrySet(), this); + return entrySet; + } + + private class EntrySet extends AbstractSet> { + public Iterator> iterator() { + return getIterator(ENTRIES); + } + + public boolean add(Map.Entry o) { + return super.add(o); + } + + public boolean contains(Object o) { + if (!(o instanceof Map.Entry)) + return false; + Map.Entry entry = (Map.Entry)o; + Object key = entry.getKey(); + Entry[] tab = table; + int hash = key.hashCode(); + int index = (hash & 0x7FFFFFFF) % tab.length; + + for (Entry e = tab[index]; e != null; e = e.next) + if (e.hash==hash && e.equals(entry)) + return true; + return false; + } + + public boolean remove(Object o) { + if (!(o instanceof Map.Entry)) + return false; + Map.Entry entry = (Map.Entry) o; + K key = entry.getKey(); + Entry[] tab = table; + int hash = key.hashCode(); + int index = (hash & 0x7FFFFFFF) % tab.length; + + for (Entry e = tab[index], prev = null; e != null; + prev = e, e = e.next) { + if (e.hash==hash && e.equals(entry)) { + modCount++; + if (prev != null) + prev.next = e.next; + else + tab[index] = e.next; + + count--; + e.value = null; + return true; + } + } + return false; + } + + public int size() { + return count; + } + + public void clear() { + Hashtable.this.clear(); + } + } + + /** + * Returns a {@link Collection} view of the values contained in this map. + * The collection is backed by the map, so changes to the map are + * reflected in the collection, and vice-versa. If the map is + * modified while an iteration over the collection is in progress + * (except through the iterator's own remove operation), + * the results of the iteration are undefined. The collection + * supports element removal, which removes the corresponding + * mapping from the map, via the Iterator.remove, + * Collection.remove, removeAll, + * retainAll and clear operations. It does not + * support the add or addAll operations. + * + * @since 1.2 + */ + public Collection values() { + if (values==null) + values = Collections.synchronizedCollection(new ValueCollection(), + this); + return values; + } + + private class ValueCollection extends AbstractCollection { + public Iterator iterator() { + return getIterator(VALUES); + } + public int size() { + return count; + } + public boolean contains(Object o) { + return containsValue(o); + } + public void clear() { + Hashtable.this.clear(); + } + } + + // Comparison and hashing + + /** + * Compares the specified Object with this Map for equality, + * as per the definition in the Map interface. + * + * @param o object to be compared for equality with this hashtable + * @return true if the specified Object is equal to this Map + * @see Map#equals(Object) + * @since 1.2 + */ + public synchronized boolean equals(Object o) { + if (o == this) + return true; + + if (!(o instanceof Map)) + return false; + Map t = (Map) o; + if (t.size() != size()) + return false; + + try { + Iterator> i = entrySet().iterator(); + while (i.hasNext()) { + Map.Entry e = i.next(); + K key = e.getKey(); + V value = e.getValue(); + if (value == null) { + if (!(t.get(key)==null && t.containsKey(key))) + return false; + } else { + if (!value.equals(t.get(key))) + return false; + } + } + } catch (ClassCastException unused) { + return false; + } catch (NullPointerException unused) { + return false; + } + + return true; + } + + /** + * Returns the hash code value for this Map as per the definition in the + * Map interface. + * + * @see Map#hashCode() + * @since 1.2 + */ + public synchronized int hashCode() { + /* + * This code detects the recursion caused by computing the hash code + * of a self-referential hash table and prevents the stack overflow + * that would otherwise result. This allows certain 1.1-era + * applets with self-referential hash tables to work. This code + * abuses the loadFactor field to do double-duty as a hashCode + * in progress flag, so as not to worsen the space performance. + * A negative load factor indicates that hash code computation is + * in progress. + */ + int h = 0; + if (count == 0 || loadFactor < 0) + return h; // Returns zero + + loadFactor = -loadFactor; // Mark hashCode computation in progress + Entry[] tab = table; + for (int i = 0; i < tab.length; i++) + for (Entry e = tab[i]; e != null; e = e.next) + h += e.key.hashCode() ^ e.value.hashCode(); + loadFactor = -loadFactor; // Mark hashCode computation complete + + return h; + } + + /** + * Hashtable collision list. + */ + private static class Entry implements Map.Entry { + int hash; + K key; + V value; + Entry next; + + protected Entry(int hash, K key, V value, Entry next) { + this.hash = hash; + this.key = key; + this.value = value; + this.next = next; + } + + protected Object clone() { + return new Entry<>(hash, key, value, + (next==null ? null : (Entry) next.clone())); + } + + // Map.Entry Ops + + public K getKey() { + return key; + } + + public V getValue() { + return value; + } + + public V setValue(V value) { + if (value == null) + throw new NullPointerException(); + + V oldValue = this.value; + this.value = value; + return oldValue; + } + + public boolean equals(Object o) { + if (!(o instanceof Map.Entry)) + return false; + Map.Entry e = (Map.Entry)o; + + return (key==null ? e.getKey()==null : key.equals(e.getKey())) && + (value==null ? e.getValue()==null : value.equals(e.getValue())); + } + + public int hashCode() { + return hash ^ (value==null ? 0 : value.hashCode()); + } + + public String toString() { + return key.toString()+"="+value.toString(); + } + } + + // Types of Enumerations/Iterations + private static final int KEYS = 0; + private static final int VALUES = 1; + private static final int ENTRIES = 2; + + /** + * A hashtable enumerator class. This class implements both the + * Enumeration and Iterator interfaces, but individual instances + * can be created with the Iterator methods disabled. This is necessary + * to avoid unintentionally increasing the capabilities granted a user + * by passing an Enumeration. + */ + private class Enumerator implements Enumeration, Iterator { + Entry[] table = Hashtable.this.table; + int index = table.length; + Entry entry = null; + Entry lastReturned = null; + int type; + + /** + * Indicates whether this Enumerator is serving as an Iterator + * or an Enumeration. (true -> Iterator). + */ + boolean iterator; + + /** + * The modCount value that the iterator believes that the backing + * Hashtable should have. If this expectation is violated, the iterator + * has detected concurrent modification. + */ + protected int expectedModCount = modCount; + + Enumerator(int type, boolean iterator) { + this.type = type; + this.iterator = iterator; + } + + public boolean hasMoreElements() { + Entry e = entry; + int i = index; + Entry[] t = table; + /* Use locals for faster loop iteration */ + while (e == null && i > 0) { + e = t[--i]; + } + entry = e; + index = i; + return e != null; + } + + public T nextElement() { + Entry et = entry; + int i = index; + Entry[] t = table; + /* Use locals for faster loop iteration */ + while (et == null && i > 0) { + et = t[--i]; + } + entry = et; + index = i; + if (et != null) { + Entry e = lastReturned = entry; + entry = e.next; + return type == KEYS ? (T)e.key : (type == VALUES ? (T)e.value : (T)e); + } + throw new NoSuchElementException("Hashtable Enumerator"); + } + + // Iterator methods + public boolean hasNext() { + return hasMoreElements(); + } + + public T next() { + if (modCount != expectedModCount) + throw new ConcurrentModificationException(); + return nextElement(); + } + + public void remove() { + if (!iterator) + throw new UnsupportedOperationException(); + if (lastReturned == null) + throw new IllegalStateException("Hashtable Enumerator"); + if (modCount != expectedModCount) + throw new ConcurrentModificationException(); + + synchronized(Hashtable.this) { + Entry[] tab = Hashtable.this.table; + int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length; + + for (Entry e = tab[index], prev = null; e != null; + prev = e, e = e.next) { + if (e == lastReturned) { + modCount++; + expectedModCount++; + if (prev == null) + tab[index] = e.next; + else + prev.next = e.next; + count--; + lastReturned = null; + return; + } + } + throw new ConcurrentModificationException(); + } + } + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/LinkedList.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/LinkedList.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,1100 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util; + +/** + * Doubly-linked list implementation of the {@code List} and {@code Deque} + * interfaces. Implements all optional list operations, and permits all + * elements (including {@code null}). + * + *

All of the operations perform as could be expected for a doubly-linked + * list. Operations that index into the list will traverse the list from + * the beginning or the end, whichever is closer to the specified index. + * + *

Note that this implementation is not synchronized. + * If multiple threads access a linked list concurrently, and at least + * one of the threads modifies the list structurally, it must be + * synchronized externally. (A structural modification is any operation + * that adds or deletes one or more elements; merely setting the value of + * an element is not a structural modification.) This is typically + * accomplished by synchronizing on some object that naturally + * encapsulates the list. + * + * If no such object exists, the list should be "wrapped" using the + * {@link Collections#synchronizedList Collections.synchronizedList} + * method. This is best done at creation time, to prevent accidental + * unsynchronized access to the list:

+ *   List list = Collections.synchronizedList(new LinkedList(...));
+ * + *

The iterators returned by this class's {@code iterator} and + * {@code listIterator} methods are fail-fast: if the list is + * structurally modified at any time after the iterator is created, in + * any way except through the Iterator's own {@code remove} or + * {@code add} methods, the iterator will throw a {@link + * ConcurrentModificationException}. Thus, in the face of concurrent + * modification, the iterator fails quickly and cleanly, rather than + * risking arbitrary, non-deterministic behavior at an undetermined + * time in the future. + * + *

Note that the fail-fast behavior of an iterator cannot be guaranteed + * as it is, generally speaking, impossible to make any hard guarantees in the + * presence of unsynchronized concurrent modification. Fail-fast iterators + * throw {@code ConcurrentModificationException} on a best-effort basis. + * Therefore, it would be wrong to write a program that depended on this + * exception for its correctness: the fail-fast behavior of iterators + * should be used only to detect bugs. + * + *

This class is a member of the + * + * Java Collections Framework. + * + * @author Josh Bloch + * @see List + * @see ArrayList + * @since 1.2 + * @param the type of elements held in this collection + */ + +public class LinkedList + extends AbstractSequentialList + implements List, Deque, Cloneable, java.io.Serializable +{ + transient int size = 0; + + /** + * Pointer to first node. + * Invariant: (first == null && last == null) || + * (first.prev == null && first.item != null) + */ + transient Node first; + + /** + * Pointer to last node. + * Invariant: (first == null && last == null) || + * (last.next == null && last.item != null) + */ + transient Node last; + + /** + * Constructs an empty list. + */ + public LinkedList() { + } + + /** + * Constructs a list containing the elements of the specified + * collection, in the order they are returned by the collection's + * iterator. + * + * @param c the collection whose elements are to be placed into this list + * @throws NullPointerException if the specified collection is null + */ + public LinkedList(Collection c) { + this(); + addAll(c); + } + + /** + * Links e as first element. + */ + private void linkFirst(E e) { + final Node f = first; + final Node newNode = new Node<>(null, e, f); + first = newNode; + if (f == null) + last = newNode; + else + f.prev = newNode; + size++; + modCount++; + } + + /** + * Links e as last element. + */ + void linkLast(E e) { + final Node l = last; + final Node newNode = new Node<>(l, e, null); + last = newNode; + if (l == null) + first = newNode; + else + l.next = newNode; + size++; + modCount++; + } + + /** + * Inserts element e before non-null Node succ. + */ + void linkBefore(E e, Node succ) { + // assert succ != null; + final Node pred = succ.prev; + final Node newNode = new Node<>(pred, e, succ); + succ.prev = newNode; + if (pred == null) + first = newNode; + else + pred.next = newNode; + size++; + modCount++; + } + + /** + * Unlinks non-null first node f. + */ + private E unlinkFirst(Node f) { + // assert f == first && f != null; + final E element = f.item; + final Node next = f.next; + f.item = null; + f.next = null; // help GC + first = next; + if (next == null) + last = null; + else + next.prev = null; + size--; + modCount++; + return element; + } + + /** + * Unlinks non-null last node l. + */ + private E unlinkLast(Node l) { + // assert l == last && l != null; + final E element = l.item; + final Node prev = l.prev; + l.item = null; + l.prev = null; // help GC + last = prev; + if (prev == null) + first = null; + else + prev.next = null; + size--; + modCount++; + return element; + } + + /** + * Unlinks non-null node x. + */ + E unlink(Node x) { + // assert x != null; + final E element = x.item; + final Node next = x.next; + final Node prev = x.prev; + + if (prev == null) { + first = next; + } else { + prev.next = next; + x.prev = null; + } + + if (next == null) { + last = prev; + } else { + next.prev = prev; + x.next = null; + } + + x.item = null; + size--; + modCount++; + return element; + } + + /** + * Returns the first element in this list. + * + * @return the first element in this list + * @throws NoSuchElementException if this list is empty + */ + public E getFirst() { + final Node f = first; + if (f == null) + throw new NoSuchElementException(); + return f.item; + } + + /** + * Returns the last element in this list. + * + * @return the last element in this list + * @throws NoSuchElementException if this list is empty + */ + public E getLast() { + final Node l = last; + if (l == null) + throw new NoSuchElementException(); + return l.item; + } + + /** + * Removes and returns the first element from this list. + * + * @return the first element from this list + * @throws NoSuchElementException if this list is empty + */ + public E removeFirst() { + final Node f = first; + if (f == null) + throw new NoSuchElementException(); + return unlinkFirst(f); + } + + /** + * Removes and returns the last element from this list. + * + * @return the last element from this list + * @throws NoSuchElementException if this list is empty + */ + public E removeLast() { + final Node l = last; + if (l == null) + throw new NoSuchElementException(); + return unlinkLast(l); + } + + /** + * Inserts the specified element at the beginning of this list. + * + * @param e the element to add + */ + public void addFirst(E e) { + linkFirst(e); + } + + /** + * Appends the specified element to the end of this list. + * + *

This method is equivalent to {@link #add}. + * + * @param e the element to add + */ + public void addLast(E e) { + linkLast(e); + } + + /** + * Returns {@code true} if this list contains the specified element. + * More formally, returns {@code true} if and only if this list contains + * at least one element {@code e} such that + * (o==null ? e==null : o.equals(e)). + * + * @param o element whose presence in this list is to be tested + * @return {@code true} if this list contains the specified element + */ + public boolean contains(Object o) { + return indexOf(o) != -1; + } + + /** + * Returns the number of elements in this list. + * + * @return the number of elements in this list + */ + public int size() { + return size; + } + + /** + * Appends the specified element to the end of this list. + * + *

This method is equivalent to {@link #addLast}. + * + * @param e element to be appended to this list + * @return {@code true} (as specified by {@link Collection#add}) + */ + public boolean add(E e) { + linkLast(e); + return true; + } + + /** + * Removes the first occurrence of the specified element from this list, + * if it is present. If this list does not contain the element, it is + * unchanged. More formally, removes the element with the lowest index + * {@code i} such that + * (o==null ? get(i)==null : o.equals(get(i))) + * (if such an element exists). Returns {@code true} if this list + * contained the specified element (or equivalently, if this list + * changed as a result of the call). + * + * @param o element to be removed from this list, if present + * @return {@code true} if this list contained the specified element + */ + public boolean remove(Object o) { + if (o == null) { + for (Node x = first; x != null; x = x.next) { + if (x.item == null) { + unlink(x); + return true; + } + } + } else { + for (Node x = first; x != null; x = x.next) { + if (o.equals(x.item)) { + unlink(x); + return true; + } + } + } + return false; + } + + /** + * Appends all of the elements in the specified collection to the end of + * this list, in the order that they are returned by the specified + * collection's iterator. The behavior of this operation is undefined if + * the specified collection is modified while the operation is in + * progress. (Note that this will occur if the specified collection is + * this list, and it's nonempty.) + * + * @param c collection containing elements to be added to this list + * @return {@code true} if this list changed as a result of the call + * @throws NullPointerException if the specified collection is null + */ + public boolean addAll(Collection c) { + return addAll(size, c); + } + + /** + * Inserts all of the elements in the specified collection into this + * list, starting at the specified position. Shifts the element + * currently at that position (if any) and any subsequent elements to + * the right (increases their indices). The new elements will appear + * in the list in the order that they are returned by the + * specified collection's iterator. + * + * @param index index at which to insert the first element + * from the specified collection + * @param c collection containing elements to be added to this list + * @return {@code true} if this list changed as a result of the call + * @throws IndexOutOfBoundsException {@inheritDoc} + * @throws NullPointerException if the specified collection is null + */ + public boolean addAll(int index, Collection c) { + checkPositionIndex(index); + + Object[] a = c.toArray(); + int numNew = a.length; + if (numNew == 0) + return false; + + Node pred, succ; + if (index == size) { + succ = null; + pred = last; + } else { + succ = node(index); + pred = succ.prev; + } + + for (Object o : a) { + @SuppressWarnings("unchecked") E e = (E) o; + Node newNode = new Node<>(pred, e, null); + if (pred == null) + first = newNode; + else + pred.next = newNode; + pred = newNode; + } + + if (succ == null) { + last = pred; + } else { + pred.next = succ; + succ.prev = pred; + } + + size += numNew; + modCount++; + return true; + } + + /** + * Removes all of the elements from this list. + * The list will be empty after this call returns. + */ + public void clear() { + // Clearing all of the links between nodes is "unnecessary", but: + // - helps a generational GC if the discarded nodes inhabit + // more than one generation + // - is sure to free memory even if there is a reachable Iterator + for (Node x = first; x != null; ) { + Node next = x.next; + x.item = null; + x.next = null; + x.prev = null; + x = next; + } + first = last = null; + size = 0; + modCount++; + } + + + // Positional Access Operations + + /** + * Returns the element at the specified position in this list. + * + * @param index index of the element to return + * @return the element at the specified position in this list + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public E get(int index) { + checkElementIndex(index); + return node(index).item; + } + + /** + * Replaces the element at the specified position in this list with the + * specified element. + * + * @param index index of the element to replace + * @param element element to be stored at the specified position + * @return the element previously at the specified position + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public E set(int index, E element) { + checkElementIndex(index); + Node x = node(index); + E oldVal = x.item; + x.item = element; + return oldVal; + } + + /** + * Inserts the specified element at the specified position in this list. + * Shifts the element currently at that position (if any) and any + * subsequent elements to the right (adds one to their indices). + * + * @param index index at which the specified element is to be inserted + * @param element element to be inserted + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public void add(int index, E element) { + checkPositionIndex(index); + + if (index == size) + linkLast(element); + else + linkBefore(element, node(index)); + } + + /** + * Removes the element at the specified position in this list. Shifts any + * subsequent elements to the left (subtracts one from their indices). + * Returns the element that was removed from the list. + * + * @param index the index of the element to be removed + * @return the element previously at the specified position + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public E remove(int index) { + checkElementIndex(index); + return unlink(node(index)); + } + + /** + * Tells if the argument is the index of an existing element. + */ + private boolean isElementIndex(int index) { + return index >= 0 && index < size; + } + + /** + * Tells if the argument is the index of a valid position for an + * iterator or an add operation. + */ + private boolean isPositionIndex(int index) { + return index >= 0 && index <= size; + } + + /** + * Constructs an IndexOutOfBoundsException detail message. + * Of the many possible refactorings of the error handling code, + * this "outlining" performs best with both server and client VMs. + */ + private String outOfBoundsMsg(int index) { + return "Index: "+index+", Size: "+size; + } + + private void checkElementIndex(int index) { + if (!isElementIndex(index)) + throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); + } + + private void checkPositionIndex(int index) { + if (!isPositionIndex(index)) + throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); + } + + /** + * Returns the (non-null) Node at the specified element index. + */ + Node node(int index) { + // assert isElementIndex(index); + + if (index < (size >> 1)) { + Node x = first; + for (int i = 0; i < index; i++) + x = x.next; + return x; + } else { + Node x = last; + for (int i = size - 1; i > index; i--) + x = x.prev; + return x; + } + } + + // Search Operations + + /** + * Returns the index of the first occurrence of the specified element + * in this list, or -1 if this list does not contain the element. + * More formally, returns the lowest index {@code i} such that + * (o==null ? get(i)==null : o.equals(get(i))), + * or -1 if there is no such index. + * + * @param o element to search for + * @return the index of the first occurrence of the specified element in + * this list, or -1 if this list does not contain the element + */ + public int indexOf(Object o) { + int index = 0; + if (o == null) { + for (Node x = first; x != null; x = x.next) { + if (x.item == null) + return index; + index++; + } + } else { + for (Node x = first; x != null; x = x.next) { + if (o.equals(x.item)) + return index; + index++; + } + } + return -1; + } + + /** + * Returns the index of the last occurrence of the specified element + * in this list, or -1 if this list does not contain the element. + * More formally, returns the highest index {@code i} such that + * (o==null ? get(i)==null : o.equals(get(i))), + * or -1 if there is no such index. + * + * @param o element to search for + * @return the index of the last occurrence of the specified element in + * this list, or -1 if this list does not contain the element + */ + public int lastIndexOf(Object o) { + int index = size; + if (o == null) { + for (Node x = last; x != null; x = x.prev) { + index--; + if (x.item == null) + return index; + } + } else { + for (Node x = last; x != null; x = x.prev) { + index--; + if (o.equals(x.item)) + return index; + } + } + return -1; + } + + // Queue operations. + + /** + * Retrieves, but does not remove, the head (first element) of this list. + * + * @return the head of this list, or {@code null} if this list is empty + * @since 1.5 + */ + public E peek() { + final Node f = first; + return (f == null) ? null : f.item; + } + + /** + * Retrieves, but does not remove, the head (first element) of this list. + * + * @return the head of this list + * @throws NoSuchElementException if this list is empty + * @since 1.5 + */ + public E element() { + return getFirst(); + } + + /** + * Retrieves and removes the head (first element) of this list. + * + * @return the head of this list, or {@code null} if this list is empty + * @since 1.5 + */ + public E poll() { + final Node f = first; + return (f == null) ? null : unlinkFirst(f); + } + + /** + * Retrieves and removes the head (first element) of this list. + * + * @return the head of this list + * @throws NoSuchElementException if this list is empty + * @since 1.5 + */ + public E remove() { + return removeFirst(); + } + + /** + * Adds the specified element as the tail (last element) of this list. + * + * @param e the element to add + * @return {@code true} (as specified by {@link Queue#offer}) + * @since 1.5 + */ + public boolean offer(E e) { + return add(e); + } + + // Deque operations + /** + * Inserts the specified element at the front of this list. + * + * @param e the element to insert + * @return {@code true} (as specified by {@link Deque#offerFirst}) + * @since 1.6 + */ + public boolean offerFirst(E e) { + addFirst(e); + return true; + } + + /** + * Inserts the specified element at the end of this list. + * + * @param e the element to insert + * @return {@code true} (as specified by {@link Deque#offerLast}) + * @since 1.6 + */ + public boolean offerLast(E e) { + addLast(e); + return true; + } + + /** + * Retrieves, but does not remove, the first element of this list, + * or returns {@code null} if this list is empty. + * + * @return the first element of this list, or {@code null} + * if this list is empty + * @since 1.6 + */ + public E peekFirst() { + final Node f = first; + return (f == null) ? null : f.item; + } + + /** + * Retrieves, but does not remove, the last element of this list, + * or returns {@code null} if this list is empty. + * + * @return the last element of this list, or {@code null} + * if this list is empty + * @since 1.6 + */ + public E peekLast() { + final Node l = last; + return (l == null) ? null : l.item; + } + + /** + * Retrieves and removes the first element of this list, + * or returns {@code null} if this list is empty. + * + * @return the first element of this list, or {@code null} if + * this list is empty + * @since 1.6 + */ + public E pollFirst() { + final Node f = first; + return (f == null) ? null : unlinkFirst(f); + } + + /** + * Retrieves and removes the last element of this list, + * or returns {@code null} if this list is empty. + * + * @return the last element of this list, or {@code null} if + * this list is empty + * @since 1.6 + */ + public E pollLast() { + final Node l = last; + return (l == null) ? null : unlinkLast(l); + } + + /** + * Pushes an element onto the stack represented by this list. In other + * words, inserts the element at the front of this list. + * + *

This method is equivalent to {@link #addFirst}. + * + * @param e the element to push + * @since 1.6 + */ + public void push(E e) { + addFirst(e); + } + + /** + * Pops an element from the stack represented by this list. In other + * words, removes and returns the first element of this list. + * + *

This method is equivalent to {@link #removeFirst()}. + * + * @return the element at the front of this list (which is the top + * of the stack represented by this list) + * @throws NoSuchElementException if this list is empty + * @since 1.6 + */ + public E pop() { + return removeFirst(); + } + + /** + * Removes the first occurrence of the specified element in this + * list (when traversing the list from head to tail). If the list + * does not contain the element, it is unchanged. + * + * @param o element to be removed from this list, if present + * @return {@code true} if the list contained the specified element + * @since 1.6 + */ + public boolean removeFirstOccurrence(Object o) { + return remove(o); + } + + /** + * Removes the last occurrence of the specified element in this + * list (when traversing the list from head to tail). If the list + * does not contain the element, it is unchanged. + * + * @param o element to be removed from this list, if present + * @return {@code true} if the list contained the specified element + * @since 1.6 + */ + public boolean removeLastOccurrence(Object o) { + if (o == null) { + for (Node x = last; x != null; x = x.prev) { + if (x.item == null) { + unlink(x); + return true; + } + } + } else { + for (Node x = last; x != null; x = x.prev) { + if (o.equals(x.item)) { + unlink(x); + return true; + } + } + } + return false; + } + + /** + * Returns a list-iterator of the elements in this list (in proper + * sequence), starting at the specified position in the list. + * Obeys the general contract of {@code List.listIterator(int)}.

+ * + * The list-iterator is fail-fast: if the list is structurally + * modified at any time after the Iterator is created, in any way except + * through the list-iterator's own {@code remove} or {@code add} + * methods, the list-iterator will throw a + * {@code ConcurrentModificationException}. Thus, in the face of + * concurrent modification, the iterator fails quickly and cleanly, rather + * than risking arbitrary, non-deterministic behavior at an undetermined + * time in the future. + * + * @param index index of the first element to be returned from the + * list-iterator (by a call to {@code next}) + * @return a ListIterator of the elements in this list (in proper + * sequence), starting at the specified position in the list + * @throws IndexOutOfBoundsException {@inheritDoc} + * @see List#listIterator(int) + */ + public ListIterator listIterator(int index) { + checkPositionIndex(index); + return new ListItr(index); + } + + private class ListItr implements ListIterator { + private Node lastReturned = null; + private Node next; + private int nextIndex; + private int expectedModCount = modCount; + + ListItr(int index) { + // assert isPositionIndex(index); + next = (index == size) ? null : node(index); + nextIndex = index; + } + + public boolean hasNext() { + return nextIndex < size; + } + + public E next() { + checkForComodification(); + if (!hasNext()) + throw new NoSuchElementException(); + + lastReturned = next; + next = next.next; + nextIndex++; + return lastReturned.item; + } + + public boolean hasPrevious() { + return nextIndex > 0; + } + + public E previous() { + checkForComodification(); + if (!hasPrevious()) + throw new NoSuchElementException(); + + lastReturned = next = (next == null) ? last : next.prev; + nextIndex--; + return lastReturned.item; + } + + public int nextIndex() { + return nextIndex; + } + + public int previousIndex() { + return nextIndex - 1; + } + + public void remove() { + checkForComodification(); + if (lastReturned == null) + throw new IllegalStateException(); + + Node lastNext = lastReturned.next; + unlink(lastReturned); + if (next == lastReturned) + next = lastNext; + else + nextIndex--; + lastReturned = null; + expectedModCount++; + } + + public void set(E e) { + if (lastReturned == null) + throw new IllegalStateException(); + checkForComodification(); + lastReturned.item = e; + } + + public void add(E e) { + checkForComodification(); + lastReturned = null; + if (next == null) + linkLast(e); + else + linkBefore(e, next); + nextIndex++; + expectedModCount++; + } + + final void checkForComodification() { + if (modCount != expectedModCount) + throw new ConcurrentModificationException(); + } + } + + private static class Node { + E item; + Node next; + Node prev; + + Node(Node prev, E element, Node next) { + this.item = element; + this.next = next; + this.prev = prev; + } + } + + /** + * @since 1.6 + */ + public Iterator descendingIterator() { + return new DescendingIterator(); + } + + /** + * Adapter to provide descending iterators via ListItr.previous + */ + private class DescendingIterator implements Iterator { + private final ListItr itr = new ListItr(size()); + public boolean hasNext() { + return itr.hasPrevious(); + } + public E next() { + return itr.previous(); + } + public void remove() { + itr.remove(); + } + } + + @SuppressWarnings("unchecked") + private LinkedList superClone() { + try { + return (LinkedList) super.clone(); + } catch (CloneNotSupportedException e) { + throw new InternalError(); + } + } + + /** + * Returns a shallow copy of this {@code LinkedList}. (The elements + * themselves are not cloned.) + * + * @return a shallow copy of this {@code LinkedList} instance + */ + public Object clone() { + LinkedList clone = superClone(); + + // Put clone into "virgin" state + clone.first = clone.last = null; + clone.size = 0; + clone.modCount = 0; + + // Initialize clone with our elements + for (Node x = first; x != null; x = x.next) + clone.add(x.item); + + return clone; + } + + /** + * Returns an array containing all of the elements in this list + * in proper sequence (from first to last element). + * + *

The returned array will be "safe" in that no references to it are + * maintained by this list. (In other words, this method must allocate + * a new array). The caller is thus free to modify the returned array. + * + *

This method acts as bridge between array-based and collection-based + * APIs. + * + * @return an array containing all of the elements in this list + * in proper sequence + */ + public Object[] toArray() { + Object[] result = new Object[size]; + int i = 0; + for (Node x = first; x != null; x = x.next) + result[i++] = x.item; + return result; + } + + /** + * Returns an array containing all of the elements in this list in + * proper sequence (from first to last element); the runtime type of + * the returned array is that of the specified array. If the list fits + * in the specified array, it is returned therein. Otherwise, a new + * array is allocated with the runtime type of the specified array and + * the size of this list. + * + *

If the list fits in the specified array with room to spare (i.e., + * the array has more elements than the list), the element in the array + * immediately following the end of the list is set to {@code null}. + * (This is useful in determining the length of the list only if + * the caller knows that the list does not contain any null elements.) + * + *

Like the {@link #toArray()} method, this method acts as bridge between + * array-based and collection-based APIs. Further, this method allows + * precise control over the runtime type of the output array, and may, + * under certain circumstances, be used to save allocation costs. + * + *

Suppose {@code x} is a list known to contain only strings. + * The following code can be used to dump the list into a newly + * allocated array of {@code String}: + * + *

+     *     String[] y = x.toArray(new String[0]);
+ * + * Note that {@code toArray(new Object[0])} is identical in function to + * {@code toArray()}. + * + * @param a the array into which the elements of the list are to + * be stored, if it is big enough; otherwise, a new array of the + * same runtime type is allocated for this purpose. + * @return an array containing the elements of the list + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in + * this list + * @throws NullPointerException if the specified array is null + */ + @SuppressWarnings("unchecked") + public T[] toArray(T[] a) { + if (a.length < size) + a = (T[])java.lang.reflect.Array.newInstance( + a.getClass().getComponentType(), size); + int i = 0; + Object[] result = a; + for (Node x = first; x != null; x = x.next) + result[i++] = x.item; + + if (a.length > size) + a[size] = null; + + return a; + } + + private static final long serialVersionUID = 876323262645176354L; + +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/PriorityQueue.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/PriorityQueue.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,731 @@ +/* + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util; + + +/** + * An unbounded priority {@linkplain Queue queue} based on a priority heap. + * The elements of the priority queue are ordered according to their + * {@linkplain Comparable natural ordering}, or by a {@link Comparator} + * provided at queue construction time, depending on which constructor is + * used. A priority queue does not permit {@code null} elements. + * A priority queue relying on natural ordering also does not permit + * insertion of non-comparable objects (doing so may result in + * {@code ClassCastException}). + * + *

The head of this queue is the least element + * with respect to the specified ordering. If multiple elements are + * tied for least value, the head is one of those elements -- ties are + * broken arbitrarily. The queue retrieval operations {@code poll}, + * {@code remove}, {@code peek}, and {@code element} access the + * element at the head of the queue. + * + *

A priority queue is unbounded, but has an internal + * capacity governing the size of an array used to store the + * elements on the queue. It is always at least as large as the queue + * size. As elements are added to a priority queue, its capacity + * grows automatically. The details of the growth policy are not + * specified. + * + *

This class and its iterator implement all of the + * optional methods of the {@link Collection} and {@link + * Iterator} interfaces. The Iterator provided in method {@link + * #iterator()} is not guaranteed to traverse the elements of + * the priority queue in any particular order. If you need ordered + * traversal, consider using {@code Arrays.sort(pq.toArray())}. + * + *

Note that this implementation is not synchronized. + * Multiple threads should not access a {@code PriorityQueue} + * instance concurrently if any of the threads modifies the queue. + * Instead, use the thread-safe {@link + * java.util.concurrent.PriorityBlockingQueue} class. + * + *

Implementation note: this implementation provides + * O(log(n)) time for the enqueing and dequeing methods + * ({@code offer}, {@code poll}, {@code remove()} and {@code add}); + * linear time for the {@code remove(Object)} and {@code contains(Object)} + * methods; and constant time for the retrieval methods + * ({@code peek}, {@code element}, and {@code size}). + * + *

This class is a member of the + * + * Java Collections Framework. + * + * @since 1.5 + * @author Josh Bloch, Doug Lea + * @param the type of elements held in this collection + */ +public class PriorityQueue extends AbstractQueue + implements java.io.Serializable { + + private static final long serialVersionUID = -7720805057305804111L; + + private static final int DEFAULT_INITIAL_CAPACITY = 11; + + /** + * Priority queue represented as a balanced binary heap: the two + * children of queue[n] are queue[2*n+1] and queue[2*(n+1)]. The + * priority queue is ordered by comparator, or by the elements' + * natural ordering, if comparator is null: For each node n in the + * heap and each descendant d of n, n <= d. The element with the + * lowest value is in queue[0], assuming the queue is nonempty. + */ + private transient Object[] queue; + + /** + * The number of elements in the priority queue. + */ + private int size = 0; + + /** + * The comparator, or null if priority queue uses elements' + * natural ordering. + */ + private final Comparator comparator; + + /** + * The number of times this priority queue has been + * structurally modified. See AbstractList for gory details. + */ + private transient int modCount = 0; + + /** + * Creates a {@code PriorityQueue} with the default initial + * capacity (11) that orders its elements according to their + * {@linkplain Comparable natural ordering}. + */ + public PriorityQueue() { + this(DEFAULT_INITIAL_CAPACITY, null); + } + + /** + * Creates a {@code PriorityQueue} with the specified initial + * capacity that orders its elements according to their + * {@linkplain Comparable natural ordering}. + * + * @param initialCapacity the initial capacity for this priority queue + * @throws IllegalArgumentException if {@code initialCapacity} is less + * than 1 + */ + public PriorityQueue(int initialCapacity) { + this(initialCapacity, null); + } + + /** + * Creates a {@code PriorityQueue} with the specified initial capacity + * that orders its elements according to the specified comparator. + * + * @param initialCapacity the initial capacity for this priority queue + * @param comparator the comparator that will be used to order this + * priority queue. If {@code null}, the {@linkplain Comparable + * natural ordering} of the elements will be used. + * @throws IllegalArgumentException if {@code initialCapacity} is + * less than 1 + */ + public PriorityQueue(int initialCapacity, + Comparator comparator) { + // Note: This restriction of at least one is not actually needed, + // but continues for 1.5 compatibility + if (initialCapacity < 1) + throw new IllegalArgumentException(); + this.queue = new Object[initialCapacity]; + this.comparator = comparator; + } + + /** + * Creates a {@code PriorityQueue} containing the elements in the + * specified collection. If the specified collection is an instance of + * a {@link SortedSet} or is another {@code PriorityQueue}, this + * priority queue will be ordered according to the same ordering. + * Otherwise, this priority queue will be ordered according to the + * {@linkplain Comparable natural ordering} of its elements. + * + * @param c the collection whose elements are to be placed + * into this priority queue + * @throws ClassCastException if elements of the specified collection + * cannot be compared to one another according to the priority + * queue's ordering + * @throws NullPointerException if the specified collection or any + * of its elements are null + */ + @SuppressWarnings("unchecked") + public PriorityQueue(Collection c) { + if (c instanceof SortedSet) { + SortedSet ss = (SortedSet) c; + this.comparator = (Comparator) ss.comparator(); + initElementsFromCollection(ss); + } + else if (c instanceof PriorityQueue) { + PriorityQueue pq = (PriorityQueue) c; + this.comparator = (Comparator) pq.comparator(); + initFromPriorityQueue(pq); + } + else { + this.comparator = null; + initFromCollection(c); + } + } + + /** + * Creates a {@code PriorityQueue} containing the elements in the + * specified priority queue. This priority queue will be + * ordered according to the same ordering as the given priority + * queue. + * + * @param c the priority queue whose elements are to be placed + * into this priority queue + * @throws ClassCastException if elements of {@code c} cannot be + * compared to one another according to {@code c}'s + * ordering + * @throws NullPointerException if the specified priority queue or any + * of its elements are null + */ + @SuppressWarnings("unchecked") + public PriorityQueue(PriorityQueue c) { + this.comparator = (Comparator) c.comparator(); + initFromPriorityQueue(c); + } + + /** + * Creates a {@code PriorityQueue} containing the elements in the + * specified sorted set. This priority queue will be ordered + * according to the same ordering as the given sorted set. + * + * @param c the sorted set whose elements are to be placed + * into this priority queue + * @throws ClassCastException if elements of the specified sorted + * set cannot be compared to one another according to the + * sorted set's ordering + * @throws NullPointerException if the specified sorted set or any + * of its elements are null + */ + @SuppressWarnings("unchecked") + public PriorityQueue(SortedSet c) { + this.comparator = (Comparator) c.comparator(); + initElementsFromCollection(c); + } + + private void initFromPriorityQueue(PriorityQueue c) { + if (c.getClass() == PriorityQueue.class) { + this.queue = c.toArray(); + this.size = c.size(); + } else { + initFromCollection(c); + } + } + + private void initElementsFromCollection(Collection c) { + Object[] a = c.toArray(); + // If c.toArray incorrectly doesn't return Object[], copy it. + if (a.getClass() != Object[].class) + a = Arrays.copyOf(a, a.length, Object[].class); + int len = a.length; + if (len == 1 || this.comparator != null) + for (int i = 0; i < len; i++) + if (a[i] == null) + throw new NullPointerException(); + this.queue = a; + this.size = a.length; + } + + /** + * Initializes queue array with elements from the given Collection. + * + * @param c the collection + */ + private void initFromCollection(Collection c) { + initElementsFromCollection(c); + heapify(); + } + + /** + * The maximum size of array to allocate. + * Some VMs reserve some header words in an array. + * Attempts to allocate larger arrays may result in + * OutOfMemoryError: Requested array size exceeds VM limit + */ + private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + + /** + * Increases the capacity of the array. + * + * @param minCapacity the desired minimum capacity + */ + private void grow(int minCapacity) { + int oldCapacity = queue.length; + // Double size if small; else grow by 50% + int newCapacity = oldCapacity + ((oldCapacity < 64) ? + (oldCapacity + 2) : + (oldCapacity >> 1)); + // overflow-conscious code + if (newCapacity - MAX_ARRAY_SIZE > 0) + newCapacity = hugeCapacity(minCapacity); + queue = Arrays.copyOf(queue, newCapacity); + } + + private static int hugeCapacity(int minCapacity) { + if (minCapacity < 0) // overflow + throw new OutOfMemoryError(); + return (minCapacity > MAX_ARRAY_SIZE) ? + Integer.MAX_VALUE : + MAX_ARRAY_SIZE; + } + + /** + * Inserts the specified element into this priority queue. + * + * @return {@code true} (as specified by {@link Collection#add}) + * @throws ClassCastException if the specified element cannot be + * compared with elements currently in this priority queue + * according to the priority queue's ordering + * @throws NullPointerException if the specified element is null + */ + public boolean add(E e) { + return offer(e); + } + + /** + * Inserts the specified element into this priority queue. + * + * @return {@code true} (as specified by {@link Queue#offer}) + * @throws ClassCastException if the specified element cannot be + * compared with elements currently in this priority queue + * according to the priority queue's ordering + * @throws NullPointerException if the specified element is null + */ + public boolean offer(E e) { + if (e == null) + throw new NullPointerException(); + modCount++; + int i = size; + if (i >= queue.length) + grow(i + 1); + size = i + 1; + if (i == 0) + queue[0] = e; + else + siftUp(i, e); + return true; + } + + public E peek() { + if (size == 0) + return null; + return (E) queue[0]; + } + + private int indexOf(Object o) { + if (o != null) { + for (int i = 0; i < size; i++) + if (o.equals(queue[i])) + return i; + } + return -1; + } + + /** + * Removes a single instance of the specified element from this queue, + * if it is present. More formally, removes an element {@code e} such + * that {@code o.equals(e)}, if this queue contains one or more such + * elements. Returns {@code true} if and only if this queue contained + * the specified element (or equivalently, if this queue changed as a + * result of the call). + * + * @param o element to be removed from this queue, if present + * @return {@code true} if this queue changed as a result of the call + */ + public boolean remove(Object o) { + int i = indexOf(o); + if (i == -1) + return false; + else { + removeAt(i); + return true; + } + } + + /** + * Version of remove using reference equality, not equals. + * Needed by iterator.remove. + * + * @param o element to be removed from this queue, if present + * @return {@code true} if removed + */ + boolean removeEq(Object o) { + for (int i = 0; i < size; i++) { + if (o == queue[i]) { + removeAt(i); + return true; + } + } + return false; + } + + /** + * Returns {@code true} if this queue contains the specified element. + * More formally, returns {@code true} if and only if this queue contains + * at least one element {@code e} such that {@code o.equals(e)}. + * + * @param o object to be checked for containment in this queue + * @return {@code true} if this queue contains the specified element + */ + public boolean contains(Object o) { + return indexOf(o) != -1; + } + + /** + * Returns an array containing all of the elements in this queue. + * The elements are in no particular order. + * + *

The returned array will be "safe" in that no references to it are + * maintained by this queue. (In other words, this method must allocate + * a new array). The caller is thus free to modify the returned array. + * + *

This method acts as bridge between array-based and collection-based + * APIs. + * + * @return an array containing all of the elements in this queue + */ + public Object[] toArray() { + return Arrays.copyOf(queue, size); + } + + /** + * Returns an array containing all of the elements in this queue; the + * runtime type of the returned array is that of the specified array. + * The returned array elements are in no particular order. + * If the queue fits in the specified array, it is returned therein. + * Otherwise, a new array is allocated with the runtime type of the + * specified array and the size of this queue. + * + *

If the queue fits in the specified array with room to spare + * (i.e., the array has more elements than the queue), the element in + * the array immediately following the end of the collection is set to + * {@code null}. + * + *

Like the {@link #toArray()} method, this method acts as bridge between + * array-based and collection-based APIs. Further, this method allows + * precise control over the runtime type of the output array, and may, + * under certain circumstances, be used to save allocation costs. + * + *

Suppose x is a queue known to contain only strings. + * The following code can be used to dump the queue into a newly + * allocated array of String: + * + *

+     *     String[] y = x.toArray(new String[0]);
+ * + * Note that toArray(new Object[0]) is identical in function to + * toArray(). + * + * @param a the array into which the elements of the queue are to + * be stored, if it is big enough; otherwise, a new array of the + * same runtime type is allocated for this purpose. + * @return an array containing all of the elements in this queue + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in + * this queue + * @throws NullPointerException if the specified array is null + */ + public T[] toArray(T[] a) { + if (a.length < size) + // Make a new array of a's runtime type, but my contents: + return (T[]) Arrays.copyOf(queue, size, a.getClass()); + System.arraycopy(queue, 0, a, 0, size); + if (a.length > size) + a[size] = null; + return a; + } + + /** + * Returns an iterator over the elements in this queue. The iterator + * does not return the elements in any particular order. + * + * @return an iterator over the elements in this queue + */ + public Iterator iterator() { + return new Itr(); + } + + private final class Itr implements Iterator { + /** + * Index (into queue array) of element to be returned by + * subsequent call to next. + */ + private int cursor = 0; + + /** + * Index of element returned by most recent call to next, + * unless that element came from the forgetMeNot list. + * Set to -1 if element is deleted by a call to remove. + */ + private int lastRet = -1; + + /** + * A queue of elements that were moved from the unvisited portion of + * the heap into the visited portion as a result of "unlucky" element + * removals during the iteration. (Unlucky element removals are those + * that require a siftup instead of a siftdown.) We must visit all of + * the elements in this list to complete the iteration. We do this + * after we've completed the "normal" iteration. + * + * We expect that most iterations, even those involving removals, + * will not need to store elements in this field. + */ + private ArrayDeque forgetMeNot = null; + + /** + * Element returned by the most recent call to next iff that + * element was drawn from the forgetMeNot list. + */ + private E lastRetElt = null; + + /** + * The modCount value that the iterator believes that the backing + * Queue should have. If this expectation is violated, the iterator + * has detected concurrent modification. + */ + private int expectedModCount = modCount; + + public boolean hasNext() { + return cursor < size || + (forgetMeNot != null && !forgetMeNot.isEmpty()); + } + + public E next() { + if (expectedModCount != modCount) + throw new ConcurrentModificationException(); + if (cursor < size) + return (E) queue[lastRet = cursor++]; + if (forgetMeNot != null) { + lastRet = -1; + lastRetElt = forgetMeNot.poll(); + if (lastRetElt != null) + return lastRetElt; + } + throw new NoSuchElementException(); + } + + public void remove() { + if (expectedModCount != modCount) + throw new ConcurrentModificationException(); + if (lastRet != -1) { + E moved = PriorityQueue.this.removeAt(lastRet); + lastRet = -1; + if (moved == null) + cursor--; + else { + if (forgetMeNot == null) + forgetMeNot = new ArrayDeque<>(); + forgetMeNot.add(moved); + } + } else if (lastRetElt != null) { + PriorityQueue.this.removeEq(lastRetElt); + lastRetElt = null; + } else { + throw new IllegalStateException(); + } + expectedModCount = modCount; + } + } + + public int size() { + return size; + } + + /** + * Removes all of the elements from this priority queue. + * The queue will be empty after this call returns. + */ + public void clear() { + modCount++; + for (int i = 0; i < size; i++) + queue[i] = null; + size = 0; + } + + public E poll() { + if (size == 0) + return null; + int s = --size; + modCount++; + E result = (E) queue[0]; + E x = (E) queue[s]; + queue[s] = null; + if (s != 0) + siftDown(0, x); + return result; + } + + /** + * Removes the ith element from queue. + * + * Normally this method leaves the elements at up to i-1, + * inclusive, untouched. Under these circumstances, it returns + * null. Occasionally, in order to maintain the heap invariant, + * it must swap a later element of the list with one earlier than + * i. Under these circumstances, this method returns the element + * that was previously at the end of the list and is now at some + * position before i. This fact is used by iterator.remove so as to + * avoid missing traversing elements. + */ + private E removeAt(int i) { + assert i >= 0 && i < size; + modCount++; + int s = --size; + if (s == i) // removed last element + queue[i] = null; + else { + E moved = (E) queue[s]; + queue[s] = null; + siftDown(i, moved); + if (queue[i] == moved) { + siftUp(i, moved); + if (queue[i] != moved) + return moved; + } + } + return null; + } + + /** + * Inserts item x at position k, maintaining heap invariant by + * promoting x up the tree until it is greater than or equal to + * its parent, or is the root. + * + * To simplify and speed up coercions and comparisons. the + * Comparable and Comparator versions are separated into different + * methods that are otherwise identical. (Similarly for siftDown.) + * + * @param k the position to fill + * @param x the item to insert + */ + private void siftUp(int k, E x) { + if (comparator != null) + siftUpUsingComparator(k, x); + else + siftUpComparable(k, x); + } + + private void siftUpComparable(int k, E x) { + Comparable key = (Comparable) x; + while (k > 0) { + int parent = (k - 1) >>> 1; + Object e = queue[parent]; + if (key.compareTo((E) e) >= 0) + break; + queue[k] = e; + k = parent; + } + queue[k] = key; + } + + private void siftUpUsingComparator(int k, E x) { + while (k > 0) { + int parent = (k - 1) >>> 1; + Object e = queue[parent]; + if (comparator.compare(x, (E) e) >= 0) + break; + queue[k] = e; + k = parent; + } + queue[k] = x; + } + + /** + * Inserts item x at position k, maintaining heap invariant by + * demoting x down the tree repeatedly until it is less than or + * equal to its children or is a leaf. + * + * @param k the position to fill + * @param x the item to insert + */ + private void siftDown(int k, E x) { + if (comparator != null) + siftDownUsingComparator(k, x); + else + siftDownComparable(k, x); + } + + private void siftDownComparable(int k, E x) { + Comparable key = (Comparable)x; + int half = size >>> 1; // loop while a non-leaf + while (k < half) { + int child = (k << 1) + 1; // assume left child is least + Object c = queue[child]; + int right = child + 1; + if (right < size && + ((Comparable) c).compareTo((E) queue[right]) > 0) + c = queue[child = right]; + if (key.compareTo((E) c) <= 0) + break; + queue[k] = c; + k = child; + } + queue[k] = key; + } + + private void siftDownUsingComparator(int k, E x) { + int half = size >>> 1; + while (k < half) { + int child = (k << 1) + 1; + Object c = queue[child]; + int right = child + 1; + if (right < size && + comparator.compare((E) c, (E) queue[right]) > 0) + c = queue[child = right]; + if (comparator.compare(x, (E) c) <= 0) + break; + queue[k] = c; + k = child; + } + queue[k] = x; + } + + /** + * Establishes the heap invariant (described above) in the entire tree, + * assuming nothing about the order of the elements prior to the call. + */ + private void heapify() { + for (int i = (size >>> 1) - 1; i >= 0; i--) + siftDown(i, (E) queue[i]); + } + + /** + * Returns the comparator used to order the elements in this + * queue, or {@code null} if this queue is sorted according to + * the {@linkplain Comparable natural ordering} of its elements. + * + * @return the comparator used to order this queue, or + * {@code null} if this queue is sorted according to the + * natural ordering of its elements + */ + public Comparator comparator() { + return comparator; + } + + +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/Queue.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/Queue.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,218 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +package java.util; + +/** + * A collection designed for holding elements prior to processing. + * Besides basic {@link java.util.Collection Collection} operations, + * queues provide additional insertion, extraction, and inspection + * operations. Each of these methods exists in two forms: one throws + * an exception if the operation fails, the other returns a special + * value (either null or false, depending on the + * operation). The latter form of the insert operation is designed + * specifically for use with capacity-restricted Queue + * implementations; in most implementations, insert operations cannot + * fail. + * + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Throws exceptionReturns special value
Insert{@link #add add(e)}{@link #offer offer(e)}
Remove{@link #remove remove()}{@link #poll poll()}
Examine{@link #element element()}{@link #peek peek()}
+ * + *

Queues typically, but do not necessarily, order elements in a + * FIFO (first-in-first-out) manner. Among the exceptions are + * priority queues, which order elements according to a supplied + * comparator, or the elements' natural ordering, and LIFO queues (or + * stacks) which order the elements LIFO (last-in-first-out). + * Whatever the ordering used, the head of the queue is that + * element which would be removed by a call to {@link #remove() } or + * {@link #poll()}. In a FIFO queue, all new elements are inserted at + * the tail of the queue. Other kinds of queues may use + * different placement rules. Every Queue implementation + * must specify its ordering properties. + * + *

The {@link #offer offer} method inserts an element if possible, + * otherwise returning false. This differs from the {@link + * java.util.Collection#add Collection.add} method, which can fail to + * add an element only by throwing an unchecked exception. The + * offer method is designed for use when failure is a normal, + * rather than exceptional occurrence, for example, in fixed-capacity + * (or "bounded") queues. + * + *

The {@link #remove()} and {@link #poll()} methods remove and + * return the head of the queue. + * Exactly which element is removed from the queue is a + * function of the queue's ordering policy, which differs from + * implementation to implementation. The remove() and + * poll() methods differ only in their behavior when the + * queue is empty: the remove() method throws an exception, + * while the poll() method returns null. + * + *

The {@link #element()} and {@link #peek()} methods return, but do + * not remove, the head of the queue. + * + *

The Queue interface does not define the blocking queue + * methods, which are common in concurrent programming. These methods, + * which wait for elements to appear or for space to become available, are + * defined in the {@link java.util.concurrent.BlockingQueue} interface, which + * extends this interface. + * + *

Queue implementations generally do not allow insertion + * of null elements, although some implementations, such as + * {@link LinkedList}, do not prohibit insertion of null. + * Even in the implementations that permit it, null should + * not be inserted into a Queue, as null is also + * used as a special return value by the poll method to + * indicate that the queue contains no elements. + * + *

Queue implementations generally do not define + * element-based versions of methods equals and + * hashCode but instead inherit the identity based versions + * from class Object, because element-based equality is not + * always well-defined for queues with the same elements but different + * ordering properties. + * + * + *

This interface is a member of the + * + * Java Collections Framework. + * + * @see java.util.Collection + * @see LinkedList + * @see PriorityQueue + * @see java.util.concurrent.LinkedBlockingQueue + * @see java.util.concurrent.BlockingQueue + * @see java.util.concurrent.ArrayBlockingQueue + * @see java.util.concurrent.LinkedBlockingQueue + * @see java.util.concurrent.PriorityBlockingQueue + * @since 1.5 + * @author Doug Lea + * @param the type of elements held in this collection + */ +public interface Queue extends Collection { + /** + * Inserts the specified element into this queue if it is possible to do so + * immediately without violating capacity restrictions, returning + * true upon success and throwing an IllegalStateException + * if no space is currently available. + * + * @param e the element to add + * @return true (as specified by {@link Collection#add}) + * @throws IllegalStateException if the element cannot be added at this + * time due to capacity restrictions + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this queue + * @throws NullPointerException if the specified element is null and + * this queue does not permit null elements + * @throws IllegalArgumentException if some property of this element + * prevents it from being added to this queue + */ + boolean add(E e); + + /** + * Inserts the specified element into this queue if it is possible to do + * so immediately without violating capacity restrictions. + * When using a capacity-restricted queue, this method is generally + * preferable to {@link #add}, which can fail to insert an element only + * by throwing an exception. + * + * @param e the element to add + * @return true if the element was added to this queue, else + * false + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this queue + * @throws NullPointerException if the specified element is null and + * this queue does not permit null elements + * @throws IllegalArgumentException if some property of this element + * prevents it from being added to this queue + */ + boolean offer(E e); + + /** + * Retrieves and removes the head of this queue. This method differs + * from {@link #poll poll} only in that it throws an exception if this + * queue is empty. + * + * @return the head of this queue + * @throws NoSuchElementException if this queue is empty + */ + E remove(); + + /** + * Retrieves and removes the head of this queue, + * or returns null if this queue is empty. + * + * @return the head of this queue, or null if this queue is empty + */ + E poll(); + + /** + * Retrieves, but does not remove, the head of this queue. This method + * differs from {@link #peek peek} only in that it throws an exception + * if this queue is empty. + * + * @return the head of this queue + * @throws NoSuchElementException if this queue is empty + */ + E element(); + + /** + * Retrieves, but does not remove, the head of this queue, + * or returns null if this queue is empty. + * + * @return the head of this queue, or null if this queue is empty + */ + E peek(); +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/Random.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/Random.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,503 @@ +/* + * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util; + +import org.apidesign.bck2brwsr.emul.lang.System; + +/** + * An instance of this class is used to generate a stream of + * pseudorandom numbers. The class uses a 48-bit seed, which is + * modified using a linear congruential formula. (See Donald Knuth, + * The Art of Computer Programming, Volume 2, Section 3.2.1.) + *

+ * If two instances of {@code Random} are created with the same + * seed, and the same sequence of method calls is made for each, they + * will generate and return identical sequences of numbers. In order to + * guarantee this property, particular algorithms are specified for the + * class {@code Random}. Java implementations must use all the algorithms + * shown here for the class {@code Random}, for the sake of absolute + * portability of Java code. However, subclasses of class {@code Random} + * are permitted to use other algorithms, so long as they adhere to the + * general contracts for all the methods. + *

+ * The algorithms implemented by class {@code Random} use a + * {@code protected} utility method that on each invocation can supply + * up to 32 pseudorandomly generated bits. + *

+ * Many applications will find the method {@link Math#random} simpler to use. + * + *

Instances of {@code java.util.Random} are threadsafe. + * However, the concurrent use of the same {@code java.util.Random} + * instance across threads may encounter contention and consequent + * poor performance. Consider instead using + * {@link java.util.concurrent.ThreadLocalRandom} in multithreaded + * designs. + * + *

Instances of {@code java.util.Random} are not cryptographically + * secure. Consider instead using {@link java.security.SecureRandom} to + * get a cryptographically secure pseudo-random number generator for use + * by security-sensitive applications. + * + * @author Frank Yellin + * @since 1.0 + */ +public +class Random implements java.io.Serializable { + /** use serialVersionUID from JDK 1.1 for interoperability */ + static final long serialVersionUID = 3905348978240129619L; + + /** + * The internal state associated with this pseudorandom number generator. + * (The specs for the methods in this class describe the ongoing + * computation of this value.) + */ + private long seed; + + private static final long multiplier = 0x5DEECE66DL; + private static final long addend = 0xBL; + private static final long mask = (1L << 48) - 1; + + /** + * Creates a new random number generator. This constructor sets + * the seed of the random number generator to a value very likely + * to be distinct from any other invocation of this constructor. + */ + public Random() { + this(seedUniquifier() ^ System.nanoTime()); + } + + private static synchronized long seedUniquifier() { + // L'Ecuyer, "Tables of Linear Congruential Generators of + // Different Sizes and Good Lattice Structure", 1999 + long current = seedUniquifier; + long next = current * 181783497276652981L; + seedUniquifier = next; + return next; + } + + private static long seedUniquifier = 8682522807148012L; + + /** + * Creates a new random number generator using a single {@code long} seed. + * The seed is the initial value of the internal state of the pseudorandom + * number generator which is maintained by method {@link #next}. + * + *

The invocation {@code new Random(seed)} is equivalent to: + *

 {@code
+     * Random rnd = new Random();
+     * rnd.setSeed(seed);}
+ * + * @param seed the initial seed + * @see #setSeed(long) + */ + public Random(long seed) { + this.seed = initialScramble(seed); + } + + private static long initialScramble(long seed) { + return (seed ^ multiplier) & mask; + } + + /** + * Sets the seed of this random number generator using a single + * {@code long} seed. The general contract of {@code setSeed} is + * that it alters the state of this random number generator object + * so as to be in exactly the same state as if it had just been + * created with the argument {@code seed} as a seed. The method + * {@code setSeed} is implemented by class {@code Random} by + * atomically updating the seed to + *
{@code (seed ^ 0x5DEECE66DL) & ((1L << 48) - 1)}
+ * and clearing the {@code haveNextNextGaussian} flag used by {@link + * #nextGaussian}. + * + *

The implementation of {@code setSeed} by class {@code Random} + * happens to use only 48 bits of the given seed. In general, however, + * an overriding method may use all 64 bits of the {@code long} + * argument as a seed value. + * + * @param seed the initial seed + */ + synchronized public void setSeed(long seed) { + this.seed = initialScramble(seed); + haveNextNextGaussian = false; + } + + /** + * Generates the next pseudorandom number. Subclasses should + * override this, as this is used by all other methods. + * + *

The general contract of {@code next} is that it returns an + * {@code int} value and if the argument {@code bits} is between + * {@code 1} and {@code 32} (inclusive), then that many low-order + * bits of the returned value will be (approximately) independently + * chosen bit values, each of which is (approximately) equally + * likely to be {@code 0} or {@code 1}. The method {@code next} is + * implemented by class {@code Random} by atomically updating the seed to + *

{@code (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)}
+ * and returning + *
{@code (int)(seed >>> (48 - bits))}.
+ * + * This is a linear congruential pseudorandom number generator, as + * defined by D. H. Lehmer and described by Donald E. Knuth in + * The Art of Computer Programming, Volume 3: + * Seminumerical Algorithms, section 3.2.1. + * + * @param bits random bits + * @return the next pseudorandom value from this random number + * generator's sequence + * @since 1.1 + */ + protected synchronized int next(int bits) { + long oldseed, nextseed; + long seed = this.seed; + oldseed = seed; + nextseed = (oldseed * multiplier + addend) & mask; + this.seed = nextseed; + return (int)(nextseed >>> (48 - bits)); + } + + /** + * Generates random bytes and places them into a user-supplied + * byte array. The number of random bytes produced is equal to + * the length of the byte array. + * + *

The method {@code nextBytes} is implemented by class {@code Random} + * as if by: + *

 {@code
+     * public void nextBytes(byte[] bytes) {
+     *   for (int i = 0; i < bytes.length; )
+     *     for (int rnd = nextInt(), n = Math.min(bytes.length - i, 4);
+     *          n-- > 0; rnd >>= 8)
+     *       bytes[i++] = (byte)rnd;
+     * }}
+ * + * @param bytes the byte array to fill with random bytes + * @throws NullPointerException if the byte array is null + * @since 1.1 + */ + public void nextBytes(byte[] bytes) { + for (int i = 0, len = bytes.length; i < len; ) + for (int rnd = nextInt(), + n = Math.min(len - i, Integer.SIZE/Byte.SIZE); + n-- > 0; rnd >>= Byte.SIZE) + bytes[i++] = (byte)rnd; + } + + /** + * Returns the next pseudorandom, uniformly distributed {@code int} + * value from this random number generator's sequence. The general + * contract of {@code nextInt} is that one {@code int} value is + * pseudorandomly generated and returned. All 232 + * possible {@code int} values are produced with + * (approximately) equal probability. + * + *

The method {@code nextInt} is implemented by class {@code Random} + * as if by: + *

 {@code
+     * public int nextInt() {
+     *   return next(32);
+     * }}
+ * + * @return the next pseudorandom, uniformly distributed {@code int} + * value from this random number generator's sequence + */ + public int nextInt() { + return next(32); + } + + /** + * Returns a pseudorandom, uniformly distributed {@code int} value + * between 0 (inclusive) and the specified value (exclusive), drawn from + * this random number generator's sequence. The general contract of + * {@code nextInt} is that one {@code int} value in the specified range + * is pseudorandomly generated and returned. All {@code n} possible + * {@code int} values are produced with (approximately) equal + * probability. The method {@code nextInt(int n)} is implemented by + * class {@code Random} as if by: + *
 {@code
+     * 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;
+     * }}
+ * + *

The hedge "approximately" is used in the foregoing description only + * because the next method is only approximately an unbiased source of + * independently chosen bits. If it were a perfect source of randomly + * chosen bits, then the algorithm shown would choose {@code int} + * values from the stated range with perfect uniformity. + *

+ * The algorithm is slightly tricky. It rejects values that would result + * in an uneven distribution (due to the fact that 2^31 is not divisible + * by n). The probability of a value being rejected depends on n. The + * worst case is n=2^30+1, for which the probability of a reject is 1/2, + * and the expected number of iterations before the loop terminates is 2. + *

+ * The algorithm treats the case where n is a power of two specially: it + * returns the correct number of high-order bits from the underlying + * pseudo-random number generator. In the absence of special treatment, + * the correct number of low-order bits would be returned. Linear + * congruential pseudo-random number generators such as the one + * implemented by this class are known to have short periods in the + * sequence of values of their low-order bits. Thus, this special case + * greatly increases the length of the sequence of values returned by + * successive calls to this method if n is a small power of two. + * + * @param n the bound on the random number to be returned. Must be + * positive. + * @return the next pseudorandom, uniformly distributed {@code int} + * value between {@code 0} (inclusive) and {@code n} (exclusive) + * from this random number generator's sequence + * @throws IllegalArgumentException if n is not positive + * @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; + } + + /** + * Returns the next pseudorandom, uniformly distributed {@code long} + * value from this random number generator's sequence. The general + * contract of {@code nextLong} is that one {@code long} value is + * pseudorandomly generated and returned. + * + *

The method {@code nextLong} is implemented by class {@code Random} + * as if by: + *

 {@code
+     * public long nextLong() {
+     *   return ((long)next(32) << 32) + next(32);
+     * }}
+ * + * Because class {@code Random} uses a seed with only 48 bits, + * this algorithm will not return all possible {@code long} values. + * + * @return the next pseudorandom, uniformly distributed {@code long} + * value from this random number generator's sequence + */ + public long nextLong() { + // it's okay that the bottom word remains signed. + return ((long)(next(32)) << 32) + next(32); + } + + /** + * Returns the next pseudorandom, uniformly distributed + * {@code boolean} value from this random number generator's + * sequence. The general contract of {@code nextBoolean} is that one + * {@code boolean} value is pseudorandomly generated and returned. The + * values {@code true} and {@code false} are produced with + * (approximately) equal probability. + * + *

The method {@code nextBoolean} is implemented by class {@code Random} + * as if by: + *

 {@code
+     * public boolean nextBoolean() {
+     *   return next(1) != 0;
+     * }}
+ * + * @return the next pseudorandom, uniformly distributed + * {@code boolean} value from this random number generator's + * sequence + * @since 1.2 + */ + public boolean nextBoolean() { + return next(1) != 0; + } + + /** + * Returns the next pseudorandom, uniformly distributed {@code float} + * value between {@code 0.0} and {@code 1.0} from this random + * number generator's sequence. + * + *

The general contract of {@code nextFloat} is that one + * {@code float} value, chosen (approximately) uniformly from the + * range {@code 0.0f} (inclusive) to {@code 1.0f} (exclusive), is + * pseudorandomly generated and returned. All 224 possible {@code float} values + * of the form m x 2-24, where m is a positive + * integer less than 224 , are + * produced with (approximately) equal probability. + * + *

The method {@code nextFloat} is implemented by class {@code Random} + * as if by: + *

 {@code
+     * public float nextFloat() {
+     *   return next(24) / ((float)(1 << 24));
+     * }}
+ * + *

The hedge "approximately" is used in the foregoing description only + * because the next method is only approximately an unbiased source of + * independently chosen bits. If it were a perfect source of randomly + * chosen bits, then the algorithm shown would choose {@code float} + * values from the stated range with perfect uniformity.

+ * [In early versions of Java, the result was incorrectly calculated as: + *

 {@code
+     *   return next(30) / ((float)(1 << 30));}
+ * This might seem to be equivalent, if not better, but in fact it + * introduced a slight nonuniformity because of the bias in the rounding + * of floating-point numbers: it was slightly more likely that the + * low-order bit of the significand would be 0 than that it would be 1.] + * + * @return the next pseudorandom, uniformly distributed {@code float} + * value between {@code 0.0} and {@code 1.0} from this + * random number generator's sequence + */ + public float nextFloat() { + return next(24) / ((float)(1 << 24)); + } + + /** + * Returns the next pseudorandom, uniformly distributed + * {@code double} value between {@code 0.0} and + * {@code 1.0} from this random number generator's sequence. + * + *

The general contract of {@code nextDouble} is that one + * {@code double} value, chosen (approximately) uniformly from the + * range {@code 0.0d} (inclusive) to {@code 1.0d} (exclusive), is + * pseudorandomly generated and returned. + * + *

The method {@code nextDouble} is implemented by class {@code Random} + * as if by: + *

 {@code
+     * public double nextDouble() {
+     *   return (((long)next(26) << 27) + next(27))
+     *     / (double)(1L << 53);
+     * }}
+ * + *

The hedge "approximately" is used in the foregoing description only + * because the {@code next} method is only approximately an unbiased + * source of independently chosen bits. If it were a perfect source of + * randomly chosen bits, then the algorithm shown would choose + * {@code double} values from the stated range with perfect uniformity. + *

[In early versions of Java, the result was incorrectly calculated as: + *

 {@code
+     *   return (((long)next(27) << 27) + next(27))
+     *     / (double)(1L << 54);}
+ * This might seem to be equivalent, if not better, but in fact it + * introduced a large nonuniformity because of the bias in the rounding + * of floating-point numbers: it was three times as likely that the + * low-order bit of the significand would be 0 than that it would be 1! + * This nonuniformity probably doesn't matter much in practice, but we + * strive for perfection.] + * + * @return the next pseudorandom, uniformly distributed {@code double} + * value between {@code 0.0} and {@code 1.0} from this + * random number generator's sequence + * @see Math#random + */ + public double nextDouble() { + return (((long)(next(26)) << 27) + next(27)) + / (double)(1L << 53); + } + + private double nextNextGaussian; + private boolean haveNextNextGaussian = false; + + /** + * Returns the next pseudorandom, Gaussian ("normally") distributed + * {@code double} value with mean {@code 0.0} and standard + * deviation {@code 1.0} from this random number generator's sequence. + *

+ * The general contract of {@code nextGaussian} is that one + * {@code double} value, chosen from (approximately) the usual + * normal distribution with mean {@code 0.0} and standard deviation + * {@code 1.0}, is pseudorandomly generated and returned. + * + *

The method {@code nextGaussian} is implemented by class + * {@code Random} as if by a threadsafe version of the following: + *

 {@code
+     * private double nextNextGaussian;
+     * private boolean haveNextNextGaussian = false;
+     *
+     * public 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 || s == 0);
+     *     double multiplier = StrictMath.sqrt(-2 * StrictMath.log(s)/s);
+     *     nextNextGaussian = v2 * multiplier;
+     *     haveNextNextGaussian = true;
+     *     return v1 * multiplier;
+     *   }
+     * }}
+ * This uses the polar method of G. E. P. Box, M. E. Muller, and + * G. Marsaglia, as described by Donald E. Knuth in The Art of + * Computer Programming, Volume 3: Seminumerical Algorithms, + * section 3.4.1, subsection C, algorithm P. Note that it generates two + * independent values at the cost of only one call to {@code StrictMath.log} + * and one call to {@code StrictMath.sqrt}. + * + * @return the next pseudorandom, Gaussian ("normally") distributed + * {@code double} value with mean {@code 0.0} and + * standard deviation {@code 1.0} from this random number + * generator's sequence + */ + synchronized public double nextGaussian() { + // See Knuth, ACP, Section 3.4.1 Algorithm C. + if (haveNextNextGaussian) { + haveNextNextGaussian = false; + return nextNextGaussian; + } else { + double v1, v2, s; + do { + v1 = 2 * nextDouble() - 1; // between -1 and 1 + v2 = 2 * nextDouble() - 1; // between -1 and 1 + s = v1 * v1 + v2 * v2; + } while (s >= 1 || s == 0); + double multiplier = Math.sqrt(-2 * Math.log(s)/s); + nextNextGaussian = v2 * multiplier; + haveNextNextGaussian = true; + return v1 * multiplier; + } + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/SortedMap.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/SortedMap.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,284 @@ +/* + * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util; + +/** + * A {@link Map} that further provides a total ordering on its keys. + * The map is ordered according to the {@linkplain Comparable natural + * ordering} of its keys, or by a {@link Comparator} typically + * provided at sorted map creation time. This order is reflected when + * iterating over the sorted map's collection views (returned by the + * {@code entrySet}, {@code keySet} and {@code values} methods). + * Several additional operations are provided to take advantage of the + * ordering. (This interface is the map analogue of {@link SortedSet}.) + * + *

All keys inserted into a sorted map must implement the {@code Comparable} + * interface (or be accepted by the specified comparator). Furthermore, all + * such keys must be mutually comparable: {@code k1.compareTo(k2)} (or + * {@code comparator.compare(k1, k2)}) must not throw a + * {@code ClassCastException} for any keys {@code k1} and {@code k2} in + * the sorted map. Attempts to violate this restriction will cause the + * offending method or constructor invocation to throw a + * {@code ClassCastException}. + * + *

Note that the ordering maintained by a sorted map (whether or not an + * explicit comparator is provided) must be consistent with equals if + * the sorted map is to correctly implement the {@code Map} interface. (See + * the {@code Comparable} interface or {@code Comparator} interface for a + * precise definition of consistent with equals.) This is so because + * the {@code Map} interface is defined in terms of the {@code equals} + * operation, but a sorted map performs all key comparisons using its + * {@code compareTo} (or {@code compare}) method, so two keys that are + * deemed equal by this method are, from the standpoint of the sorted map, + * equal. The behavior of a tree map is well-defined even if its + * ordering is inconsistent with equals; it just fails to obey the general + * contract of the {@code Map} interface. + * + *

All general-purpose sorted map implementation classes should provide four + * "standard" constructors. It is not possible to enforce this recommendation + * though as required constructors cannot be specified by interfaces. The + * expected "standard" constructors for all sorted map implementations are: + *

    + *
  1. A void (no arguments) constructor, which creates an empty sorted map + * sorted according to the natural ordering of its keys.
  2. + *
  3. A constructor with a single argument of type {@code Comparator}, which + * creates an empty sorted map sorted according to the specified comparator.
  4. + *
  5. A constructor with a single argument of type {@code Map}, which creates + * a new map with the same key-value mappings as its argument, sorted + * according to the keys' natural ordering.
  6. + *
  7. A constructor with a single argument of type {@code SortedMap}, which + * creates a new sorted map with the same key-value mappings and the same + * ordering as the input sorted map.
  8. + *
+ * + *

Note: several methods return submaps with restricted key + * ranges. Such ranges are half-open, that is, they include their low + * endpoint but not their high endpoint (where applicable). If you need a + * closed range (which includes both endpoints), and the key type + * allows for calculation of the successor of a given key, merely request + * the subrange from {@code lowEndpoint} to + * {@code successor(highEndpoint)}. For example, suppose that {@code m} + * is a map whose keys are strings. The following idiom obtains a view + * containing all of the key-value mappings in {@code m} whose keys are + * between {@code low} and {@code high}, inclusive:

+ *   SortedMap<String, V> sub = m.subMap(low, high+"\0");
+ * + * A similar technique can be used to generate an open range + * (which contains neither endpoint). The following idiom obtains a + * view containing all of the key-value mappings in {@code m} whose keys + * are between {@code low} and {@code high}, exclusive:
+ *   SortedMap<String, V> sub = m.subMap(low+"\0", high);
+ * + *

This interface is a member of the + * + * Java Collections Framework. + * + * @param the type of keys maintained by this map + * @param the type of mapped values + * + * @author Josh Bloch + * @see Map + * @see TreeMap + * @see SortedSet + * @see Comparator + * @see Comparable + * @see Collection + * @see ClassCastException + * @since 1.2 + */ + +public interface SortedMap extends Map { + /** + * Returns the comparator used to order the keys in this map, or + * {@code null} if this map uses the {@linkplain Comparable + * natural ordering} of its keys. + * + * @return the comparator used to order the keys in this map, + * or {@code null} if this map uses the natural ordering + * of its keys + */ + Comparator comparator(); + + /** + * Returns a view of the portion of this map whose keys range from + * {@code fromKey}, inclusive, to {@code toKey}, exclusive. (If + * {@code fromKey} and {@code toKey} are equal, the returned map + * is empty.) The returned map is backed by this map, so changes + * in the returned map are reflected in this map, and vice-versa. + * The returned map supports all optional map operations that this + * map supports. + * + *

The returned map will throw an {@code IllegalArgumentException} + * on an attempt to insert a key outside its range. + * + * @param fromKey low endpoint (inclusive) of the keys in the returned map + * @param toKey high endpoint (exclusive) of the keys in the returned map + * @return a view of the portion of this map whose keys range from + * {@code fromKey}, inclusive, to {@code toKey}, exclusive + * @throws ClassCastException if {@code fromKey} and {@code toKey} + * cannot be compared to one another using this map's comparator + * (or, if the map has no comparator, using natural ordering). + * Implementations may, but are not required to, throw this + * exception if {@code fromKey} or {@code toKey} + * cannot be compared to keys currently in the map. + * @throws NullPointerException if {@code fromKey} or {@code toKey} + * is null and this map does not permit null keys + * @throws IllegalArgumentException if {@code fromKey} is greater than + * {@code toKey}; or if this map itself has a restricted + * range, and {@code fromKey} or {@code toKey} lies + * outside the bounds of the range + */ + SortedMap subMap(K fromKey, K toKey); + + /** + * Returns a view of the portion of this map whose keys are + * strictly less than {@code toKey}. The returned map is backed + * by this map, so changes in the returned map are reflected in + * this map, and vice-versa. The returned map supports all + * optional map operations that this map supports. + * + *

The returned map will throw an {@code IllegalArgumentException} + * on an attempt to insert a key outside its range. + * + * @param toKey high endpoint (exclusive) of the keys in the returned map + * @return a view of the portion of this map whose keys are strictly + * less than {@code toKey} + * @throws ClassCastException if {@code toKey} is not compatible + * with this map's comparator (or, if the map has no comparator, + * if {@code toKey} does not implement {@link Comparable}). + * Implementations may, but are not required to, throw this + * exception if {@code toKey} cannot be compared to keys + * currently in the map. + * @throws NullPointerException if {@code toKey} is null and + * this map does not permit null keys + * @throws IllegalArgumentException if this map itself has a + * restricted range, and {@code toKey} lies outside the + * bounds of the range + */ + SortedMap headMap(K toKey); + + /** + * Returns a view of the portion of this map whose keys are + * greater than or equal to {@code fromKey}. The returned map is + * backed by this map, so changes in the returned map are + * reflected in this map, and vice-versa. The returned map + * supports all optional map operations that this map supports. + * + *

The returned map will throw an {@code IllegalArgumentException} + * on an attempt to insert a key outside its range. + * + * @param fromKey low endpoint (inclusive) of the keys in the returned map + * @return a view of the portion of this map whose keys are greater + * than or equal to {@code fromKey} + * @throws ClassCastException if {@code fromKey} is not compatible + * with this map's comparator (or, if the map has no comparator, + * if {@code fromKey} does not implement {@link Comparable}). + * Implementations may, but are not required to, throw this + * exception if {@code fromKey} cannot be compared to keys + * currently in the map. + * @throws NullPointerException if {@code fromKey} is null and + * this map does not permit null keys + * @throws IllegalArgumentException if this map itself has a + * restricted range, and {@code fromKey} lies outside the + * bounds of the range + */ + SortedMap tailMap(K fromKey); + + /** + * Returns the first (lowest) key currently in this map. + * + * @return the first (lowest) key currently in this map + * @throws NoSuchElementException if this map is empty + */ + K firstKey(); + + /** + * Returns the last (highest) key currently in this map. + * + * @return the last (highest) key currently in this map + * @throws NoSuchElementException if this map is empty + */ + K lastKey(); + + /** + * Returns a {@link Set} view of the keys contained in this map. + * The set's iterator returns the keys in ascending order. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. If the map is modified + * while an iteration over the set is in progress (except through + * the iterator's own {@code remove} operation), the results of + * the iteration are undefined. The set supports element removal, + * which removes the corresponding mapping from the map, via the + * {@code Iterator.remove}, {@code Set.remove}, + * {@code removeAll}, {@code retainAll}, and {@code clear} + * operations. It does not support the {@code add} or {@code addAll} + * operations. + * + * @return a set view of the keys contained in this map, sorted in + * ascending order + */ + Set keySet(); + + /** + * Returns a {@link Collection} view of the values contained in this map. + * The collection's iterator returns the values in ascending order + * of the corresponding keys. + * The collection is backed by the map, so changes to the map are + * reflected in the collection, and vice-versa. If the map is + * modified while an iteration over the collection is in progress + * (except through the iterator's own {@code remove} operation), + * the results of the iteration are undefined. The collection + * supports element removal, which removes the corresponding + * mapping from the map, via the {@code Iterator.remove}, + * {@code Collection.remove}, {@code removeAll}, + * {@code retainAll} and {@code clear} operations. It does not + * support the {@code add} or {@code addAll} operations. + * + * @return a collection view of the values contained in this map, + * sorted in ascending key order + */ + Collection values(); + + /** + * Returns a {@link Set} view of the mappings contained in this map. + * The set's iterator returns the entries in ascending key order. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. If the map is modified + * while an iteration over the set is in progress (except through + * the iterator's own {@code remove} operation, or through the + * {@code setValue} operation on a map entry returned by the + * iterator) the results of the iteration are undefined. The set + * supports element removal, which removes the corresponding + * mapping from the map, via the {@code Iterator.remove}, + * {@code Set.remove}, {@code removeAll}, {@code retainAll} and + * {@code clear} operations. It does not support the + * {@code add} or {@code addAll} operations. + * + * @return a set view of the mappings contained in this map, + * sorted in ascending key order + */ + Set> entrySet(); +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/SortedSet.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/SortedSet.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,222 @@ +/* + * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util; + +/** + * A {@link Set} that further provides a total ordering on its elements. + * The elements are ordered using their {@linkplain Comparable natural + * ordering}, or by a {@link Comparator} typically provided at sorted + * set creation time. The set's iterator will traverse the set in + * ascending element order. Several additional operations are provided + * to take advantage of the ordering. (This interface is the set + * analogue of {@link SortedMap}.) + * + *

All elements inserted into a sorted set must implement the Comparable + * interface (or be accepted by the specified comparator). Furthermore, all + * such elements must be mutually comparable: e1.compareTo(e2) + * (or comparator.compare(e1, e2)) must not throw a + * ClassCastException for any elements e1 and e2 in + * the sorted set. Attempts to violate this restriction will cause the + * offending method or constructor invocation to throw a + * ClassCastException. + * + *

Note that the ordering maintained by a sorted set (whether or not an + * explicit comparator is provided) must be consistent with equals if + * the sorted set is to correctly implement the Set interface. (See + * the Comparable interface or Comparator interface for a + * precise definition of consistent with equals.) This is so because + * the Set interface is defined in terms of the equals + * operation, but a sorted set performs all element comparisons using its + * compareTo (or compare) method, so two elements that are + * deemed equal by this method are, from the standpoint of the sorted set, + * equal. The behavior of a sorted set is well-defined even if its + * ordering is inconsistent with equals; it just fails to obey the general + * contract of the Set interface. + * + *

All general-purpose sorted set implementation classes should + * provide four "standard" constructors: 1) A void (no arguments) + * constructor, which creates an empty sorted set sorted according to + * the natural ordering of its elements. 2) A constructor with a + * single argument of type Comparator, which creates an empty + * sorted set sorted according to the specified comparator. 3) A + * constructor with a single argument of type Collection, + * which creates a new sorted set with the same elements as its + * argument, sorted according to the natural ordering of the elements. + * 4) A constructor with a single argument of type SortedSet, + * which creates a new sorted set with the same elements and the same + * ordering as the input sorted set. There is no way to enforce this + * recommendation, as interfaces cannot contain constructors. + * + *

Note: several methods return subsets with restricted ranges. + * Such ranges are half-open, that is, they include their low + * endpoint but not their high endpoint (where applicable). + * If you need a closed range (which includes both endpoints), and + * the element type allows for calculation of the successor of a given + * value, merely request the subrange from lowEndpoint to + * successor(highEndpoint). For example, suppose that s + * is a sorted set of strings. The following idiom obtains a view + * containing all of the strings in s from low to + * high, inclusive:

+ *   SortedSet<String> sub = s.subSet(low, high+"\0");
+ * + * A similar technique can be used to generate an open range (which + * contains neither endpoint). The following idiom obtains a view + * containing all of the Strings in s from low to + * high, exclusive:
+ *   SortedSet<String> sub = s.subSet(low+"\0", high);
+ * + *

This interface is a member of the + * + * Java Collections Framework. + * + * @param the type of elements maintained by this set + * + * @author Josh Bloch + * @see Set + * @see TreeSet + * @see SortedMap + * @see Collection + * @see Comparable + * @see Comparator + * @see ClassCastException + * @since 1.2 + */ + +public interface SortedSet extends Set { + /** + * Returns the comparator used to order the elements in this set, + * or null if this set uses the {@linkplain Comparable + * natural ordering} of its elements. + * + * @return the comparator used to order the elements in this set, + * or null if this set uses the natural ordering + * of its elements + */ + Comparator comparator(); + + /** + * Returns a view of the portion of this set whose elements range + * from fromElement, inclusive, to toElement, + * exclusive. (If fromElement and toElement are + * equal, the returned set is empty.) The returned set is backed + * by this set, so changes in the returned set are reflected in + * this set, and vice-versa. The returned set supports all + * optional set operations that this set supports. + * + *

The returned set will throw an IllegalArgumentException + * on an attempt to insert an element outside its range. + * + * @param fromElement low endpoint (inclusive) of the returned set + * @param toElement high endpoint (exclusive) of the returned set + * @return a view of the portion of this set whose elements range from + * fromElement, inclusive, to toElement, exclusive + * @throws ClassCastException if fromElement and + * toElement cannot be compared to one another using this + * set's comparator (or, if the set has no comparator, using + * natural ordering). Implementations may, but are not required + * to, throw this exception if fromElement or + * toElement cannot be compared to elements currently in + * the set. + * @throws NullPointerException if fromElement or + * toElement is null and this set does not permit null + * elements + * @throws IllegalArgumentException if fromElement is + * greater than toElement; or if this set itself + * has a restricted range, and fromElement or + * toElement lies outside the bounds of the range + */ + SortedSet subSet(E fromElement, E toElement); + + /** + * Returns a view of the portion of this set whose elements are + * strictly less than toElement. The returned set is + * backed by this set, so changes in the returned set are + * reflected in this set, and vice-versa. The returned set + * supports all optional set operations that this set supports. + * + *

The returned set will throw an IllegalArgumentException + * on an attempt to insert an element outside its range. + * + * @param toElement high endpoint (exclusive) of the returned set + * @return a view of the portion of this set whose elements are strictly + * less than toElement + * @throws ClassCastException if toElement is not compatible + * with this set's comparator (or, if the set has no comparator, + * if toElement does not implement {@link Comparable}). + * Implementations may, but are not required to, throw this + * exception if toElement cannot be compared to elements + * currently in the set. + * @throws NullPointerException if toElement is null and + * this set does not permit null elements + * @throws IllegalArgumentException if this set itself has a + * restricted range, and toElement lies outside the + * bounds of the range + */ + SortedSet headSet(E toElement); + + /** + * Returns a view of the portion of this set whose elements are + * greater than or equal to fromElement. The returned + * set is backed by this set, so changes in the returned set are + * reflected in this set, and vice-versa. The returned set + * supports all optional set operations that this set supports. + * + *

The returned set will throw an IllegalArgumentException + * on an attempt to insert an element outside its range. + * + * @param fromElement low endpoint (inclusive) of the returned set + * @return a view of the portion of this set whose elements are greater + * than or equal to fromElement + * @throws ClassCastException if fromElement is not compatible + * with this set's comparator (or, if the set has no comparator, + * if fromElement does not implement {@link Comparable}). + * Implementations may, but are not required to, throw this + * exception if fromElement cannot be compared to elements + * currently in the set. + * @throws NullPointerException if fromElement is null + * and this set does not permit null elements + * @throws IllegalArgumentException if this set itself has a + * restricted range, and fromElement lies outside the + * bounds of the range + */ + SortedSet tailSet(E fromElement); + + /** + * Returns the first (lowest) element currently in this set. + * + * @return the first (lowest) element currently in this set + * @throws NoSuchElementException if this set is empty + */ + E first(); + + /** + * Returns the last (highest) element currently in this set. + * + * @return the last (highest) element currently in this set + * @throws NoSuchElementException if this set is empty + */ + E last(); +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/Stack.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/Stack.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,141 @@ +/* + * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util; + +/** + * The Stack class represents a last-in-first-out + * (LIFO) stack of objects. It extends class Vector with five + * operations that allow a vector to be treated as a stack. The usual + * push and pop operations are provided, as well as a + * method to peek at the top item on the stack, a method to test + * for whether the stack is empty, and a method to search + * the stack for an item and discover how far it is from the top. + *

+ * When a stack is first created, it contains no items. + * + *

A more complete and consistent set of LIFO stack operations is + * provided by the {@link Deque} interface and its implementations, which + * should be used in preference to this class. For example: + *

   {@code
+ *   Deque stack = new ArrayDeque();}
+ * + * @author Jonathan Payne + * @since JDK1.0 + */ +public +class Stack extends Vector { + /** + * Creates an empty Stack. + */ + public Stack() { + } + + /** + * Pushes an item onto the top of this stack. This has exactly + * the same effect as: + *
+     * addElement(item)
+ * + * @param item the item to be pushed onto this stack. + * @return the item argument. + * @see java.util.Vector#addElement + */ + public E push(E item) { + addElement(item); + + return item; + } + + /** + * Removes the object at the top of this stack and returns that + * object as the value of this function. + * + * @return The object at the top of this stack (the last item + * of the Vector object). + * @throws EmptyStackException if this stack is empty. + */ + public synchronized E pop() { + E obj; + int len = size(); + + obj = peek(); + removeElementAt(len - 1); + + return obj; + } + + /** + * Looks at the object at the top of this stack without removing it + * from the stack. + * + * @return the object at the top of this stack (the last item + * of the Vector object). + * @throws EmptyStackException if this stack is empty. + */ + public synchronized E peek() { + int len = size(); + + if (len == 0) + throw new EmptyStackException(); + return elementAt(len - 1); + } + + /** + * Tests if this stack is empty. + * + * @return true if and only if this stack contains + * no items; false otherwise. + */ + public boolean empty() { + return size() == 0; + } + + /** + * Returns the 1-based position where an object is on this stack. + * If the object o occurs as an item in this stack, this + * method returns the distance from the top of the stack of the + * occurrence nearest the top of the stack; the topmost item on the + * stack is considered to be at distance 1. The equals + * method is used to compare o to the + * items in this stack. + * + * @param o the desired object. + * @return the 1-based position from the top of the stack where + * the object is located; the return value -1 + * indicates that the object is not on the stack. + */ + public synchronized int search(Object o) { + int i = lastIndexOf(o); + + if (i >= 0) { + return size() - i; + } + return -1; + } + + /** use serialVersionUID from JDK 1.0.2 for interoperability */ + private static final long serialVersionUID = 1224463164541339165L; +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/StringTokenizer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/StringTokenizer.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,431 @@ +/* + * Copyright (c) 1994, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util; + +import java.lang.*; + +/** + * The string tokenizer class allows an application to break a + * string into tokens. The tokenization method is much simpler than + * the one used by the StreamTokenizer class. The + * StringTokenizer methods do not distinguish among + * identifiers, numbers, and quoted strings, nor do they recognize + * and skip comments. + *

+ * The set of delimiters (the characters that separate tokens) may + * be specified either at creation time or on a per-token basis. + *

+ * An instance of StringTokenizer behaves in one of two + * ways, depending on whether it was created with the + * returnDelims flag having the value true + * or false: + *

    + *
  • If the flag is false, delimiter characters serve to + * separate tokens. A token is a maximal sequence of consecutive + * characters that are not delimiters. + *
  • If the flag is true, delimiter characters are themselves + * considered to be tokens. A token is thus either one delimiter + * character, or a maximal sequence of consecutive characters that are + * not delimiters. + *

+ * A StringTokenizer object internally maintains a current + * position within the string to be tokenized. Some operations advance this + * current position past the characters processed.

+ * A token is returned by taking a substring of the string that was used to + * create the StringTokenizer object. + *

+ * The following is one example of the use of the tokenizer. The code: + *

+ *     StringTokenizer st = new StringTokenizer("this is a test");
+ *     while (st.hasMoreTokens()) {
+ *         System.out.println(st.nextToken());
+ *     }
+ * 
+ *

+ * prints the following output: + *

+ *     this
+ *     is
+ *     a
+ *     test
+ * 
+ * + *

+ * StringTokenizer is a legacy class that is retained for + * compatibility reasons although its use is discouraged in new code. It is + * recommended that anyone seeking this functionality use the split + * method of String or the java.util.regex package instead. + *

+ * The following example illustrates how the String.split + * method can be used to break up a string into its basic tokens: + *

+ *     String[] result = "this is a test".split("\\s");
+ *     for (int x=0; x<result.length; x++)
+ *         System.out.println(result[x]);
+ * 
+ *

+ * prints the following output: + *

+ *     this
+ *     is
+ *     a
+ *     test
+ * 
+ * + * @author unascribed + * @see java.io.StreamTokenizer + * @since JDK1.0 + */ +public +class StringTokenizer implements Enumeration { + private int currentPosition; + private int newPosition; + private int maxPosition; + private String str; + private String delimiters; + private boolean retDelims; + private boolean delimsChanged; + + /** + * maxDelimCodePoint stores the value of the delimiter character with the + * highest value. It is used to optimize the detection of delimiter + * characters. + * + * It is unlikely to provide any optimization benefit in the + * hasSurrogates case because most string characters will be + * smaller than the limit, but we keep it so that the two code + * paths remain similar. + */ + private int maxDelimCodePoint; + + /** + * If delimiters include any surrogates (including surrogate + * pairs), hasSurrogates is true and the tokenizer uses the + * different code path. This is because String.indexOf(int) + * doesn't handle unpaired surrogates as a single character. + */ + private boolean hasSurrogates = false; + + /** + * When hasSurrogates is true, delimiters are converted to code + * points and isDelimiter(int) is used to determine if the given + * codepoint is a delimiter. + */ + private int[] delimiterCodePoints; + + /** + * Set maxDelimCodePoint to the highest char in the delimiter set. + */ + private void setMaxDelimCodePoint() { + if (delimiters == null) { + maxDelimCodePoint = 0; + return; + } + + int m = 0; + int c; + int count = 0; + for (int i = 0; i < delimiters.length(); i += Character.charCount(c)) { + c = delimiters.charAt(i); + if (c >= Character.MIN_HIGH_SURROGATE && c <= Character.MAX_LOW_SURROGATE) { + c = delimiters.codePointAt(i); + hasSurrogates = true; + } + if (m < c) + m = c; + count++; + } + maxDelimCodePoint = m; + + if (hasSurrogates) { + delimiterCodePoints = new int[count]; + for (int i = 0, j = 0; i < count; i++, j += Character.charCount(c)) { + c = delimiters.codePointAt(j); + delimiterCodePoints[i] = c; + } + } + } + + /** + * Constructs a string tokenizer for the specified string. All + * characters in the delim argument are the delimiters + * for separating tokens. + *

+ * If the returnDelims flag is true, then + * the delimiter characters are also returned as tokens. Each + * delimiter is returned as a string of length one. If the flag is + * false, the delimiter characters are skipped and only + * serve as separators between tokens. + *

+ * Note that if delim is null, this constructor does + * not throw an exception. However, trying to invoke other methods on the + * resulting StringTokenizer may result in a + * NullPointerException. + * + * @param str a string to be parsed. + * @param delim the delimiters. + * @param returnDelims flag indicating whether to return the delimiters + * as tokens. + * @exception NullPointerException if str is null + */ + public StringTokenizer(String str, String delim, boolean returnDelims) { + currentPosition = 0; + newPosition = -1; + delimsChanged = false; + this.str = str; + maxPosition = str.length(); + delimiters = delim; + retDelims = returnDelims; + setMaxDelimCodePoint(); + } + + /** + * Constructs a string tokenizer for the specified string. The + * characters in the delim argument are the delimiters + * for separating tokens. Delimiter characters themselves will not + * be treated as tokens. + *

+ * Note that if delim is null, this constructor does + * not throw an exception. However, trying to invoke other methods on the + * resulting StringTokenizer may result in a + * NullPointerException. + * + * @param str a string to be parsed. + * @param delim the delimiters. + * @exception NullPointerException if str is null + */ + public StringTokenizer(String str, String delim) { + this(str, delim, false); + } + + /** + * Constructs a string tokenizer for the specified string. The + * tokenizer uses the default delimiter set, which is + * " \t\n\r\f": the space character, + * the tab character, the newline character, the carriage-return character, + * and the form-feed character. Delimiter characters themselves will + * not be treated as tokens. + * + * @param str a string to be parsed. + * @exception NullPointerException if str is null + */ + public StringTokenizer(String str) { + this(str, " \t\n\r\f", false); + } + + /** + * Skips delimiters starting from the specified position. If retDelims + * is false, returns the index of the first non-delimiter character at or + * after startPos. If retDelims is true, startPos is returned. + */ + private int skipDelimiters(int startPos) { + if (delimiters == null) + throw new NullPointerException(); + + int position = startPos; + while (!retDelims && position < maxPosition) { + if (!hasSurrogates) { + char c = str.charAt(position); + if ((c > maxDelimCodePoint) || (delimiters.indexOf(c) < 0)) + break; + position++; + } else { + int c = str.codePointAt(position); + if ((c > maxDelimCodePoint) || !isDelimiter(c)) { + break; + } + position += Character.charCount(c); + } + } + return position; + } + + /** + * Skips ahead from startPos and returns the index of the next delimiter + * character encountered, or maxPosition if no such delimiter is found. + */ + private int scanToken(int startPos) { + int position = startPos; + while (position < maxPosition) { + if (!hasSurrogates) { + char c = str.charAt(position); + if ((c <= maxDelimCodePoint) && (delimiters.indexOf(c) >= 0)) + break; + position++; + } else { + int c = str.codePointAt(position); + if ((c <= maxDelimCodePoint) && isDelimiter(c)) + break; + position += Character.charCount(c); + } + } + if (retDelims && (startPos == position)) { + if (!hasSurrogates) { + char c = str.charAt(position); + if ((c <= maxDelimCodePoint) && (delimiters.indexOf(c) >= 0)) + position++; + } else { + int c = str.codePointAt(position); + if ((c <= maxDelimCodePoint) && isDelimiter(c)) + position += Character.charCount(c); + } + } + return position; + } + + private boolean isDelimiter(int codePoint) { + for (int i = 0; i < delimiterCodePoints.length; i++) { + if (delimiterCodePoints[i] == codePoint) { + return true; + } + } + return false; + } + + /** + * Tests if there are more tokens available from this tokenizer's string. + * If this method returns true, then a subsequent call to + * nextToken with no argument will successfully return a token. + * + * @return true if and only if there is at least one token + * in the string after the current position; false + * otherwise. + */ + public boolean hasMoreTokens() { + /* + * Temporarily store this position and use it in the following + * nextToken() method only if the delimiters haven't been changed in + * that nextToken() invocation. + */ + newPosition = skipDelimiters(currentPosition); + return (newPosition < maxPosition); + } + + /** + * Returns the next token from this string tokenizer. + * + * @return the next token from this string tokenizer. + * @exception NoSuchElementException if there are no more tokens in this + * tokenizer's string. + */ + public String nextToken() { + /* + * If next position already computed in hasMoreElements() and + * delimiters have changed between the computation and this invocation, + * then use the computed value. + */ + + currentPosition = (newPosition >= 0 && !delimsChanged) ? + newPosition : skipDelimiters(currentPosition); + + /* Reset these anyway */ + delimsChanged = false; + newPosition = -1; + + if (currentPosition >= maxPosition) + throw new NoSuchElementException(); + int start = currentPosition; + currentPosition = scanToken(currentPosition); + return str.substring(start, currentPosition); + } + + /** + * Returns the next token in this string tokenizer's string. First, + * the set of characters considered to be delimiters by this + * StringTokenizer object is changed to be the characters in + * the string delim. Then the next token in the string + * after the current position is returned. The current position is + * advanced beyond the recognized token. The new delimiter set + * remains the default after this call. + * + * @param delim the new delimiters. + * @return the next token, after switching to the new delimiter set. + * @exception NoSuchElementException if there are no more tokens in this + * tokenizer's string. + * @exception NullPointerException if delim is null + */ + public String nextToken(String delim) { + delimiters = delim; + + /* delimiter string specified, so set the appropriate flag. */ + delimsChanged = true; + + setMaxDelimCodePoint(); + return nextToken(); + } + + /** + * Returns the same value as the hasMoreTokens + * method. It exists so that this class can implement the + * Enumeration interface. + * + * @return true if there are more tokens; + * false otherwise. + * @see java.util.Enumeration + * @see java.util.StringTokenizer#hasMoreTokens() + */ + public boolean hasMoreElements() { + return hasMoreTokens(); + } + + /** + * Returns the same value as the nextToken method, + * except that its declared return value is Object rather than + * String. It exists so that this class can implement the + * Enumeration interface. + * + * @return the next token in the string. + * @exception NoSuchElementException if there are no more tokens in this + * tokenizer's string. + * @see java.util.Enumeration + * @see java.util.StringTokenizer#nextToken() + */ + public Object nextElement() { + return nextToken(); + } + + /** + * Calculates the number of times that this tokenizer's + * nextToken method can be called before it generates an + * exception. The current position is not advanced. + * + * @return the number of tokens remaining in the string using the current + * delimiter set. + * @see java.util.StringTokenizer#nextToken() + */ + public int countTokens() { + int count = 0; + int currpos = currentPosition; + while (currpos < maxPosition) { + currpos = skipDelimiters(currpos); + if (currpos >= maxPosition) + break; + currpos = scanToken(currpos); + count++; + } + return count; + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/TimSort.java --- a/emul/compact/src/main/java/java/util/TimSort.java Tue Feb 05 16:40:01 2013 +0100 +++ b/emul/compact/src/main/java/java/util/TimSort.java Tue Feb 05 17:04:22 2013 +0100 @@ -25,7 +25,6 @@ package java.util; -import org.apidesign.bck2brwsr.emul.lang.System; /** * A stable, adaptive, iterative mergesort that requires far fewer than diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/Vector.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/Vector.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,1194 @@ +/* + * Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util; + + +/** + * The {@code Vector} class implements a growable array of + * objects. Like an array, it contains components that can be + * accessed using an integer index. However, the size of a + * {@code Vector} can grow or shrink as needed to accommodate + * adding and removing items after the {@code Vector} has been created. + * + *

Each vector tries to optimize storage management by maintaining a + * {@code capacity} and a {@code capacityIncrement}. The + * {@code capacity} is always at least as large as the vector + * size; it is usually larger because as components are added to the + * vector, the vector's storage increases in chunks the size of + * {@code capacityIncrement}. An application can increase the + * capacity of a vector before inserting a large number of + * components; this reduces the amount of incremental reallocation. + * + *

+ * The iterators returned by this class's {@link #iterator() iterator} and + * {@link #listIterator(int) listIterator} methods are fail-fast: + * if the vector is structurally modified at any time after the iterator is + * created, in any way except through the iterator's own + * {@link ListIterator#remove() remove} or + * {@link ListIterator#add(Object) add} methods, the iterator will throw a + * {@link ConcurrentModificationException}. Thus, in the face of + * concurrent modification, the iterator fails quickly and cleanly, rather + * than risking arbitrary, non-deterministic behavior at an undetermined + * time in the future. The {@link Enumeration Enumerations} returned by + * the {@link #elements() elements} method are not fail-fast. + * + *

Note that the fail-fast behavior of an iterator cannot be guaranteed + * as it is, generally speaking, impossible to make any hard guarantees in the + * presence of unsynchronized concurrent modification. Fail-fast iterators + * throw {@code ConcurrentModificationException} on a best-effort basis. + * Therefore, it would be wrong to write a program that depended on this + * exception for its correctness: the fail-fast behavior of iterators + * should be used only to detect bugs. + * + *

As of the Java 2 platform v1.2, this class was retrofitted to + * implement the {@link List} interface, making it a member of the + * + * Java Collections Framework. Unlike the new collection + * implementations, {@code Vector} is synchronized. If a thread-safe + * implementation is not needed, it is recommended to use {@link + * ArrayList} in place of {@code Vector}. + * + * @author Lee Boynton + * @author Jonathan Payne + * @see Collection + * @see LinkedList + * @since JDK1.0 + */ +public class Vector + extends AbstractList + implements List, RandomAccess, Cloneable, java.io.Serializable +{ + /** + * The array buffer into which the components of the vector are + * stored. The capacity of the vector is the length of this array buffer, + * and is at least large enough to contain all the vector's elements. + * + *

Any array elements following the last element in the Vector are null. + * + * @serial + */ + protected Object[] elementData; + + /** + * The number of valid components in this {@code Vector} object. + * Components {@code elementData[0]} through + * {@code elementData[elementCount-1]} are the actual items. + * + * @serial + */ + protected int elementCount; + + /** + * The amount by which the capacity of the vector is automatically + * incremented when its size becomes greater than its capacity. If + * the capacity increment is less than or equal to zero, the capacity + * of the vector is doubled each time it needs to grow. + * + * @serial + */ + protected int capacityIncrement; + + /** use serialVersionUID from JDK 1.0.2 for interoperability */ + private static final long serialVersionUID = -2767605614048989439L; + + /** + * Constructs an empty vector with the specified initial capacity and + * capacity increment. + * + * @param initialCapacity the initial capacity of the vector + * @param capacityIncrement the amount by which the capacity is + * increased when the vector overflows + * @throws IllegalArgumentException if the specified initial capacity + * is negative + */ + public Vector(int initialCapacity, int capacityIncrement) { + super(); + if (initialCapacity < 0) + throw new IllegalArgumentException("Illegal Capacity: "+ + initialCapacity); + this.elementData = new Object[initialCapacity]; + this.capacityIncrement = capacityIncrement; + } + + /** + * Constructs an empty vector with the specified initial capacity and + * with its capacity increment equal to zero. + * + * @param initialCapacity the initial capacity of the vector + * @throws IllegalArgumentException if the specified initial capacity + * is negative + */ + public Vector(int initialCapacity) { + this(initialCapacity, 0); + } + + /** + * Constructs an empty vector so that its internal data array + * has size {@code 10} and its standard capacity increment is + * zero. + */ + public Vector() { + this(10); + } + + /** + * Constructs a vector containing the elements of the specified + * collection, in the order they are returned by the collection's + * iterator. + * + * @param c the collection whose elements are to be placed into this + * vector + * @throws NullPointerException if the specified collection is null + * @since 1.2 + */ + public Vector(Collection c) { + elementData = c.toArray(); + elementCount = elementData.length; + // c.toArray might (incorrectly) not return Object[] (see 6260652) + if (elementData.getClass() != Object[].class) + elementData = Arrays.copyOf(elementData, elementCount, Object[].class); + } + + /** + * Copies the components of this vector into the specified array. + * The item at index {@code k} in this vector is copied into + * component {@code k} of {@code anArray}. + * + * @param anArray the array into which the components get copied + * @throws NullPointerException if the given array is null + * @throws IndexOutOfBoundsException if the specified array is not + * large enough to hold all the components of this vector + * @throws ArrayStoreException if a component of this vector is not of + * a runtime type that can be stored in the specified array + * @see #toArray(Object[]) + */ + public synchronized void copyInto(Object[] anArray) { + System.arraycopy(elementData, 0, anArray, 0, elementCount); + } + + /** + * Trims the capacity of this vector to be the vector's current + * size. If the capacity of this vector is larger than its current + * size, then the capacity is changed to equal the size by replacing + * its internal data array, kept in the field {@code elementData}, + * with a smaller one. An application can use this operation to + * minimize the storage of a vector. + */ + public synchronized void trimToSize() { + modCount++; + int oldCapacity = elementData.length; + if (elementCount < oldCapacity) { + elementData = Arrays.copyOf(elementData, elementCount); + } + } + + /** + * Increases the capacity of this vector, if necessary, to ensure + * that it can hold at least the number of components specified by + * the minimum capacity argument. + * + *

If the current capacity of this vector is less than + * {@code minCapacity}, then its capacity is increased by replacing its + * internal data array, kept in the field {@code elementData}, with a + * larger one. The size of the new data array will be the old size plus + * {@code capacityIncrement}, unless the value of + * {@code capacityIncrement} is less than or equal to zero, in which case + * the new capacity will be twice the old capacity; but if this new size + * is still smaller than {@code minCapacity}, then the new capacity will + * be {@code minCapacity}. + * + * @param minCapacity the desired minimum capacity + */ + public synchronized void ensureCapacity(int minCapacity) { + if (minCapacity > 0) { + modCount++; + ensureCapacityHelper(minCapacity); + } + } + + /** + * This implements the unsynchronized semantics of ensureCapacity. + * Synchronized methods in this class can internally call this + * method for ensuring capacity without incurring the cost of an + * extra synchronization. + * + * @see #ensureCapacity(int) + */ + private void ensureCapacityHelper(int minCapacity) { + // overflow-conscious code + if (minCapacity - elementData.length > 0) + grow(minCapacity); + } + + /** + * The maximum size of array to allocate. + * Some VMs reserve some header words in an array. + * Attempts to allocate larger arrays may result in + * OutOfMemoryError: Requested array size exceeds VM limit + */ + private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + + private void grow(int minCapacity) { + // overflow-conscious code + int oldCapacity = elementData.length; + int newCapacity = oldCapacity + ((capacityIncrement > 0) ? + capacityIncrement : oldCapacity); + if (newCapacity - minCapacity < 0) + newCapacity = minCapacity; + if (newCapacity - MAX_ARRAY_SIZE > 0) + newCapacity = hugeCapacity(minCapacity); + elementData = Arrays.copyOf(elementData, newCapacity); + } + + private static int hugeCapacity(int minCapacity) { + if (minCapacity < 0) // overflow + throw new OutOfMemoryError(); + return (minCapacity > MAX_ARRAY_SIZE) ? + Integer.MAX_VALUE : + MAX_ARRAY_SIZE; + } + + /** + * Sets the size of this vector. If the new size is greater than the + * current size, new {@code null} items are added to the end of + * the vector. If the new size is less than the current size, all + * components at index {@code newSize} and greater are discarded. + * + * @param newSize the new size of this vector + * @throws ArrayIndexOutOfBoundsException if the new size is negative + */ + public synchronized void setSize(int newSize) { + modCount++; + if (newSize > elementCount) { + ensureCapacityHelper(newSize); + } else { + for (int i = newSize ; i < elementCount ; i++) { + elementData[i] = null; + } + } + elementCount = newSize; + } + + /** + * Returns the current capacity of this vector. + * + * @return the current capacity (the length of its internal + * data array, kept in the field {@code elementData} + * of this vector) + */ + public synchronized int capacity() { + return elementData.length; + } + + /** + * Returns the number of components in this vector. + * + * @return the number of components in this vector + */ + public synchronized int size() { + return elementCount; + } + + /** + * Tests if this vector has no components. + * + * @return {@code true} if and only if this vector has + * no components, that is, its size is zero; + * {@code false} otherwise. + */ + public synchronized boolean isEmpty() { + return elementCount == 0; + } + + /** + * Returns an enumeration of the components of this vector. The + * returned {@code Enumeration} object will generate all items in + * this vector. The first item generated is the item at index {@code 0}, + * then the item at index {@code 1}, and so on. + * + * @return an enumeration of the components of this vector + * @see Iterator + */ + public Enumeration elements() { + return new Enumeration() { + int count = 0; + + public boolean hasMoreElements() { + return count < elementCount; + } + + public E nextElement() { + synchronized (Vector.this) { + if (count < elementCount) { + return elementData(count++); + } + } + throw new NoSuchElementException("Vector Enumeration"); + } + }; + } + + /** + * Returns {@code true} if this vector contains the specified element. + * More formally, returns {@code true} if and only if this vector + * contains at least one element {@code e} such that + * (o==null ? e==null : o.equals(e)). + * + * @param o element whose presence in this vector is to be tested + * @return {@code true} if this vector contains the specified element + */ + public boolean contains(Object o) { + return indexOf(o, 0) >= 0; + } + + /** + * Returns the index of the first occurrence of the specified element + * in this vector, or -1 if this vector does not contain the element. + * More formally, returns the lowest index {@code i} such that + * (o==null ? get(i)==null : o.equals(get(i))), + * or -1 if there is no such index. + * + * @param o element to search for + * @return the index of the first occurrence of the specified element in + * this vector, or -1 if this vector does not contain the element + */ + public int indexOf(Object o) { + return indexOf(o, 0); + } + + /** + * Returns the index of the first occurrence of the specified element in + * this vector, searching forwards from {@code index}, or returns -1 if + * the element is not found. + * More formally, returns the lowest index {@code i} such that + * (i >= index && (o==null ? get(i)==null : o.equals(get(i)))), + * or -1 if there is no such index. + * + * @param o element to search for + * @param index index to start searching from + * @return the index of the first occurrence of the element in + * this vector at position {@code index} or later in the vector; + * {@code -1} if the element is not found. + * @throws IndexOutOfBoundsException if the specified index is negative + * @see Object#equals(Object) + */ + public synchronized int indexOf(Object o, int index) { + if (o == null) { + for (int i = index ; i < elementCount ; i++) + if (elementData[i]==null) + return i; + } else { + for (int i = index ; i < elementCount ; i++) + if (o.equals(elementData[i])) + return i; + } + return -1; + } + + /** + * Returns the index of the last occurrence of the specified element + * in this vector, or -1 if this vector does not contain the element. + * More formally, returns the highest index {@code i} such that + * (o==null ? get(i)==null : o.equals(get(i))), + * or -1 if there is no such index. + * + * @param o element to search for + * @return the index of the last occurrence of the specified element in + * this vector, or -1 if this vector does not contain the element + */ + public synchronized int lastIndexOf(Object o) { + return lastIndexOf(o, elementCount-1); + } + + /** + * Returns the index of the last occurrence of the specified element in + * this vector, searching backwards from {@code index}, or returns -1 if + * the element is not found. + * More formally, returns the highest index {@code i} such that + * (i <= index && (o==null ? get(i)==null : o.equals(get(i)))), + * or -1 if there is no such index. + * + * @param o element to search for + * @param index index to start searching backwards from + * @return the index of the last occurrence of the element at position + * less than or equal to {@code index} in this vector; + * -1 if the element is not found. + * @throws IndexOutOfBoundsException if the specified index is greater + * than or equal to the current size of this vector + */ + public synchronized int lastIndexOf(Object o, int index) { + if (index >= elementCount) + throw new IndexOutOfBoundsException(index + " >= "+ elementCount); + + if (o == null) { + for (int i = index; i >= 0; i--) + if (elementData[i]==null) + return i; + } else { + for (int i = index; i >= 0; i--) + if (o.equals(elementData[i])) + return i; + } + return -1; + } + + /** + * Returns the component at the specified index. + * + *

This method is identical in functionality to the {@link #get(int)} + * method (which is part of the {@link List} interface). + * + * @param index an index into this vector + * @return the component at the specified index + * @throws ArrayIndexOutOfBoundsException if the index is out of range + * ({@code index < 0 || index >= size()}) + */ + public synchronized E elementAt(int index) { + if (index >= elementCount) { + throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount); + } + + return elementData(index); + } + + /** + * Returns the first component (the item at index {@code 0}) of + * this vector. + * + * @return the first component of this vector + * @throws NoSuchElementException if this vector has no components + */ + public synchronized E firstElement() { + if (elementCount == 0) { + throw new NoSuchElementException(); + } + return elementData(0); + } + + /** + * Returns the last component of the vector. + * + * @return the last component of the vector, i.e., the component at index + * size() - 1. + * @throws NoSuchElementException if this vector is empty + */ + public synchronized E lastElement() { + if (elementCount == 0) { + throw new NoSuchElementException(); + } + return elementData(elementCount - 1); + } + + /** + * Sets the component at the specified {@code index} of this + * vector to be the specified object. The previous component at that + * position is discarded. + * + *

The index must be a value greater than or equal to {@code 0} + * and less than the current size of the vector. + * + *

This method is identical in functionality to the + * {@link #set(int, Object) set(int, E)} + * method (which is part of the {@link List} interface). Note that the + * {@code set} method reverses the order of the parameters, to more closely + * match array usage. Note also that the {@code set} method returns the + * old value that was stored at the specified position. + * + * @param obj what the component is to be set to + * @param index the specified index + * @throws ArrayIndexOutOfBoundsException if the index is out of range + * ({@code index < 0 || index >= size()}) + */ + public synchronized void setElementAt(E obj, int index) { + if (index >= elementCount) { + throw new ArrayIndexOutOfBoundsException(index + " >= " + + elementCount); + } + elementData[index] = obj; + } + + /** + * Deletes the component at the specified index. Each component in + * this vector with an index greater or equal to the specified + * {@code index} is shifted downward to have an index one + * smaller than the value it had previously. The size of this vector + * is decreased by {@code 1}. + * + *

The index must be a value greater than or equal to {@code 0} + * and less than the current size of the vector. + * + *

This method is identical in functionality to the {@link #remove(int)} + * method (which is part of the {@link List} interface). Note that the + * {@code remove} method returns the old value that was stored at the + * specified position. + * + * @param index the index of the object to remove + * @throws ArrayIndexOutOfBoundsException if the index is out of range + * ({@code index < 0 || index >= size()}) + */ + public synchronized void removeElementAt(int index) { + modCount++; + if (index >= elementCount) { + throw new ArrayIndexOutOfBoundsException(index + " >= " + + elementCount); + } + else if (index < 0) { + throw new ArrayIndexOutOfBoundsException(index); + } + int j = elementCount - index - 1; + if (j > 0) { + System.arraycopy(elementData, index + 1, elementData, index, j); + } + elementCount--; + elementData[elementCount] = null; /* to let gc do its work */ + } + + /** + * Inserts the specified object as a component in this vector at the + * specified {@code index}. Each component in this vector with + * an index greater or equal to the specified {@code index} is + * shifted upward to have an index one greater than the value it had + * previously. + * + *

The index must be a value greater than or equal to {@code 0} + * and less than or equal to the current size of the vector. (If the + * index is equal to the current size of the vector, the new element + * is appended to the Vector.) + * + *

This method is identical in functionality to the + * {@link #add(int, Object) add(int, E)} + * method (which is part of the {@link List} interface). Note that the + * {@code add} method reverses the order of the parameters, to more closely + * match array usage. + * + * @param obj the component to insert + * @param index where to insert the new component + * @throws ArrayIndexOutOfBoundsException if the index is out of range + * ({@code index < 0 || index > size()}) + */ + public synchronized void insertElementAt(E obj, int index) { + modCount++; + if (index > elementCount) { + throw new ArrayIndexOutOfBoundsException(index + + " > " + elementCount); + } + ensureCapacityHelper(elementCount + 1); + System.arraycopy(elementData, index, elementData, index + 1, elementCount - index); + elementData[index] = obj; + elementCount++; + } + + /** + * Adds the specified component to the end of this vector, + * increasing its size by one. The capacity of this vector is + * increased if its size becomes greater than its capacity. + * + *

This method is identical in functionality to the + * {@link #add(Object) add(E)} + * method (which is part of the {@link List} interface). + * + * @param obj the component to be added + */ + public synchronized void addElement(E obj) { + modCount++; + ensureCapacityHelper(elementCount + 1); + elementData[elementCount++] = obj; + } + + /** + * Removes the first (lowest-indexed) occurrence of the argument + * from this vector. If the object is found in this vector, each + * component in the vector with an index greater or equal to the + * object's index is shifted downward to have an index one smaller + * than the value it had previously. + * + *

This method is identical in functionality to the + * {@link #remove(Object)} method (which is part of the + * {@link List} interface). + * + * @param obj the component to be removed + * @return {@code true} if the argument was a component of this + * vector; {@code false} otherwise. + */ + public synchronized boolean removeElement(Object obj) { + modCount++; + int i = indexOf(obj); + if (i >= 0) { + removeElementAt(i); + return true; + } + return false; + } + + /** + * Removes all components from this vector and sets its size to zero. + * + *

This method is identical in functionality to the {@link #clear} + * method (which is part of the {@link List} interface). + */ + public synchronized void removeAllElements() { + modCount++; + // Let gc do its work + for (int i = 0; i < elementCount; i++) + elementData[i] = null; + + elementCount = 0; + } + + /** + * Returns a clone of this vector. The copy will contain a + * reference to a clone of the internal data array, not a reference + * to the original internal data array of this {@code Vector} object. + * + * @return a clone of this vector + */ + public synchronized Object clone() { + try { + @SuppressWarnings("unchecked") + Vector v = (Vector) super.clone(); + v.elementData = Arrays.copyOf(elementData, elementCount); + v.modCount = 0; + return v; + } catch (CloneNotSupportedException e) { + // this shouldn't happen, since we are Cloneable + throw new InternalError(); + } + } + + /** + * Returns an array containing all of the elements in this Vector + * in the correct order. + * + * @since 1.2 + */ + public synchronized Object[] toArray() { + return Arrays.copyOf(elementData, elementCount); + } + + /** + * Returns an array containing all of the elements in this Vector in the + * correct order; the runtime type of the returned array is that of the + * specified array. If the Vector fits in the specified array, it is + * returned therein. Otherwise, a new array is allocated with the runtime + * type of the specified array and the size of this Vector. + * + *

If the Vector fits in the specified array with room to spare + * (i.e., the array has more elements than the Vector), + * the element in the array immediately following the end of the + * Vector is set to null. (This is useful in determining the length + * of the Vector only if the caller knows that the Vector + * does not contain any null elements.) + * + * @param a the array into which the elements of the Vector are to + * be stored, if it is big enough; otherwise, a new array of the + * same runtime type is allocated for this purpose. + * @return an array containing the elements of the Vector + * @throws ArrayStoreException if the runtime type of a is not a supertype + * of the runtime type of every element in this Vector + * @throws NullPointerException if the given array is null + * @since 1.2 + */ + @SuppressWarnings("unchecked") + public synchronized T[] toArray(T[] a) { + if (a.length < elementCount) + return (T[]) Arrays.copyOf(elementData, elementCount, a.getClass()); + + System.arraycopy(elementData, 0, a, 0, elementCount); + + if (a.length > elementCount) + a[elementCount] = null; + + return a; + } + + // Positional Access Operations + + @SuppressWarnings("unchecked") + E elementData(int index) { + return (E) elementData[index]; + } + + /** + * Returns the element at the specified position in this Vector. + * + * @param index index of the element to return + * @return object at the specified index + * @throws ArrayIndexOutOfBoundsException if the index is out of range + * ({@code index < 0 || index >= size()}) + * @since 1.2 + */ + public synchronized E get(int index) { + if (index >= elementCount) + throw new ArrayIndexOutOfBoundsException(index); + + return elementData(index); + } + + /** + * Replaces the element at the specified position in this Vector with the + * specified element. + * + * @param index index of the element to replace + * @param element element to be stored at the specified position + * @return the element previously at the specified position + * @throws ArrayIndexOutOfBoundsException if the index is out of range + * ({@code index < 0 || index >= size()}) + * @since 1.2 + */ + public synchronized E set(int index, E element) { + if (index >= elementCount) + throw new ArrayIndexOutOfBoundsException(index); + + E oldValue = elementData(index); + elementData[index] = element; + return oldValue; + } + + /** + * Appends the specified element to the end of this Vector. + * + * @param e element to be appended to this Vector + * @return {@code true} (as specified by {@link Collection#add}) + * @since 1.2 + */ + public synchronized boolean add(E e) { + modCount++; + ensureCapacityHelper(elementCount + 1); + elementData[elementCount++] = e; + return true; + } + + /** + * Removes the first occurrence of the specified element in this Vector + * If the Vector does not contain the element, it is unchanged. More + * formally, removes the element with the lowest index i such that + * {@code (o==null ? get(i)==null : o.equals(get(i)))} (if such + * an element exists). + * + * @param o element to be removed from this Vector, if present + * @return true if the Vector contained the specified element + * @since 1.2 + */ + public boolean remove(Object o) { + return removeElement(o); + } + + /** + * Inserts the specified element at the specified position in this Vector. + * Shifts the element currently at that position (if any) and any + * subsequent elements to the right (adds one to their indices). + * + * @param index index at which the specified element is to be inserted + * @param element element to be inserted + * @throws ArrayIndexOutOfBoundsException if the index is out of range + * ({@code index < 0 || index > size()}) + * @since 1.2 + */ + public void add(int index, E element) { + insertElementAt(element, index); + } + + /** + * Removes the element at the specified position in this Vector. + * Shifts any subsequent elements to the left (subtracts one from their + * indices). Returns the element that was removed from the Vector. + * + * @throws ArrayIndexOutOfBoundsException if the index is out of range + * ({@code index < 0 || index >= size()}) + * @param index the index of the element to be removed + * @return element that was removed + * @since 1.2 + */ + public synchronized E remove(int index) { + modCount++; + if (index >= elementCount) + throw new ArrayIndexOutOfBoundsException(index); + E oldValue = elementData(index); + + int numMoved = elementCount - index - 1; + if (numMoved > 0) + System.arraycopy(elementData, index+1, elementData, index, + numMoved); + elementData[--elementCount] = null; // Let gc do its work + + return oldValue; + } + + /** + * Removes all of the elements from this Vector. The Vector will + * be empty after this call returns (unless it throws an exception). + * + * @since 1.2 + */ + public void clear() { + removeAllElements(); + } + + // Bulk Operations + + /** + * Returns true if this Vector contains all of the elements in the + * specified Collection. + * + * @param c a collection whose elements will be tested for containment + * in this Vector + * @return true if this Vector contains all of the elements in the + * specified collection + * @throws NullPointerException if the specified collection is null + */ + public synchronized boolean containsAll(Collection c) { + return super.containsAll(c); + } + + /** + * Appends all of the elements in the specified Collection to the end of + * this Vector, in the order that they are returned by the specified + * Collection's Iterator. The behavior of this operation is undefined if + * the specified Collection is modified while the operation is in progress. + * (This implies that the behavior of this call is undefined if the + * specified Collection is this Vector, and this Vector is nonempty.) + * + * @param c elements to be inserted into this Vector + * @return {@code true} if this Vector changed as a result of the call + * @throws NullPointerException if the specified collection is null + * @since 1.2 + */ + public synchronized boolean addAll(Collection c) { + modCount++; + Object[] a = c.toArray(); + int numNew = a.length; + ensureCapacityHelper(elementCount + numNew); + System.arraycopy(a, 0, elementData, elementCount, numNew); + elementCount += numNew; + return numNew != 0; + } + + /** + * Removes from this Vector all of its elements that are contained in the + * specified Collection. + * + * @param c a collection of elements to be removed from the Vector + * @return true if this Vector changed as a result of the call + * @throws ClassCastException if the types of one or more elements + * in this vector are incompatible with the specified + * collection + * (optional) + * @throws NullPointerException if this vector contains one or more null + * elements and the specified collection does not support null + * elements + * (optional), + * or if the specified collection is null + * @since 1.2 + */ + public synchronized boolean removeAll(Collection c) { + return super.removeAll(c); + } + + /** + * Retains only the elements in this Vector that are contained in the + * specified Collection. In other words, removes from this Vector all + * of its elements that are not contained in the specified Collection. + * + * @param c a collection of elements to be retained in this Vector + * (all other elements are removed) + * @return true if this Vector changed as a result of the call + * @throws ClassCastException if the types of one or more elements + * in this vector are incompatible with the specified + * collection + * (optional) + * @throws NullPointerException if this vector contains one or more null + * elements and the specified collection does not support null + * elements + * (optional), + * or if the specified collection is null + * @since 1.2 + */ + public synchronized boolean retainAll(Collection c) { + return super.retainAll(c); + } + + /** + * Inserts all of the elements in the specified Collection into this + * Vector at the specified position. Shifts the element currently at + * that position (if any) and any subsequent elements to the right + * (increases their indices). The new elements will appear in the Vector + * in the order that they are returned by the specified Collection's + * iterator. + * + * @param index index at which to insert the first element from the + * specified collection + * @param c elements to be inserted into this Vector + * @return {@code true} if this Vector changed as a result of the call + * @throws ArrayIndexOutOfBoundsException if the index is out of range + * ({@code index < 0 || index > size()}) + * @throws NullPointerException if the specified collection is null + * @since 1.2 + */ + public synchronized boolean addAll(int index, Collection c) { + modCount++; + if (index < 0 || index > elementCount) + throw new ArrayIndexOutOfBoundsException(index); + + Object[] a = c.toArray(); + int numNew = a.length; + ensureCapacityHelper(elementCount + numNew); + + int numMoved = elementCount - index; + if (numMoved > 0) + System.arraycopy(elementData, index, elementData, index + numNew, + numMoved); + + System.arraycopy(a, 0, elementData, index, numNew); + elementCount += numNew; + return numNew != 0; + } + + /** + * Compares the specified Object with this Vector for equality. Returns + * true if and only if the specified Object is also a List, both Lists + * have the same size, and all corresponding pairs of elements in the two + * Lists are equal. (Two elements {@code e1} and + * {@code e2} are equal if {@code (e1==null ? e2==null : + * e1.equals(e2))}.) In other words, two Lists are defined to be + * equal if they contain the same elements in the same order. + * + * @param o the Object to be compared for equality with this Vector + * @return true if the specified Object is equal to this Vector + */ + public synchronized boolean equals(Object o) { + return super.equals(o); + } + + /** + * Returns the hash code value for this Vector. + */ + public synchronized int hashCode() { + return super.hashCode(); + } + + /** + * Returns a string representation of this Vector, containing + * the String representation of each element. + */ + public synchronized String toString() { + return super.toString(); + } + + /** + * Returns a view of the portion of this List between fromIndex, + * inclusive, and toIndex, exclusive. (If fromIndex and toIndex are + * equal, the returned List is empty.) The returned List is backed by this + * List, so changes in the returned List are reflected in this List, and + * vice-versa. The returned List supports all of the optional List + * operations supported by this List. + * + *

This method eliminates the need for explicit range operations (of + * the sort that commonly exist for arrays). Any operation that expects + * a List can be used as a range operation by operating on a subList view + * instead of a whole List. For example, the following idiom + * removes a range of elements from a List: + *

+     *      list.subList(from, to).clear();
+     * 
+ * Similar idioms may be constructed for indexOf and lastIndexOf, + * and all of the algorithms in the Collections class can be applied to + * a subList. + * + *

The semantics of the List returned by this method become undefined if + * the backing list (i.e., this List) is structurally modified in + * any way other than via the returned List. (Structural modifications are + * those that change the size of the List, or otherwise perturb it in such + * a fashion that iterations in progress may yield incorrect results.) + * + * @param fromIndex low endpoint (inclusive) of the subList + * @param toIndex high endpoint (exclusive) of the subList + * @return a view of the specified range within this List + * @throws IndexOutOfBoundsException if an endpoint index value is out of range + * {@code (fromIndex < 0 || toIndex > size)} + * @throws IllegalArgumentException if the endpoint indices are out of order + * {@code (fromIndex > toIndex)} + */ + public synchronized List subList(int fromIndex, int toIndex) { + return Collections.synchronizedList(super.subList(fromIndex, toIndex), + this); + } + + /** + * Removes from this list all of the elements whose index is between + * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive. + * Shifts any succeeding elements to the left (reduces their index). + * This call shortens the list by {@code (toIndex - fromIndex)} elements. + * (If {@code toIndex==fromIndex}, this operation has no effect.) + */ + protected synchronized void removeRange(int fromIndex, int toIndex) { + modCount++; + int numMoved = elementCount - toIndex; + System.arraycopy(elementData, toIndex, elementData, fromIndex, + numMoved); + + // Let gc do its work + int newElementCount = elementCount - (toIndex-fromIndex); + while (elementCount != newElementCount) + elementData[--elementCount] = null; + } + + /** + * Returns a list iterator over the elements in this list (in proper + * sequence), starting at the specified position in the list. + * The specified index indicates the first element that would be + * returned by an initial call to {@link ListIterator#next next}. + * An initial call to {@link ListIterator#previous previous} would + * return the element with the specified index minus one. + * + *

The returned list iterator is fail-fast. + * + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public synchronized ListIterator listIterator(int index) { + if (index < 0 || index > elementCount) + throw new IndexOutOfBoundsException("Index: "+index); + return new ListItr(index); + } + + /** + * Returns a list iterator over the elements in this list (in proper + * sequence). + * + *

The returned list iterator is fail-fast. + * + * @see #listIterator(int) + */ + public synchronized ListIterator listIterator() { + return new ListItr(0); + } + + /** + * Returns an iterator over the elements in this list in proper sequence. + * + *

The returned iterator is fail-fast. + * + * @return an iterator over the elements in this list in proper sequence + */ + public synchronized Iterator iterator() { + return new Itr(); + } + + /** + * An optimized version of AbstractList.Itr + */ + private class Itr implements Iterator { + int cursor; // index of next element to return + int lastRet = -1; // index of last element returned; -1 if no such + int expectedModCount = modCount; + + public boolean hasNext() { + // Racy but within spec, since modifications are checked + // within or after synchronization in next/previous + return cursor != elementCount; + } + + public E next() { + synchronized (Vector.this) { + checkForComodification(); + int i = cursor; + if (i >= elementCount) + throw new NoSuchElementException(); + cursor = i + 1; + return elementData(lastRet = i); + } + } + + public void remove() { + if (lastRet == -1) + throw new IllegalStateException(); + synchronized (Vector.this) { + checkForComodification(); + Vector.this.remove(lastRet); + expectedModCount = modCount; + } + cursor = lastRet; + lastRet = -1; + } + + final void checkForComodification() { + if (modCount != expectedModCount) + throw new ConcurrentModificationException(); + } + } + + /** + * An optimized version of AbstractList.ListItr + */ + final class ListItr extends Itr implements ListIterator { + ListItr(int index) { + super(); + cursor = index; + } + + public boolean hasPrevious() { + return cursor != 0; + } + + public int nextIndex() { + return cursor; + } + + public int previousIndex() { + return cursor - 1; + } + + public E previous() { + synchronized (Vector.this) { + checkForComodification(); + int i = cursor - 1; + if (i < 0) + throw new NoSuchElementException(); + cursor = i; + return elementData(lastRet = i); + } + } + + public void set(E e) { + if (lastRet == -1) + throw new IllegalStateException(); + synchronized (Vector.this) { + checkForComodification(); + Vector.this.set(lastRet, e); + } + } + + public void add(E e) { + int i = cursor; + synchronized (Vector.this) { + checkForComodification(); + Vector.this.add(i, e); + expectedModCount = modCount; + } + cursor = i + 1; + lastRet = -1; + } + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/concurrent/Callable.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/concurrent/Callable.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,65 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +package java.util.concurrent; + +/** + * A task that returns a result and may throw an exception. + * Implementors define a single method with no arguments called + * call. + * + *

The Callable interface is similar to {@link + * java.lang.Runnable}, in that both are designed for classes whose + * instances are potentially executed by another thread. A + * Runnable, however, does not return a result and cannot + * throw a checked exception. + * + *

The {@link Executors} class contains utility methods to + * convert from other common forms to Callable classes. + * + * @see Executor + * @since 1.5 + * @author Doug Lea + * @param the result type of method call + */ +public interface Callable { + /** + * Computes a result, or throws an exception if unable to do so. + * + * @return computed result + * @throws Exception if unable to compute a result + */ + V call() throws Exception; +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/main/java/java/util/concurrent/TimeUnit.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/concurrent/TimeUnit.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,367 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +package java.util.concurrent; + +/** + * A TimeUnit represents time durations at a given unit of + * granularity and provides utility methods to convert across units, + * and to perform timing and delay operations in these units. A + * TimeUnit does not maintain time information, but only + * helps organize and use time representations that may be maintained + * separately across various contexts. A nanosecond is defined as one + * thousandth of a microsecond, a microsecond as one thousandth of a + * millisecond, a millisecond as one thousandth of a second, a minute + * as sixty seconds, an hour as sixty minutes, and a day as twenty four + * hours. + * + *

A TimeUnit is mainly used to inform time-based methods + * how a given timing parameter should be interpreted. For example, + * the following code will timeout in 50 milliseconds if the {@link + * java.util.concurrent.locks.Lock lock} is not available: + * + *

  Lock lock = ...;
+ *  if (lock.tryLock(50L, TimeUnit.MILLISECONDS)) ...
+ * 
+ * while this code will timeout in 50 seconds: + *
+ *  Lock lock = ...;
+ *  if (lock.tryLock(50L, TimeUnit.SECONDS)) ...
+ * 
+ * + * Note however, that there is no guarantee that a particular timeout + * implementation will be able to notice the passage of time at the + * same granularity as the given TimeUnit. + * + * @since 1.5 + * @author Doug Lea + */ +public enum TimeUnit { + NANOSECONDS { + public long toNanos(long d) { return d; } + public long toMicros(long d) { return d/(C1/C0); } + public long toMillis(long d) { return d/(C2/C0); } + public long toSeconds(long d) { return d/(C3/C0); } + public long toMinutes(long d) { return d/(C4/C0); } + public long toHours(long d) { return d/(C5/C0); } + public long toDays(long d) { return d/(C6/C0); } + public long convert(long d, TimeUnit u) { return u.toNanos(d); } + int excessNanos(long d, long m) { return (int)(d - (m*C2)); } + }, + MICROSECONDS { + public long toNanos(long d) { return x(d, C1/C0, MAX/(C1/C0)); } + public long toMicros(long d) { return d; } + public long toMillis(long d) { return d/(C2/C1); } + public long toSeconds(long d) { return d/(C3/C1); } + public long toMinutes(long d) { return d/(C4/C1); } + public long toHours(long d) { return d/(C5/C1); } + public long toDays(long d) { return d/(C6/C1); } + public long convert(long d, TimeUnit u) { return u.toMicros(d); } + int excessNanos(long d, long m) { return (int)((d*C1) - (m*C2)); } + }, + MILLISECONDS { + public long toNanos(long d) { return x(d, C2/C0, MAX/(C2/C0)); } + public long toMicros(long d) { return x(d, C2/C1, MAX/(C2/C1)); } + public long toMillis(long d) { return d; } + public long toSeconds(long d) { return d/(C3/C2); } + public long toMinutes(long d) { return d/(C4/C2); } + public long toHours(long d) { return d/(C5/C2); } + public long toDays(long d) { return d/(C6/C2); } + public long convert(long d, TimeUnit u) { return u.toMillis(d); } + int excessNanos(long d, long m) { return 0; } + }, + SECONDS { + public long toNanos(long d) { return x(d, C3/C0, MAX/(C3/C0)); } + public long toMicros(long d) { return x(d, C3/C1, MAX/(C3/C1)); } + public long toMillis(long d) { return x(d, C3/C2, MAX/(C3/C2)); } + public long toSeconds(long d) { return d; } + public long toMinutes(long d) { return d/(C4/C3); } + public long toHours(long d) { return d/(C5/C3); } + public long toDays(long d) { return d/(C6/C3); } + public long convert(long d, TimeUnit u) { return u.toSeconds(d); } + int excessNanos(long d, long m) { return 0; } + }, + MINUTES { + public long toNanos(long d) { return x(d, C4/C0, MAX/(C4/C0)); } + public long toMicros(long d) { return x(d, C4/C1, MAX/(C4/C1)); } + public long toMillis(long d) { return x(d, C4/C2, MAX/(C4/C2)); } + public long toSeconds(long d) { return x(d, C4/C3, MAX/(C4/C3)); } + public long toMinutes(long d) { return d; } + public long toHours(long d) { return d/(C5/C4); } + public long toDays(long d) { return d/(C6/C4); } + public long convert(long d, TimeUnit u) { return u.toMinutes(d); } + int excessNanos(long d, long m) { return 0; } + }, + HOURS { + public long toNanos(long d) { return x(d, C5/C0, MAX/(C5/C0)); } + public long toMicros(long d) { return x(d, C5/C1, MAX/(C5/C1)); } + public long toMillis(long d) { return x(d, C5/C2, MAX/(C5/C2)); } + public long toSeconds(long d) { return x(d, C5/C3, MAX/(C5/C3)); } + public long toMinutes(long d) { return x(d, C5/C4, MAX/(C5/C4)); } + public long toHours(long d) { return d; } + public long toDays(long d) { return d/(C6/C5); } + public long convert(long d, TimeUnit u) { return u.toHours(d); } + int excessNanos(long d, long m) { return 0; } + }, + DAYS { + public long toNanos(long d) { return x(d, C6/C0, MAX/(C6/C0)); } + public long toMicros(long d) { return x(d, C6/C1, MAX/(C6/C1)); } + public long toMillis(long d) { return x(d, C6/C2, MAX/(C6/C2)); } + public long toSeconds(long d) { return x(d, C6/C3, MAX/(C6/C3)); } + public long toMinutes(long d) { return x(d, C6/C4, MAX/(C6/C4)); } + public long toHours(long d) { return x(d, C6/C5, MAX/(C6/C5)); } + public long toDays(long d) { return d; } + public long convert(long d, TimeUnit u) { return u.toDays(d); } + int excessNanos(long d, long m) { return 0; } + }; + + // Handy constants for conversion methods + static final long C0 = 1L; + static final long C1 = C0 * 1000L; + static final long C2 = C1 * 1000L; + static final long C3 = C2 * 1000L; + static final long C4 = C3 * 60L; + static final long C5 = C4 * 60L; + static final long C6 = C5 * 24L; + + static final long MAX = Long.MAX_VALUE; + + /** + * Scale d by m, checking for overflow. + * This has a short name to make above code more readable. + */ + static long x(long d, long m, long over) { + if (d > over) return Long.MAX_VALUE; + if (d < -over) return Long.MIN_VALUE; + return d * m; + } + + // To maintain full signature compatibility with 1.5, and to improve the + // clarity of the generated javadoc (see 6287639: Abstract methods in + // enum classes should not be listed as abstract), method convert + // etc. are not declared abstract but otherwise act as abstract methods. + + /** + * Convert the given time duration in the given unit to this + * unit. Conversions from finer to coarser granularities + * truncate, so lose precision. For example converting + * 999 milliseconds to seconds results in + * 0. Conversions from coarser to finer granularities + * with arguments that would numerically overflow saturate to + * Long.MIN_VALUE if negative or Long.MAX_VALUE + * if positive. + * + *

For example, to convert 10 minutes to milliseconds, use: + * TimeUnit.MILLISECONDS.convert(10L, TimeUnit.MINUTES) + * + * @param sourceDuration the time duration in the given sourceUnit + * @param sourceUnit the unit of the sourceDuration argument + * @return the converted duration in this unit, + * or Long.MIN_VALUE if conversion would negatively + * overflow, or Long.MAX_VALUE if it would positively overflow. + */ + public long convert(long sourceDuration, TimeUnit sourceUnit) { + throw new AbstractMethodError(); + } + + /** + * Equivalent to NANOSECONDS.convert(duration, this). + * @param duration the duration + * @return the converted duration, + * or Long.MIN_VALUE if conversion would negatively + * overflow, or Long.MAX_VALUE if it would positively overflow. + * @see #convert + */ + public long toNanos(long duration) { + throw new AbstractMethodError(); + } + + /** + * Equivalent to MICROSECONDS.convert(duration, this). + * @param duration the duration + * @return the converted duration, + * or Long.MIN_VALUE if conversion would negatively + * overflow, or Long.MAX_VALUE if it would positively overflow. + * @see #convert + */ + public long toMicros(long duration) { + throw new AbstractMethodError(); + } + + /** + * Equivalent to MILLISECONDS.convert(duration, this). + * @param duration the duration + * @return the converted duration, + * or Long.MIN_VALUE if conversion would negatively + * overflow, or Long.MAX_VALUE if it would positively overflow. + * @see #convert + */ + public long toMillis(long duration) { + throw new AbstractMethodError(); + } + + /** + * Equivalent to SECONDS.convert(duration, this). + * @param duration the duration + * @return the converted duration, + * or Long.MIN_VALUE if conversion would negatively + * overflow, or Long.MAX_VALUE if it would positively overflow. + * @see #convert + */ + public long toSeconds(long duration) { + throw new AbstractMethodError(); + } + + /** + * Equivalent to MINUTES.convert(duration, this). + * @param duration the duration + * @return the converted duration, + * or Long.MIN_VALUE if conversion would negatively + * overflow, or Long.MAX_VALUE if it would positively overflow. + * @see #convert + * @since 1.6 + */ + public long toMinutes(long duration) { + throw new AbstractMethodError(); + } + + /** + * Equivalent to HOURS.convert(duration, this). + * @param duration the duration + * @return the converted duration, + * or Long.MIN_VALUE if conversion would negatively + * overflow, or Long.MAX_VALUE if it would positively overflow. + * @see #convert + * @since 1.6 + */ + public long toHours(long duration) { + throw new AbstractMethodError(); + } + + /** + * Equivalent to DAYS.convert(duration, this). + * @param duration the duration + * @return the converted duration + * @see #convert + * @since 1.6 + */ + public long toDays(long duration) { + throw new AbstractMethodError(); + } + + /** + * Utility to compute the excess-nanosecond argument to wait, + * sleep, join. + * @param d the duration + * @param m the number of milliseconds + * @return the number of nanoseconds + */ + abstract int excessNanos(long d, long m); + + /** + * Performs a timed {@link Object#wait(long, int) Object.wait} + * using this time unit. + * This is a convenience method that converts timeout arguments + * into the form required by the Object.wait method. + * + *

For example, you could implement a blocking poll + * method (see {@link BlockingQueue#poll BlockingQueue.poll}) + * using: + * + *

 {@code
+     * public synchronized Object poll(long timeout, TimeUnit unit)
+     *     throws InterruptedException {
+     *   while (empty) {
+     *     unit.timedWait(this, timeout);
+     *     ...
+     *   }
+     * }}
+ * + * @param obj the object to wait on + * @param timeout the maximum time to wait. If less than + * or equal to zero, do not wait at all. + * @throws InterruptedException if interrupted while waiting + */ + public void timedWait(Object obj, long timeout) + throws InterruptedException { + if (timeout > 0) { + long ms = toMillis(timeout); + int ns = excessNanos(timeout, ms); + obj.wait(ms, ns); + } + } + + /** + * Performs a timed {@link Thread#join(long, int) Thread.join} + * using this time unit. + * This is a convenience method that converts time arguments into the + * form required by the Thread.join method. + * + * @param thread the thread to wait for + * @param timeout the maximum time to wait. If less than + * or equal to zero, do not wait at all. + * @throws InterruptedException if interrupted while waiting + */ +// public void timedJoin(Thread thread, long timeout) +// throws InterruptedException { +// if (timeout > 0) { +// long ms = toMillis(timeout); +// int ns = excessNanos(timeout, ms); +// thread.join(ms, ns); +// } +// } + + /** + * Performs a {@link Thread#sleep(long, int) Thread.sleep} using + * this time unit. + * This is a convenience method that converts time arguments into the + * form required by the Thread.sleep method. + * + * @param timeout the minimum time to sleep. If less than + * or equal to zero, do not sleep at all. + * @throws InterruptedException if interrupted while sleeping + */ + public void sleep(long timeout) throws InterruptedException { + if (timeout > 0) { + long ms = toMillis(timeout); + int ns = excessNanos(timeout, ms); + Object o = new Object(); + synchronized (o) { + o.wait(ms, ns); + } + } + } + +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/CollectionsTest.java --- a/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/CollectionsTest.java Tue Feb 05 16:40:01 2013 +0100 +++ b/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/CollectionsTest.java Tue Feb 05 17:04:22 2013 +0100 @@ -25,6 +25,8 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Vector; import org.apidesign.bck2brwsr.vmtest.Compare; import org.apidesign.bck2brwsr.vmtest.VMTest; import org.testng.annotations.Factory; @@ -92,10 +94,9 @@ map.put("nine", 9); map.put("ten", 10); - Map.Entry[] arr = map.entrySet().toArray(new Map.Entry[map.size()]); - Arrays.sort(arr, new C()); - - return Arrays.asList(arr).toString(); + List> arr = new Vector<>(); + arr.addAll(map.entrySet()); + return arr.toString(); } @Factory diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/RandomTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/RandomTest.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,40 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program 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, version 2 of the License. + * + * This program 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 this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.compact.tck; + +import java.util.Random; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class RandomTest { + @Compare public boolean canInstantiateRandom() { + Random r = new Random(); + r.nextInt(); + return r != null; + } + + + @Factory public static Object[] create() { + return VMTest.create(RandomTest.class); + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ReaderTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ReaderTest.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,60 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program 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, version 2 of the License. + * + * This program 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 this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.compact.tck; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Arrays; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class ReaderTest { + @Compare public String readUTFString() throws IOException { + byte[] arr = { + (byte)-59, (byte)-67, (byte)108, (byte)117, (byte)-59, (byte)-91, + (byte)111, (byte)117, (byte)-60, (byte)-115, (byte)107, (byte)-61, + (byte)-67, (byte)32, (byte)107, (byte)-59, (byte)-81, (byte)-59, + (byte)-120 + }; + ByteArrayInputStream is = new ByteArrayInputStream(arr); + InputStreamReader r = new InputStreamReader(is); + + StringBuilder sb = new StringBuilder(); + for (;;) { + int ch = r.read(); + if (ch == -1) { + break; + } + sb.append((char)ch); + } + return sb.toString().toString(); + } + @Compare public String stringToBytes() { + return Arrays.toString("Žluťoučký kůň".getBytes()); + } + + @Factory public static Object[] create() { + return VMTest.create(ReaderTest.class); + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/lang/ArrayStoreException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/java/lang/ArrayStoreException.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * Thrown to indicate that an attempt has been made to store the + * wrong type of object into an array of objects. For example, the + * following code generates an ArrayStoreException: + *

+ *     Object x[] = new String[3];
+ *     x[0] = new Integer(0);
+ * 
+ * + * @author unascribed + * @since JDK1.0 + */ +public +class ArrayStoreException extends RuntimeException { + private static final long serialVersionUID = -4522193890499838241L; + + /** + * Constructs an ArrayStoreException with no detail message. + */ + public ArrayStoreException() { + super(); + } + + /** + * Constructs an ArrayStoreException with the specified + * detail message. + * + * @param s the detail message. + */ + public ArrayStoreException(String s) { + super(s); + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/lang/Class.java --- a/emul/mini/src/main/java/java/lang/Class.java Tue Feb 05 16:40:01 2013 +0100 +++ b/emul/mini/src/main/java/java/lang/Class.java Tue Feb 05 17:04:22 2013 +0100 @@ -347,6 +347,9 @@ * @since JDK1.1 */ public boolean isInstance(Object obj) { + if (obj == null) { + return false; + } if (isArray()) { return isAssignableFrom(obj.getClass()); } @@ -909,7 +912,50 @@ } return m; } - + + /** + * Returns an array of {@code Method} objects reflecting all the + * methods declared by the class or interface represented by this + * {@code Class} object. This includes public, protected, default + * (package) access, and private methods, but excludes inherited methods. + * The elements in the array returned are not sorted and are not in any + * particular order. This method returns an array of length 0 if the class + * or interface declares no methods, or if this {@code Class} object + * represents a primitive type, an array class, or void. The class + * initialization method {@code } is not included in the + * returned array. If the class declares multiple public member methods + * with the same parameter types, they are all included in the returned + * array. + * + *

See The Java Language Specification, section 8.2. + * + * @return the array of {@code Method} objects representing all the + * declared methods of this class + * @exception SecurityException + * If a security manager, s, is present and any of the + * following conditions is met: + * + *

    + * + *
  • invocation of + * {@link SecurityManager#checkMemberAccess + * s.checkMemberAccess(this, Member.DECLARED)} denies + * access to the declared methods within this class + * + *
  • the caller's class loader is not the same as or an + * ancestor of the class loader for the current class and + * invocation of {@link SecurityManager#checkPackageAccess + * s.checkPackageAccess()} denies access to the package + * of this class + * + *
+ * + * @since JDK1.1 + */ + public Method[] getDeclaredMethods() throws SecurityException { + throw new SecurityException(); + } + /** * Character.isDigit answers {@code true} to some non-ascii * digits. This one does not. @@ -1096,6 +1142,48 @@ public ClassLoader getClassLoader() { throw new SecurityException(); } + + /** + * Determines the interfaces implemented by the class or interface + * represented by this object. + * + *

If this object represents a class, the return value is an array + * containing objects representing all interfaces implemented by the + * class. The order of the interface objects in the array corresponds to + * the order of the interface names in the {@code implements} clause + * of the declaration of the class represented by this object. For + * example, given the declaration: + *

+ * {@code class Shimmer implements FloorWax, DessertTopping { ... }} + *
+ * suppose the value of {@code s} is an instance of + * {@code Shimmer}; the value of the expression: + *
+ * {@code s.getClass().getInterfaces()[0]} + *
+ * is the {@code Class} object that represents interface + * {@code FloorWax}; and the value of: + *
+ * {@code s.getClass().getInterfaces()[1]} + *
+ * is the {@code Class} object that represents interface + * {@code DessertTopping}. + * + *

If this object represents an interface, the array contains objects + * representing all interfaces extended by the interface. The order of the + * interface objects in the array corresponds to the order of the interface + * names in the {@code extends} clause of the declaration of the + * interface represented by this object. + * + *

If this object represents a class or interface that implements no + * interfaces, the method returns an array of length 0. + * + *

If this object represents a primitive type or void, the method + * returns an array of length 0. + * + * @return an array of interfaces implemented by this class. + */ + public native Class[] getInterfaces(); /** * Returns the {@code Class} representing the component type of an diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/lang/Cloneable.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/java/lang/Cloneable.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1995, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * A class implements the Cloneable interface to + * indicate to the {@link java.lang.Object#clone()} method that it + * is legal for that method to make a + * field-for-field copy of instances of that class. + *

+ * Invoking Object's clone method on an instance that does not implement the + * Cloneable interface results in the exception + * CloneNotSupportedException being thrown. + *

+ * By convention, classes that implement this interface should override + * Object.clone (which is protected) with a public method. + * See {@link java.lang.Object#clone()} for details on overriding this + * method. + *

+ * Note that this interface does not contain the clone method. + * Therefore, it is not possible to clone an object merely by virtue of the + * fact that it implements this interface. Even if the clone method is invoked + * reflectively, there is no guarantee that it will succeed. + * + * @author unascribed + * @see java.lang.CloneNotSupportedException + * @see java.lang.Object#clone() + * @since JDK1.0 + */ +public interface Cloneable { +} diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/lang/IllegalAccessError.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/java/lang/IllegalAccessError.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * Thrown if an application attempts to access or modify a field, or + * to call a method that it does not have access to. + *

+ * Normally, this error is caught by the compiler; this error can + * only occur at run time if the definition of a class has + * incompatibly changed. + * + * @author unascribed + * @since JDK1.0 + */ +public class IllegalAccessError extends IncompatibleClassChangeError { + private static final long serialVersionUID = -8988904074992417891L; + + /** + * Constructs an IllegalAccessError with no detail message. + */ + public IllegalAccessError() { + super(); + } + + /** + * Constructs an IllegalAccessError with the specified + * detail message. + * + * @param s the detail message. + */ + public IllegalAccessError(String s) { + super(s); + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/lang/IncompatibleClassChangeError.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/java/lang/IncompatibleClassChangeError.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 1994, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * Thrown when an incompatible class change has occurred to some class + * definition. The definition of some class, on which the currently + * executing method depends, has since changed. + * + * @author unascribed + * @since JDK1.0 + */ +public +class IncompatibleClassChangeError extends LinkageError { + private static final long serialVersionUID = -4914975503642802119L; + + /** + * Constructs an IncompatibleClassChangeError with no + * detail message. + */ + public IncompatibleClassChangeError () { + super(); + } + + /** + * Constructs an IncompatibleClassChangeError with the + * specified detail message. + * + * @param s the detail message. + */ + public IncompatibleClassChangeError(String s) { + super(s); + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/lang/Math.java --- a/emul/mini/src/main/java/java/lang/Math.java Tue Feb 05 16:40:01 2013 +0100 +++ b/emul/mini/src/main/java/java/lang/Math.java Tue Feb 05 17:04:22 2013 +0100 @@ -375,6 +375,68 @@ public static double floor(double a) { throw new UnsupportedOperationException(); } + /** + * Computes the remainder operation on two arguments as prescribed + * by the IEEE 754 standard. + * The remainder value is mathematically equal to + * f1 - f2 × n, + * where n is the mathematical integer closest to the exact + * mathematical value of the quotient {@code f1/f2}, and if two + * mathematical integers are equally close to {@code f1/f2}, + * then n is the integer that is even. If the remainder is + * zero, its sign is the same as the sign of the first argument. + * Special cases: + *

  • If either argument is NaN, or the first argument is infinite, + * or the second argument is positive zero or negative zero, then the + * result is NaN. + *
  • If the first argument is finite and the second argument is + * infinite, then the result is the same as the first argument.
+ * + * @param f1 the dividend. + * @param f2 the divisor. + * @return the remainder when {@code f1} is divided by + * {@code f2}. + */ + public static double IEEEremainder(double f1, double f2) { + return f1 - (f2 * Math.round(f1 / f2)); + } + + /** + * Returns the {@code double} value that is closest in value + * to the argument and is equal to a mathematical integer. If two + * {@code double} values that are mathematical integers are + * equally close, the result is the integer value that is + * even. Special cases: + *
  • If the argument value is already equal to a mathematical + * integer, then the result is the same as the argument. + *
  • If the argument is NaN or an infinity or positive zero or negative + * zero, then the result is the same as the argument.
+ * + * @param a a {@code double} value. + * @return the closest floating-point value to {@code a} that is + * equal to a mathematical integer. + */ + public static double rint(double a) { + double ceil = ceil(a); + double floor = floor(a); + + double dc = ceil - a; + double df = a - floor; + + if (dc < df) { + return ceil; + } else if (dc > df) { + return floor; + } + + int tenC = (int) (ceil % 10.0); + + if (tenC % 2 == 0) { + return ceil; + } else { + return floor; + } + } /** * Returns the angle theta from the conversion of rectangular @@ -929,9 +991,11 @@ * @author Joseph D. Darcy * @since 1.5 */ -// public static double signum(double d) { -// return sun.misc.FpUtils.signum(d); -// } + public static double signum(double d) { + if (d < 0.0) { return -1.0; } + if (d > 0.0) { return 1.0; } + return d; + } /** * Returns the signum function of the argument; zero if the argument @@ -950,9 +1014,11 @@ * @author Joseph D. Darcy * @since 1.5 */ -// public static float signum(float f) { -// return sun.misc.FpUtils.signum(f); -// } + public static float signum(float f) { + if (f < 0.0f) { return -1.0f; } + if (f > 0.0f) { return 1.0f; } + return f; + } /** * Returns the first floating-point argument with the sign of the diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/lang/Override.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/java/lang/Override.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +import java.lang.annotation.*; + +/** + * Indicates that a method declaration is intended to override a + * method declaration in a supertype. If a method is annotated with + * this annotation type compilers are required to generate an error + * message unless at least one of the following conditions hold: + * + *
  • + * The method does override or implement a method declared in a + * supertype. + *
  • + * The method has a signature that is override-equivalent to that of + * any public method declared in {@linkplain Object}. + *
+ * + * @author Peter von der Ahé + * @author Joshua Bloch + * @jls 9.6.1.4 Override + * @since 1.5 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.SOURCE) +public @interface Override { +} diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/lang/String.java --- a/emul/mini/src/main/java/java/lang/String.java Tue Feb 05 16:40:01 2013 +0100 +++ b/emul/mini/src/main/java/java/lang/String.java Tue Feb 05 17:04:22 2013 +0100 @@ -25,6 +25,7 @@ package java.lang; +import java.io.UnsupportedEncodingException; import java.util.Comparator; import org.apidesign.bck2brwsr.core.ExtraJavaScript; import org.apidesign.bck2brwsr.core.JavaScriptBody; @@ -200,6 +201,10 @@ * If the {@code offset} and {@code count} arguments index * characters outside the bounds of the {@code value} array */ + public String(char value[], int offset, int count) { + initFromCharArray(value, offset, count); + } + @JavaScriptBody(args = { "charArr", "off", "cnt" }, body = "var up = off + cnt;\n" + "for (var i = off; i < up; i++) {\n" + @@ -207,8 +212,7 @@ "}\n" + "this._r(charArr.slice(off, up).join(\"\"));\n" ) - public String(char value[], int offset, int count) { - } + private native void initFromCharArray(char value[], int offset, int count); /** * Allocates a new {@code String} that contains characters from a subarray @@ -415,17 +419,11 @@ * * @since JDK1.1 */ -// public String(byte bytes[], int offset, int length, String charsetName) -// throws UnsupportedEncodingException -// { -// if (charsetName == null) -// throw new NullPointerException("charsetName"); -// checkBounds(bytes, offset, length); -// char[] v = StringCoding.decode(charsetName, bytes, offset, length); -// this.offset = 0; -// this.count = v.length; -// this.value = v; -// } + public String(byte bytes[], int offset, int length, String charsetName) + throws UnsupportedEncodingException + { + this(checkUTF8(bytes, charsetName), offset, length); + } /** * Constructs a new {@code String} by decoding the specified subarray of @@ -492,11 +490,11 @@ * * @since JDK1.1 */ -// public String(byte bytes[], String charsetName) -// throws UnsupportedEncodingException -// { -// this(bytes, 0, bytes.length, charsetName); -// } + public String(byte bytes[], String charsetName) + throws UnsupportedEncodingException + { + this(bytes, 0, bytes.length, charsetName); + } /** * Constructs a new {@code String} by decoding the specified array of @@ -553,10 +551,14 @@ public String(byte bytes[], int offset, int length) { checkBounds(bytes, offset, length); char[] v = new char[length]; - for (int i = 0; i < length; i++) { - v[i] = (char)bytes[offset++]; + int[] at = { offset }; + int end = offset + length; + int chlen = 0; + while (at[0] < end) { + int ch = nextChar(bytes, at); + v[chlen++] = (char)ch; } - this.r = new String(v, 0, v.length); + initFromCharArray(v, 0, chlen); } /** @@ -925,12 +927,12 @@ * * @since JDK1.1 */ -// public byte[] getBytes(String charsetName) -// throws UnsupportedEncodingException -// { -// if (charsetName == null) throw new NullPointerException(); -// return StringCoding.encode(charsetName, value, offset, count); -// } + public byte[] getBytes(String charsetName) + throws UnsupportedEncodingException + { + checkUTF8(null, charsetName); + return getBytes(); + } /** * Encodes this {@code String} into a sequence of bytes using the given @@ -971,10 +973,24 @@ * @since JDK1.1 */ public byte[] getBytes() { - byte[] arr = new byte[length()]; - for (int i = 0; i < arr.length; i++) { - final char v = charAt(i); - arr[i] = (byte)v; + int len = length(); + byte[] arr = new byte[len]; + for (int i = 0, j = 0; j < len; j++) { + final int v = charAt(j); + if (v < 128) { + arr[i++] = (byte) v; + continue; + } + if (v < 0x0800) { + arr = System.expandArray(arr, i + 1); + arr[i++] = (byte) (0xC0 | (v >> 6)); + arr[i++] = (byte) (0x80 | (0x3F & v)); + continue; + } + arr = System.expandArray(arr, i + 2); + arr[i++] = (byte) (0xE0 | (v >> 12)); + arr[i++] = (byte) (0x80 | ((v >> 6) & 0x7F)); + arr[i++] = (byte) (0x80 | (0x3F & v)); } return arr; } @@ -1210,7 +1226,7 @@ private static int offset() { return 0; } - + private static class CaseInsensitiveComparator implements Comparator, java.io.Serializable { // use serialVersionUID from JDK 1.2.2 for interoperability @@ -3007,4 +3023,57 @@ * guaranteed to be from a pool of unique strings. */ public native String intern(); + + + private static T checkUTF8(T data, String charsetName) + throws UnsupportedEncodingException { + if (charsetName == null) { + throw new NullPointerException("charsetName"); + } + if (!charsetName.equalsIgnoreCase("UTF-8") + && !charsetName.equalsIgnoreCase("UTF8")) { + throw new UnsupportedEncodingException(charsetName); + } + return data; + } + + private static int nextChar(byte[] arr, int[] index) throws IndexOutOfBoundsException { + int c = arr[index[0]++] & 0xff; + switch (c >> 4) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + /* 0xxxxxxx*/ + return c; + case 12: + case 13: { + /* 110x xxxx 10xx xxxx*/ + int char2 = (int) arr[index[0]++]; + if ((char2 & 0xC0) != 0x80) { + throw new IndexOutOfBoundsException("malformed input"); + } + return (((c & 0x1F) << 6) | (char2 & 0x3F)); + } + case 14: { + /* 1110 xxxx 10xx xxxx 10xx xxxx */ + int char2 = arr[index[0]++]; + int char3 = arr[index[0]++]; + if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) { + throw new IndexOutOfBoundsException("malformed input"); + } + return (((c & 0x0F) << 12) + | ((char2 & 0x3F) << 6) + | ((char3 & 0x3F) << 0)); + } + default: + /* 10xx xxxx, 1111 xxxx */ + throw new IndexOutOfBoundsException("malformed input"); + } + + } } diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/lang/reflect/Constructor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/java/lang/reflect/Constructor.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,567 @@ +/* + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.reflect; + +import java.lang.annotation.Annotation; +import org.apidesign.bck2brwsr.emul.reflect.TypeProvider; + +/** + * {@code Constructor} provides information about, and access to, a single + * constructor for a class. + * + *

{@code Constructor} permits widening conversions to occur when matching the + * actual parameters to newInstance() with the underlying + * constructor's formal parameters, but throws an + * {@code IllegalArgumentException} if a narrowing conversion would occur. + * + * @param the class in which the constructor is declared + * + * @see Member + * @see java.lang.Class + * @see java.lang.Class#getConstructors() + * @see java.lang.Class#getConstructor(Class[]) + * @see java.lang.Class#getDeclaredConstructors() + * + * @author Kenneth Russell + * @author Nakul Saraiya + */ +public final + class Constructor extends AccessibleObject implements + GenericDeclaration, + Member { + + private Class clazz; + private int slot; + private Class[] parameterTypes; + private Class[] exceptionTypes; + private int modifiers; + // Generics and annotations support + private transient String signature; + private byte[] annotations; + private byte[] parameterAnnotations; + + + // For sharing of ConstructorAccessors. This branching structure + // is currently only two levels deep (i.e., one root Constructor + // and potentially many Constructor objects pointing to it.) + private Constructor root; + + /** + * Package-private constructor used by ReflectAccess to enable + * instantiation of these objects in Java code from the java.lang + * package via sun.reflect.LangReflectAccess. + */ + Constructor(Class declaringClass, + Class[] parameterTypes, + Class[] checkedExceptions, + int modifiers, + int slot, + String signature, + byte[] annotations, + byte[] parameterAnnotations) + { + this.clazz = declaringClass; + this.parameterTypes = parameterTypes; + this.exceptionTypes = checkedExceptions; + this.modifiers = modifiers; + this.slot = slot; + this.signature = signature; + this.annotations = annotations; + this.parameterAnnotations = parameterAnnotations; + } + + /** + * Package-private routine (exposed to java.lang.Class via + * ReflectAccess) which returns a copy of this Constructor. The copy's + * "root" field points to this Constructor. + */ + Constructor copy() { + return this; + } + + /** + * Returns the {@code Class} object representing the class that declares + * the constructor represented by this {@code Constructor} object. + */ + public Class getDeclaringClass() { + return clazz; + } + + /** + * Returns the name of this constructor, as a string. This is + * the binary name of the constructor's declaring class. + */ + public String getName() { + return getDeclaringClass().getName(); + } + + /** + * Returns the Java language modifiers for the constructor + * represented by this {@code Constructor} object, as an integer. The + * {@code Modifier} class should be used to decode the modifiers. + * + * @see Modifier + */ + public int getModifiers() { + return modifiers; + } + + /** + * Returns an array of {@code TypeVariable} objects that represent the + * type variables declared by the generic declaration represented by this + * {@code GenericDeclaration} object, in declaration order. Returns an + * array of length 0 if the underlying generic declaration declares no type + * variables. + * + * @return an array of {@code TypeVariable} objects that represent + * the type variables declared by this generic declaration + * @throws GenericSignatureFormatError if the generic + * signature of this generic declaration does not conform to + * the format specified in + * The Java™ Virtual Machine Specification + * @since 1.5 + */ + public TypeVariable>[] getTypeParameters() { + return TypeProvider.getDefault().getTypeParameters(this); + } + + + /** + * Returns an array of {@code Class} objects that represent the formal + * parameter types, in declaration order, of the constructor + * represented by this {@code Constructor} object. Returns an array of + * length 0 if the underlying constructor takes no parameters. + * + * @return the parameter types for the constructor this object + * represents + */ + public Class[] getParameterTypes() { + return (Class[]) parameterTypes.clone(); + } + + + /** + * Returns an array of {@code Type} objects that represent the formal + * parameter types, in declaration order, of the method represented by + * this {@code Constructor} object. Returns an array of length 0 if the + * underlying method takes no parameters. + * + *

If a formal parameter type is a parameterized type, + * the {@code Type} object returned for it must accurately reflect + * the actual type parameters used in the source code. + * + *

If a formal parameter type is a type variable or a parameterized + * type, it is created. Otherwise, it is resolved. + * + * @return an array of {@code Type}s that represent the formal + * parameter types of the underlying method, in declaration order + * @throws GenericSignatureFormatError + * if the generic method signature does not conform to the format + * specified in + * The Java™ Virtual Machine Specification + * @throws TypeNotPresentException if any of the parameter + * types of the underlying method refers to a non-existent type + * declaration + * @throws MalformedParameterizedTypeException if any of + * the underlying method's parameter types refer to a parameterized + * type that cannot be instantiated for any reason + * @since 1.5 + */ + public Type[] getGenericParameterTypes() { + return TypeProvider.getDefault().getGenericParameterTypes(this); + } + + + /** + * Returns an array of {@code Class} objects that represent the types + * of exceptions declared to be thrown by the underlying constructor + * represented by this {@code Constructor} object. Returns an array of + * length 0 if the constructor declares no exceptions in its {@code throws} clause. + * + * @return the exception types declared as being thrown by the + * constructor this object represents + */ + public Class[] getExceptionTypes() { + return (Class[])exceptionTypes.clone(); + } + + + /** + * Returns an array of {@code Type} objects that represent the + * exceptions declared to be thrown by this {@code Constructor} object. + * Returns an array of length 0 if the underlying method declares + * no exceptions in its {@code throws} clause. + * + *

If an exception type is a type variable or a parameterized + * type, it is created. Otherwise, it is resolved. + * + * @return an array of Types that represent the exception types + * thrown by the underlying method + * @throws GenericSignatureFormatError + * if the generic method signature does not conform to the format + * specified in + * The Java™ Virtual Machine Specification + * @throws TypeNotPresentException if the underlying method's + * {@code throws} clause refers to a non-existent type declaration + * @throws MalformedParameterizedTypeException if + * the underlying method's {@code throws} clause refers to a + * parameterized type that cannot be instantiated for any reason + * @since 1.5 + */ + public Type[] getGenericExceptionTypes() { + return TypeProvider.getDefault().getGenericExceptionTypes(this); + } + + /** + * Compares this {@code Constructor} against the specified object. + * Returns true if the objects are the same. Two {@code Constructor} objects are + * the same if they were declared by the same class and have the + * same formal parameter types. + */ + public boolean equals(Object obj) { + if (obj != null && obj instanceof Constructor) { + Constructor other = (Constructor)obj; + if (getDeclaringClass() == other.getDeclaringClass()) { + /* Avoid unnecessary cloning */ + Class[] params1 = parameterTypes; + Class[] params2 = other.parameterTypes; + if (params1.length == params2.length) { + for (int i = 0; i < params1.length; i++) { + if (params1[i] != params2[i]) + return false; + } + return true; + } + } + } + return false; + } + + /** + * Returns a hashcode for this {@code Constructor}. The hashcode is + * the same as the hashcode for the underlying constructor's + * declaring class name. + */ + public int hashCode() { + return getDeclaringClass().getName().hashCode(); + } + + /** + * Returns a string describing this {@code Constructor}. The string is + * formatted as the constructor access modifiers, if any, + * followed by the fully-qualified name of the declaring class, + * followed by a parenthesized, comma-separated list of the + * constructor's formal parameter types. For example: + *

+     *    public java.util.Hashtable(int,float)
+     * 
+ * + *

The only possible modifiers for constructors are the access + * modifiers {@code public}, {@code protected} or + * {@code private}. Only one of these may appear, or none if the + * constructor has default (package) access. + */ + public String toString() { + try { + StringBuffer sb = new StringBuffer(); + int mod = getModifiers() & Modifier.constructorModifiers(); + if (mod != 0) { + sb.append(Modifier.toString(mod) + " "); + } + sb.append(Field.getTypeName(getDeclaringClass())); + sb.append("("); + Class[] params = parameterTypes; // avoid clone + for (int j = 0; j < params.length; j++) { + sb.append(Field.getTypeName(params[j])); + if (j < (params.length - 1)) + sb.append(","); + } + sb.append(")"); + Class[] exceptions = exceptionTypes; // avoid clone + if (exceptions.length > 0) { + sb.append(" throws "); + for (int k = 0; k < exceptions.length; k++) { + sb.append(exceptions[k].getName()); + if (k < (exceptions.length - 1)) + sb.append(","); + } + } + return sb.toString(); + } catch (Exception e) { + return "<" + e + ">"; + } + } + + /** + * Returns a string describing this {@code Constructor}, + * including type parameters. The string is formatted as the + * constructor access modifiers, if any, followed by an + * angle-bracketed comma separated list of the constructor's type + * parameters, if any, followed by the fully-qualified name of the + * declaring class, followed by a parenthesized, comma-separated + * list of the constructor's generic formal parameter types. + * + * If this constructor was declared to take a variable number of + * arguments, instead of denoting the last parameter as + * "Type[]", it is denoted as + * "Type...". + * + * A space is used to separate access modifiers from one another + * and from the type parameters or return type. If there are no + * type parameters, the type parameter list is elided; if the type + * parameter list is present, a space separates the list from the + * class name. If the constructor is declared to throw + * exceptions, the parameter list is followed by a space, followed + * by the word "{@code throws}" followed by a + * comma-separated list of the thrown exception types. + * + *

The only possible modifiers for constructors are the access + * modifiers {@code public}, {@code protected} or + * {@code private}. Only one of these may appear, or none if the + * constructor has default (package) access. + * + * @return a string describing this {@code Constructor}, + * include type parameters + * + * @since 1.5 + */ + public String toGenericString() { + try { + StringBuilder sb = new StringBuilder(); + int mod = getModifiers() & Modifier.constructorModifiers(); + if (mod != 0) { + sb.append(Modifier.toString(mod) + " "); + } + TypeVariable[] typeparms = getTypeParameters(); + if (typeparms.length > 0) { + boolean first = true; + sb.append("<"); + for(TypeVariable typeparm: typeparms) { + if (!first) + sb.append(","); + // Class objects can't occur here; no need to test + // and call Class.getName(). + sb.append(typeparm.toString()); + first = false; + } + sb.append("> "); + } + sb.append(Field.getTypeName(getDeclaringClass())); + sb.append("("); + Type[] params = getGenericParameterTypes(); + for (int j = 0; j < params.length; j++) { + String param = (params[j] instanceof Class)? + Field.getTypeName((Class)params[j]): + (params[j].toString()); + if (isVarArgs() && (j == params.length - 1)) // replace T[] with T... + param = param.replaceFirst("\\[\\]$", "..."); + sb.append(param); + if (j < (params.length - 1)) + sb.append(","); + } + sb.append(")"); + Type[] exceptions = getGenericExceptionTypes(); + if (exceptions.length > 0) { + sb.append(" throws "); + for (int k = 0; k < exceptions.length; k++) { + sb.append((exceptions[k] instanceof Class)? + ((Class)exceptions[k]).getName(): + exceptions[k].toString()); + if (k < (exceptions.length - 1)) + sb.append(","); + } + } + return sb.toString(); + } catch (Exception e) { + return "<" + e + ">"; + } + } + + /** + * Uses the constructor represented by this {@code Constructor} object to + * create and initialize a new instance of the constructor's + * declaring class, with the specified initialization parameters. + * Individual parameters are automatically unwrapped to match + * primitive formal parameters, and both primitive and reference + * parameters are subject to method invocation conversions as necessary. + * + *

If the number of formal parameters required by the underlying constructor + * is 0, the supplied {@code initargs} array may be of length 0 or null. + * + *

If the constructor's declaring class is an inner class in a + * non-static context, the first argument to the constructor needs + * to be the enclosing instance; see section 15.9.3 of + * The Java™ Language Specification. + * + *

If the required access and argument checks succeed and the + * instantiation will proceed, the constructor's declaring class + * is initialized if it has not already been initialized. + * + *

If the constructor completes normally, returns the newly + * created and initialized instance. + * + * @param initargs array of objects to be passed as arguments to + * the constructor call; values of primitive types are wrapped in + * a wrapper object of the appropriate type (e.g. a {@code float} + * in a {@link java.lang.Float Float}) + * + * @return a new object created by calling the constructor + * this object represents + * + * @exception IllegalAccessException if this {@code Constructor} object + * is enforcing Java language access control and the underlying + * constructor is inaccessible. + * @exception IllegalArgumentException if the number of actual + * and formal parameters differ; if an unwrapping + * conversion for primitive arguments fails; or if, + * after possible unwrapping, a parameter value + * cannot be converted to the corresponding formal + * parameter type by a method invocation conversion; if + * this constructor pertains to an enum type. + * @exception InstantiationException if the class that declares the + * underlying constructor represents an abstract class. + * @exception InvocationTargetException if the underlying constructor + * throws an exception. + * @exception ExceptionInInitializerError if the initialization provoked + * by this method fails. + */ + public T newInstance(Object ... initargs) + throws InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException + { + throw new SecurityException(); + } + + /** + * Returns {@code true} if this constructor was declared to take + * a variable number of arguments; returns {@code false} + * otherwise. + * + * @return {@code true} if an only if this constructor was declared to + * take a variable number of arguments. + * @since 1.5 + */ + public boolean isVarArgs() { + return (getModifiers() & Modifier.VARARGS) != 0; + } + + /** + * Returns {@code true} if this constructor is a synthetic + * constructor; returns {@code false} otherwise. + * + * @return true if and only if this constructor is a synthetic + * constructor as defined by + * The Java™ Language Specification. + * @since 1.5 + */ + public boolean isSynthetic() { + return Modifier.isSynthetic(getModifiers()); + } + + int getSlot() { + return slot; + } + + String getSignature() { + return signature; + } + + byte[] getRawAnnotations() { + return annotations; + } + + byte[] getRawParameterAnnotations() { + return parameterAnnotations; + } + + /** + * @throws NullPointerException {@inheritDoc} + * @since 1.5 + */ + public T getAnnotation(Class annotationClass) { + if (annotationClass == null) + throw new NullPointerException(); + + return null; // XXX (T) declaredAnnotations().get(annotationClass); + } + + /** + * @since 1.5 + */ + public Annotation[] getDeclaredAnnotations() { + return new Annotation[0]; // XXX AnnotationParser.toArray(declaredAnnotations()); + } + + /** + * Returns an array of arrays that represent the annotations on the formal + * parameters, in declaration order, of the method represented by + * this {@code Constructor} object. (Returns an array of length zero if the + * underlying method is parameterless. If the method has one or more + * parameters, a nested array of length zero is returned for each parameter + * with no annotations.) The annotation objects contained in the returned + * arrays are serializable. The caller of this method is free to modify + * the returned arrays; it will have no effect on the arrays returned to + * other callers. + * + * @return an array of arrays that represent the annotations on the formal + * parameters, in declaration order, of the method represented by this + * Constructor object + * @since 1.5 + */ + public Annotation[][] getParameterAnnotations() { + int numParameters = parameterTypes.length; + if (parameterAnnotations == null) + return new Annotation[numParameters][0]; + + return new Annotation[numParameters][0]; // XXX +/* + Annotation[][] result = AnnotationParser.parseParameterAnnotations( + parameterAnnotations, + sun.misc.SharedSecrets.getJavaLangAccess(). + getConstantPool(getDeclaringClass()), + getDeclaringClass()); + if (result.length != numParameters) { + Class declaringClass = getDeclaringClass(); + if (declaringClass.isEnum() || + declaringClass.isAnonymousClass() || + declaringClass.isLocalClass() ) + ; // Can't do reliable parameter counting + else { + if (!declaringClass.isMemberClass() || // top-level + // Check for the enclosing instance parameter for + // non-static member classes + (declaringClass.isMemberClass() && + ((declaringClass.getModifiers() & Modifier.STATIC) == 0) && + result.length + 1 != numParameters) ) { + throw new AnnotationFormatError( + "Parameter annotations don't match number of parameters"); + } + } + } + return result; + */ + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/lang/reflect/InvocationHandler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/java/lang/reflect/InvocationHandler.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 1999, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.reflect; + +/** + * {@code InvocationHandler} is the interface implemented by + * the invocation handler of a proxy instance. + * + *

Each proxy instance has an associated invocation handler. + * When a method is invoked on a proxy instance, the method + * invocation is encoded and dispatched to the {@code invoke} + * method of its invocation handler. + * + * @author Peter Jones + * @see Proxy + * @since 1.3 + */ +public interface InvocationHandler { + + /** + * Processes a method invocation on a proxy instance and returns + * the result. This method will be invoked on an invocation handler + * when a method is invoked on a proxy instance that it is + * associated with. + * + * @param proxy the proxy instance that the method was invoked on + * + * @param method the {@code Method} instance corresponding to + * the interface method invoked on the proxy instance. The declaring + * class of the {@code Method} object will be the interface that + * the method was declared in, which may be a superinterface of the + * proxy interface that the proxy class inherits the method through. + * + * @param args an array of objects containing the values of the + * arguments passed in the method invocation on the proxy instance, + * or {@code null} if interface method takes no arguments. + * Arguments of primitive types are wrapped in instances of the + * appropriate primitive wrapper class, such as + * {@code java.lang.Integer} or {@code java.lang.Boolean}. + * + * @return the value to return from the method invocation on the + * proxy instance. If the declared return type of the interface + * method is a primitive type, then the value returned by + * this method must be an instance of the corresponding primitive + * wrapper class; otherwise, it must be a type assignable to the + * declared return type. If the value returned by this method is + * {@code null} and the interface method's return type is + * primitive, then a {@code NullPointerException} will be + * thrown by the method invocation on the proxy instance. If the + * value returned by this method is otherwise not compatible with + * the interface method's declared return type as described above, + * a {@code ClassCastException} will be thrown by the method + * invocation on the proxy instance. + * + * @throws Throwable the exception to throw from the method + * invocation on the proxy instance. The exception's type must be + * assignable either to any of the exception types declared in the + * {@code throws} clause of the interface method or to the + * unchecked exception types {@code java.lang.RuntimeException} + * or {@code java.lang.Error}. If a checked exception is + * thrown by this method that is not assignable to any of the + * exception types declared in the {@code throws} clause of + * the interface method, then an + * {@link UndeclaredThrowableException} containing the + * exception that was thrown by this method will be thrown by the + * method invocation on the proxy instance. + * + * @see UndeclaredThrowableException + */ + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable; +} diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/lang/reflect/Proxy.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/java/lang/reflect/Proxy.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,407 @@ +/* + * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.reflect; + + +/** + * {@code Proxy} provides static methods for creating dynamic proxy + * classes and instances, and it is also the superclass of all + * dynamic proxy classes created by those methods. + * + *

To create a proxy for some interface {@code Foo}: + *

+ *     InvocationHandler handler = new MyInvocationHandler(...);
+ *     Class proxyClass = Proxy.getProxyClass(
+ *         Foo.class.getClassLoader(), new Class[] { Foo.class });
+ *     Foo f = (Foo) proxyClass.
+ *         getConstructor(new Class[] { InvocationHandler.class }).
+ *         newInstance(new Object[] { handler });
+ * 
+ * or more simply: + *
+ *     Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
+ *                                          new Class[] { Foo.class },
+ *                                          handler);
+ * 
+ * + *

A dynamic proxy class (simply referred to as a proxy + * class below) is a class that implements a list of interfaces + * specified at runtime when the class is created, with behavior as + * described below. + * + * A proxy interface is such an interface that is implemented + * by a proxy class. + * + * A proxy instance is an instance of a proxy class. + * + * Each proxy instance has an associated invocation handler + * object, which implements the interface {@link InvocationHandler}. + * A method invocation on a proxy instance through one of its proxy + * interfaces will be dispatched to the {@link InvocationHandler#invoke + * invoke} method of the instance's invocation handler, passing the proxy + * instance, a {@code java.lang.reflect.Method} object identifying + * the method that was invoked, and an array of type {@code Object} + * containing the arguments. The invocation handler processes the + * encoded method invocation as appropriate and the result that it + * returns will be returned as the result of the method invocation on + * the proxy instance. + * + *

A proxy class has the following properties: + * + *

    + *
  • Proxy classes are public, final, and not abstract. + * + *
  • The unqualified name of a proxy class is unspecified. The space + * of class names that begin with the string {@code "$Proxy"} + * should be, however, reserved for proxy classes. + * + *
  • A proxy class extends {@code java.lang.reflect.Proxy}. + * + *
  • A proxy class implements exactly the interfaces specified at its + * creation, in the same order. + * + *
  • If a proxy class implements a non-public interface, then it will + * be defined in the same package as that interface. Otherwise, the + * package of a proxy class is also unspecified. Note that package + * sealing will not prevent a proxy class from being successfully defined + * in a particular package at runtime, and neither will classes already + * defined by the same class loader and the same package with particular + * signers. + * + *
  • Since a proxy class implements all of the interfaces specified at + * its creation, invoking {@code getInterfaces} on its + * {@code Class} object will return an array containing the same + * list of interfaces (in the order specified at its creation), invoking + * {@code getMethods} on its {@code Class} object will return + * an array of {@code Method} objects that include all of the + * methods in those interfaces, and invoking {@code getMethod} will + * find methods in the proxy interfaces as would be expected. + * + *
  • The {@link Proxy#isProxyClass Proxy.isProxyClass} method will + * return true if it is passed a proxy class-- a class returned by + * {@code Proxy.getProxyClass} or the class of an object returned by + * {@code Proxy.newProxyInstance}-- and false otherwise. + * + *
  • The {@code java.security.ProtectionDomain} of a proxy class + * is the same as that of system classes loaded by the bootstrap class + * loader, such as {@code java.lang.Object}, because the code for a + * proxy class is generated by trusted system code. This protection + * domain will typically be granted + * {@code java.security.AllPermission}. + * + *
  • Each proxy class has one public constructor that takes one argument, + * an implementation of the interface {@link InvocationHandler}, to set + * the invocation handler for a proxy instance. Rather than having to use + * the reflection API to access the public constructor, a proxy instance + * can be also be created by calling the {@link Proxy#newProxyInstance + * Proxy.newProxyInstance} method, which combines the actions of calling + * {@link Proxy#getProxyClass Proxy.getProxyClass} with invoking the + * constructor with an invocation handler. + *
+ * + *

A proxy instance has the following properties: + * + *

    + *
  • Given a proxy instance {@code proxy} and one of the + * interfaces implemented by its proxy class {@code Foo}, the + * following expression will return true: + *
    + *     {@code proxy instanceof Foo}
    + * 
    + * and the following cast operation will succeed (rather than throwing + * a {@code ClassCastException}): + *
    + *     {@code (Foo) proxy}
    + * 
    + * + *
  • Each proxy instance has an associated invocation handler, the one + * that was passed to its constructor. The static + * {@link Proxy#getInvocationHandler Proxy.getInvocationHandler} method + * will return the invocation handler associated with the proxy instance + * passed as its argument. + * + *
  • An interface method invocation on a proxy instance will be + * encoded and dispatched to the invocation handler's {@link + * InvocationHandler#invoke invoke} method as described in the + * documentation for that method. + * + *
  • An invocation of the {@code hashCode}, + * {@code equals}, or {@code toString} methods declared in + * {@code java.lang.Object} on a proxy instance will be encoded and + * dispatched to the invocation handler's {@code invoke} method in + * the same manner as interface method invocations are encoded and + * dispatched, as described above. The declaring class of the + * {@code Method} object passed to {@code invoke} will be + * {@code java.lang.Object}. Other public methods of a proxy + * instance inherited from {@code java.lang.Object} are not + * overridden by a proxy class, so invocations of those methods behave + * like they do for instances of {@code java.lang.Object}. + *
+ * + *

Methods Duplicated in Multiple Proxy Interfaces

+ * + *

When two or more interfaces of a proxy class contain a method with + * the same name and parameter signature, the order of the proxy class's + * interfaces becomes significant. When such a duplicate method + * is invoked on a proxy instance, the {@code Method} object passed + * to the invocation handler will not necessarily be the one whose + * declaring class is assignable from the reference type of the interface + * that the proxy's method was invoked through. This limitation exists + * because the corresponding method implementation in the generated proxy + * class cannot determine which interface it was invoked through. + * Therefore, when a duplicate method is invoked on a proxy instance, + * the {@code Method} object for the method in the foremost interface + * that contains the method (either directly or inherited through a + * superinterface) in the proxy class's list of interfaces is passed to + * the invocation handler's {@code invoke} method, regardless of the + * reference type through which the method invocation occurred. + * + *

If a proxy interface contains a method with the same name and + * parameter signature as the {@code hashCode}, {@code equals}, + * or {@code toString} methods of {@code java.lang.Object}, + * when such a method is invoked on a proxy instance, the + * {@code Method} object passed to the invocation handler will have + * {@code java.lang.Object} as its declaring class. In other words, + * the public, non-final methods of {@code java.lang.Object} + * logically precede all of the proxy interfaces for the determination of + * which {@code Method} object to pass to the invocation handler. + * + *

Note also that when a duplicate method is dispatched to an + * invocation handler, the {@code invoke} method may only throw + * checked exception types that are assignable to one of the exception + * types in the {@code throws} clause of the method in all of + * the proxy interfaces that it can be invoked through. If the + * {@code invoke} method throws a checked exception that is not + * assignable to any of the exception types declared by the method in one + * of the proxy interfaces that it can be invoked through, then an + * unchecked {@code UndeclaredThrowableException} will be thrown by + * the invocation on the proxy instance. This restriction means that not + * all of the exception types returned by invoking + * {@code getExceptionTypes} on the {@code Method} object + * passed to the {@code invoke} method can necessarily be thrown + * successfully by the {@code invoke} method. + * + * @author Peter Jones + * @see InvocationHandler + * @since 1.3 + */ +public class Proxy implements java.io.Serializable { + + private static final long serialVersionUID = -2222568056686623797L; + + + + /** + * the invocation handler for this proxy instance. + * @serial + */ + protected InvocationHandler h; + + /** + * Prohibits instantiation. + */ + private Proxy() { + } + + /** + * Constructs a new {@code Proxy} instance from a subclass + * (typically, a dynamic proxy class) with the specified value + * for its invocation handler. + * + * @param h the invocation handler for this proxy instance + */ + protected Proxy(InvocationHandler h) { + this.h = h; + } + + /** + * Returns the {@code java.lang.Class} object for a proxy class + * given a class loader and an array of interfaces. The proxy class + * will be defined by the specified class loader and will implement + * all of the supplied interfaces. If a proxy class for the same + * permutation of interfaces has already been defined by the class + * loader, then the existing proxy class will be returned; otherwise, + * a proxy class for those interfaces will be generated dynamically + * and defined by the class loader. + * + *

There are several restrictions on the parameters that may be + * passed to {@code Proxy.getProxyClass}: + * + *

    + *
  • All of the {@code Class} objects in the + * {@code interfaces} array must represent interfaces, not + * classes or primitive types. + * + *
  • No two elements in the {@code interfaces} array may + * refer to identical {@code Class} objects. + * + *
  • All of the interface types must be visible by name through the + * specified class loader. In other words, for class loader + * {@code cl} and every interface {@code i}, the following + * expression must be true: + *
    +     *     Class.forName(i.getName(), false, cl) == i
    +     * 
    + * + *
  • All non-public interfaces must be in the same package; + * otherwise, it would not be possible for the proxy class to + * implement all of the interfaces, regardless of what package it is + * defined in. + * + *
  • For any set of member methods of the specified interfaces + * that have the same signature: + *
      + *
    • If the return type of any of the methods is a primitive + * type or void, then all of the methods must have that same + * return type. + *
    • Otherwise, one of the methods must have a return type that + * is assignable to all of the return types of the rest of the + * methods. + *
    + * + *
  • The resulting proxy class must not exceed any limits imposed + * on classes by the virtual machine. For example, the VM may limit + * the number of interfaces that a class may implement to 65535; in + * that case, the size of the {@code interfaces} array must not + * exceed 65535. + *
+ * + *

If any of these restrictions are violated, + * {@code Proxy.getProxyClass} will throw an + * {@code IllegalArgumentException}. If the {@code interfaces} + * array argument or any of its elements are {@code null}, a + * {@code NullPointerException} will be thrown. + * + *

Note that the order of the specified proxy interfaces is + * significant: two requests for a proxy class with the same combination + * of interfaces but in a different order will result in two distinct + * proxy classes. + * + * @param loader the class loader to define the proxy class + * @param interfaces the list of interfaces for the proxy class + * to implement + * @return a proxy class that is defined in the specified class loader + * and that implements the specified interfaces + * @throws IllegalArgumentException if any of the restrictions on the + * parameters that may be passed to {@code getProxyClass} + * are violated + * @throws NullPointerException if the {@code interfaces} array + * argument or any of its elements are {@code null} + */ + public static Class getProxyClass(ClassLoader loader, + Class... interfaces) + throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * Returns an instance of a proxy class for the specified interfaces + * that dispatches method invocations to the specified invocation + * handler. This method is equivalent to: + *

+     *     Proxy.getProxyClass(loader, interfaces).
+     *         getConstructor(new Class[] { InvocationHandler.class }).
+     *         newInstance(new Object[] { handler });
+     * 
+ * + *

{@code Proxy.newProxyInstance} throws + * {@code IllegalArgumentException} for the same reasons that + * {@code Proxy.getProxyClass} does. + * + * @param loader the class loader to define the proxy class + * @param interfaces the list of interfaces for the proxy class + * to implement + * @param h the invocation handler to dispatch method invocations to + * @return a proxy instance with the specified invocation handler of a + * proxy class that is defined by the specified class loader + * and that implements the specified interfaces + * @throws IllegalArgumentException if any of the restrictions on the + * parameters that may be passed to {@code getProxyClass} + * are violated + * @throws NullPointerException if the {@code interfaces} array + * argument or any of its elements are {@code null}, or + * if the invocation handler, {@code h}, is + * {@code null} + */ + public static Object newProxyInstance(ClassLoader loader, + Class[] interfaces, + InvocationHandler h) + throws IllegalArgumentException + { + if (h == null) { + throw new NullPointerException(); + } + throw new IllegalArgumentException(); + } + + /** + * Returns true if and only if the specified class was dynamically + * generated to be a proxy class using the {@code getProxyClass} + * method or the {@code newProxyInstance} method. + * + *

The reliability of this method is important for the ability + * to use it to make security decisions, so its implementation should + * not just test if the class in question extends {@code Proxy}. + * + * @param cl the class to test + * @return {@code true} if the class is a proxy class and + * {@code false} otherwise + * @throws NullPointerException if {@code cl} is {@code null} + */ + public static boolean isProxyClass(Class cl) { + if (cl == null) { + throw new NullPointerException(); + } + + return false; + } + + /** + * Returns the invocation handler for the specified proxy instance. + * + * @param proxy the proxy instance to return the invocation handler for + * @return the invocation handler for the proxy instance + * @throws IllegalArgumentException if the argument is not a + * proxy instance + */ + public static InvocationHandler getInvocationHandler(Object proxy) + throws IllegalArgumentException + { + /* + * Verify that the object is actually a proxy instance. + */ + if (!isProxyClass(proxy.getClass())) { + throw new IllegalArgumentException("not a proxy instance"); + } + + Proxy p = (Proxy) proxy; + return p.h; + } + + private static native Class defineClass0(ClassLoader loader, String name, + byte[] b, int off, int len); +} diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/lang/reflect/UndeclaredThrowableException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/java/lang/reflect/UndeclaredThrowableException.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,119 @@ +/* + * Copyright (c) 1999, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.reflect; + +/** + * Thrown by a method invocation on a proxy instance if its invocation + * handler's {@link InvocationHandler#invoke invoke} method throws a + * checked exception (a {@code Throwable} that is not assignable + * to {@code RuntimeException} or {@code Error}) that + * is not assignable to any of the exception types declared in the + * {@code throws} clause of the method that was invoked on the + * proxy instance and dispatched to the invocation handler. + * + *

An {@code UndeclaredThrowableException} instance contains + * the undeclared checked exception that was thrown by the invocation + * handler, and it can be retrieved with the + * {@code getUndeclaredThrowable()} method. + * {@code UndeclaredThrowableException} extends + * {@code RuntimeException}, so it is an unchecked exception + * that wraps a checked exception. + * + *

As of release 1.4, this exception has been retrofitted to + * conform to the general purpose exception-chaining mechanism. The + * "undeclared checked exception that was thrown by the invocation + * handler" that may be provided at construction time and accessed via + * the {@link #getUndeclaredThrowable()} method is now known as the + * cause, and may be accessed via the {@link + * Throwable#getCause()} method, as well as the aforementioned "legacy + * method." + * + * @author Peter Jones + * @see InvocationHandler + * @since 1.3 + */ +public class UndeclaredThrowableException extends RuntimeException { + static final long serialVersionUID = 330127114055056639L; + + /** + * the undeclared checked exception that was thrown + * @serial + */ + private Throwable undeclaredThrowable; + + /** + * Constructs an {@code UndeclaredThrowableException} with the + * specified {@code Throwable}. + * + * @param undeclaredThrowable the undeclared checked exception + * that was thrown + */ + public UndeclaredThrowableException(Throwable undeclaredThrowable) { + super((Throwable) null); // Disallow initCause + this.undeclaredThrowable = undeclaredThrowable; + } + + /** + * Constructs an {@code UndeclaredThrowableException} with the + * specified {@code Throwable} and a detail message. + * + * @param undeclaredThrowable the undeclared checked exception + * that was thrown + * @param s the detail message + */ + public UndeclaredThrowableException(Throwable undeclaredThrowable, + String s) + { + super(s, null); // Disallow initCause + this.undeclaredThrowable = undeclaredThrowable; + } + + /** + * Returns the {@code Throwable} instance wrapped in this + * {@code UndeclaredThrowableException}, which may be {@code null}. + * + *

This method predates the general-purpose exception chaining facility. + * The {@link Throwable#getCause()} method is now the preferred means of + * obtaining this information. + * + * @return the undeclared checked exception that was thrown + */ + public Throwable getUndeclaredThrowable() { + return undeclaredThrowable; + } + + /** + * Returns the cause of this exception (the {@code Throwable} + * instance wrapped in this {@code UndeclaredThrowableException}, + * which may be {@code null}). + * + * @return the cause of this exception. + * @since 1.4 + */ + public Throwable getCause() { + return undeclaredThrowable; + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/net/URL.java --- a/emul/mini/src/main/java/java/net/URL.java Tue Feb 05 16:40:01 2013 +0100 +++ b/emul/mini/src/main/java/java/net/URL.java Tue Feb 05 17:04:22 2013 +0100 @@ -25,6 +25,7 @@ package java.net; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import org.apidesign.bck2brwsr.core.JavaScriptBody; @@ -505,6 +506,17 @@ public URL(URL context, String spec, URLStreamHandler handler) throws MalformedURLException { + this(findContext(context), spec, handler != null); + } + + private URL(URL context, String spec, boolean ishandler) + throws MalformedURLException { + // Check for permission to specify a handler + if (ishandler) { + throw new SecurityException(); + } + URLStreamHandler handler = null; + String original = spec; int i, limit, c; int start = 0; @@ -512,10 +524,6 @@ boolean aRef=false; boolean isRelative = false; - // Check for permission to specify a handler - if (handler != null) { - throw new SecurityException(); - } try { limit = spec.length(); @@ -962,7 +970,11 @@ if (is != null) { return is; } - throw new IOException(); + byte[] arr = (byte[]) getContent(new Class[] { byte[].class }); + if (arr == null) { + throw new IOException(); + } + return new ByteArrayInputStream(arr); } /** @@ -987,6 +999,17 @@ ) private static native String loadText(String url) throws IOException; + @JavaScriptBody(args = { "url", "arr" }, body = "" + + "var request = new XMLHttpRequest();\n" + + "request.open('GET', url, false);\n" + + "request.overrideMimeType('text\\/plain; charset=x-user-defined');\n" + + "request.send();\n" + + "var t = request.responseText;\n" + + "for (var i = 0; i < t.length; i++) arr.push(t.charCodeAt(i) & 0xff);\n" + + "return arr;\n" + ) + private static native Object loadBytes(String url, byte[] arr) throws IOException; + /** * Gets the contents of this URL. This method is a shorthand for: *

@@ -1005,7 +1028,10 @@
     throws java.io.IOException {
         for (Class c : classes) {
             if (c == String.class) {
-                return getContent();
+                return loadText(toExternalForm());
+            }
+            if (c == byte[].class) {
+                return loadBytes(toExternalForm(), new byte[0]);
             }
         }
         return null;
@@ -1016,6 +1042,23 @@
         return universal;
     }
 
+    private static URL findContext(URL context) throws MalformedURLException {
+        if (context == null) {
+            String base = findBaseURL();
+            if (base != null) {
+                context = new URL(null, base, false);
+            }
+        }
+        return context;
+    }
+    
+    @JavaScriptBody(args = {}, body = 
+          "if (typeof window !== 'object') return null;\n"
+        + "if (!window.location) return null;\n"
+        + "if (!window.location.href) return null;\n"
+        + "return window.location.href;\n"
+    )
+    private static native String findBaseURL();
 }
 class Parts {
     String path, query, ref;
diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/util/zip/Adler32.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/emul/mini/src/main/java/java/util/zip/Adler32.java	Tue Feb 05 17:04:22 2013 +0100
@@ -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., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.util.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.
+ *

+ * The specification for Adler32 may be found in RFC 1950. + * (ZLIB Compressed Data Format Specification version 3.3) + *

+ *

+ * From that document: + *

+ * "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. + *

+ * 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." + *

+ * "8.2. The Adler-32 algorithm + *

+ * The Adler-32 algorithm is much faster than the CRC32 algorithm yet + * still provides an extremely low probability of undetected errors. + *

+ * 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.) + *

+ * 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 Adler32 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 -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/util/zip/CRC32.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/java/util/zip/CRC32.java Tue Feb 05 17:04:22 2013 +0100 @@ -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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.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 -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/util/zip/Checksum.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/java/util/zip/Checksum.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1996, 1999, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util.zip; + +/** + * An interface representing a data checksum. + * + * @author David Connelly + */ +public +interface Checksum { + /** + * Updates the current checksum with the specified byte. + * + * @param b the byte to update the checksum with + */ + public void update(int b); + + /** + * Updates the current checksum with the specified array of bytes. + * @param b the byte array to update the checksum with + * @param off the start offset of the data + * @param len the number of bytes to use for the update + */ + public void update(byte[] b, int off, int len); + + /** + * Returns the current checksum value. + * @return the current checksum value + */ + public long getValue(); + + /** + * Resets the checksum to its initial value. + */ + public void reset(); +} diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/util/zip/DataFormatException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/java/util/zip/DataFormatException.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util.zip; + +/** + * Signals that a data format error has occurred. + * + * @author David Connelly + */ +public +class DataFormatException extends Exception { + private static final long serialVersionUID = 2219632870893641452L; + + /** + * Constructs a DataFormatException with no detail message. + */ + public DataFormatException() { + super(); + } + + /** + * Constructs a DataFormatException with the specified detail message. + * A detail message is a String that describes this particular exception. + * @param s the String containing a detail message + */ + public DataFormatException(String s) { + super(s); + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/util/zip/Inflater.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/java/util/zip/Inflater.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,1475 @@ +/* Inflater.java - Decompress a data stream + Copyright (C) 1999, 2000, 2001, 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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +import org.apidesign.bck2brwsr.emul.lang.System; + +/** + * This class provides support for general purpose decompression using the + * popular ZLIB compression library. The ZLIB compression library was + * initially developed as part of the PNG graphics standard and is not + * protected by patents. It is fully described in the specifications at + * the java.util.zip + * package description. + * + *

The following code fragment demonstrates a trivial compression + * and decompression of a string using Deflater and + * Inflater. + * + *

+ * try {
+ *     // Encode a String into bytes
+ *     String inputString = "blahblahblah\u20AC\u20AC";
+ *     byte[] input = inputString.getBytes("UTF-8");
+ *
+ *     // Compress the bytes
+ *     byte[] output = new byte[100];
+ *     Deflater compresser = new Deflater();
+ *     compresser.setInput(input);
+ *     compresser.finish();
+ *     int compressedDataLength = compresser.deflate(output);
+ *
+ *     // Decompress the bytes
+ *     Inflater decompresser = new Inflater();
+ *     decompresser.setInput(output, 0, compressedDataLength);
+ *     byte[] result = new byte[100];
+ *     int resultLength = decompresser.inflate(result);
+ *     decompresser.end();
+ *
+ *     // Decode the bytes into a String
+ *     String outputString = new String(result, 0, resultLength, "UTF-8");
+ * } catch(java.io.UnsupportedEncodingException ex) {
+ *     // handle
+ * } catch (java.util.zip.DataFormatException ex) {
+ *     // handle
+ * }
+ * 
+ * + * @see Deflater + * @author David Connelly + * + */ + +/* 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 + * setInput(), then inflate() it. If inflate doesn't + * inflate any bytes there may be three reasons: + *
    + *
  • needsInput() returns true because the input buffer is empty. + * You have to provide more input with setInput(). + * NOTE: needsInput() also returns true when, the stream is finished. + *
  • + *
  • needsDictionary() returns true, you have to provide a preset + * dictionary with setDictionary().
  • + *
  • finished() returns true, the inflater has finished.
  • + *
+ * 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.
+ * + * 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 + * undefined. + * @deprecated Just clear all references to inflater instead. + */ + 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)getBytesRead(); + } + + /** + * Gets the total number of output bytes returned by inflate(). + * @return the total number of output bytes. + */ + public int getTotalOut() + { + return (int)totalOut; + } + + public long getBytesWritten() { + return totalOut; + } + + public long getBytesRead() { + return totalIn - getRemaining(); + } + + + /** + * 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 buffer 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 buffer 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 + { + /* Special case: len may be zero */ + if (len == 0) + return 0; + /* Check for correct buff, off, len triple */ + if (0 > off || off > off + len || off + len > buf.length) + throw new ArrayIndexOutOfBoundsException(); + int count = 0; + int more; + do + { + if (mode != DECODE_CHKSUM) + { + /* Don't give away any output, if we are waiting for the + * checksum in the input stream. + * + * With this trick we have always: + * needsInput() and not finished() + * implies more output can be produced. + */ + more = outputWindow.copyOutput(buf, off, len); + adler.update(buf, off, more); + off += more; + count += more; + totalOut += more; + len -= more; + if (len == 0) + return count; + } + } + while (decode() || (outputWindow.getAvailable() > 0 + && mode != DECODE_CHKSUM)); + 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().
+ * + * NOTE: 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 buffer 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 buffer 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; + } + private static final int DEFLATED = 8; + /** + * 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) != (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(); + } + } + + + interface DeflaterConstants { + final static boolean DEBUGGING = false; + + final static int STORED_BLOCK = 0; + final static int STATIC_TREES = 1; + final static int DYN_TREES = 2; + final static int PRESET_DICT = 0x20; + + final static int DEFAULT_MEM_LEVEL = 8; + + final static int MAX_MATCH = 258; + final static int MIN_MATCH = 3; + + final static int MAX_WBITS = 15; + final static int WSIZE = 1 << MAX_WBITS; + final static int WMASK = WSIZE - 1; + + final static int HASH_BITS = DEFAULT_MEM_LEVEL + 7; + final static int HASH_SIZE = 1 << HASH_BITS; + final static int HASH_MASK = HASH_SIZE - 1; + final static int HASH_SHIFT = (HASH_BITS + MIN_MATCH - 1) / MIN_MATCH; + + final static int MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1; + final static int MAX_DIST = WSIZE - MIN_LOOKAHEAD; + + final static int PENDING_BUF_SIZE = 1 << (DEFAULT_MEM_LEVEL + 8); + final static int MAX_BLOCK_SIZE = Math.min(65535, PENDING_BUF_SIZE-5); + + final static int DEFLATE_STORED = 0; + final static int DEFLATE_FAST = 1; + final static int DEFLATE_SLOW = 2; + + final static int GOOD_LENGTH[] = { 0,4, 4, 4, 4, 8, 8, 8, 32, 32 }; + final static int MAX_LAZY[] = { 0,4, 5, 6, 4,16, 16, 32, 128, 258 }; + final static int NICE_LENGTH[] = { 0,8,16,32,16,32,128,128, 258, 258 }; + final static int MAX_CHAIN[] = { 0,4, 8,32,16,32,128,256,1024,4096 }; + final static int COMPR_FUNC[] = { 0,1, 1, 1, 1, 2, 2, 2, 2, 2 }; + } + private static class InflaterHuffmanTree { + private final static int MAX_BITLEN = 15; + private short[] tree; + + public 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 IllegalStateException + ("InflaterHuffmanTree: static tree length illegal"); + } + } + + /** + * Constructs a Huffman tree from the array of code lengths. + * + * @param codeLengths the array of code lengths + */ + public 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[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 = 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)); + } + } + private final static String bit4Reverse = + "\000\010\004\014\002\012\006\016\001\011\005\015\003\013\007\017"; + 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)); + } + + /** + * 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. + */ + public 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; + } + } + } + private static 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); + } + } + /** + * 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 + */ + + private static 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; + } + } + /* + * 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 JDK 1.1 + */ + + private static class OutputWindow + { + private final int WINDOW_SIZE = 1 << 15; + private 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 final 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 -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/util/zip/InflaterInputStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/java/util/zip/InflaterInputStream.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,288 @@ +/* + * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util.zip; + +import java.io.FilterInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.EOFException; + +/** + * This class implements a stream filter for uncompressing data in the + * "deflate" compression format. It is also used as the basis for other + * decompression filters, such as GZIPInputStream. + * + * @see Inflater + * @author David Connelly + */ +public +class InflaterInputStream extends FilterInputStream { + /** + * Decompressor for this stream. + */ + protected Inflater inf; + + /** + * Input buffer for decompression. + */ + protected byte[] buf; + + /** + * Length of input buffer. + */ + protected int len; + + private boolean closed = false; + // this flag is set to true after EOF has reached + private boolean reachEOF = false; + + /** + * Check to make sure that this stream has not been closed + */ + private void ensureOpen() throws IOException { + if (closed) { + throw new IOException("Stream closed"); + } + } + + + /** + * Creates a new input stream with the specified decompressor and + * buffer size. + * @param in the input stream + * @param inf the decompressor ("inflater") + * @param size the input buffer size + * @exception IllegalArgumentException if size is <= 0 + */ + public InflaterInputStream(InputStream in, Inflater inf, int size) { + super(in); + if (in == null || inf == null) { + throw new NullPointerException(); + } else if (size <= 0) { + throw new IllegalArgumentException("buffer size <= 0"); + } + this.inf = inf; + buf = new byte[size]; + } + + /** + * Creates a new input stream with the specified decompressor and a + * default buffer size. + * @param in the input stream + * @param inf the decompressor ("inflater") + */ + public InflaterInputStream(InputStream in, Inflater inf) { + this(in, inf, 512); + } + + boolean usesDefaultInflater = false; + + /** + * Creates a new input stream with a default decompressor and buffer size. + * @param in the input stream + */ + public InflaterInputStream(InputStream in) { + this(in, new Inflater()); + usesDefaultInflater = true; + } + + private byte[] singleByteBuf = new byte[1]; + + /** + * Reads a byte of uncompressed data. This method will block until + * enough input is available for decompression. + * @return the byte read, or -1 if end of compressed input is reached + * @exception IOException if an I/O error has occurred + */ + public int read() throws IOException { + ensureOpen(); + return read(singleByteBuf, 0, 1) == -1 ? -1 : singleByteBuf[0] & 0xff; + } + + /** + * Reads uncompressed data into an array of bytes. If len is not + * zero, the method will block until some input can be decompressed; otherwise, + * no bytes are read and 0 is returned. + * @param b the buffer into which the data is read + * @param off the start offset in the destination array b + * @param len the maximum number of bytes read + * @return the actual number of bytes read, or -1 if the end of the + * compressed input is reached or a preset dictionary is needed + * @exception NullPointerException If b is null. + * @exception IndexOutOfBoundsException If off is negative, + * len is negative, or len is greater than + * b.length - off + * @exception ZipException if a ZIP format error has occurred + * @exception IOException if an I/O error has occurred + */ + public int read(byte[] b, int off, int len) throws IOException { + ensureOpen(); + if (b == null) { + throw new NullPointerException(); + } else if (off < 0 || len < 0 || len > b.length - off) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return 0; + } + try { + int n; + while ((n = inf.inflate(b, off, len)) == 0) { + if (inf.finished() || inf.needsDictionary()) { + reachEOF = true; + return -1; + } + if (inf.needsInput()) { + fill(); + } + } + return n; + } catch (DataFormatException e) { + String s = e.getMessage(); + throw new ZipException(s != null ? s : "Invalid ZLIB data format"); + } + } + + /** + * Returns 0 after EOF has been reached, otherwise always return 1. + *

+ * Programs should not count on this method to return the actual number + * of bytes that could be read without blocking. + * + * @return 1 before EOF and 0 after EOF. + * @exception IOException if an I/O error occurs. + * + */ + public int available() throws IOException { + ensureOpen(); + if (reachEOF) { + return 0; + } else { + return 1; + } + } + + private byte[] b = new byte[512]; + + /** + * Skips specified number of bytes of uncompressed data. + * @param n the number of bytes to skip + * @return the actual number of bytes skipped. + * @exception IOException if an I/O error has occurred + * @exception IllegalArgumentException if n < 0 + */ + public long skip(long n) throws IOException { + if (n < 0) { + throw new IllegalArgumentException("negative skip length"); + } + ensureOpen(); + int max = (int)Math.min(n, Integer.MAX_VALUE); + int total = 0; + while (total < max) { + int len = max - total; + if (len > b.length) { + len = b.length; + } + len = read(b, 0, len); + if (len == -1) { + reachEOF = true; + break; + } + total += len; + } + return total; + } + + /** + * Closes this input stream and releases any system resources associated + * with the stream. + * @exception IOException if an I/O error has occurred + */ + public void close() throws IOException { + if (!closed) { + if (usesDefaultInflater) + inf.end(); + in.close(); + closed = true; + } + } + + /** + * Fills input buffer with more data to decompress. + * @exception IOException if an I/O error has occurred + */ + protected void fill() throws IOException { + ensureOpen(); + len = in.read(buf, 0, buf.length); + if (len == -1) { + throw new EOFException("Unexpected end of ZLIB input stream"); + } + inf.setInput(buf, 0, len); + } + + /** + * Tests if this input stream supports the mark and + * reset methods. The markSupported + * method of InflaterInputStream returns + * false. + * + * @return a boolean indicating if this stream type supports + * the mark and reset methods. + * @see java.io.InputStream#mark(int) + * @see java.io.InputStream#reset() + */ + public boolean markSupported() { + return false; + } + + /** + * Marks the current position in this input stream. + * + *

The mark method of InflaterInputStream + * does nothing. + * + * @param readlimit the maximum limit of bytes that can be read before + * the mark position becomes invalid. + * @see java.io.InputStream#reset() + */ + public synchronized void mark(int readlimit) { + } + + /** + * Repositions this stream to the position at the time the + * mark method was last called on this input stream. + * + *

The method reset for class + * InflaterInputStream does nothing except throw an + * IOException. + * + * @exception IOException if this method is invoked. + * @see java.io.InputStream#mark(int) + * @see java.io.IOException + */ + public synchronized void reset() throws IOException { + throw new IOException("mark/reset not supported"); + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/util/zip/ZStreamRef.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/java/util/zip/ZStreamRef.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util.zip; + +/** + * A reference to the native zlib's z_stream structure. + */ + +class ZStreamRef { + + private long address; + ZStreamRef (long address) { + this.address = address; + } + + long address() { + return address; + } + + void clear() { + address = 0; + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/util/zip/ZipConstants.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/java/util/zip/ZipConstants.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 1995, 1996, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util.zip; + +/* + * This interface defines the constants that are used by the classes + * which manipulate ZIP files. + * + * @author David Connelly + */ +interface ZipConstants { + /* + * Header signatures + */ + static long LOCSIG = 0x04034b50L; // "PK\003\004" + static long EXTSIG = 0x08074b50L; // "PK\007\008" + static long CENSIG = 0x02014b50L; // "PK\001\002" + static long ENDSIG = 0x06054b50L; // "PK\005\006" + + /* + * Header sizes in bytes (including signatures) + */ + static final int LOCHDR = 30; // LOC header size + static final int EXTHDR = 16; // EXT header size + static final int CENHDR = 46; // CEN header size + static final int ENDHDR = 22; // END header size + + /* + * Local file (LOC) header field offsets + */ + static final int LOCVER = 4; // version needed to extract + static final int LOCFLG = 6; // general purpose bit flag + static final int LOCHOW = 8; // compression method + static final int LOCTIM = 10; // modification time + static final int LOCCRC = 14; // uncompressed file crc-32 value + static final int LOCSIZ = 18; // compressed size + static final int LOCLEN = 22; // uncompressed size + static final int LOCNAM = 26; // filename length + static final int LOCEXT = 28; // extra field length + + /* + * Extra local (EXT) header field offsets + */ + static final int EXTCRC = 4; // uncompressed file crc-32 value + static final int EXTSIZ = 8; // compressed size + static final int EXTLEN = 12; // uncompressed size + + /* + * Central directory (CEN) header field offsets + */ + static final int CENVEM = 4; // version made by + static final int CENVER = 6; // version needed to extract + static final int CENFLG = 8; // encrypt, decrypt flags + static final int CENHOW = 10; // compression method + static final int CENTIM = 12; // modification time + static final int CENCRC = 16; // uncompressed file crc-32 value + static final int CENSIZ = 20; // compressed size + static final int CENLEN = 24; // uncompressed size + static final int CENNAM = 28; // filename length + static final int CENEXT = 30; // extra field length + static final int CENCOM = 32; // comment length + static final int CENDSK = 34; // disk number start + static final int CENATT = 36; // internal file attributes + static final int CENATX = 38; // external file attributes + static final int CENOFF = 42; // LOC header offset + + /* + * End of central directory (END) header field offsets + */ + static final int ENDSUB = 8; // number of entries on this disk + static final int ENDTOT = 10; // total number of entries + static final int ENDSIZ = 12; // central directory size in bytes + static final int ENDOFF = 16; // offset of first CEN header + static final int ENDCOM = 20; // zip file comment length +} diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/util/zip/ZipConstants64.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/java/util/zip/ZipConstants64.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 1995, 1996, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util.zip; + +/* + * This class defines the constants that are used by the classes + * which manipulate Zip64 files. + */ + +class ZipConstants64 { + + /* + * ZIP64 constants + */ + static final long ZIP64_ENDSIG = 0x06064b50L; // "PK\006\006" + static final long ZIP64_LOCSIG = 0x07064b50L; // "PK\006\007" + static final int ZIP64_ENDHDR = 56; // ZIP64 end header size + static final int ZIP64_LOCHDR = 20; // ZIP64 end loc header size + static final int ZIP64_EXTHDR = 24; // EXT header size + static final int ZIP64_EXTID = 0x0001; // Extra field Zip64 header ID + + static final int ZIP64_MAGICCOUNT = 0xFFFF; + static final long ZIP64_MAGICVAL = 0xFFFFFFFFL; + + /* + * Zip64 End of central directory (END) header field offsets + */ + static final int ZIP64_ENDLEN = 4; // size of zip64 end of central dir + static final int ZIP64_ENDVEM = 12; // version made by + static final int ZIP64_ENDVER = 14; // version needed to extract + static final int ZIP64_ENDNMD = 16; // number of this disk + static final int ZIP64_ENDDSK = 20; // disk number of start + static final int ZIP64_ENDTOD = 24; // total number of entries on this disk + static final int ZIP64_ENDTOT = 32; // total number of entries + static final int ZIP64_ENDSIZ = 40; // central directory size in bytes + static final int ZIP64_ENDOFF = 48; // offset of first CEN header + static final int ZIP64_ENDEXT = 56; // zip64 extensible data sector + + /* + * Zip64 End of central directory locator field offsets + */ + static final int ZIP64_LOCDSK = 4; // disk number start + static final int ZIP64_LOCOFF = 8; // offset of zip64 end + static final int ZIP64_LOCTOT = 16; // total number of disks + + /* + * Zip64 Extra local (EXT) header field offsets + */ + static final int ZIP64_EXTCRC = 4; // uncompressed file crc-32 value + static final int ZIP64_EXTSIZ = 8; // compressed size, 8-byte + static final int ZIP64_EXTLEN = 16; // uncompressed size, 8-byte + + /* + * Language encoding flag EFS + */ + static final int EFS = 0x800; // If this bit is set the filename and + // comment fields for this file must be + // encoded using UTF-8. + + private ZipConstants64() {} +} diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/util/zip/ZipEntry.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/java/util/zip/ZipEntry.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,331 @@ +/* + * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util.zip; + +/** + * This class is used to represent a ZIP file entry. + * + * @author David Connelly + */ +public +class ZipEntry implements ZipConstants, Cloneable { + String name; // entry name + long time = -1; // modification time (in DOS time) + long crc = -1; // crc-32 of entry data + long size = -1; // uncompressed size of entry data + long csize = -1; // compressed size of entry data + int method = -1; // compression method + int flag = 0; // general purpose flag + byte[] extra; // optional extra field data for entry + String comment; // optional comment string for entry + + /** + * Compression method for uncompressed entries. + */ + public static final int STORED = 0; + + /** + * Compression method for compressed (deflated) entries. + */ + public static final int DEFLATED = 8; + + /** + * Creates a new zip entry with the specified name. + * + * @param name the entry name + * @exception NullPointerException if the entry name is null + * @exception IllegalArgumentException if the entry name is longer than + * 0xFFFF bytes + */ + public ZipEntry(String name) { + if (name == null) { + throw new NullPointerException(); + } + if (name.length() > 0xFFFF) { + throw new IllegalArgumentException("entry name too long"); + } + this.name = name; + } + + /** + * Creates a new zip entry with fields taken from the specified + * zip entry. + * @param e a zip Entry object + */ + public ZipEntry(ZipEntry e) { + name = e.name; + time = e.time; + crc = e.crc; + size = e.size; + csize = e.csize; + method = e.method; + flag = e.flag; + extra = e.extra; + comment = e.comment; + } + + /* + * Creates a new un-initialized zip entry + */ + ZipEntry() {} + + /** + * Returns the name of the entry. + * @return the name of the entry + */ + public String getName() { + return name; + } + + /** + * Sets the modification time of the entry. + * @param time the entry modification time in number of milliseconds + * since the epoch + * @see #getTime() + */ + public void setTime(long time) { + this.time = javaToDosTime(time); + } + + /** + * Returns the modification time of the entry, or -1 if not specified. + * @return the modification time of the entry, or -1 if not specified + * @see #setTime(long) + */ + public long getTime() { + return time != -1 ? dosToJavaTime(time) : -1; + } + + /** + * Sets the uncompressed size of the entry data. + * @param size the uncompressed size in bytes + * @exception IllegalArgumentException if the specified size is less + * than 0, is greater than 0xFFFFFFFF when + * ZIP64 format is not supported, + * or is less than 0 when ZIP64 is supported + * @see #getSize() + */ + public void setSize(long size) { + if (size < 0) { + throw new IllegalArgumentException("invalid entry size"); + } + this.size = size; + } + + /** + * Returns the uncompressed size of the entry data, or -1 if not known. + * @return the uncompressed size of the entry data, or -1 if not known + * @see #setSize(long) + */ + public long getSize() { + return size; + } + + /** + * Returns the size of the compressed entry data, or -1 if not known. + * In the case of a stored entry, the compressed size will be the same + * as the uncompressed size of the entry. + * @return the size of the compressed entry data, or -1 if not known + * @see #setCompressedSize(long) + */ + public long getCompressedSize() { + return csize; + } + + /** + * Sets the size of the compressed entry data. + * @param csize the compressed size to set to + * @see #getCompressedSize() + */ + public void setCompressedSize(long csize) { + this.csize = csize; + } + + /** + * Sets the CRC-32 checksum of the uncompressed entry data. + * @param crc the CRC-32 value + * @exception IllegalArgumentException if the specified CRC-32 value is + * less than 0 or greater than 0xFFFFFFFF + * @see #getCrc() + */ + public void setCrc(long crc) { + if (crc < 0 || crc > 0xFFFFFFFFL) { + throw new IllegalArgumentException("invalid entry crc-32"); + } + this.crc = crc; + } + + /** + * Returns the CRC-32 checksum of the uncompressed entry data, or -1 if + * not known. + * @return the CRC-32 checksum of the uncompressed entry data, or -1 if + * not known + * @see #setCrc(long) + */ + public long getCrc() { + return crc; + } + + /** + * Sets the compression method for the entry. + * @param method the compression method, either STORED or DEFLATED + * @exception IllegalArgumentException if the specified compression + * method is invalid + * @see #getMethod() + */ + public void setMethod(int method) { + if (method != STORED && method != DEFLATED) { + throw new IllegalArgumentException("invalid compression method"); + } + this.method = method; + } + + /** + * Returns the compression method of the entry, or -1 if not specified. + * @return the compression method of the entry, or -1 if not specified + * @see #setMethod(int) + */ + public int getMethod() { + return method; + } + + /** + * Sets the optional extra field data for the entry. + * @param extra the extra field data bytes + * @exception IllegalArgumentException if the length of the specified + * extra field data is greater than 0xFFFF bytes + * @see #getExtra() + */ + public void setExtra(byte[] extra) { + if (extra != null && extra.length > 0xFFFF) { + throw new IllegalArgumentException("invalid extra field length"); + } + this.extra = extra; + } + + /** + * Returns the extra field data for the entry, or null if none. + * @return the extra field data for the entry, or null if none + * @see #setExtra(byte[]) + */ + public byte[] getExtra() { + return extra; + } + + /** + * Sets the optional comment string for the entry. + * + *

ZIP entry comments have maximum length of 0xffff. If the length of the + * specified comment string is greater than 0xFFFF bytes after encoding, only + * the first 0xFFFF bytes are output to the ZIP file entry. + * + * @param comment the comment string + * + * @see #getComment() + */ + public void setComment(String comment) { + this.comment = comment; + } + + /** + * Returns the comment string for the entry, or null if none. + * @return the comment string for the entry, or null if none + * @see #setComment(String) + */ + public String getComment() { + return comment; + } + + /** + * Returns true if this is a directory entry. A directory entry is + * defined to be one whose name ends with a '/'. + * @return true if this is a directory entry + */ + public boolean isDirectory() { + return name.endsWith("/"); + } + + /** + * Returns a string representation of the ZIP entry. + */ + public String toString() { + return getName(); + } + + /* + * Converts DOS time to Java time (number of milliseconds since epoch). + */ + private static long dosToJavaTime(long dtime) { + return dtime; + /* XXX: + Date d = new Date((int)(((dtime >> 25) & 0x7f) + 80), + (int)(((dtime >> 21) & 0x0f) - 1), + (int)((dtime >> 16) & 0x1f), + (int)((dtime >> 11) & 0x1f), + (int)((dtime >> 5) & 0x3f), + (int)((dtime << 1) & 0x3e)); + return d.getTime(); + */ + } + + /* + * Converts Java time to DOS time. + */ + private static long javaToDosTime(long time) { + return time; + /* XXX: + Date d = new Date(time); + int year = d.getYear() + 1900; + if (year < 1980) { + return (1 << 21) | (1 << 16); + } + return (year - 1980) << 25 | (d.getMonth() + 1) << 21 | + d.getDate() << 16 | d.getHours() << 11 | d.getMinutes() << 5 | + d.getSeconds() >> 1; + */ + } + + /** + * Returns the hash code value for this entry. + */ + public int hashCode() { + return name.hashCode(); + } + + /** + * Returns a copy of this entry. + */ + public Object clone() { + try { + ZipEntry e = (ZipEntry)super.clone(); + e.extra = (extra == null) ? null : extra.clone(); + return e; + } catch (CloneNotSupportedException e) { + // This should never happen, since we are Cloneable + throw new IllegalStateException(); + } + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/util/zip/ZipException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/java/util/zip/ZipException.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util.zip; + +import java.io.IOException; + +/** + * Signals that a Zip exception of some sort has occurred. + * + * @author unascribed + * @see java.io.IOException + * @since JDK1.0 + */ + +public +class ZipException extends IOException { + private static final long serialVersionUID = 8000196834066748623L; + + /** + * Constructs a ZipException with null + * as its error detail message. + */ + public ZipException() { + super(); + } + + /** + * Constructs a ZipException with the specified detail + * message. + * + * @param s the detail message. + */ + + public ZipException(String s) { + super(s); + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/util/zip/ZipInputStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/java/util/zip/ZipInputStream.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,467 @@ +/* + * Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util.zip; + +import java.io.InputStream; +import java.io.IOException; +import java.io.EOFException; +import java.io.PushbackInputStream; +import static java.util.zip.ZipConstants64.*; +import org.apidesign.bck2brwsr.core.JavaScriptBody; + +/** + * This class implements an input stream filter for reading files in the + * ZIP file format. Includes support for both compressed and uncompressed + * entries. + * + * @author David Connelly + */ +public +class ZipInputStream extends InflaterInputStream implements ZipConstants { + private ZipEntry entry; + private int flag; + private CRC32 crc = new CRC32(); + private long remaining; + private byte[] tmpbuf = new byte[512]; + + private static final int STORED = ZipEntry.STORED; + private static final int DEFLATED = ZipEntry.DEFLATED; + + private boolean closed = false; + // this flag is set to true after EOF has reached for + // one entry + private boolean entryEOF = false; + + /** + * Check to make sure that this stream has not been closed + */ + private void ensureOpen() throws IOException { + if (closed) { + throw new IOException("Stream closed"); + } + } + + /** + * Creates a new ZIP input stream. + * + *

The UTF-8 {@link java.nio.charset.Charset charset} is used to + * decode the entry names. + * + * @param in the actual input stream + */ + public ZipInputStream(InputStream in) { +// this(in, "UTF-8"); + super(new PushbackInputStream(in, 512), new Inflater(true), 512); + usesDefaultInflater = true; + if(in == null) { + throw new NullPointerException("in is null"); + } + } + + /** + * Creates a new ZIP input stream. + * + * @param in the actual input stream + * + * @param charset + * The {@linkplain java.nio.charset.Charset charset} to be + * used to decode the ZIP entry name (ignored if the + * language + * encoding bit of the ZIP entry's general purpose bit + * flag is set). + * + * @since 1.7 + * + public ZipInputStream(InputStream in, Charset charset) { + super(new PushbackInputStream(in, 512), new Inflater(true), 512); + usesDefaultInflater = true; + if(in == null) { + throw new NullPointerException("in is null"); + } + if (charset == null) + throw new NullPointerException("charset is null"); + this.zc = ZipCoder.get(charset); + } + */ + + /** + * Reads the next ZIP file entry and positions the stream at the + * beginning of the entry data. + * @return the next ZIP file entry, or null if there are no more entries + * @exception ZipException if a ZIP file error has occurred + * @exception IOException if an I/O error has occurred + */ + public ZipEntry getNextEntry() throws IOException { + ensureOpen(); + if (entry != null) { + closeEntry(); + } + crc.reset(); + inf.reset(); + if ((entry = readLOC()) == null) { + return null; + } + if (entry.method == STORED) { + remaining = entry.size; + } + entryEOF = false; + return entry; + } + + /** + * Closes the current ZIP entry and positions the stream for reading the + * next entry. + * @exception ZipException if a ZIP file error has occurred + * @exception IOException if an I/O error has occurred + */ + public void closeEntry() throws IOException { + ensureOpen(); + while (read(tmpbuf, 0, tmpbuf.length) != -1) ; + entryEOF = true; + } + + /** + * Returns 0 after EOF has reached for the current entry data, + * otherwise always return 1. + *

+ * Programs should not count on this method to return the actual number + * of bytes that could be read without blocking. + * + * @return 1 before EOF and 0 after EOF has reached for current entry. + * @exception IOException if an I/O error occurs. + * + */ + public int available() throws IOException { + ensureOpen(); + if (entryEOF) { + return 0; + } else { + return 1; + } + } + + /** + * Reads from the current ZIP entry into an array of bytes. + * If len is not zero, the method + * blocks until some input is available; otherwise, no + * bytes are read and 0 is returned. + * @param b the buffer into which the data is read + * @param off the start offset in the destination array b + * @param len the maximum number of bytes read + * @return the actual number of bytes read, or -1 if the end of the + * entry is reached + * @exception NullPointerException if b is null. + * @exception IndexOutOfBoundsException if off is negative, + * len is negative, or len is greater than + * b.length - off + * @exception ZipException if a ZIP file error has occurred + * @exception IOException if an I/O error has occurred + */ + public int read(byte[] b, int off, int len) throws IOException { + ensureOpen(); + if (off < 0 || len < 0 || off > b.length - len) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return 0; + } + + if (entry == null) { + return -1; + } + switch (entry.method) { + case DEFLATED: + len = super.read(b, off, len); + if (len == -1) { + readEnd(entry); + entryEOF = true; + entry = null; + } else { + crc.update(b, off, len); + } + return len; + case STORED: + if (remaining <= 0) { + entryEOF = true; + entry = null; + return -1; + } + if (len > remaining) { + len = (int)remaining; + } + len = in.read(b, off, len); + if (len == -1) { + throw new ZipException("unexpected EOF"); + } + crc.update(b, off, len); + remaining -= len; + if (remaining == 0 && entry.crc != crc.getValue()) { + throw new ZipException( + "invalid entry CRC (expected 0x" + Long.toHexString(entry.crc) + + " but got 0x" + Long.toHexString(crc.getValue()) + ")"); + } + return len; + default: + throw new ZipException("invalid compression method"); + } + } + + /** + * Skips specified number of bytes in the current ZIP entry. + * @param n the number of bytes to skip + * @return the actual number of bytes skipped + * @exception ZipException if a ZIP file error has occurred + * @exception IOException if an I/O error has occurred + * @exception IllegalArgumentException if n < 0 + */ + public long skip(long n) throws IOException { + if (n < 0) { + throw new IllegalArgumentException("negative skip length"); + } + ensureOpen(); + int max = (int)Math.min(n, Integer.MAX_VALUE); + int total = 0; + while (total < max) { + int len = max - total; + if (len > tmpbuf.length) { + len = tmpbuf.length; + } + len = read(tmpbuf, 0, len); + if (len == -1) { + entryEOF = true; + break; + } + total += len; + } + return total; + } + + /** + * Closes this input stream and releases any system resources associated + * with the stream. + * @exception IOException if an I/O error has occurred + */ + public void close() throws IOException { + if (!closed) { + super.close(); + closed = true; + } + } + + private byte[] b = new byte[256]; + + /* + * Reads local file (LOC) header for next entry. + */ + private ZipEntry readLOC() throws IOException { + try { + readFully(tmpbuf, 0, LOCHDR); + } catch (EOFException e) { + return null; + } + if (get32(tmpbuf, 0) != LOCSIG) { + return null; + } + // get flag first, we need check EFS. + flag = get16(tmpbuf, LOCFLG); + // get the entry name and create the ZipEntry first + int len = get16(tmpbuf, LOCNAM); + int blen = b.length; + if (len > blen) { + do + blen = blen * 2; + while (len > blen); + b = new byte[blen]; + } + readFully(b, 0, len); + // Force to use UTF-8 if the EFS bit is ON, even the cs is NOT UTF-8 + ZipEntry e = createZipEntry(((flag & EFS) != 0) + ? toStringUTF8(b, len) + : toString(b, len)); + // now get the remaining fields for the entry + if ((flag & 1) == 1) { + throw new ZipException("encrypted ZIP entry not supported"); + } + e.method = get16(tmpbuf, LOCHOW); + e.time = get32(tmpbuf, LOCTIM); + if ((flag & 8) == 8) { + /* "Data Descriptor" present */ + if (e.method != DEFLATED) { + throw new ZipException( + "only DEFLATED entries can have EXT descriptor"); + } + } else { + e.crc = get32(tmpbuf, LOCCRC); + e.csize = get32(tmpbuf, LOCSIZ); + e.size = get32(tmpbuf, LOCLEN); + } + len = get16(tmpbuf, LOCEXT); + if (len > 0) { + byte[] bb = new byte[len]; + readFully(bb, 0, len); + e.setExtra(bb); + // extra fields are in "HeaderID(2)DataSize(2)Data... format + if (e.csize == ZIP64_MAGICVAL || e.size == ZIP64_MAGICVAL) { + int off = 0; + while (off + 4 < len) { + int sz = get16(bb, off + 2); + if (get16(bb, off) == ZIP64_EXTID) { + off += 4; + // LOC extra zip64 entry MUST include BOTH original and + // compressed file size fields + if (sz < 16 || (off + sz) > len ) { + // Invalid zip64 extra fields, simply skip. Even it's + // rare, it's possible the entry size happens to be + // the magic value and it "accidnetly" has some bytes + // in extra match the id. + return e; + } + e.size = get64(bb, off); + e.csize = get64(bb, off + 8); + break; + } + off += (sz + 4); + } + } + } + return e; + } + + /** + * Creates a new ZipEntry object for the specified + * entry name. + * + * @param name the ZIP file entry name + * @return the ZipEntry just created + */ + protected ZipEntry createZipEntry(String name) { + return new ZipEntry(name); + } + + /* + * Reads end of deflated entry as well as EXT descriptor if present. + */ + private void readEnd(ZipEntry e) throws IOException { + int n = inf.getRemaining(); + if (n > 0) { + ((PushbackInputStream)in).unread(buf, len - n, n); + } + if ((flag & 8) == 8) { + /* "Data Descriptor" present */ + if (inf.getBytesWritten() > ZIP64_MAGICVAL || + inf.getBytesRead() > ZIP64_MAGICVAL) { + // ZIP64 format + readFully(tmpbuf, 0, ZIP64_EXTHDR); + long sig = get32(tmpbuf, 0); + if (sig != EXTSIG) { // no EXTSIG present + e.crc = sig; + e.csize = get64(tmpbuf, ZIP64_EXTSIZ - ZIP64_EXTCRC); + e.size = get64(tmpbuf, ZIP64_EXTLEN - ZIP64_EXTCRC); + ((PushbackInputStream)in).unread( + tmpbuf, ZIP64_EXTHDR - ZIP64_EXTCRC - 1, ZIP64_EXTCRC); + } else { + e.crc = get32(tmpbuf, ZIP64_EXTCRC); + e.csize = get64(tmpbuf, ZIP64_EXTSIZ); + e.size = get64(tmpbuf, ZIP64_EXTLEN); + } + } else { + readFully(tmpbuf, 0, EXTHDR); + long sig = get32(tmpbuf, 0); + if (sig != EXTSIG) { // no EXTSIG present + e.crc = sig; + e.csize = get32(tmpbuf, EXTSIZ - EXTCRC); + e.size = get32(tmpbuf, EXTLEN - EXTCRC); + ((PushbackInputStream)in).unread( + tmpbuf, EXTHDR - EXTCRC - 1, EXTCRC); + } else { + e.crc = get32(tmpbuf, EXTCRC); + e.csize = get32(tmpbuf, EXTSIZ); + e.size = get32(tmpbuf, EXTLEN); + } + } + } + if (e.size != inf.getBytesWritten()) { + throw new ZipException( + "invalid entry size (expected " + e.size + + " but got " + inf.getBytesWritten() + " bytes)"); + } + if (e.csize != inf.getBytesRead()) { + throw new ZipException( + "invalid entry compressed size (expected " + e.csize + + " but got " + inf.getBytesRead() + " bytes)"); + } + if (e.crc != crc.getValue()) { + throw new ZipException( + "invalid entry CRC (expected 0x" + Long.toHexString(e.crc) + + " but got 0x" + Long.toHexString(crc.getValue()) + ")"); + } + } + + /* + * Reads bytes, blocking until all bytes are read. + */ + private void readFully(byte[] b, int off, int len) throws IOException { + while (len > 0) { + int n = in.read(b, off, len); + if (n == -1) { + throw new EOFException(); + } + off += n; + len -= n; + } + } + + /* + * Fetches unsigned 16-bit value from byte array at specified offset. + * The bytes are assumed to be in Intel (little-endian) byte order. + */ + private static final int get16(byte b[], int off) { + return (b[off] & 0xff) | ((b[off+1] & 0xff) << 8); + } + + /* + * Fetches unsigned 32-bit value from byte array at specified offset. + * The bytes are assumed to be in Intel (little-endian) byte order. + */ + private static final long get32(byte b[], int off) { + return (get16(b, off) | ((long)get16(b, off+2) << 16)) & 0xffffffffL; + } + + /* + * Fetches signed 64-bit value from byte array at specified offset. + * The bytes are assumed to be in Intel (little-endian) byte order. + */ + private static final long get64(byte b[], int off) { + return get32(b, off) | (get32(b, off+4) << 32); + } + + private static String toStringUTF8(byte[] arr, int len) { + return new String(arr, 0, len); + } + + private static String toString(byte[] b, int len) { + return new String(b, 0, len); + } +} diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/java/util/zip/package.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/java/util/zip/package.html Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,98 @@ + + + + + + + + +Provides classes for reading and writing the standard ZIP and GZIP +file formats. Also includes classes for compressing and decompressing +data using the DEFLATE compression algorithm, which is used by the +ZIP and GZIP file formats. Additionally, there are utility classes +for computing the CRC-32 and Adler-32 checksums of arbitrary +input streams. + + +

Package Specification

+ + + + + + + +@since JDK1.1 + + + + diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/lang/System.java --- a/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/lang/System.java Tue Feb 05 16:40:01 2013 +0100 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/lang/System.java Tue Feb 05 17:04:22 2013 +0100 @@ -39,4 +39,19 @@ "}" ) public static native void arraycopy(Object value, int srcBegin, Object dst, int dstBegin, int count); + + @JavaScriptBody(args = { "arr", "expectedSize" }, body = + "while (expectedSize-- > arr.length) { arr.push(0); }; return arr;" + ) + public static native byte[] expandArray(byte[] arr, int expectedSize); + + @JavaScriptBody(args = {}, body = "new Date().getMilliseconds();") + public static native long currentTimeMillis(); + + public static long nanoTime() { + return 1000000L * currentTimeMillis(); + } + @JavaScriptBody(args = { "obj" }, body="return vm.java_lang_Object(false).hashCode__I.call(obj);") + public static native int identityHashCode(Object obj); + } diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/MethodImpl.java --- a/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/MethodImpl.java Tue Feb 05 16:40:01 2013 +0100 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/MethodImpl.java Tue Feb 05 17:04:22 2013 +0100 @@ -1,26 +1,19 @@ -/* - * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * This program 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, version 2 of the License. * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * This program 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 version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. */ package org.apidesign.bck2brwsr.emul.reflect; diff -r eecf6077ec4e -r 1ff540c1650f emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/TypeProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/TypeProvider.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,40 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program 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, version 2 of the License. + * + * This program 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 this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.emul.reflect; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; + +/** + * + * @author Jaroslav Tulach + */ +public abstract class TypeProvider { + private TypeProvider() { + } + + public static TypeProvider getDefault() { + return null; + } + + public abstract TypeVariable>[] getTypeParameters(Constructor c); + public abstract Type[] getGenericParameterTypes(Constructor c); + public abstract Type[] getGenericExceptionTypes(Constructor c); + +} diff -r eecf6077ec4e -r 1ff540c1650f javaquery/api/pom.xml --- a/javaquery/api/pom.xml Tue Feb 05 16:40:01 2013 +0100 +++ b/javaquery/api/pom.xml Tue Feb 05 17:04:22 2013 +0100 @@ -43,6 +43,7 @@ org.netbeans.api org-openide-util-lookup + provided org.apidesign.bck2brwsr diff -r eecf6077ec4e -r 1ff540c1650f javaquery/demo-calculator/nbactions.xml --- a/javaquery/demo-calculator/nbactions.xml Tue Feb 05 16:40:01 2013 +0100 +++ b/javaquery/demo-calculator/nbactions.xml Tue Feb 05 17:04:22 2013 +0100 @@ -23,7 +23,7 @@ run process-classes - org.codehaus.mojo:exec-maven-plugin:1.2.1:exec + org.apidesign.bck2brwsr:mojo:0.3-SNAPSHOT:brwsr diff -r eecf6077ec4e -r 1ff540c1650f javaquery/demo-calculator/pom.xml --- a/javaquery/demo-calculator/pom.xml Tue Feb 05 16:40:01 2013 +0100 +++ b/javaquery/demo-calculator/pom.xml Tue Feb 05 17:04:22 2013 +0100 @@ -16,34 +16,21 @@ - - org.apidesign.bck2brwsr - mojo - 0.3-SNAPSHOT - - - - j2js - - - - - org.codehaus.mojo - exec-maven-plugin - 1.2.1 + org.apidesign.bck2brwsr + mojo + 0.3-SNAPSHOT - exec + j2js + brwsr - xdg-open - - ${project.build.directory}/classes/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calculator.xhtml - + ${project.build.directory}/${project.build.finalName}-bck2brwsr/public_html/ + index.xhtml @@ -55,6 +42,24 @@ 1.7 + + maven-assembly-plugin + 2.4 + + + distro-assembly + package + + single + + + + src/main/assembly/bck2brwsr.xml + + + + + diff -r eecf6077ec4e -r 1ff540c1650f javaquery/demo-calculator/src/main/assembly/bck2brwsr.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/demo-calculator/src/main/assembly/bck2brwsr.xml Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,54 @@ + + + + + bck2brwsr + + zip + dir + + public_html + + + false + runtime + lib + + + + + ${project.build.directory}/${project.build.finalName}.jar + / + + + ${project.build.directory}/classes/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calculator.xhtml + / + index.xhtml + + + ${project.build.directory}/classes/org/apidesign/bck2brwsr/demo/calc/staticcompilation/bootjava.js + / + bck2brwsr.js + + + + \ No newline at end of file diff -r eecf6077ec4e -r 1ff540c1650f javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calculator.xhtml --- a/javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calculator.xhtml Tue Feb 05 16:40:01 2013 +0100 +++ b/javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calculator.xhtml Tue Feb 05 17:04:22 2013 +0100 @@ -77,78 +77,6 @@
- diff -r eecf6077ec4e -r 1ff540c1650f mojo/src/main/java/org/apidesign/bck2brwsr/mojo/BrswrMojo.java --- a/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/BrswrMojo.java Tue Feb 05 16:40:01 2013 +0100 +++ b/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/BrswrMojo.java Tue Feb 05 17:04:22 2013 +0100 @@ -51,21 +51,28 @@ /** Root of the class files */ @Parameter(defaultValue="${project.build.directory}/classes") private File classes; + + /** Root of all pages, and files, etc. */ + @Parameter + private File directory; @Override public void execute() throws MojoExecutionException { if (startpage == null) { throw new MojoExecutionException("You have to provide a start page"); } - + try { - URLClassLoader url = buildClassLoader(classes, prj.getDependencyArtifacts()); - Closeable httpServer; - try { - httpServer = Launcher.showURL(url, startpage()); - } catch (Exception ex) { - throw new MojoExecutionException("Can't open " + startpage(), ex); + if (directory != null) { + httpServer = Launcher.showDir(directory, startpage); + } else { + URLClassLoader url = buildClassLoader(classes, prj.getDependencyArtifacts()); + try { + httpServer = Launcher.showURL(url, startpage()); + } catch (Exception ex) { + throw new MojoExecutionException("Can't open " + startpage(), ex); + } } System.in.read(); httpServer.close(); diff -r eecf6077ec4e -r 1ff540c1650f mojo/src/main/resources/archetype-resources/pom.xml --- a/mojo/src/main/resources/archetype-resources/pom.xml Tue Feb 05 16:40:01 2013 +0100 +++ b/mojo/src/main/resources/archetype-resources/pom.xml Tue Feb 05 17:04:22 2013 +0100 @@ -60,7 +60,7 @@ test
- ${project.groupId} + org.apidesign.bck2brwsr vmtest 0.3-SNAPSHOT test diff -r eecf6077ec4e -r 1ff540c1650f vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Tue Feb 05 16:40:01 2013 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Tue Feb 05 17:04:22 2013 +0100 @@ -1278,7 +1278,7 @@ int indx = readIntArg(byteCodes, i); final String type = jc.getClassName(indx); if (!type.startsWith("[")) { - emit(out, "var @2 = @1.$instOf_@3 ? 1 : 0;", + emit(out, "var @2 = @1 != null && @1.$instOf_@3 ? 1 : 0;", smapper.popA(), smapper.pushI(), type.replace('/', '_')); } else { diff -r eecf6077ec4e -r 1ff540c1650f vm/src/main/java/org/apidesign/vm4brwsr/VM.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Tue Feb 05 16:40:01 2013 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Tue Feb 05 17:04:22 2013 +0100 @@ -32,6 +32,7 @@ static { // uses VMLazy to load dynamic classes VMLazy.init(); + Zips.init(); } @Override @@ -116,6 +117,12 @@ + " var args = arguments;\n" + " var loader = {};\n" + " loader.vm = vm;\n" + + " if (args.length == 1 && typeof args[0] !== 'function') {;\n" + + " var classpath = args[0];\n" + + " args[0] = function(name) {\n" + + " return vm.org_apidesign_vm4brwsr_Zips(false).loadFromCp___3B_3Ljava_lang_Object_2Ljava_lang_String_2(classpath, name);\n" + + " };\n" + + " };\n" + " loader.loadClass = function(name) {\n" + " var attr = name.replace__Ljava_lang_String_2CC('.','_');\n" + " var fn = vm[attr];\n" diff -r eecf6077ec4e -r 1ff540c1650f vm/src/main/java/org/apidesign/vm4brwsr/Zips.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/Zips.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,97 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program 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, version 2 of the License. + * + * This program 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 this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import java.io.IOException; +import java.net.URL; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import org.apidesign.bck2brwsr.core.JavaScriptBody; + +/** Conversion from classpath to load function. + * + * @author Jaroslav Tulach + */ +final class Zips { + private Zips() { + } + + public static void init() { + } + + public static byte[] loadFromCp(Object[] classpath, String res) { + for (int i = 0; i < classpath.length; i++) { + Object c = classpath[i]; + if (c instanceof String) { + try { + c = classpath[i] = toZip((String)c); + } catch (IOException ex) { + classpath[i] = ex; + } + } + if (c instanceof Zips) { + Object checkRes = ((Zips)c).findRes(res); + if (checkRes instanceof byte[]) { + return (byte[])checkRes; + } + } + } + return null; + } + + @JavaScriptBody(args = { "res" }, body = "var r = this[res]; return r ? r : null;") + private native byte[] findRes(String res); + + @JavaScriptBody(args = { "res", "arr" }, body = "this[res] = arr;") + private native void putRes(String res, byte[] arr); + + private static Zips toZip(String path) throws IOException { + URL u = new URL(path); + ZipInputStream zip = new ZipInputStream(u.openStream()); + Zips z = new Zips(); + for (;;) { + ZipEntry entry = zip.getNextEntry(); + if (entry == null) { + break; + } + byte[] arr = new byte[4096]; + int offset = 0; + for (;;) { + int len = zip.read(arr, offset, arr.length - offset); + if (len == -1) { + break; + } + offset += len; + if (offset == arr.length) { + enlargeArray(arr, arr.length + 4096); + } + } + sliceArray(arr, offset); + z.putRes(entry.getName(), arr); + } + return z; + } + + @JavaScriptBody(args = { "arr", "len" }, body = "while (arr.length < len) arr.push(0);") + private static native void enlargeArray(byte[] arr, int len); + + @JavaScriptBody(args = { "arr", "len" }, body = "arr.splice(len, arr.length - len);") + private static native void sliceArray(byte[] arr, int len); + + +} diff -r eecf6077ec4e -r 1ff540c1650f vm/src/test/java/org/apidesign/vm4brwsr/Instance.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/Instance.java Tue Feb 05 16:40:01 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/Instance.java Tue Feb 05 17:04:22 2013 +0100 @@ -68,12 +68,12 @@ GetByte i = new InstanceSub(7, 2.2d); return i.getByte(); } - public static boolean instanceOf(boolean sub) { + public static boolean instanceOf(int sub) { Instance i = createInstance(sub); return isInstanceSubOf(i); } public static int castsWork(boolean interfc) { - Instance i = createInstance(true); + Instance i = createInstance(2); if (interfc) { GetByte b = (GetByte)i; } else { @@ -85,11 +85,16 @@ private static boolean isInstanceSubOf(Instance instance) { return instance instanceof InstanceSub; } - private static Instance createInstance(boolean sub) { - return sub ? new InstanceSub(3, 0) : new Instance(); + private static Instance createInstance(int type) { + switch (type) { + case 0: return null; + case 1: return new Instance(); + case 2: return new InstanceSub(3, 0); + } + throw new IllegalArgumentException(); } private static boolean isNull() { - return createInstance(true) == null; + return createInstance(2) == null; } @JavaScriptBody(args = "obj", body = "return obj.constructor;") diff -r eecf6077ec4e -r 1ff540c1650f vm/src/test/java/org/apidesign/vm4brwsr/InstanceTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/InstanceTest.java Tue Feb 05 16:40:01 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/InstanceTest.java Tue Feb 05 17:04:22 2013 +0100 @@ -80,16 +80,23 @@ @Test public void isInstanceOf() throws Exception { assertExec( "Yes, we are instance", - Instance.class, "instanceOf__ZZ", - Double.valueOf(1.0), true + Instance.class, "instanceOf__ZI", + Double.valueOf(1.0), 2 ); } @Test public void notInstanceOf() throws Exception { assertExec( "No, we are not an instance", - Instance.class, "instanceOf__ZZ", - Double.valueOf(0.0), false + Instance.class, "instanceOf__ZI", + Double.valueOf(0.0), 1 + ); + } + @Test public void nullInstanceOf() throws Exception { + assertExec( + "No, null is not an instance", + Instance.class, "instanceOf__ZI", + Double.valueOf(0.0), 0 ); } diff -r eecf6077ec4e -r 1ff540c1650f vm/src/test/java/org/apidesign/vm4brwsr/StaticMethodTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/StaticMethodTest.java Tue Feb 05 16:40:01 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/StaticMethodTest.java Tue Feb 05 17:04:22 2013 +0100 @@ -78,6 +78,89 @@ 3.0d, 1l ); } + + @Test public void rintNegativeUp() throws Exception { + final double cnts = -453904.634; + assertExec( + "Should round up to end with 5", + Math.class, "rint__DD", + -453905.0, cnts + ); + } + + @Test public void rintNegativeDown() throws Exception { + final double cnts = -453904.434; + assertExec( + "Should round up to end with 4", + Math.class, "rint__DD", + -453904.0, cnts + ); + } + + @Test public void rintPositiveUp() throws Exception { + final double cnts = 453904.634; + assertExec( + "Should round up to end with 5", + Math.class, "rint__DD", + 453905.0, cnts + ); + } + @Test public void rintPositiveDown() throws Exception { + final double cnts = 453904.434; + assertExec( + "Should round up to end with 4", + Math.class, "rint__DD", + 453904.0, cnts + ); + } + @Test public void rintOneHalf() throws Exception { + final double cnts = 1.5; + assertExec( + "Should round up to end with 2", + Math.class, "rint__DD", + 2.0, cnts + ); + } + @Test public void rintNegativeOneHalf() throws Exception { + final double cnts = -1.5; + assertExec( + "Should round up to end with 2", + Math.class, "rint__DD", + -2.0, cnts + ); + } + @Test public void rintTwoAndHalf() throws Exception { + final double cnts = 2.5; + assertExec( + "Should round up to end with 2", + Math.class, "rint__DD", + 2.0, cnts + ); + } + @Test public void rintNegativeTwoOneHalf() throws Exception { + final double cnts = -2.5; + assertExec( + "Should round up to end with 2", + Math.class, "rint__DD", + -2.0, cnts + ); + } + + @Test public void ieeeReminder1() throws Exception { + assertExec( + "Same result 1", + Math.class, "IEEEremainder__DDD", + Math.IEEEremainder(10.0, 4.5), 10.0, 4.5 + ); + } + + @Test public void ieeeReminder2() throws Exception { + assertExec( + "Same result 1", + Math.class, "IEEEremainder__DDD", + Math.IEEEremainder(Integer.MAX_VALUE, -4.5), Integer.MAX_VALUE, -4.5 + ); + } @Test public void divAndRound() throws Exception { assertExec( diff -r eecf6077ec4e -r 1ff540c1650f vm/src/test/java/org/apidesign/vm4brwsr/StringSample.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/StringSample.java Tue Feb 05 16:40:01 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/StringSample.java Tue Feb 05 17:04:22 2013 +0100 @@ -68,6 +68,15 @@ return chars('a', (char)30, 'b') instanceof String; } + public static String getBytes(String s) { + byte[] arr = s.getBytes(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < arr.length; i++) { + sb.append(arr[i]).append(" "); + } + return sb.toString().toString(); + } + public static String insertBuffer() { StringBuilder sb = new StringBuilder(); sb.append("Jardo!"); diff -r eecf6077ec4e -r 1ff540c1650f vm/src/test/java/org/apidesign/vm4brwsr/StringTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/StringTest.java Tue Feb 05 16:40:01 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/StringTest.java Tue Feb 05 17:04:22 2013 +0100 @@ -75,6 +75,16 @@ ); } + @Test public void getBytes() throws Exception { + final String horse = "Žluťoučký kůň"; + final String expected = StringSample.getBytes(horse); + assertExec( + "Bytes look simplar", + StringSample.class, "getBytes__Ljava_lang_String_2Ljava_lang_String_2", + expected, horse + ); + } + @Test(timeOut=10000) public void toStringConcatenation() throws Exception { assertExec( "Five executions should generate 5Hello World!", diff -r eecf6077ec4e -r 1ff540c1650f vmtest/pom.xml --- a/vmtest/pom.xml Tue Feb 05 16:40:01 2013 +0100 +++ b/vmtest/pom.xml Tue Feb 05 17:04:22 2013 +0100 @@ -58,5 +58,10 @@ launcher ${project.version} + + org.netbeans.api + org-openide-util-lookup + provided + diff -r eecf6077ec4e -r 1ff540c1650f vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/HttpResource.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/HttpResource.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,43 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program 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, version 2 of the License. + * + * This program 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 this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vmtest; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** Exposes an HTTP page to the running {@link BrwsrTest}, so it can access + * under the relative path. + * + * @author Jaroslav Tulach + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD, ElementType.TYPE}) +public @interface HttpResource { + /** path on the server that the test can use to access the exposed resource */ + String path(); + /** the content of the HttpResource */ + String content(); + /** resource relative to the class that should be used instead of content. + * Leave content equal to empty string. + */ + String resource() default ""; + /** mime type of the resource */ + String mimeType(); +} diff -r eecf6077ec4e -r 1ff540c1650f vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java --- a/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java Tue Feb 05 16:40:01 2013 +0100 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java Tue Feb 05 17:04:22 2013 +0100 @@ -17,16 +17,18 @@ */ package org.apidesign.bck2brwsr.vmtest.impl; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.Map; -import java.util.WeakHashMap; import org.apidesign.bck2brwsr.launcher.Launcher; -import org.apidesign.bck2brwsr.launcher.MethodInvocation; +import org.apidesign.bck2brwsr.launcher.InvocationContext; +import org.apidesign.bck2brwsr.vmtest.HtmlFragment; +import org.apidesign.bck2brwsr.vmtest.HttpResource; import org.testng.ITest; import org.testng.annotations.Test; @@ -39,22 +41,36 @@ private final Launcher l; private final String type; private final boolean fail; + private final HtmlFragment html; + private final HttpResource http; Object value; - private final String html; - Bck2BrwsrCase(Method m, String type, Launcher l, boolean fail, String html) { + Bck2BrwsrCase(Method m, String type, Launcher l, boolean fail, HtmlFragment html, HttpResource http) { this.l = l; this.m = m; this.type = type; this.fail = fail; this.html = html; + this.http = http; } @Test(groups = "run") public void executeCode() throws Throwable { if (l != null) { - MethodInvocation c = l.invokeMethod(m.getDeclaringClass(), m.getName(), html); - String res = c.toString(); + InvocationContext c = l.createInvocation(m.getDeclaringClass(), m.getName()); + if (html != null) { + c.setHtmlFragment(html.value()); + } + if (http != null) { + if (!http.content().isEmpty()) { + InputStream is = new ByteArrayInputStream(http.content().getBytes("UTF-8")); + c.setHttpResource(http.path(), http.mimeType(), is); + } else { + InputStream is = m.getDeclaringClass().getResourceAsStream(http.resource()); + c.setHttpResource(http.path(), http.mimeType(), is); + } + } + String res = c.invoke(); value = res; if (fail) { int idx = res.indexOf(':'); @@ -94,6 +110,9 @@ } catch (InvocationTargetException ex) { Throwable t = ex.getTargetException(); value = t.getClass().getName() + ":" + t.getMessage(); + if (t instanceof AssertionError) { + throw t; + } } } } diff -r eecf6077ec4e -r 1ff540c1650f vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/CompareCase.java --- a/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/CompareCase.java Tue Feb 05 16:40:01 2013 +0100 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/CompareCase.java Tue Feb 05 17:04:22 2013 +0100 @@ -111,17 +111,17 @@ if (c == null) { return; } - final Bck2BrwsrCase real = new Bck2BrwsrCase(m, "Java", null, false, null); + final Bck2BrwsrCase real = new Bck2BrwsrCase(m, "Java", null, false, null, null); ret.add(real); if (c.scripting()) { - final Bck2BrwsrCase js = new Bck2BrwsrCase(m, "JavaScript", l.javaScript(), false, null); + final Bck2BrwsrCase js = new Bck2BrwsrCase(m, "JavaScript", l.javaScript(), false, null, null); ret.add(js); ret.add(new CompareCase(m, real, js)); } for (String b : brwsr) { final Launcher s = l.brwsr(b); ret.add(s); - final Bck2BrwsrCase cse = new Bck2BrwsrCase(m, b, s, false, null); + final Bck2BrwsrCase cse = new Bck2BrwsrCase(m, b, s, false, null, null); ret.add(cse); ret.add(new CompareCase(m, real, cse)); } @@ -135,16 +135,19 @@ if (f == null) { f = m.getDeclaringClass().getAnnotation(HtmlFragment.class); } - String html = f == null ? null : f.value(); + HttpResource r = m.getAnnotation(HttpResource.class); + if (r == null) { + r = m.getDeclaringClass().getAnnotation(HttpResource.class); + } if (brwsr.length == 0) { final Launcher s = l.brwsr(null); ret.add(s); - ret.add(new Bck2BrwsrCase(m, "Brwsr", s, true, html)); + ret.add(new Bck2BrwsrCase(m, "Brwsr", s, true, f, r)); } else { for (String b : brwsr) { final Launcher s = l.brwsr(b); ret.add(s); - ret.add(new Bck2BrwsrCase(m, b, s, true, html)); + ret.add(new Bck2BrwsrCase(m, b, s, true, f, r)); } } } diff -r eecf6077ec4e -r 1ff540c1650f vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/GenerateZip.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/GenerateZip.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,36 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program 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, version 2 of the License. + * + * This program 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 this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vmtest.impl; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** Annotation to generate a ZIP or JAR file during build. + * + * @author Jaroslav Tulach + */ +@Retention(RetentionPolicy.SOURCE) +@interface GenerateZip { + String name(); + + /** manifest for the file */ + String manifest() default ""; + + /** Array of names (at odd positions) and their content (on even) */ + String[] contents(); +} diff -r eecf6077ec4e -r 1ff540c1650f vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/GenerateZipProcessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/GenerateZipProcessor.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,98 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program 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, version 2 of the License. + * + * This program 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 this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vmtest.impl; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.Processor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic; +import javax.tools.FileObject; +import javax.tools.StandardLocation; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author Jaroslav Tulach + */ +@ServiceProvider(service = Processor.class) +@SupportedAnnotationTypes("org.apidesign.bck2brwsr.vmtest.impl.GenerateZip") +public class GenerateZipProcessor extends AbstractProcessor { + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + for (Element e : roundEnv.getElementsAnnotatedWith(GenerateZip.class)) { + GenerateZip gz = e.getAnnotation(GenerateZip.class); + if (gz == null) { + continue; + } + PackageElement pe = findPackage(e); + try { + generateJar(pe, gz, e); + } catch (IOException ex) { + processingEnv.getMessager().printMessage( + Diagnostic.Kind.ERROR, + "Can't generate JAR " + gz.name() + ": " + ex.getMessage() + ); + } + } + return true; + } + + private static PackageElement findPackage(Element e) { + while (e.getKind() != ElementKind.PACKAGE) { + e = e.getEnclosingElement(); + } + return (PackageElement)e; + } + + private void generateJar(PackageElement pe, GenerateZip gz, Element e) throws IOException { + FileObject res = processingEnv.getFiler().createResource( + StandardLocation.CLASS_OUTPUT, + pe.getQualifiedName().toString(), + gz.name(), e + ); + OutputStream os = res.openOutputStream(); + JarOutputStream jar; + if (gz.manifest().isEmpty()) { + jar = new JarOutputStream(os); + } else { + Manifest mf = new Manifest(new ByteArrayInputStream(gz.manifest().getBytes("UTF-8"))); + jar = new JarOutputStream(os, mf); + } + String[] arr = gz.contents(); + for (int i = 0; i < arr.length; i += 2) { + JarEntry je = new JarEntry(arr[i]); + jar.putNextEntry(je); + jar.write(arr[i + 1].getBytes("UTF-8")); + jar.closeEntry(); + } + } + +} diff -r eecf6077ec4e -r 1ff540c1650f vmtest/src/test/java/org/apidesign/bck2brwsr/tck/AssertionTest.java --- a/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/AssertionTest.java Tue Feb 05 16:40:01 2013 +0100 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/AssertionTest.java Tue Feb 05 17:04:22 2013 +0100 @@ -28,8 +28,12 @@ public class AssertionTest { @Compare public Object checkAssert() throws ClassNotFoundException { - assert false : "Is assertion status on?"; - return null; + try { + assert false : "Is assertion status on?"; + return null; + } catch (AssertionError ex) { + return ex.getClass().getName(); + } } @Factory diff -r eecf6077ec4e -r 1ff540c1650f vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java --- a/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java Tue Feb 05 16:40:01 2013 +0100 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java Tue Feb 05 17:04:22 2013 +0100 @@ -17,6 +17,7 @@ */ package org.apidesign.bck2brwsr.tck; +import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import org.apidesign.bck2brwsr.vmtest.Compare; @@ -120,6 +121,21 @@ NullField nf = new NullField(); return ("" + nf.name).toString(); } + @Compare + public String toUTFString() throws UnsupportedEncodingException { + byte[] arr = { + (byte) -59, (byte) -67, (byte) 108, (byte) 117, (byte) -59, (byte) -91, + (byte) 111, (byte) 117, (byte) -60, (byte) -115, (byte) 107, (byte) -61, + (byte) -67, (byte) 32, (byte) 107, (byte) -59, (byte) -81, (byte) -59, + (byte) -120 + }; + return new String(arr, "utf-8"); + } + + @Compare + public int stringToBytesLenght() throws UnsupportedEncodingException { + return "Žluťoučký kůň".getBytes("utf8").length; + } @Factory public static Object[] create() { diff -r eecf6077ec4e -r 1ff540c1650f vmtest/src/test/java/org/apidesign/bck2brwsr/tck/HttpResourceTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/HttpResourceTest.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,94 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program 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, version 2 of the License. + * + * This program 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 this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import java.io.InputStream; +import java.net.URL; +import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.apidesign.bck2brwsr.vmtest.BrwsrTest; +import org.apidesign.bck2brwsr.vmtest.HttpResource; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class HttpResourceTest { + + @HttpResource(path = "/xhr", content = "Hello Brwsr!", mimeType = "text/plain") + @BrwsrTest + public String testReadContentViaXHR() throws Exception { + String msg = read("/xhr"); + assert "Hello Brwsr!".equals(msg) : "The message was " + msg; + return msg; + } + + @HttpResource(path = "/url", content = "Hello via URL!", mimeType = "text/plain") + @BrwsrTest + public String testReadContentViaURL() throws Exception { + URL url = new URL("http:/url"); + String msg = (String) url.getContent(); + assert "Hello via URL!".equals(msg) : "The message was " + msg; + return msg; + } + @HttpResource(path = "/url", content = "Hello via URL!", mimeType = "text/plain") + @BrwsrTest + public String testReadContentViaURLWithStringParam() throws Exception { + URL url = new URL("http:/url"); + String msg = (String) url.getContent(new Class[] { String.class }); + assert "Hello via URL!".equals(msg) : "The message was " + msg; + return msg; + } + + @HttpResource(path = "/bytes", content = "", resource = "0xfe", mimeType = "x-application/binary") + @BrwsrTest + public void testReadByte() throws Exception { + URL url = new URL("http:/bytes"); + final Object res = url.getContent(new Class[] { byte[].class }); + assert res instanceof byte[] : "Expecting byte[]: " + res; + byte[] arr = (byte[]) res; + assert arr.length == 1 : "One byte " + arr.length; + assert arr[0] == 0xfe : "It is 0xfe: " + Integer.toHexString(arr[0]); + } + + @HttpResource(path = "/bytes", content = "", resource = "0xfe", mimeType = "x-application/binary") + @BrwsrTest + public void testReadByteViaInputStream() throws Exception { + URL url = new URL("http:/bytes"); + InputStream is = url.openStream(); + byte[] arr = new byte[10]; + int len = is.read(arr); + assert len == 1 : "One byte " + len; + assert arr[0] == 0xfe : "It is 0xfe: " + Integer.toHexString(arr[0]); + } + + @JavaScriptBody(args = { "url" }, body = + "var req = new XMLHttpRequest();\n" + + "req.open('GET', url, false);\n" + + "req.send();\n" + + "return req.responseText;" + ) + private static native String read(String url); + + + @Factory + public static Object[] create() { + return VMTest.create(HttpResourceTest.class); + } +} diff -r eecf6077ec4e -r 1ff540c1650f vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/CRC32Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/CRC32Test.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,41 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program 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, version 2 of the License. + * + * This program 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 this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vmtest.impl; + +import java.io.UnsupportedEncodingException; +import java.util.zip.CRC32; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class CRC32Test { + + @Compare public long crc1() throws UnsupportedEncodingException { + CRC32 crc = new CRC32(); + crc.update("Hello World!".getBytes("UTF-8")); + return crc.getValue(); + } + + @Factory public static Object[] create() { + return VMTest.create(CRC32Test.class); + } +} diff -r eecf6077ec4e -r 1ff540c1650f vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipFileTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipFileTest.java Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,84 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program 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, version 2 of the License. + * + * This program 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 this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vmtest.impl; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.apidesign.bck2brwsr.vmtest.BrwsrTest; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.HttpResource; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +@GenerateZip(name = "readAnEntry.zip", contents = { + "my/main/file.txt", "Hello World!" +}) +public class ZipFileTest { + + @Compare public String readAnEntry() throws IOException { + InputStream is = ZipFileTest.class.getResourceAsStream("readAnEntry.zip"); + ZipInputStream zip = new ZipInputStream(is); + ZipEntry entry = zip.getNextEntry(); + assertEquals(entry.getName(), "my/main/file.txt", "Correct entry"); + + byte[] arr = new byte[4096]; + int len = zip.read(arr); + + assertEquals(zip.getNextEntry(), null, "No next entry"); + + final String ret = new String(arr, 0, len, "UTF-8"); + return ret; + } + + @JavaScriptBody(args = { "res", "path" }, body = + "var myvm = new bck2brwsr(path);\n" + + "var cls = myvm.loadClass('java.lang.String');\n" + + "return cls.getClass__Ljava_lang_Class_2().getResourceAsStream__Ljava_io_InputStream_2Ljava_lang_String_2(res);\n" + ) + private static native Object loadVMResource(String res, String...path); + + @HttpResource(path = "/readAnEntry.jar", mimeType = "x-application/zip", content = "", resource="readAnEntry.zip") + @BrwsrTest public void canVmLoadResourceFromZip() throws IOException { + Object res = loadVMResource("/my/main/file.txt", "/readAnEntry.jar"); + assert res instanceof InputStream : "Got array of bytes: " + res; + InputStream is = (InputStream)res; + + byte[] arr = new byte[4096]; + int len = is.read(arr); + + final String ret = new String(arr, 0, len, "UTF-8"); + + assertEquals(ret, "Hello World!", "Can read the bytes"); + } + + private static void assertEquals(Object real, Object exp, String msg) { + assert Objects.equals(exp, real) : msg + " exp: " + exp + " real: " + real; + } + + @Factory public static Object[] create() { + return VMTest.create(ZipFileTest.class); + } +} diff -r eecf6077ec4e -r 1ff540c1650f vmtest/src/test/resources/org/apidesign/bck2brwsr/tck/0xfe --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/resources/org/apidesign/bck2brwsr/tck/0xfe Tue Feb 05 17:04:22 2013 +0100 @@ -0,0 +1,1 @@ + \ No newline at end of file