# HG changeset patch # User Jaroslav Tulach # Date 1359732942 -3600 # Node ID 74b8fb771bc2e626a38c2e738e149c7cbf4aa355 # Parent bc6f3be91306d23724a5ff999de5704bac69a040# Parent 48ef38e9677e68cc73af3a8ae86e0f6dab7f7f40 Merging the two heads on jdk7-b147 branch diff -r bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 2013 +0100 @@ -0,0 +1,247 @@ +/* + * 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; + +/** + * 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 2013 +0100 @@ -0,0 +1,525 @@ +/* + * 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; + +/** + * 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 2013 +0100 @@ -0,0 +1,3508 @@ +/* + * 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.io.ObjectStreamClass.WeakClassKey; +import java.lang.ref.ReferenceQueue; +import java.lang.reflect.Array; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.Arrays; +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicBoolean; +import static java.io.ObjectStreamClass.processQueue; + +/** + * 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); + } + + private static class Caches { + /** cache of subclass security audit results */ + static final ConcurrentMap subclassAudits = + new ConcurrentHashMap<>(); + + /** queue for WeakReferences to audited subclasses */ + static final ReferenceQueue> subclassAuditsQueue = + new ReferenceQueue<>(); + } + + /** 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 SerialCallbackContext 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 { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); + } + bin = null; + handles = null; + vlist = null; + enableOverride = true; + } + + /** + * 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 + { + if (enableOverride) { + return readObjectOverride(); + } + + // if nested read, passHandle contains handle of enclosing object + int outerHandle = passHandle; + try { + Object obj = readObject0(false); + 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(); + } + } + } + + /** + * 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 = curContext.getObj(); + ObjectStreamClass curDesc = 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 = curContext.getObj(); + ObjectStreamClass curDesc = 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 + { + if (enable == enableResolve) { + return enable; + } + if (enable) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(SUBSTITUTION_PERMISSION); + } + } + enableResolve = enable; + return !enableResolve; + } + + /** + * 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; + } + SecurityManager sm = System.getSecurityManager(); + if (sm == null) { + return; + } + processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits); + WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue); + Boolean result = Caches.subclassAudits.get(key); + if (result == null) { + result = Boolean.valueOf(auditSubclass(cl)); + Caches.subclassAudits.putIfAbsent(key, result); + } + if (result.booleanValue()) { + return; + } + sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); + } + + /** + * Performs reflective checks on given subclass to verify that it doesn't + * override security-sensitive non-final methods. Returns true if subclass + * is "safe", false otherwise. + */ + private static boolean auditSubclass(final Class subcl) { + Boolean result = AccessController.doPrivileged( + new PrivilegedAction() { + public Boolean run() { + for (Class cl = subcl; + cl != ObjectInputStream.class; + cl = cl.getSuperclass()) + { + try { + cl.getDeclaredMethod( + "readUnshared", (Class[]) null); + return Boolean.FALSE; + } catch (NoSuchMethodException ex) { + } + try { + cl.getDeclaredMethod("readFields", (Class[]) null); + return Boolean.FALSE; + } catch (NoSuchMethodException ex) { + } + } + return Boolean.TRUE; + } + } + ); + return result.booleanValue(); + } + + /** + * 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 + { + SerialCallbackContext 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) + { + SerialCallbackContext oldContext = curContext; + + try { + curContext = 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 { + + private static class Callback { + final ObjectInputValidation obj; + final int priority; + Callback next; + final AccessControlContext acc; + + Callback(ObjectInputValidation obj, int priority, Callback next, + AccessControlContext acc) + { + this.obj = obj; + this.priority = priority; + this.next = next; + this.acc = acc; + } + } + + /** linked list of callbacks */ + private Callback list; + + /** + * 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"); + } + + Callback prev = null, cur = list; + while (cur != null && priority < cur.priority) { + prev = cur; + cur = cur.next; + } + AccessControlContext acc = AccessController.getContext(); + if (prev != null) { + prev.next = new Callback(obj, priority, cur, acc); + } else { + list = new Callback(obj, priority, list, acc); + } + } + + /** + * 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 { + try { + while (list != null) { + AccessController.doPrivileged( + new PrivilegedExceptionAction() + { + public Void run() throws InvalidObjectException { + list.obj.validateObject(); + return null; + } + }, list.acc); + list = list.next; + } + } catch (PrivilegedActionException ex) { + list = null; + throw (InvalidObjectException) ex.getException(); + } + } + + /** + * Resets the callback list to its initial (empty) state. + */ + public void clear() { + list = null; + } + } + + /** + * 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 2013 +0100 @@ -0,0 +1,2454 @@ +/* + * 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.io.ObjectStreamClass.WeakClassKey; +import java.lang.ref.ReferenceQueue; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import static java.io.ObjectStreamClass.processQueue; +import java.io.SerialCallbackContext; + +/** + * 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 +{ + + private static class Caches { + /** cache of subclass security audit results */ + static final ConcurrentMap subclassAudits = + new ConcurrentHashMap<>(); + + /** queue for WeakReferences to audited subclasses */ + static final ReferenceQueue> subclassAuditsQueue = + new ReferenceQueue<>(); + } + + /** 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 SerialCallbackContext 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 = + java.security.AccessController.doPrivileged( + new sun.security.action.GetBooleanAction( + "sun.io.serialization.extendedDebugInfo")).booleanValue(); + + /** + * 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 { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); + } + bout = null; + handles = null; + subs = null; + enableOverride = true; + debugInfoStack = null; + } + + /** + * 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 = curContext.getObj(); + ObjectStreamClass curDesc = 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 = curContext.getObj(); + ObjectStreamClass curDesc = 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 + { + if (enable == enableReplace) { + return enable; + } + if (enable) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(SUBSTITUTION_PERMISSION); + } + } + enableReplace = enable; + return !enableReplace; + } + + /** + * 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; + } + SecurityManager sm = System.getSecurityManager(); + if (sm == null) { + return; + } + processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits); + WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue); + Boolean result = Caches.subclassAudits.get(key); + if (result == null) { + result = Boolean.valueOf(auditSubclass(cl)); + Caches.subclassAudits.putIfAbsent(key, result); + } + if (result.booleanValue()) { + return; + } + sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); + } + + /** + * Performs reflective checks on given subclass to verify that it doesn't + * override security-sensitive non-final methods. Returns true if subclass + * is "safe", false otherwise. + */ + private static boolean auditSubclass(final Class subcl) { + Boolean result = AccessController.doPrivileged( + new PrivilegedAction() { + public Boolean run() { + for (Class cl = subcl; + cl != ObjectOutputStream.class; + cl = cl.getSuperclass()) + { + try { + cl.getDeclaredMethod( + "writeUnshared", new Class[] { Object.class }); + return Boolean.FALSE; + } catch (NoSuchMethodException ex) { + } + try { + cl.getDeclaredMethod("putFields", (Class[]) null); + return Boolean.FALSE; + } catch (NoSuchMethodException ex) { + } + } + return Boolean.TRUE; + } + } + ); + return result.booleanValue(); + } + + /** + * 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"); + } + SerialCallbackContext 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; + SerialCallbackContext oldContext = curContext; + + if (extendedDebugInfo) { + debugInfoStack.push( + "custom writeObject data (class \"" + + slotDesc.getName() + "\")"); + } + try { + curContext = 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 2013 +0100 @@ -0,0 +1,2304 @@ +/* + * 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.security.AccessController; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import sun.misc.Unsafe; +import sun.reflect.ReflectionFactory; + +/** + * 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; + + /** reflection factory for obtaining serialization constructors */ + private static final ReflectionFactory reflFactory = + AccessController.doPrivileged( + new ReflectionFactory.GetReflectionFactoryAction()); + + private static class Caches { + /** cache mapping local classes -> descriptors */ + static final ConcurrentMap> localDescs = + new ConcurrentHashMap<>(); + + /** cache mapping field group/local desc pairs -> field reflectors */ + static final ConcurrentMap> reflectors = + new ConcurrentHashMap<>(); + + /** queue for WeakReferences to local classes */ + private static final ReferenceQueue> localDescsQueue = + new ReferenceQueue<>(); + /** queue for WeakReferences to field reflectors keys */ + private static final ReferenceQueue> reflectorsQueue = + new ReferenceQueue<>(); + } + + /** 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) { + suid = AccessController.doPrivileged( + new PrivilegedAction() { + public Long run() { + 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; + } + processQueue(Caches.localDescsQueue, Caches.localDescs); + WeakClassKey key = new WeakClassKey(cl, Caches.localDescsQueue); + Reference ref = Caches.localDescs.get(key); + Object entry = null; + if (ref != null) { + entry = ref.get(); + } + EntryFuture future = null; + if (entry == null) { + EntryFuture newEntry = new EntryFuture(); + Reference newRef = new SoftReference<>(newEntry); + do { + if (ref != null) { + Caches.localDescs.remove(key, ref); + } + ref = Caches.localDescs.putIfAbsent(key, newRef); + if (ref != null) { + entry = ref.get(); + } + } while (ref != null && entry == null); + if (entry == null) { + future = newEntry; + } + } + + if (entry instanceof ObjectStreamClass) { // check common case first + return (ObjectStreamClass) entry; + } + if (entry instanceof EntryFuture) { + future = (EntryFuture) entry; + if (future.getOwner() == Thread.currentThread()) { + /* + * 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; + } + if (future.set(entry)) { + Caches.localDescs.put(key, new SoftReference(entry)); + } else { + // 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 final Thread owner = Thread.currentThread(); + 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; + } + } + if (interrupted) { + AccessController.doPrivileged( + new PrivilegedAction() { + public Void run() { + Thread.currentThread().interrupt(); + return null; + } + } + ); + } + return entry; + } + + /** + * Returns the thread that created this EntryFuture. + */ + Thread getOwner() { + return owner; + } + } + + /** + * 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; + + if (serializable) { + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + if (isEnum) { + suid = Long.valueOf(0); + fields = NO_FIELDS; + return null; + } + if (cl.isArray()) { + fields = NO_FIELDS; + return null; + } + + suid = getDeclaredSUID(cl); + try { + fields = getSerialFields(cl); + computeFieldOffsets(); + } catch (InvalidClassException e) { + serializeEx = deserializeEx = e; + fields = NO_FIELDS; + } + + if (externalizable) { + cons = getExternalizableConstructor(cl); + } else { + cons = getSerializableConstructor(cl); + writeObjectMethod = getPrivateMethod(cl, "writeObject", + new Class[] { ObjectOutputStream.class }, + Void.TYPE); + readObjectMethod = getPrivateMethod(cl, "readObject", + new Class[] { ObjectInputStream.class }, + Void.TYPE); + readObjectNoDataMethod = getPrivateMethod( + cl, "readObjectNoData", null, Void.TYPE); + hasWriteObjectData = (writeObjectMethod != null); + } + writeReplaceMethod = getInheritableMethod( + cl, "writeReplace", null, Object.class); + readResolveMethod = getInheritableMethod( + cl, "readResolve", null, Object.class); + return null; + } + }); + } else { + suid = Long.valueOf(0); + fields = NO_FIELDS; + } + + try { + fieldRefl = getReflector(fields, this); + } catch (InvalidClassException ex) { + // field mismatches impossible when matching local fields vs. self + throw new InternalError(); + } + + 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; + } + fieldRefl = getReflector(fields, localDesc); + } + + /** + * 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; + } + } + fieldRefl = getReflector(fields, localDesc); + // reassign to matched fields so as to reflect local unshared settings + fields = fieldRefl.getFields(); + } + + /** + * 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) { + fieldRefl.getPrimFieldValues(obj, 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) { + fieldRefl.setPrimFieldValues(obj, 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) { + fieldRefl.getObjFieldValues(obj, 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) { + fieldRefl.setObjFieldValues(obj, 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) { + try { + Constructor cons = cl.getDeclaredConstructor((Class[]) null); + cons.setAccessible(true); + return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ? + cons : null; + } catch (NoSuchMethodException ex) { + return null; + } + } + + /** + * 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; + } + } + try { + Constructor cons = initCl.getDeclaredConstructor((Class[]) null); + int mods = cons.getModifiers(); + if ((mods & Modifier.PRIVATE) != 0 || + ((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0 && + !packageEquals(cl, initCl))) + { + return null; + } + cons = reflFactory.newConstructorForSerialization(cl, cons); + cons.setAccessible(true); + return cons; + } catch (NoSuchMethodException ex) { + return null; + } + } + + /** + * 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) + { + Method meth = null; + Class defCl = cl; + while (defCl != null) { + try { + meth = defCl.getDeclaredMethod(name, argTypes); + break; + } catch (NoSuchMethodException ex) { + defCl = defCl.getSuperclass(); + } + } + + if ((meth == null) || (meth.getReturnType() != returnType)) { + return null; + } + meth.setAccessible(true); + int mods = meth.getModifiers(); + if ((mods & (Modifier.STATIC | Modifier.ABSTRACT)) != 0) { + return null; + } else if ((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) { + return meth; + } else if ((mods & Modifier.PRIVATE) != 0) { + return (cl == defCl) ? meth : null; + } else { + return packageEquals(cl, defCl) ? meth : null; + } + } + + /** + * 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) + { + try { + Method meth = cl.getDeclaredMethod(name, argTypes); + meth.setAccessible(true); + int mods = meth.getModifiers(); + return ((meth.getReturnType() == returnType) && + ((mods & Modifier.STATIC) == 0) && + ((mods & Modifier.PRIVATE) != 0)) ? meth : null; + } catch (NoSuchMethodException ex) { + return null; + } + } + + /** + * 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 + { + ObjectStreamField[] serialPersistentFields = null; + try { + Field f = cl.getDeclaredField("serialPersistentFields"); + int mask = Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL; + if ((f.getModifiers() & mask) == mask) { + f.setAccessible(true); + serialPersistentFields = (ObjectStreamField[]) f.get(null); + } + } catch (Exception ex) { + } + if (serialPersistentFields == null) { + return null; + } else if (serialPersistentFields.length == 0) { + return NO_FIELDS; + } + + ObjectStreamField[] boundFields = + new ObjectStreamField[serialPersistentFields.length]; + Set fieldNames = new HashSet<>(serialPersistentFields.length); + + for (int i = 0; i < serialPersistentFields.length; i++) { + ObjectStreamField spf = serialPersistentFields[i]; + + String fname = spf.getName(); + if (fieldNames.contains(fname)) { + throw new InvalidClassException( + "multiple serializable fields named " + fname); + } + fieldNames.add(fname); + + try { + Field f = cl.getDeclaredField(fname); + if ((f.getType() == spf.getType()) && + ((f.getModifiers() & Modifier.STATIC) == 0)) + { + boundFields[i] = + new ObjectStreamField(f, spf.isUnshared(), true); + } + } catch (NoSuchFieldException ex) { + } + if (boundFields[i] == null) { + boundFields[i] = new ObjectStreamField( + fname, spf.getType(), spf.isUnshared()); + } + } + return boundFields; + } + + /** + * 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) { + Field[] clFields = cl.getDeclaredFields(); + ArrayList list = new ArrayList<>(); + int mask = Modifier.STATIC | Modifier.TRANSIENT; + + for (int i = 0; i < clFields.length; i++) { + if ((clFields[i].getModifiers() & mask) == 0) { + list.add(new ObjectStreamField(clFields[i], false, true)); + } + } + int size = list.size(); + return (size == 0) ? NO_FIELDS : + list.toArray(new ObjectStreamField[size]); + } + + /** + * Returns explicit serial version UID value declared by given class, or + * null if none. + */ + private static Long getDeclaredSUID(Class cl) { + try { + Field f = cl.getDeclaredField("serialVersionUID"); + int mask = Modifier.STATIC | Modifier.FINAL; + if ((f.getModifiers() & mask) == mask) { + f.setAccessible(true); + return Long.valueOf(f.getLong(null)); + } + } catch (Exception ex) { + } + return null; + } + + /** + * Computes the default serial version UID value for the given class. + */ + private static long computeDefaultSUID(Class cl) { + if (!Serializable.class.isAssignableFrom(cl) || Proxy.isProxyClass(cl)) + { + return 0L; + } + + try { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + DataOutputStream dout = new DataOutputStream(bout); + + dout.writeUTF(cl.getName()); + + int classMods = cl.getModifiers() & + (Modifier.PUBLIC | Modifier.FINAL | + Modifier.INTERFACE | Modifier.ABSTRACT); + + /* + * compensate for javac bug in which ABSTRACT bit was set for an + * interface only if the interface declared methods + */ + Method[] methods = cl.getDeclaredMethods(); + if ((classMods & Modifier.INTERFACE) != 0) { + classMods = (methods.length > 0) ? + (classMods | Modifier.ABSTRACT) : + (classMods & ~Modifier.ABSTRACT); + } + dout.writeInt(classMods); + + if (!cl.isArray()) { + /* + * compensate for change in 1.2FCS in which + * Class.getInterfaces() was modified to return Cloneable and + * Serializable for array classes. + */ + Class[] interfaces = cl.getInterfaces(); + String[] ifaceNames = new String[interfaces.length]; + for (int i = 0; i < interfaces.length; i++) { + ifaceNames[i] = interfaces[i].getName(); + } + Arrays.sort(ifaceNames); + for (int i = 0; i < ifaceNames.length; i++) { + dout.writeUTF(ifaceNames[i]); + } + } + + Field[] fields = cl.getDeclaredFields(); + MemberSignature[] fieldSigs = new MemberSignature[fields.length]; + for (int i = 0; i < fields.length; i++) { + fieldSigs[i] = new MemberSignature(fields[i]); + } + Arrays.sort(fieldSigs, new Comparator() { + public int compare(MemberSignature ms1, MemberSignature ms2) { + return ms1.name.compareTo(ms2.name); + } + }); + for (int i = 0; i < fieldSigs.length; i++) { + MemberSignature sig = fieldSigs[i]; + int mods = sig.member.getModifiers() & + (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED | + Modifier.STATIC | Modifier.FINAL | Modifier.VOLATILE | + Modifier.TRANSIENT); + if (((mods & Modifier.PRIVATE) == 0) || + ((mods & (Modifier.STATIC | Modifier.TRANSIENT)) == 0)) + { + dout.writeUTF(sig.name); + dout.writeInt(mods); + dout.writeUTF(sig.signature); + } + } + + if (hasStaticInitializer(cl)) { + dout.writeUTF(""); + dout.writeInt(Modifier.STATIC); + dout.writeUTF("()V"); + } + + Constructor[] cons = cl.getDeclaredConstructors(); + MemberSignature[] consSigs = new MemberSignature[cons.length]; + for (int i = 0; i < cons.length; i++) { + consSigs[i] = new MemberSignature(cons[i]); + } + Arrays.sort(consSigs, new Comparator() { + public int compare(MemberSignature ms1, MemberSignature ms2) { + return ms1.signature.compareTo(ms2.signature); + } + }); + for (int i = 0; i < consSigs.length; i++) { + MemberSignature sig = consSigs[i]; + int mods = sig.member.getModifiers() & + (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED | + Modifier.STATIC | Modifier.FINAL | + Modifier.SYNCHRONIZED | Modifier.NATIVE | + Modifier.ABSTRACT | Modifier.STRICT); + if ((mods & Modifier.PRIVATE) == 0) { + dout.writeUTF(""); + dout.writeInt(mods); + dout.writeUTF(sig.signature.replace('/', '.')); + } + } + + MemberSignature[] methSigs = new MemberSignature[methods.length]; + for (int i = 0; i < methods.length; i++) { + methSigs[i] = new MemberSignature(methods[i]); + } + Arrays.sort(methSigs, new Comparator() { + public int compare(MemberSignature ms1, MemberSignature ms2) { + int comp = ms1.name.compareTo(ms2.name); + if (comp == 0) { + comp = ms1.signature.compareTo(ms2.signature); + } + return comp; + } + }); + for (int i = 0; i < methSigs.length; i++) { + MemberSignature sig = methSigs[i]; + int mods = sig.member.getModifiers() & + (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED | + Modifier.STATIC | Modifier.FINAL | + Modifier.SYNCHRONIZED | Modifier.NATIVE | + Modifier.ABSTRACT | Modifier.STRICT); + if ((mods & Modifier.PRIVATE) == 0) { + dout.writeUTF(sig.name); + dout.writeInt(mods); + dout.writeUTF(sig.signature.replace('/', '.')); + } + } + + dout.flush(); + + MessageDigest md = MessageDigest.getInstance("SHA"); + byte[] hashBytes = md.digest(bout.toByteArray()); + long hash = 0; + for (int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) { + hash = (hash << 8) | (hashBytes[i] & 0xFF); + } + return hash; + } catch (IOException ex) { + throw new InternalError(); + } catch (NoSuchAlgorithmException ex) { + throw new SecurityException(ex.getMessage()); + } + } + + /** + * Returns true if the given class defines a static initializer method, + * false otherwise. + */ + private native static boolean hasStaticInitializer(Class cl); + + /** + * Class for computing and caching field/constructor/method signatures + * during serialVersionUID calculation. + */ + private static class MemberSignature { + + public final Member member; + public final String name; + public final String signature; + + public MemberSignature(Field field) { + member = field; + name = field.getName(); + signature = getClassSignature(field.getType()); + } + + public MemberSignature(Constructor cons) { + member = cons; + name = cons.getName(); + signature = getMethodSignature( + cons.getParameterTypes(), Void.TYPE); + } + + public MemberSignature(Method meth) { + member = meth; + name = meth.getName(); + signature = getMethodSignature( + meth.getParameterTypes(), meth.getReturnType()); + } + } + + /** + * Class for setting and retrieving serializable field values in batch. + */ + // REMIND: dynamically generate these? + private static class FieldReflector { + + /** handle for performing unsafe operations */ + private static final Unsafe unsafe = Unsafe.getUnsafe(); + + /** fields to operate on */ + private final ObjectStreamField[] fields; + /** number of primitive fields */ + private final int numPrimFields; + /** unsafe field keys for reading fields - may contain dupes */ + private final long[] readKeys; + /** unsafe fields keys for writing fields - no dupes */ + private final long[] writeKeys; + /** field data offsets */ + private final int[] offsets; + /** field type codes */ + private final char[] typeCodes; + /** field types */ + private final Class[] types; + + /** + * Constructs FieldReflector capable of setting/getting values from the + * subset of fields whose ObjectStreamFields contain non-null + * reflective Field objects. ObjectStreamFields with null Fields are + * treated as filler, for which get operations return default values + * and set operations discard given values. + */ + FieldReflector(ObjectStreamField[] fields) { + this.fields = fields; + int nfields = fields.length; + readKeys = new long[nfields]; + writeKeys = new long[nfields]; + offsets = new int[nfields]; + typeCodes = new char[nfields]; + ArrayList> typeList = new ArrayList<>(); + Set usedKeys = new HashSet<>(); + + + for (int i = 0; i < nfields; i++) { + ObjectStreamField f = fields[i]; + Field rf = f.getField(); + long key = (rf != null) ? + unsafe.objectFieldOffset(rf) : Unsafe.INVALID_FIELD_OFFSET; + readKeys[i] = key; + writeKeys[i] = usedKeys.add(key) ? + key : Unsafe.INVALID_FIELD_OFFSET; + offsets[i] = f.getOffset(); + typeCodes[i] = f.getTypeCode(); + if (!f.isPrimitive()) { + typeList.add((rf != null) ? rf.getType() : null); + } + } + + types = typeList.toArray(new Class[typeList.size()]); + numPrimFields = nfields - types.length; + } + + /** + * Returns list of ObjectStreamFields representing fields operated on + * by this reflector. The shared/unshared values and Field objects + * contained by ObjectStreamFields in the list reflect their bindings + * to locally defined serializable fields. + */ + ObjectStreamField[] getFields() { + return fields; + } + + /** + * Fetches the serializable primitive field values of object obj and + * marshals them into byte array buf starting at offset 0. The caller + * is responsible for ensuring that obj is of the proper type. + */ + void getPrimFieldValues(Object obj, byte[] buf) { + if (obj == null) { + throw new NullPointerException(); + } + /* assuming checkDefaultSerialize() has been called on the class + * descriptor this FieldReflector was obtained from, no field keys + * in array should be equal to Unsafe.INVALID_FIELD_OFFSET. + */ + for (int i = 0; i < numPrimFields; i++) { + long key = readKeys[i]; + int off = offsets[i]; + switch (typeCodes[i]) { + case 'Z': + Bits.putBoolean(buf, off, unsafe.getBoolean(obj, key)); + break; + + case 'B': + buf[off] = unsafe.getByte(obj, key); + break; + + case 'C': + Bits.putChar(buf, off, unsafe.getChar(obj, key)); + break; + + case 'S': + Bits.putShort(buf, off, unsafe.getShort(obj, key)); + break; + + case 'I': + Bits.putInt(buf, off, unsafe.getInt(obj, key)); + break; + + case 'F': + Bits.putFloat(buf, off, unsafe.getFloat(obj, key)); + break; + + case 'J': + Bits.putLong(buf, off, unsafe.getLong(obj, key)); + break; + + case 'D': + Bits.putDouble(buf, off, unsafe.getDouble(obj, key)); + break; + + default: + throw new InternalError(); + } + } + } + + /** + * Sets the serializable primitive fields of object obj using values + * unmarshalled from byte array buf starting at offset 0. The caller + * is responsible for ensuring that obj is of the proper type. + */ + void setPrimFieldValues(Object obj, byte[] buf) { + if (obj == null) { + throw new NullPointerException(); + } + for (int i = 0; i < numPrimFields; i++) { + long key = writeKeys[i]; + if (key == Unsafe.INVALID_FIELD_OFFSET) { + continue; // discard value + } + int off = offsets[i]; + switch (typeCodes[i]) { + case 'Z': + unsafe.putBoolean(obj, key, Bits.getBoolean(buf, off)); + break; + + case 'B': + unsafe.putByte(obj, key, buf[off]); + break; + + case 'C': + unsafe.putChar(obj, key, Bits.getChar(buf, off)); + break; + + case 'S': + unsafe.putShort(obj, key, Bits.getShort(buf, off)); + break; + + case 'I': + unsafe.putInt(obj, key, Bits.getInt(buf, off)); + break; + + case 'F': + unsafe.putFloat(obj, key, Bits.getFloat(buf, off)); + break; + + case 'J': + unsafe.putLong(obj, key, Bits.getLong(buf, off)); + break; + + case 'D': + unsafe.putDouble(obj, key, Bits.getDouble(buf, off)); + break; + + default: + throw new InternalError(); + } + } + } + + /** + * Fetches the serializable object field values of object obj and + * stores them in array vals starting at offset 0. The caller is + * responsible for ensuring that obj is of the proper type. + */ + void getObjFieldValues(Object obj, Object[] vals) { + if (obj == null) { + throw new NullPointerException(); + } + /* assuming checkDefaultSerialize() has been called on the class + * descriptor this FieldReflector was obtained from, no field keys + * in array should be equal to Unsafe.INVALID_FIELD_OFFSET. + */ + for (int i = numPrimFields; i < fields.length; i++) { + switch (typeCodes[i]) { + case 'L': + case '[': + vals[offsets[i]] = unsafe.getObject(obj, readKeys[i]); + break; + + default: + throw new InternalError(); + } + } + } + + /** + * Sets the serializable object fields of object obj using values from + * array vals starting at offset 0. The caller is responsible for + * ensuring that obj is of the proper type; however, attempts to set a + * field with a value of the wrong type will trigger an appropriate + * ClassCastException. + */ + void setObjFieldValues(Object obj, Object[] vals) { + if (obj == null) { + throw new NullPointerException(); + } + for (int i = numPrimFields; i < fields.length; i++) { + long key = writeKeys[i]; + if (key == Unsafe.INVALID_FIELD_OFFSET) { + continue; // discard value + } + switch (typeCodes[i]) { + case 'L': + case '[': + Object val = vals[offsets[i]]; + if (val != null && + !types[i - numPrimFields].isInstance(val)) + { + Field f = fields[i].getField(); + throw new ClassCastException( + "cannot assign instance of " + + val.getClass().getName() + " to field " + + f.getDeclaringClass().getName() + "." + + f.getName() + " of type " + + f.getType().getName() + " in instance of " + + obj.getClass().getName()); + } + unsafe.putObject(obj, key, val); + break; + + default: + throw new InternalError(); + } + } + } + } + + /** + * Matches given set of serializable fields with serializable fields + * described by the given local class descriptor, and returns a + * FieldReflector instance capable of setting/getting values from the + * subset of fields that match (non-matching fields are treated as filler, + * for which get operations return default values and set operations + * discard given values). Throws InvalidClassException if unresolvable + * type conflicts exist between the two sets of fields. + */ + private static FieldReflector getReflector(ObjectStreamField[] fields, + ObjectStreamClass localDesc) + throws InvalidClassException + { + // class irrelevant if no fields + Class cl = (localDesc != null && fields.length > 0) ? + localDesc.cl : null; + processQueue(Caches.reflectorsQueue, Caches.reflectors); + FieldReflectorKey key = new FieldReflectorKey(cl, fields, + Caches.reflectorsQueue); + Reference ref = Caches.reflectors.get(key); + Object entry = null; + if (ref != null) { + entry = ref.get(); + } + EntryFuture future = null; + if (entry == null) { + EntryFuture newEntry = new EntryFuture(); + Reference newRef = new SoftReference<>(newEntry); + do { + if (ref != null) { + Caches.reflectors.remove(key, ref); + } + ref = Caches.reflectors.putIfAbsent(key, newRef); + if (ref != null) { + entry = ref.get(); + } + } while (ref != null && entry == null); + if (entry == null) { + future = newEntry; + } + } + + if (entry instanceof FieldReflector) { // check common case first + return (FieldReflector) entry; + } else if (entry instanceof EntryFuture) { + entry = ((EntryFuture) entry).get(); + } else if (entry == null) { + try { + entry = new FieldReflector(matchFields(fields, localDesc)); + } catch (Throwable th) { + entry = th; + } + future.set(entry); + Caches.reflectors.put(key, new SoftReference(entry)); + } + + if (entry instanceof FieldReflector) { + return (FieldReflector) entry; + } else if (entry instanceof InvalidClassException) { + throw (InvalidClassException) entry; + } else if (entry instanceof RuntimeException) { + throw (RuntimeException) entry; + } else if (entry instanceof Error) { + throw (Error) entry; + } else { + throw new InternalError("unexpected entry: " + entry); + } + } + + /** + * FieldReflector cache lookup key. Keys are considered equal if they + * refer to the same class and equivalent field formats. + */ + private static class FieldReflectorKey extends WeakReference> { + + private final String sigs; + private final int hash; + private final boolean nullClass; + + FieldReflectorKey(Class cl, ObjectStreamField[] fields, + ReferenceQueue> queue) + { + super(cl, queue); + nullClass = (cl == null); + StringBuilder sbuf = new StringBuilder(); + for (int i = 0; i < fields.length; i++) { + ObjectStreamField f = fields[i]; + sbuf.append(f.getName()).append(f.getSignature()); + } + sigs = sbuf.toString(); + hash = System.identityHashCode(cl) + sigs.hashCode(); + } + + public int hashCode() { + return hash; + } + + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + + if (obj instanceof FieldReflectorKey) { + FieldReflectorKey other = (FieldReflectorKey) obj; + Class referent; + return (nullClass ? other.nullClass + : ((referent = get()) != null) && + (referent == other.get())) && + sigs.equals(other.sigs); + } else { + return false; + } + } + } + + /** + * Matches given set of serializable fields with serializable fields + * obtained from the given local class descriptor (which contain bindings + * to reflective Field objects). Returns list of ObjectStreamFields in + * which each ObjectStreamField whose signature matches that of a local + * field contains a Field object for that field; unmatched + * ObjectStreamFields contain null Field objects. Shared/unshared settings + * of the returned ObjectStreamFields also reflect those of matched local + * ObjectStreamFields. Throws InvalidClassException if unresolvable type + * conflicts exist between the two sets of fields. + */ + private static ObjectStreamField[] matchFields(ObjectStreamField[] fields, + ObjectStreamClass localDesc) + throws InvalidClassException + { + ObjectStreamField[] localFields = (localDesc != null) ? + localDesc.fields : NO_FIELDS; + + /* + * Even if fields == localFields, we cannot simply return localFields + * here. In previous implementations of serialization, + * ObjectStreamField.getType() returned Object.class if the + * ObjectStreamField represented a non-primitive field and belonged to + * a non-local class descriptor. To preserve this (questionable) + * behavior, the ObjectStreamField instances returned by matchFields + * cannot report non-primitive types other than Object.class; hence + * localFields cannot be returned directly. + */ + + ObjectStreamField[] matches = new ObjectStreamField[fields.length]; + for (int i = 0; i < fields.length; i++) { + ObjectStreamField f = fields[i], m = null; + for (int j = 0; j < localFields.length; j++) { + ObjectStreamField lf = localFields[j]; + if (f.getName().equals(lf.getName())) { + if ((f.isPrimitive() || lf.isPrimitive()) && + f.getTypeCode() != lf.getTypeCode()) + { + throw new InvalidClassException(localDesc.name, + "incompatible types for field " + f.getName()); + } + if (lf.getField() != null) { + m = new ObjectStreamField( + lf.getField(), lf.isUnshared(), false); + } else { + m = new ObjectStreamField( + lf.getName(), lf.getSignature(), lf.isUnshared()); + } + } + } + if (m == null) { + m = new ObjectStreamField( + f.getName(), f.getSignature(), false); + } + m.setOffset(f.getOffset()); + matches[i] = m; + } + return matches; + } + + /** + * Removes from the specified map any keys that have been enqueued + * on the specified reference queue. + */ + static void processQueue(ReferenceQueue> queue, + ConcurrentMap>, ?> map) + { + Reference> ref; + while((ref = queue.poll()) != null) { + map.remove(ref); + } + } + + /** + * Weak key for Class objects. + * + **/ + static class WeakClassKey extends WeakReference> { + /** + * saved value of the referent's identity hash code, to maintain + * a consistent hash code after the referent has been cleared + */ + private final int hash; + + /** + * Create a new WeakClassKey to the given object, registered + * with a queue. + */ + WeakClassKey(Class cl, ReferenceQueue> refQueue) { + super(cl, refQueue); + hash = System.identityHashCode(cl); + } + + /** + * Returns the identity hash code of the original referent. + */ + public int hashCode() { + return hash; + } + + /** + * Returns true if the given object is this identical + * WeakClassKey instance, or, if this object's referent has not + * been cleared, if the given object is another WeakClassKey + * instance with the identical non-null referent as this one. + */ + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + + if (obj instanceof WeakClassKey) { + Object referent = get(); + return (referent != null) && + (referent == ((WeakClassKey) obj).get()); + } else { + return false; + } + } + } +} diff -r bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 2013 +0100 @@ -0,0 +1,235 @@ +/* + * 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 */ + + /** + * Enable substitution of one object for another during + * serialization/deserialization. + * + * @see java.io.ObjectOutputStream#enableReplaceObject(boolean) + * @see java.io.ObjectInputStream#enableResolveObject(boolean) + * @since 1.2 + */ + final static SerializablePermission SUBSTITUTION_PERMISSION = + new SerializablePermission("enableSubstitution"); + + /** + * Enable overriding of readObject and writeObject. + * + * @see java.io.ObjectOutputStream#writeObjectOverride(Object) + * @see java.io.ObjectInputStream#readObjectOverride() + * @since 1.2 + */ + final static SerializablePermission SUBCLASS_IMPLEMENTATION_PERMISSION = + new SerializablePermission("enableSubclassImplementation"); + /** + * 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 2013 +0100 @@ -0,0 +1,238 @@ +/* + * 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; + +import sun.misc.Cleaner; + + +/** + * 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; + + /* High-priority thread to enqueue pending References + */ + private static class ReferenceHandler extends Thread { + + ReferenceHandler(ThreadGroup g, String name) { + super(g, name); + } + + public void run() { + for (;;) { + + Reference r; + synchronized (lock) { + if (pending != null) { + r = pending; + Reference rn = r.next; + pending = (rn == r) ? null : rn; + r.next = r; + } else { + try { + lock.wait(); + } catch (InterruptedException x) { } + continue; + } + } + + // Fast path for cleaners + if (r instanceof Cleaner) { + ((Cleaner)r).clean(); + continue; + } + + ReferenceQueue q = r.queue; + if (q != ReferenceQueue.NULL) q.enqueue(r); + } + } + } + + static { + ThreadGroup tg = Thread.currentThread().getThreadGroup(); + for (ThreadGroup tgn = tg; + tgn != null; + tg = tgn, tgn = tg.getParent()); + Thread handler = new ReferenceHandler(tg, "Reference Handler"); + /* If there were a special system-only priority greater than + * MAX_PRIORITY, it would be used here + */ + handler.setPriority(Thread.MAX_PRIORITY); + handler.setDaemon(true); + handler.start(); + } + + + /* -- 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 2013 +0100 @@ -0,0 +1,154 @@ +/* + * 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++; + if (r instanceof FinalReference) { + sun.misc.VM.addFinalRefCount(1); + } + 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--; + if (r instanceof FinalReference) { + sun.misc.VM.addFinalRefCount(-1); + } + 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 2013 +0100 @@ -0,0 +1,683 @@ +/* + * 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 sun.reflect.ConstructorAccessor; +import sun.reflect.Reflection; +import sun.reflect.generics.repository.ConstructorRepository; +import sun.reflect.generics.factory.CoreReflectionFactory; +import sun.reflect.generics.factory.GenericsFactory; +import sun.reflect.generics.scope.ConstructorScope; +import java.lang.annotation.Annotation; +import java.util.Map; +import sun.reflect.annotation.AnnotationParser; +import java.lang.annotation.AnnotationFormatError; +import java.lang.reflect.Modifier; + +/** + * {@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; + // generic info repository; lazily initialized + private transient ConstructorRepository genericInfo; + private byte[] annotations; + private byte[] parameterAnnotations; + + // Generics infrastructure + // Accessor for factory + private GenericsFactory getFactory() { + // create scope and factory + return CoreReflectionFactory.make(this, ConstructorScope.make(this)); + } + + // Accessor for generic info repository + private ConstructorRepository getGenericInfo() { + // lazily initialize repository if necessary + if (genericInfo == null) { + // create and cache generic info repository + genericInfo = + ConstructorRepository.make(getSignature(), + getFactory()); + } + return genericInfo; //return cached repository + } + + private volatile ConstructorAccessor constructorAccessor; + // 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() { + // This routine enables sharing of ConstructorAccessor objects + // among Constructor objects which refer to the same underlying + // method in the VM. (All of this contortion is only necessary + // because of the "accessibility" bit in AccessibleObject, + // which implicitly requires that new java.lang.reflect + // objects be fabricated for each reflective call on Class + // objects.) + Constructor res = new Constructor<>(clazz, + parameterTypes, + exceptionTypes, modifiers, slot, + signature, + annotations, + parameterAnnotations); + res.root = this; + // Might as well eagerly propagate this if already present + res.constructorAccessor = constructorAccessor; + return res; + } + + /** + * 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() { + if (getSignature() != null) { + return (TypeVariable>[])getGenericInfo().getTypeParameters(); + } else + return (TypeVariable>[])new TypeVariable[0]; + } + + + /** + * 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() { + if (getSignature() != null) + return getGenericInfo().getParameterTypes(); + else + return getParameterTypes(); + } + + + /** + * 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() { + Type[] result; + if (getSignature() != null && + ( (result = getGenericInfo().getExceptionTypes()).length > 0 )) + return result; + else + return getExceptionTypes(); + } + + /** + * 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 + { + if (!override) { + if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { + Class caller = Reflection.getCallerClass(2); + + checkAccess(caller, clazz, null, modifiers); + } + } + if ((clazz.getModifiers() & Modifier.ENUM) != 0) + throw new IllegalArgumentException("Cannot reflectively create enum objects"); + ConstructorAccessor ca = constructorAccessor; // read volatile + if (ca == null) { + ca = acquireConstructorAccessor(); + } + return (T) ca.newInstance(initargs); + } + + /** + * 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()); + } + + // NOTE that there is no synchronization used here. It is correct + // (though not efficient) to generate more than one + // ConstructorAccessor for a given Constructor. However, avoiding + // synchronization will probably make the implementation more + // scalable. + private ConstructorAccessor acquireConstructorAccessor() { + // First check to see if one has been created yet, and take it + // if so. + ConstructorAccessor tmp = null; + if (root != null) tmp = root.getConstructorAccessor(); + if (tmp != null) { + constructorAccessor = tmp; + } else { + // Otherwise fabricate one and propagate it up to the root + tmp = reflectionFactory.newConstructorAccessor(this); + setConstructorAccessor(tmp); + } + + return tmp; + } + + // Returns ConstructorAccessor for this Constructor object, not + // looking up the chain to the root + ConstructorAccessor getConstructorAccessor() { + return constructorAccessor; + } + + // Sets the ConstructorAccessor for this Constructor object and + // (recursively) its root + void setConstructorAccessor(ConstructorAccessor accessor) { + constructorAccessor = accessor; + // Propagate up + if (root != null) { + root.setConstructorAccessor(accessor); + } + } + + 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 (T) declaredAnnotations().get(annotationClass); + } + + /** + * @since 1.5 + */ + public Annotation[] getDeclaredAnnotations() { + return AnnotationParser.toArray(declaredAnnotations()); + } + + private transient Map, Annotation> declaredAnnotations; + + private synchronized Map, Annotation> declaredAnnotations() { + if (declaredAnnotations == null) { + declaredAnnotations = AnnotationParser.parseAnnotations( + annotations, sun.misc.SharedSecrets.getJavaLangAccess(). + getConstantPool(getDeclaringClass()), + getDeclaringClass()); + } + return 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]; + + 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 2013 +0100 @@ -0,0 +1,666 @@ +/* + * 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; + +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.List; +import java.util.WeakHashMap; +import sun.misc.ProxyGenerator; + +/** + * {@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; + + /** prefix for all proxy class names */ + private final static String proxyClassNamePrefix = "$Proxy"; + + /** parameter types of a proxy class constructor */ + private final static Class[] constructorParams = + { InvocationHandler.class }; + + /** maps a class loader to the proxy class cache for that loader */ + private static Map, Object>> loaderToCache + = new WeakHashMap<>(); + + /** marks that a particular proxy class is currently being generated */ + private static Object pendingGenerationMarker = new Object(); + + /** next number to use for generation of unique proxy class names */ + private static long nextUniqueNumber = 0; + private static Object nextUniqueNumberLock = new Object(); + + /** set of all generated proxy classes, for isProxyClass implementation */ + private static Map, Void> proxyClasses = + Collections.synchronizedMap(new WeakHashMap, Void>()); + + /** + * 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 + { + if (interfaces.length > 65535) { + throw new IllegalArgumentException("interface limit exceeded"); + } + + Class proxyClass = null; + + /* collect interface names to use as key for proxy class cache */ + String[] interfaceNames = new String[interfaces.length]; + + // for detecting duplicates + Set> interfaceSet = new HashSet<>(); + + for (int i = 0; i < interfaces.length; i++) { + /* + * Verify that the class loader resolves the name of this + * interface to the same Class object. + */ + String interfaceName = interfaces[i].getName(); + Class interfaceClass = null; + try { + interfaceClass = Class.forName(interfaceName, false, loader); + } catch (ClassNotFoundException e) { + } + if (interfaceClass != interfaces[i]) { + throw new IllegalArgumentException( + interfaces[i] + " is not visible from class loader"); + } + + /* + * Verify that the Class object actually represents an + * interface. + */ + if (!interfaceClass.isInterface()) { + throw new IllegalArgumentException( + interfaceClass.getName() + " is not an interface"); + } + + /* + * Verify that this interface is not a duplicate. + */ + if (interfaceSet.contains(interfaceClass)) { + throw new IllegalArgumentException( + "repeated interface: " + interfaceClass.getName()); + } + interfaceSet.add(interfaceClass); + + interfaceNames[i] = interfaceName; + } + + /* + * Using string representations of the proxy interfaces as + * keys in the proxy class cache (instead of their Class + * objects) is sufficient because we require the proxy + * interfaces to be resolvable by name through the supplied + * class loader, and it has the advantage that using a string + * representation of a class makes for an implicit weak + * reference to the class. + */ + List key = Arrays.asList(interfaceNames); + + /* + * Find or create the proxy class cache for the class loader. + */ + Map, Object> cache; + synchronized (loaderToCache) { + cache = loaderToCache.get(loader); + if (cache == null) { + cache = new HashMap<>(); + loaderToCache.put(loader, cache); + } + /* + * This mapping will remain valid for the duration of this + * method, without further synchronization, because the mapping + * will only be removed if the class loader becomes unreachable. + */ + } + + /* + * Look up the list of interfaces in the proxy class cache using + * the key. This lookup will result in one of three possible + * kinds of values: + * null, if there is currently no proxy class for the list of + * interfaces in the class loader, + * the pendingGenerationMarker object, if a proxy class for the + * list of interfaces is currently being generated, + * or a weak reference to a Class object, if a proxy class for + * the list of interfaces has already been generated. + */ + synchronized (cache) { + /* + * Note that we need not worry about reaping the cache for + * entries with cleared weak references because if a proxy class + * has been garbage collected, its class loader will have been + * garbage collected as well, so the entire cache will be reaped + * from the loaderToCache map. + */ + do { + Object value = cache.get(key); + if (value instanceof Reference) { + proxyClass = (Class) ((Reference) value).get(); + } + if (proxyClass != null) { + // proxy class already generated: return it + return proxyClass; + } else if (value == pendingGenerationMarker) { + // proxy class being generated: wait for it + try { + cache.wait(); + } catch (InterruptedException e) { + /* + * The class generation that we are waiting for should + * take a small, bounded time, so we can safely ignore + * thread interrupts here. + */ + } + continue; + } else { + /* + * No proxy class for this list of interfaces has been + * generated or is being generated, so we will go and + * generate it now. Mark it as pending generation. + */ + cache.put(key, pendingGenerationMarker); + break; + } + } while (true); + } + + try { + String proxyPkg = null; // package to define proxy class in + + /* + * Record the package of a non-public proxy interface so that the + * proxy class will be defined in the same package. Verify that + * all non-public proxy interfaces are in the same package. + */ + for (int i = 0; i < interfaces.length; i++) { + int flags = interfaces[i].getModifiers(); + if (!Modifier.isPublic(flags)) { + String name = interfaces[i].getName(); + int n = name.lastIndexOf('.'); + String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); + if (proxyPkg == null) { + proxyPkg = pkg; + } else if (!pkg.equals(proxyPkg)) { + throw new IllegalArgumentException( + "non-public interfaces from different packages"); + } + } + } + + if (proxyPkg == null) { // if no non-public proxy interfaces, + proxyPkg = ""; // use the unnamed package + } + + { + /* + * Choose a name for the proxy class to generate. + */ + long num; + synchronized (nextUniqueNumberLock) { + num = nextUniqueNumber++; + } + String proxyName = proxyPkg + proxyClassNamePrefix + num; + /* + * Verify that the class loader hasn't already + * defined a class with the chosen name. + */ + + /* + * Generate the specified proxy class. + */ + byte[] proxyClassFile = ProxyGenerator.generateProxyClass( + proxyName, interfaces); + try { + proxyClass = defineClass0(loader, proxyName, + proxyClassFile, 0, proxyClassFile.length); + } catch (ClassFormatError e) { + /* + * A ClassFormatError here means that (barring bugs in the + * proxy class generation code) there was some other + * invalid aspect of the arguments supplied to the proxy + * class creation (such as virtual machine limitations + * exceeded). + */ + throw new IllegalArgumentException(e.toString()); + } + } + // add to set of all generated proxy classes, for isProxyClass + proxyClasses.put(proxyClass, null); + + } finally { + /* + * We must clean up the "pending generation" state of the proxy + * class cache entry somehow. If a proxy class was successfully + * generated, store it in the cache (with a weak reference); + * otherwise, remove the reserved entry. In all cases, notify + * all waiters on reserved entries in this cache. + */ + synchronized (cache) { + if (proxyClass != null) { + cache.put(key, new WeakReference>(proxyClass)); + } else { + cache.remove(key); + } + cache.notifyAll(); + } + } + return proxyClass; + } + + /** + * 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(); + } + + /* + * Look up or generate the designated proxy class. + */ + Class cl = getProxyClass(loader, interfaces); + + /* + * Invoke its constructor with the designated invocation handler. + */ + try { + Constructor cons = cl.getConstructor(constructorParams); + return cons.newInstance(new Object[] { h }); + } catch (NoSuchMethodException e) { + throw new InternalError(e.toString()); + } catch (IllegalAccessException e) { + throw new InternalError(e.toString()); + } catch (InstantiationException e) { + throw new InternalError(e.toString()); + } catch (InvocationTargetException e) { + throw new InternalError(e.toString()); + } + } + + /** + * 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 proxyClasses.containsKey(cl); + } + + /** + * 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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.util.zip; + +/** + * A class that can be used to compute the CRC-32 of a data stream. + * + * @see Checksum + * @author David Connelly + */ +public +class CRC32 implements Checksum { + private int crc; + + /** + * Creates a new CRC32 object. + */ + public CRC32() { + } + + + /** + * Updates the CRC-32 checksum with the specified byte (the low + * eight bits of the argument b). + * + * @param b the byte to update the checksum with + */ + public void update(int b) { + crc = update(crc, b); + } + + /** + * Updates the CRC-32 checksum with the specified array of bytes. + */ + public void update(byte[] b, int off, int len) { + if (b == null) { + throw new NullPointerException(); + } + if (off < 0 || len < 0 || off > b.length - len) { + throw new ArrayIndexOutOfBoundsException(); + } + crc = updateBytes(crc, b, off, len); + } + + /** + * Updates the CRC-32 checksum with the specified array of bytes. + * + * @param b the array of bytes to update the checksum with + */ + public void update(byte[] b) { + crc = updateBytes(crc, b, 0, b.length); + } + + /** + * Resets CRC-32 to initial value. + */ + public void reset() { + crc = 0; + } + + /** + * Returns CRC-32 value. + */ + public long getValue() { + return (long)crc & 0xffffffffL; + } + + private native static int update(int crc, int b); + private native static int updateBytes(int crc, byte[] b, int off, int len); +} diff -r bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 2013 +0100 @@ -0,0 +1,402 @@ +/* + * 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.util.zip; + +/** + * 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 + * + */ +public +class Inflater { + + private final ZStreamRef zsRef; + private byte[] buf = defaultBuf; + private int off, len; + private boolean finished; + private boolean needDict; + + private static final byte[] defaultBuf = new byte[0]; + + static { + /* Zip library is loaded from System.initializeSystemClass */ + initIDs(); + } + + /** + * Creates a new decompressor. If the parameter 'nowrap' is true then + * the ZLIB header and checksum fields will not be used. This provides + * compatibility with the compression format used by both GZIP and PKZIP. + *

+ * Note: When using the 'nowrap' option it is also necessary to provide + * an extra "dummy" byte as input. This is required by the ZLIB native + * library in order to support certain optimizations. + * + * @param nowrap if true then support GZIP compatible compression + */ + public Inflater(boolean nowrap) { + zsRef = new ZStreamRef(init(nowrap)); + } + + /** + * Creates a new decompressor. + */ + public Inflater() { + this(false); + } + + /** + * Sets input data for decompression. Should be called whenever + * needsInput() returns true indicating that more input data is + * required. + * @param b the input data bytes + * @param off the start offset of the input data + * @param len the length of the input data + * @see Inflater#needsInput + */ + public void setInput(byte[] b, int off, int len) { + if (b == null) { + throw new NullPointerException(); + } + if (off < 0 || len < 0 || off > b.length - len) { + throw new ArrayIndexOutOfBoundsException(); + } + synchronized (zsRef) { + this.buf = b; + this.off = off; + this.len = len; + } + } + + /** + * Sets input data for decompression. Should be called whenever + * needsInput() returns true indicating that more input data is + * required. + * @param b the input data bytes + * @see Inflater#needsInput + */ + public void setInput(byte[] b) { + setInput(b, 0, b.length); + } + + /** + * Sets the preset dictionary to the given array of bytes. Should be + * called when inflate() returns 0 and needsDictionary() returns true + * indicating that a preset dictionary is required. The method getAdler() + * can be used to get the Adler-32 value of the dictionary needed. + * @param b the dictionary data bytes + * @param off the start offset of the data + * @param len the length of the data + * @see Inflater#needsDictionary + * @see Inflater#getAdler + */ + public void setDictionary(byte[] b, int off, int len) { + if (b == null) { + throw new NullPointerException(); + } + if (off < 0 || len < 0 || off > b.length - len) { + throw new ArrayIndexOutOfBoundsException(); + } + synchronized (zsRef) { + ensureOpen(); + setDictionary(zsRef.address(), b, off, len); + needDict = false; + } + } + + /** + * Sets the preset dictionary to the given array of bytes. Should be + * called when inflate() returns 0 and needsDictionary() returns true + * indicating that a preset dictionary is required. The method getAdler() + * can be used to get the Adler-32 value of the dictionary needed. + * @param b the dictionary data bytes + * @see Inflater#needsDictionary + * @see Inflater#getAdler + */ + public void setDictionary(byte[] b) { + setDictionary(b, 0, b.length); + } + + /** + * Returns the total number of bytes remaining in the input buffer. + * This can be used to find out what bytes still remain in the input + * buffer after decompression has finished. + * @return the total number of bytes remaining in the input buffer + */ + public int getRemaining() { + synchronized (zsRef) { + return len; + } + } + + /** + * Returns true if no data remains in the input buffer. This can + * be used to determine if #setInput should be called in order + * to provide more input. + * @return true if no data remains in the input buffer + */ + public boolean needsInput() { + synchronized (zsRef) { + return len <= 0; + } + } + + /** + * Returns true if a preset dictionary is needed for decompression. + * @return true if a preset dictionary is needed for decompression + * @see Inflater#setDictionary + */ + public boolean needsDictionary() { + synchronized (zsRef) { + return needDict; + } + } + + /** + * Returns true if the end of the compressed data stream has been + * reached. + * @return true if the end of the compressed data stream has been + * reached + */ + public boolean finished() { + synchronized (zsRef) { + return finished; + } + } + + /** + * Uncompresses bytes into specified buffer. Returns actual number + * of bytes uncompressed. A return value of 0 indicates that + * needsInput() or needsDictionary() should be called in order to + * determine if more input data or a preset dictionary is required. + * In the latter case, getAdler() can be used to get the Adler-32 + * value of the dictionary required. + * @param b the buffer for the uncompressed data + * @param off the start offset of the data + * @param len the maximum number of uncompressed bytes + * @return the actual number of uncompressed bytes + * @exception DataFormatException if the compressed data format is invalid + * @see Inflater#needsInput + * @see Inflater#needsDictionary + */ + public int inflate(byte[] b, int off, int len) + throws DataFormatException + { + if (b == null) { + throw new NullPointerException(); + } + if (off < 0 || len < 0 || off > b.length - len) { + throw new ArrayIndexOutOfBoundsException(); + } + synchronized (zsRef) { + ensureOpen(); + return inflateBytes(zsRef.address(), b, off, len); + } + } + + /** + * Uncompresses bytes into specified buffer. Returns actual number + * of bytes uncompressed. A return value of 0 indicates that + * needsInput() or needsDictionary() should be called in order to + * determine if more input data or a preset dictionary is required. + * In the latter case, getAdler() can be used to get the Adler-32 + * value of the dictionary required. + * @param b the buffer for the uncompressed data + * @return the actual number of uncompressed bytes + * @exception DataFormatException if the compressed data format is invalid + * @see Inflater#needsInput + * @see Inflater#needsDictionary + */ + public int inflate(byte[] b) throws DataFormatException { + return inflate(b, 0, b.length); + } + + /** + * Returns the ADLER-32 value of the uncompressed data. + * @return the ADLER-32 value of the uncompressed data + */ + public int getAdler() { + synchronized (zsRef) { + ensureOpen(); + return getAdler(zsRef.address()); + } + } + + /** + * Returns the total number of compressed bytes input so far. + * + *

Since the number of bytes may be greater than + * Integer.MAX_VALUE, the {@link #getBytesRead()} method is now + * the preferred means of obtaining this information.

+ * + * @return the total number of compressed bytes input so far + */ + public int getTotalIn() { + return (int) getBytesRead(); + } + + /** + * Returns the total number of compressed bytes input so far.

+ * + * @return the total (non-negative) number of compressed bytes input so far + * @since 1.5 + */ + public long getBytesRead() { + synchronized (zsRef) { + ensureOpen(); + return getBytesRead(zsRef.address()); + } + } + + /** + * Returns the total number of uncompressed bytes output so far. + * + *

Since the number of bytes may be greater than + * Integer.MAX_VALUE, the {@link #getBytesWritten()} method is now + * the preferred means of obtaining this information.

+ * + * @return the total number of uncompressed bytes output so far + */ + public int getTotalOut() { + return (int) getBytesWritten(); + } + + /** + * Returns the total number of uncompressed bytes output so far.

+ * + * @return the total (non-negative) number of uncompressed bytes output so far + * @since 1.5 + */ + public long getBytesWritten() { + synchronized (zsRef) { + ensureOpen(); + return getBytesWritten(zsRef.address()); + } + } + + /** + * Resets inflater so that a new set of input data can be processed. + */ + public void reset() { + synchronized (zsRef) { + ensureOpen(); + reset(zsRef.address()); + buf = defaultBuf; + finished = false; + needDict = false; + off = len = 0; + } + } + + /** + * Closes the decompressor and discards any unprocessed input. + * This method should be called when the decompressor is no longer + * being used, but will also be called automatically by the finalize() + * method. Once this method is called, the behavior of the Inflater + * object is undefined. + */ + public void end() { + synchronized (zsRef) { + long addr = zsRef.address(); + zsRef.clear(); + if (addr != 0) { + end(addr); + buf = null; + } + } + } + + /** + * Closes the decompressor when garbage is collected. + */ + protected void finalize() { + end(); + } + + private void ensureOpen () { + assert Thread.holdsLock(zsRef); + if (zsRef.address() == 0) + throw new NullPointerException("Inflater has been closed"); + } + + boolean ended() { + synchronized (zsRef) { + return zsRef.address() == 0; + } + } + + private native static void initIDs(); + private native static long init(boolean nowrap); + private native static void setDictionary(long addr, byte[] b, int off, + int len); + private native int inflateBytes(long addr, byte[] b, int off, int len) + throws DataFormatException; + private native static int getAdler(long addr); + private native static long getBytesRead(long addr); + private native static long getBytesWritten(long addr); + private native static void reset(long addr); + private native static void end(long addr); +} diff -r bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 2013 +0100 @@ -0,0 +1,327 @@ +/* + * 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; + +import java.util.Date; + +/** + * 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) { + 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) { + 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 InternalError(); + } + } +} diff -r bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 2013 +0100 @@ -0,0 +1,456 @@ +/* + * 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 java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import static java.util.zip.ZipConstants64.*; + +/** + * 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; + + private ZipCoder zc; + + /** + * 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, StandardCharsets.UTF_8); + } + + /** + * 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) + ? zc.toStringUTF8(b, len) + : zc.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); + } +} diff -r bc6f3be91306 -r 74b8fb771bc2 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 Fri Feb 01 16:35:42 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 + + + +