# HG changeset patch # User Jaroslav Tulach # Date 1359967076 -3600 # Node ID ef21eeecf47c54d623ebefbdd2ba7e78158cbdb1 # Parent 8d0be6a9a80981d5981d9ca3ec900eff33e1a1d6# Parent 6b77554987e02b02e7f00e2a609533a97142b867 Emulation of ZipInputStream seems to be in good shape. Merging, so David can use the latest achievements in main development line diff -r 8d0be6a9a809 -r ef21eeecf47c core/pom.xml --- a/core/pom.xml Fri Feb 01 16:34:51 2013 +0100 +++ b/core/pom.xml Mon Feb 04 09:37:56 2013 +0100 @@ -38,6 +38,7 @@ org.netbeans.api org-openide-util-lookup + provided Contains esential annotations for associating JavaScript code with diff -r 8d0be6a9a809 -r ef21eeecf47c emul/compact/pom.xml --- a/emul/compact/pom.xml Fri Feb 01 16:34:51 2013 +0100 +++ b/emul/compact/pom.xml Mon Feb 04 09:37:56 2013 +0100 @@ -27,6 +27,11 @@ ${project.version} test + + org.netbeans.api + org-openide-util-lookup + test + diff -r 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c emul/mini/src/main/java/java/lang/Class.java --- a/emul/mini/src/main/java/java/lang/Class.java Fri Feb 01 16:34:51 2013 +0100 +++ b/emul/mini/src/main/java/java/lang/Class.java Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c emul/mini/src/main/java/java/lang/String.java --- a/emul/mini/src/main/java/java/lang/String.java Fri Feb 01 16:34:51 2013 +0100 +++ b/emul/mini/src/main/java/java/lang/String.java Mon Feb 04 09:37:56 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 @@ -1224,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 @@ -3021,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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c emul/mini/src/main/java/java/net/URL.java --- a/emul/mini/src/main/java/java/net/URL.java Fri Feb 01 16:34:51 2013 +0100 +++ b/emul/mini/src/main/java/java/net/URL.java Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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	Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Fri Feb 01 16:34:51 2013 +0100 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/lang/System.java Mon Feb 04 09:37:56 2013 +0100 @@ -51,4 +51,7 @@ public static long nanoTime() { return 1000L * currentTimeMillis(); } + @JavaScriptBody(args = { "obj" }, body="return vm.java_lang_Object(false).hashCode__I.call(obj);") + public static native int identityHashCode(Object obj); + } diff -r 8d0be6a9a809 -r ef21eeecf47c 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 Fri Feb 01 16:34:51 2013 +0100 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/MethodImpl.java Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c javaquery/api/pom.xml --- a/javaquery/api/pom.xml Fri Feb 01 16:34:51 2013 +0100 +++ b/javaquery/api/pom.xml Mon Feb 04 09:37:56 2013 +0100 @@ -43,6 +43,7 @@ org.netbeans.api org-openide-util-lookup + provided org.apidesign.bck2brwsr diff -r 8d0be6a9a809 -r ef21eeecf47c javaquery/demo-calculator/nbactions.xml --- a/javaquery/demo-calculator/nbactions.xml Fri Feb 01 16:34:51 2013 +0100 +++ b/javaquery/demo-calculator/nbactions.xml Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c javaquery/demo-calculator/pom.xml --- a/javaquery/demo-calculator/pom.xml Fri Feb 01 16:34:51 2013 +0100 +++ b/javaquery/demo-calculator/pom.xml Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Fri Feb 01 16:34:51 2013 +0100 +++ b/javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calculator.xhtml Mon Feb 04 09:37:56 2013 +0100 @@ -77,78 +77,6 @@
- diff -r 8d0be6a9a809 -r ef21eeecf47c mojo/src/main/java/org/apidesign/bck2brwsr/mojo/BrswrMojo.java --- a/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/BrswrMojo.java Fri Feb 01 16:34:51 2013 +0100 +++ b/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/BrswrMojo.java Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Fri Feb 01 16:34:51 2013 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Mon Feb 04 09:37:56 2013 +0100 @@ -1269,7 +1269,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 8d0be6a9a809 -r ef21eeecf47c vm/src/main/java/org/apidesign/vm4brwsr/VM.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Fri Feb 01 16:34:51 2013 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c vm/src/test/java/org/apidesign/vm4brwsr/Instance.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/Instance.java Fri Feb 01 16:34:51 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/Instance.java Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c vm/src/test/java/org/apidesign/vm4brwsr/InstanceTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/InstanceTest.java Fri Feb 01 16:34:51 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/InstanceTest.java Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c vmtest/pom.xml --- a/vmtest/pom.xml Fri Feb 01 16:34:51 2013 +0100 +++ b/vmtest/pom.xml Mon Feb 04 09:37:56 2013 +0100 @@ -58,5 +58,10 @@ launcher ${project.version}
+ + org.netbeans.api + org-openide-util-lookup + provided + diff -r 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java --- a/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java Fri Feb 01 16:34:51 2013 +0100 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java Mon Feb 04 09:37:56 2013 +0100 @@ -17,14 +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 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; @@ -37,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(':'); @@ -92,6 +110,9 @@ } catch (InvocationTargetException ex) { Throwable t = ex.getTargetException(); value = t.getClass().getName() + ":" + t.getMessage(); + if (t instanceof AssertionError) { + throw t; + } } } } diff -r 8d0be6a9a809 -r ef21eeecf47c vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/CompareCase.java --- a/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/CompareCase.java Fri Feb 01 16:34:51 2013 +0100 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/CompareCase.java Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c vmtest/src/test/java/org/apidesign/bck2brwsr/tck/AssertionTest.java --- a/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/AssertionTest.java Fri Feb 01 16:34:51 2013 +0100 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/AssertionTest.java Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java --- a/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java Fri Feb 01 16:34:51 2013 +0100 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 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 8d0be6a9a809 -r ef21eeecf47c 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 Mon Feb 04 09:37:56 2013 +0100 @@ -0,0 +1,1 @@ + \ No newline at end of file