# HG changeset patch # User Jaroslav Tulach # Date 1361120314 -3600 # Node ID 41e1dc88a399ab8a5bf0b509a5b0c582025e42bc # Parent d443b6c05a013b4040f97ee28f30d1b2ed3b0f1c# Parent c6878807b0d47eae0301efde41bf60f55c8caa6f Bringing in latest default branch diff -r d443b6c05a01 -r 41e1dc88a399 benchmarks/matrix-multiplication/pom.xml --- a/benchmarks/matrix-multiplication/pom.xml Fri Jan 25 15:08:24 2013 +0100 +++ b/benchmarks/matrix-multiplication/pom.xml Sun Feb 17 17:58:34 2013 +0100 @@ -7,6 +7,11 @@ matrix.multiplication 0.3-SNAPSHOT jar + + benchmarks + org.apidesign.bck2brwsr + 0.3-SNAPSHOT + Matrix multiplication @@ -25,6 +30,14 @@ 1.7 + + org.apache.maven.plugins + maven-deploy-plugin + 2.7 + + true + + diff -r d443b6c05a01 -r 41e1dc88a399 core/pom.xml --- a/core/pom.xml Fri Jan 25 15:08:24 2013 +0100 +++ b/core/pom.xml Sun Feb 17 17:58:34 2013 +0100 @@ -38,6 +38,7 @@ org.netbeans.api org-openide-util-lookup + provided Contains esential annotations for associating JavaScript code with diff -r d443b6c05a01 -r 41e1dc88a399 dew/src/main/java/org/apidesign/bck2brwsr/dew/Dew.java --- a/dew/src/main/java/org/apidesign/bck2brwsr/dew/Dew.java Fri Jan 25 15:08:24 2013 +0100 +++ b/dew/src/main/java/org/apidesign/bck2brwsr/dew/Dew.java Sun Feb 17 17:58:34 2013 +0100 @@ -96,7 +96,9 @@ } if (r.equals("/result.html")) { response.setContentType("text/html"); - response.getOutputBuffer().write(data.getHtml()); + if (data != null) { + response.getOutputBuffer().write(data.getHtml()); + } response.setStatus(HttpStatus.OK_200); return; } diff -r d443b6c05a01 -r 41e1dc88a399 dew/src/main/resources/org/apidesign/bck2brwsr/dew/js/app.js --- a/dew/src/main/resources/org/apidesign/bck2brwsr/dew/js/app.js Fri Jan 25 15:08:24 2013 +0100 +++ b/dew/src/main/resources/org/apidesign/bck2brwsr/dew/js/app.js Sun Feb 17 17:58:34 2013 +0100 @@ -112,7 +112,7 @@ " var arr = eval('(' + request.responseText + ')');\n" + " return arr;\n" + " }\n" + -" var vm = new bck2brwsr(ldCls);\n" + +" var vm = bck2brwsr(ldCls);\n" + " vm.loadClass('${fqn}');\n" + " \n" + ""; diff -r d443b6c05a01 -r 41e1dc88a399 emul/compact/pom.xml --- a/emul/compact/pom.xml Fri Jan 25 15:08:24 2013 +0100 +++ b/emul/compact/pom.xml Sun Feb 17 17:58:34 2013 +0100 @@ -10,7 +10,7 @@ org.apidesign.bck2brwsr emul 0.3-SNAPSHOT - Compact API Profile + Bck2Brwsr API Profile http://maven.apache.org UTF-8 @@ -20,6 +20,7 @@ ${project.groupId} emul.mini ${project.version} + provided ${project.groupId} @@ -27,6 +28,11 @@ ${project.version} test + + org.netbeans.api + org-openide-util-lookup + test + @@ -42,6 +48,25 @@ 1.7 + + maven-assembly-plugin + 2.4 + + + rt + package + + single + + + + src/main/assembly/rt.xml + + bck2brwsr-${project.version} + + + + diff -r d443b6c05a01 -r 41e1dc88a399 emul/compact/src/main/assembly/rt.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/assembly/rt.xml Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,26 @@ + + + rt + + jar + + false + + + true + true + provided + + + META-INF/maven/** + + + + + + + ${project.build.outputDirectory} + / + + + \ No newline at end of file diff -r d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.beans; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.EventListener; +import java.util.EventListenerProxy; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import org.apidesign.bck2brwsr.emul.lang.System; + +/** + * This is an abstract class that provides base functionality + * for the {@link PropertyChangeSupport PropertyChangeSupport} class + * and the {@link VetoableChangeSupport VetoableChangeSupport} class. + * + * @see PropertyChangeListenerMap + * @see VetoableChangeListenerMap + * + * @author Sergey A. Malenkov + */ +abstract class ChangeListenerMap { + private Map map; + + /** + * Creates an array of listeners. + * This method can be optimized by using + * the same instance of the empty array + * when {@code length} is equal to {@code 0}. + * + * @param length the array length + * @return an array with specified length + */ + protected abstract L[] newArray(int length); + + /** + * Creates a proxy listener for the specified property. + * + * @param name the name of the property to listen on + * @param listener the listener to process events + * @return a proxy listener + */ + protected abstract L newProxy(String name, L listener); + + /** + * Adds a listener to the list of listeners for the specified property. + * This listener is called as many times as it was added. + * + * @param name the name of the property to listen on + * @param listener the listener to process events + */ + public final synchronized void add(String name, L listener) { + if (this.map == null) { + this.map = new HashMap(); + } + L[] array = this.map.get(name); + int size = (array != null) + ? array.length + : 0; + + L[] clone = newArray(size + 1); + clone[size] = listener; + if (array != null) { + System.arraycopy(array, 0, clone, 0, size); + } + this.map.put(name, clone); + } + + /** + * Removes a listener from the list of listeners for the specified property. + * If the listener was added more than once to the same event source, + * this listener will be notified one less time after being removed. + * + * @param name the name of the property to listen on + * @param listener the listener to process events + */ + public final synchronized void remove(String name, L listener) { + if (this.map != null) { + L[] array = this.map.get(name); + if (array != null) { + for (int i = 0; i < array.length; i++) { + if (listener.equals(array[i])) { + int size = array.length - 1; + if (size > 0) { + L[] clone = newArray(size); + System.arraycopy(array, 0, clone, 0, i); + System.arraycopy(array, i + 1, clone, i, size - i); + this.map.put(name, clone); + } + else { + this.map.remove(name); + if (this.map.isEmpty()) { + this.map = null; + } + } + break; + } + } + } + } + } + + /** + * Returns the list of listeners for the specified property. + * + * @param name the name of the property + * @return the corresponding list of listeners + */ + public final synchronized L[] get(String name) { + return (this.map != null) + ? this.map.get(name) + : null; + } + + /** + * Sets new list of listeners for the specified property. + * + * @param name the name of the property + * @param listeners new list of listeners + */ + public final void set(String name, L[] listeners) { + if (listeners != null) { + if (this.map == null) { + this.map = new HashMap(); + } + this.map.put(name, listeners); + } + else if (this.map != null) { + this.map.remove(name); + if (this.map.isEmpty()) { + this.map = null; + } + } + } + + /** + * Returns all listeners in the map. + * + * @return an array of all listeners + */ + public final synchronized L[] getListeners() { + if (this.map == null) { + return newArray(0); + } + List list = new ArrayList(); + + L[] listeners = this.map.get(null); + if (listeners != null) { + for (L listener : listeners) { + list.add(listener); + } + } + for (Entry entry : this.map.entrySet()) { + String name = entry.getKey(); + if (name != null) { + for (L listener : entry.getValue()) { + list.add(newProxy(name, listener)); + } + } + } + return list.toArray(newArray(list.size())); + } + + /** + * Returns listeners that have been associated with the named property. + * + * @param name the name of the property + * @return an array of listeners for the named property + */ + public final L[] getListeners(String name) { + if (name != null) { + L[] listeners = get(name); + if (listeners != null) { + return listeners.clone(); + } + } + return newArray(0); + } + + /** + * Indicates whether the map contains + * at least one listener to be notified. + * + * @param name the name of the property + * @return {@code true} if at least one listener exists or + * {@code false} otherwise + */ + public final synchronized boolean hasListeners(String name) { + if (this.map == null) { + return false; + } + L[] array = this.map.get(null); + return (array != null) || ((name != null) && (null != this.map.get(name))); + } + + /** + * Returns a set of entries from the map. + * Each entry is a pair consisted of the property name + * and the corresponding list of listeners. + * + * @return a set of entries from the map + */ + public final Set> getEntries() { + return (this.map != null) + ? this.map.entrySet() + : Collections.>emptySet(); + } + + /** + * Extracts a real listener from the proxy listener. + * It is necessary because default proxy class is not serializable. + * + * @return a real listener + */ + public final L extract(L listener) { + while (listener instanceof EventListenerProxy) { + EventListenerProxy proxy = (EventListenerProxy) listener; + listener = proxy.getListener(); + } + return listener; + } +} diff -r d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,526 @@ +/* + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.beans; + +import java.io.Serializable; +import java.io.ObjectStreamField; +import java.io.ObjectOutputStream; +import java.io.ObjectInputStream; +import java.io.IOException; +import java.util.Hashtable; +import java.util.Map.Entry; +import org.apidesign.bck2brwsr.emul.lang.System; + +/** + * This is a utility class that can be used by beans that support constrained + * properties. It manages a list of listeners and dispatches + * {@link PropertyChangeEvent}s to them. You can use an instance of this class + * as a member field of your bean and delegate these types of work to it. + * The {@link VetoableChangeListener} can be registered for all properties + * or for a property specified by name. + *

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

+ * At serialization time we skip non-serializable listeners and + * only serialize the serializable listeners. + */ + private void writeObject(ObjectOutputStream s) throws IOException { + Hashtable children = null; + VetoableChangeListener[] listeners = null; + synchronized (this.map) { + for (Entry entry : this.map.getEntries()) { + String property = entry.getKey(); + if (property == null) { + listeners = entry.getValue(); + } else { + if (children == null) { + children = new Hashtable(); + } + VetoableChangeSupport vcs = new VetoableChangeSupport(this.source); + vcs.map.set(null, entry.getValue()); + children.put(property, vcs); + } + } + } + ObjectOutputStream.PutField fields = s.putFields(); + fields.put("children", children); + fields.put("source", this.source); + fields.put("vetoableChangeSupportSerializedDataVersion", 2); + s.writeFields(); + + if (listeners != null) { + for (VetoableChangeListener l : listeners) { + if (l instanceof Serializable) { + s.writeObject(l); + } + } + } + s.writeObject(null); + } + + private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException { + this.map = new VetoableChangeListenerMap(); + + ObjectInputStream.GetField fields = s.readFields(); + + Hashtable children = (Hashtable) fields.get("children", null); + this.source = fields.get("source", null); + fields.get("vetoableChangeSupportSerializedDataVersion", 2); + + Object listenerOrNull; + while (null != (listenerOrNull = s.readObject())) { + this.map.add(null, (VetoableChangeListener)listenerOrNull); + } + if (children != null) { + for (Entry entry : children.entrySet()) { + for (VetoableChangeListener listener : entry.getValue().getVetoableChangeListeners()) { + this.map.add(entry.getKey(), listener); + } + } + } + } + + /** + * The object to be provided as the "source" for any generated events. + */ + private Object source; + + /** + * @serialField children Hashtable + * @serialField source Object + * @serialField vetoableChangeSupportSerializedDataVersion int + */ + private static final ObjectStreamField[] serialPersistentFields = { + new ObjectStreamField("children", Hashtable.class), + new ObjectStreamField("source", Object.class), + new ObjectStreamField("vetoableChangeSupportSerializedDataVersion", Integer.TYPE) + }; + + /** + * Serialization version ID, so we're compatible with JDK 1.1 + */ + static final long serialVersionUID = -5090210921595982017L; + + /** + * This is a {@link ChangeListenerMap ChangeListenerMap} implementation + * that works with {@link VetoableChangeListener VetoableChangeListener} objects. + */ + private static final class VetoableChangeListenerMap extends ChangeListenerMap { + private static final VetoableChangeListener[] EMPTY = {}; + + /** + * Creates an array of {@link VetoableChangeListener VetoableChangeListener} objects. + * This method uses the same instance of the empty array + * when {@code length} equals {@code 0}. + * + * @param length the array length + * @return an array with specified length + */ + @Override + protected VetoableChangeListener[] newArray(int length) { + return (0 < length) + ? new VetoableChangeListener[length] + : EMPTY; + } + + /** + * Creates a {@link VetoableChangeListenerProxy VetoableChangeListenerProxy} + * object for the specified property. + * + * @param name the name of the property to listen on + * @param listener the listener to process events + * @return a {@code VetoableChangeListenerProxy} object + */ + @Override + protected VetoableChangeListener newProxy(String name, VetoableChangeListener listener) { + return new VetoableChangeListenerProxy(name, listener); + } + } +} diff -r d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 emul/compact/src/main/java/java/io/BufferedOutputStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/BufferedOutputStream.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,143 @@ +/* + * Copyright (c) 1994, 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.io; + +/** + * The class implements a buffered output stream. By setting up such + * an output stream, an application can write bytes to the underlying + * output stream without necessarily causing a call to the underlying + * system for each byte written. + * + * @author Arthur van Hoff + * @since JDK1.0 + */ +public +class BufferedOutputStream extends FilterOutputStream { + /** + * The internal buffer where data is stored. + */ + protected byte buf[]; + + /** + * The number of valid bytes in the buffer. This value is always + * in the range 0 through buf.length; elements + * buf[0] through buf[count-1] contain valid + * byte data. + */ + protected int count; + + /** + * Creates a new buffered output stream to write data to the + * specified underlying output stream. + * + * @param out the underlying output stream. + */ + public BufferedOutputStream(OutputStream out) { + this(out, 8192); + } + + /** + * Creates a new buffered output stream to write data to the + * specified underlying output stream with the specified buffer + * size. + * + * @param out the underlying output stream. + * @param size the buffer size. + * @exception IllegalArgumentException if size <= 0. + */ + public BufferedOutputStream(OutputStream out, int size) { + super(out); + if (size <= 0) { + throw new IllegalArgumentException("Buffer size <= 0"); + } + buf = new byte[size]; + } + + /** Flush the internal buffer */ + private void flushBuffer() throws IOException { + if (count > 0) { + out.write(buf, 0, count); + count = 0; + } + } + + /** + * Writes the specified byte to this buffered output stream. + * + * @param b the byte to be written. + * @exception IOException if an I/O error occurs. + */ + public synchronized void write(int b) throws IOException { + if (count >= buf.length) { + flushBuffer(); + } + buf[count++] = (byte)b; + } + + /** + * Writes len bytes from the specified byte array + * starting at offset off to this buffered output stream. + * + *

Ordinarily this method stores bytes from the given array into this + * stream's buffer, flushing the buffer to the underlying output stream as + * needed. If the requested length is at least as large as this stream's + * buffer, however, then this method will flush the buffer and write the + * bytes directly to the underlying output stream. Thus redundant + * BufferedOutputStreams will not copy data unnecessarily. + * + * @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. + */ + public synchronized void write(byte b[], int off, int len) throws IOException { + if (len >= buf.length) { + /* If the request length exceeds the size of the output buffer, + flush the output buffer and then write the data directly. + In this way buffered streams will cascade harmlessly. */ + flushBuffer(); + out.write(b, off, len); + return; + } + if (len > buf.length - count) { + flushBuffer(); + } + System.arraycopy(b, off, buf, count, len); + count += len; + } + + /** + * Flushes this buffered output stream. This forces any buffered + * output bytes to be written out to the underlying output stream. + * + * @exception IOException if an I/O error occurs. + * @see java.io.FilterOutputStream#out + */ + public synchronized void flush() throws IOException { + flushBuffer(); + out.flush(); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/compact/src/main/java/java/io/BufferedReader.java --- a/emul/compact/src/main/java/java/io/BufferedReader.java Fri Jan 25 15:08:24 2013 +0100 +++ b/emul/compact/src/main/java/java/io/BufferedReader.java Sun Feb 17 17:58:34 2013 +0100 @@ -25,7 +25,6 @@ package java.io; -import org.apidesign.bck2brwsr.emul.lang.System; /** diff -r d443b6c05a01 -r 41e1dc88a399 emul/compact/src/main/java/java/io/ByteArrayOutputStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/io/ByteArrayOutputStream.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,272 @@ +/* + * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +import java.util.Arrays; + +/** + * This class implements an output stream in which the data is + * written into a byte array. The buffer automatically grows as data + * is written to it. + * The data can be retrieved using toByteArray() and + * toString(). + *

+ * Closing a ByteArrayOutputStream has no effect. The methods in + * this class can be called after the stream has been closed without + * generating an IOException. + * + * @author Arthur van Hoff + * @since JDK1.0 + */ + +public class ByteArrayOutputStream extends OutputStream { + + /** + * The buffer where data is stored. + */ + protected byte buf[]; + + /** + * The number of valid bytes in the buffer. + */ + protected int count; + + /** + * Creates a new byte array output stream. The buffer capacity is + * initially 32 bytes, though its size increases if necessary. + */ + public ByteArrayOutputStream() { + this(32); + } + + /** + * Creates a new byte array output stream, with a buffer capacity of + * the specified size, in bytes. + * + * @param size the initial size. + * @exception IllegalArgumentException if size is negative. + */ + public ByteArrayOutputStream(int size) { + if (size < 0) { + throw new IllegalArgumentException("Negative initial size: " + + size); + } + buf = new byte[size]; + } + + /** + * Increases the capacity if necessary to ensure that it can hold + * at least the number of elements specified by the minimum + * capacity argument. + * + * @param minCapacity the desired minimum capacity + * @throws OutOfMemoryError if {@code minCapacity < 0}. This is + * interpreted as a request for the unsatisfiably large capacity + * {@code (long) Integer.MAX_VALUE + (minCapacity - Integer.MAX_VALUE)}. + */ + private void ensureCapacity(int minCapacity) { + // overflow-conscious code + if (minCapacity - buf.length > 0) + grow(minCapacity); + } + + /** + * Increases the capacity to ensure that it can hold at least the + * number of elements specified by the minimum capacity argument. + * + * @param minCapacity the desired minimum capacity + */ + private void grow(int minCapacity) { + // overflow-conscious code + int oldCapacity = buf.length; + int newCapacity = oldCapacity << 1; + if (newCapacity - minCapacity < 0) + newCapacity = minCapacity; + if (newCapacity < 0) { + if (minCapacity < 0) // overflow + throw new OutOfMemoryError(); + newCapacity = Integer.MAX_VALUE; + } + buf = Arrays.copyOf(buf, newCapacity); + } + + /** + * Writes the specified byte to this byte array output stream. + * + * @param b the byte to be written. + */ + public synchronized void write(int b) { + ensureCapacity(count + 1); + buf[count] = (byte) b; + count += 1; + } + + /** + * Writes len bytes from the specified byte array + * starting at offset off to this byte array output stream. + * + * @param b the data. + * @param off the start offset in the data. + * @param len the number of bytes to write. + */ + public synchronized void write(byte b[], int off, int len) { + if ((off < 0) || (off > b.length) || (len < 0) || + ((off + len) - b.length > 0)) { + throw new IndexOutOfBoundsException(); + } + ensureCapacity(count + len); + System.arraycopy(b, off, buf, count, len); + count += len; + } + + /** + * Writes the complete contents of this byte array output stream to + * the specified output stream argument, as if by calling the output + * stream's write method using out.write(buf, 0, count). + * + * @param out the output stream to which to write the data. + * @exception IOException if an I/O error occurs. + */ + public synchronized void writeTo(OutputStream out) throws IOException { + out.write(buf, 0, count); + } + + /** + * Resets the count field of this byte array output + * stream to zero, so that all currently accumulated output in the + * output stream is discarded. The output stream can be used again, + * reusing the already allocated buffer space. + * + * @see java.io.ByteArrayInputStream#count + */ + public synchronized void reset() { + count = 0; + } + + /** + * Creates a newly allocated byte array. Its size is the current + * size of this output stream and the valid contents of the buffer + * have been copied into it. + * + * @return the current contents of this output stream, as a byte array. + * @see java.io.ByteArrayOutputStream#size() + */ + public synchronized byte toByteArray()[] { + return Arrays.copyOf(buf, count); + } + + /** + * Returns the current size of the buffer. + * + * @return the value of the count field, which is the number + * of valid bytes in this output stream. + * @see java.io.ByteArrayOutputStream#count + */ + public synchronized int size() { + return count; + } + + /** + * Converts the buffer's contents into a string decoding bytes using the + * platform's default character set. The length of the new String + * is a function of the character set, and hence may not be equal to the + * size of the buffer. + * + *

This method always replaces malformed-input and unmappable-character + * sequences with the default replacement string for the platform's + * default character set. The {@linkplain java.nio.charset.CharsetDecoder} + * class should be used when more control over the decoding process is + * required. + * + * @return String decoded from the buffer's contents. + * @since JDK1.1 + */ + public synchronized String toString() { + return new String(buf, 0, count); + } + + /** + * Converts the buffer's contents into a string by decoding the bytes using + * the specified {@link java.nio.charset.Charset charsetName}. The length of + * the new String is a function of the charset, and hence may not be + * equal to the length of the byte array. + * + *

This method always replaces malformed-input and unmappable-character + * sequences with this charset's default replacement string. The {@link + * java.nio.charset.CharsetDecoder} class should be used when more control + * over the decoding process is required. + * + * @param charsetName the name of a supported + * {@linkplain java.nio.charset.Charset charset} + * @return String decoded from the buffer's contents. + * @exception UnsupportedEncodingException + * If the named charset is not supported + * @since JDK1.1 + */ + public synchronized String toString(String charsetName) + throws UnsupportedEncodingException + { + return new String(buf, 0, count, charsetName); + } + + /** + * Creates a newly allocated string. Its size is the current size of + * the output stream and the valid contents of the buffer have been + * copied into it. Each character c in the resulting string is + * constructed from the corresponding element b in the byte + * array such that: + *

+     *     c == (char)(((hibyte & 0xff) << 8) | (b & 0xff))
+     * 
+ * + * @deprecated This method does not properly convert bytes into characters. + * As of JDK 1.1, the preferred way to do this is via the + * toString(String enc) method, which takes an encoding-name + * argument, or the toString() method, which uses the + * platform's default character encoding. + * + * @param hibyte the high byte of each resulting Unicode character. + * @return the current contents of the output stream, as a string. + * @see java.io.ByteArrayOutputStream#size() + * @see java.io.ByteArrayOutputStream#toString(String) + * @see java.io.ByteArrayOutputStream#toString() + */ + @Deprecated + public synchronized String toString(int hibyte) { + return new String(buf, hibyte, 0, count); + } + + /** + * Closing a ByteArrayOutputStream has no effect. The methods in + * this class can be called after the stream has been closed without + * generating an IOException. + *

+ * + */ + public void close() throws IOException { + } + +} diff -r d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 emul/compact/src/main/java/java/io/InputStreamReader.java --- a/emul/compact/src/main/java/java/io/InputStreamReader.java Fri Jan 25 15:08:24 2013 +0100 +++ b/emul/compact/src/main/java/java/io/InputStreamReader.java Sun Feb 17 17:58:34 2013 +0100 @@ -156,9 +156,39 @@ * @exception IOException If an I/O error occurs */ public int read() throws IOException { - return ((InputStream)lock).read(); + final InputStream is = (InputStream)lock; + int c = is.read(); + if (c == -1) { + return -1; + } + c = (int) c & 0xff; + switch (c >> 4) { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + /* 0xxxxxxx*/ + return c; + case 12: case 13: { + /* 110x xxxx 10xx xxxx*/ + int char2 = (int) is.read(); + if ((char2 & 0xC0) != 0x80) + throw new UTFDataFormatException("malformed input"); + return (((c & 0x1F) << 6) | (char2 & 0x3F)); + } + case 14: { + /* 1110 xxxx 10xx xxxx 10xx xxxx */ + int char2 = is.read(); + int char3 = is.read(); + if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) + throw new UTFDataFormatException("malformed input"); + return (((c & 0x0F) << 12) | + ((char2 & 0x3F) << 6) | + ((char3 & 0x3F) << 0)); + } + default: + /* 10xx xxxx, 1111 xxxx */ + throw new UTFDataFormatException("malformed input"); + } } - + /** * Reads characters into a portion of an array. * diff -r d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,3357 @@ +/* + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +import java.lang.reflect.Array; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; +import java.util.Arrays; +import java.util.HashMap; +import org.apidesign.bck2brwsr.emul.lang.System; + +/** + * An ObjectInputStream deserializes primitive data and objects previously + * written using an ObjectOutputStream. + * + *

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

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

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

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

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

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

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

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

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

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

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

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

+ * + *

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Note that the exception propagation algorithm used depends on handles + * being assigned/finished in LIFO order; however, for simplicity as well + * as memory conservation, it does not enforce this constraint. + */ + // REMIND: add full description of exception propagation algorithm? + private static class HandleTable { + + /* status codes indicating whether object has associated exception */ + private static final byte STATUS_OK = 1; + private static final byte STATUS_UNKNOWN = 2; + private static final byte STATUS_EXCEPTION = 3; + + /** array mapping handle -> object status */ + byte[] status; + /** array mapping handle -> object/exception (depending on status) */ + Object[] entries; + /** array mapping handle -> list of dependent handles (if any) */ + HandleList[] deps; + /** lowest unresolved dependency */ + int lowDep = -1; + /** number of handles in table */ + int size = 0; + + /** + * Creates handle table with the given initial capacity. + */ + HandleTable(int initialCapacity) { + status = new byte[initialCapacity]; + entries = new Object[initialCapacity]; + deps = new HandleList[initialCapacity]; + } + + /** + * Assigns next available handle to given object, and returns assigned + * handle. Once object has been completely deserialized (and all + * dependencies on other objects identified), the handle should be + * "closed" by passing it to finish(). + */ + int assign(Object obj) { + if (size >= entries.length) { + grow(); + } + status[size] = STATUS_UNKNOWN; + entries[size] = obj; + return size++; + } + + /** + * Registers a dependency (in exception status) of one handle on + * another. The dependent handle must be "open" (i.e., assigned, but + * not finished yet). No action is taken if either dependent or target + * handle is NULL_HANDLE. + */ + void markDependency(int dependent, int target) { + if (dependent == NULL_HANDLE || target == NULL_HANDLE) { + return; + } + switch (status[dependent]) { + + case STATUS_UNKNOWN: + switch (status[target]) { + case STATUS_OK: + // ignore dependencies on objs with no exception + break; + + case STATUS_EXCEPTION: + // eagerly propagate exception + markException(dependent, + (ClassNotFoundException) entries[target]); + break; + + case STATUS_UNKNOWN: + // add to dependency list of target + if (deps[target] == null) { + deps[target] = new HandleList(); + } + deps[target].add(dependent); + + // remember lowest unresolved target seen + if (lowDep < 0 || lowDep > target) { + lowDep = target; + } + break; + + default: + throw new InternalError(); + } + break; + + case STATUS_EXCEPTION: + break; + + default: + throw new InternalError(); + } + } + + /** + * Associates a ClassNotFoundException (if one not already associated) + * with the currently active handle and propagates it to other + * referencing objects as appropriate. The specified handle must be + * "open" (i.e., assigned, but not finished yet). + */ + void markException(int handle, ClassNotFoundException ex) { + switch (status[handle]) { + case STATUS_UNKNOWN: + status[handle] = STATUS_EXCEPTION; + entries[handle] = ex; + + // propagate exception to dependents + HandleList dlist = deps[handle]; + if (dlist != null) { + int ndeps = dlist.size(); + for (int i = 0; i < ndeps; i++) { + markException(dlist.get(i), ex); + } + deps[handle] = null; + } + break; + + case STATUS_EXCEPTION: + break; + + default: + throw new InternalError(); + } + } + + /** + * Marks given handle as finished, meaning that no new dependencies + * will be marked for handle. Calls to the assign and finish methods + * must occur in LIFO order. + */ + void finish(int handle) { + int end; + if (lowDep < 0) { + // no pending unknowns, only resolve current handle + end = handle + 1; + } else if (lowDep >= handle) { + // pending unknowns now clearable, resolve all upward handles + end = size; + lowDep = -1; + } else { + // unresolved backrefs present, can't resolve anything yet + return; + } + + // change STATUS_UNKNOWN -> STATUS_OK in selected span of handles + for (int i = handle; i < end; i++) { + switch (status[i]) { + case STATUS_UNKNOWN: + status[i] = STATUS_OK; + deps[i] = null; + break; + + case STATUS_OK: + case STATUS_EXCEPTION: + break; + + default: + throw new InternalError(); + } + } + } + + /** + * Assigns a new object to the given handle. The object previously + * associated with the handle is forgotten. This method has no effect + * if the given handle already has an exception associated with it. + * This method may be called at any time after the handle is assigned. + */ + void setObject(int handle, Object obj) { + switch (status[handle]) { + case STATUS_UNKNOWN: + case STATUS_OK: + entries[handle] = obj; + break; + + case STATUS_EXCEPTION: + break; + + default: + throw new InternalError(); + } + } + + /** + * Looks up and returns object associated with the given handle. + * Returns null if the given handle is NULL_HANDLE, or if it has an + * associated ClassNotFoundException. + */ + Object lookupObject(int handle) { + return (handle != NULL_HANDLE && + status[handle] != STATUS_EXCEPTION) ? + entries[handle] : null; + } + + /** + * Looks up and returns ClassNotFoundException associated with the + * given handle. Returns null if the given handle is NULL_HANDLE, or + * if there is no ClassNotFoundException associated with the handle. + */ + ClassNotFoundException lookupException(int handle) { + return (handle != NULL_HANDLE && + status[handle] == STATUS_EXCEPTION) ? + (ClassNotFoundException) entries[handle] : null; + } + + /** + * Resets table to its initial state. + */ + void clear() { + Arrays.fill(status, 0, size, (byte) 0); + Arrays.fill(entries, 0, size, null); + Arrays.fill(deps, 0, size, null); + lowDep = -1; + size = 0; + } + + /** + * Returns number of handles registered in table. + */ + int size() { + return size; + } + + /** + * Expands capacity of internal arrays. + */ + private void grow() { + int newCapacity = (entries.length << 1) + 1; + + byte[] newStatus = new byte[newCapacity]; + Object[] newEntries = new Object[newCapacity]; + HandleList[] newDeps = new HandleList[newCapacity]; + + System.arraycopy(status, 0, newStatus, 0, size); + System.arraycopy(entries, 0, newEntries, 0, size); + System.arraycopy(deps, 0, newDeps, 0, size); + + status = newStatus; + entries = newEntries; + deps = newDeps; + } + + /** + * Simple growable list of (integer) handles. + */ + private static class HandleList { + private int[] list = new int[4]; + private int size = 0; + + public HandleList() { + } + + public void add(int handle) { + if (size >= list.length) { + int[] newList = new int[list.length << 1]; + System.arraycopy(list, 0, newList, 0, list.length); + list = newList; + } + list[size++] = handle; + } + + public int get(int index) { + if (index >= size) { + throw new ArrayIndexOutOfBoundsException(); + } + return list[index]; + } + + public int size() { + return size; + } + } + } + + /** + * Method for cloning arrays in case of using unsharing reading + */ + private static Object cloneArray(Object array) { + if (array instanceof Object[]) { + return ((Object[]) array).clone(); + } else if (array instanceof boolean[]) { + return ((boolean[]) array).clone(); + } else if (array instanceof byte[]) { + return ((byte[]) array).clone(); + } else if (array instanceof char[]) { + return ((char[]) array).clone(); + } else if (array instanceof double[]) { + return ((double[]) array).clone(); + } else if (array instanceof float[]) { + return ((float[]) array).clone(); + } else if (array instanceof int[]) { + return ((int[]) array).clone(); + } else if (array instanceof long[]) { + return ((long[]) array).clone(); + } else if (array instanceof short[]) { + return ((short[]) array).clone(); + } else { + throw new AssertionError(); + } + } + +} diff -r d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,2369 @@ +/* + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.apidesign.bck2brwsr.emul.lang.System; + +/** + * An ObjectOutputStream writes primitive data types and graphs of Java objects + * to an OutputStream. The objects can be read (reconstituted) using an + * ObjectInputStream. Persistent storage of objects can be accomplished by + * using a file for the stream. If the stream is a network socket stream, the + * objects can be reconstituted on another host or in another process. + * + *

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

+ * + * This protocol is written by JVM 1.2. + * + * Externalizable data is written in block data mode and is + * terminated with TC_ENDBLOCKDATA. Externalizable classdescriptor + * flags has SC_BLOCK_DATA enabled. JVM 1.1.6 and greater can + * read this format change. + * + * Enables writing a nonSerializable class descriptor into the + * stream. The serialVersionUID of a nonSerializable class is + * set to 0L. + * + * @see java.io.ObjectOutputStream#useProtocolVersion(int) + * @see #SC_BLOCK_DATA + * @since 1.2 + */ + public final static int PROTOCOL_VERSION_2 = 2; +} diff -r d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 emul/compact/src/main/java/java/lang/AbstractMethodError.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/lang/AbstractMethodError.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 1994, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * Thrown when an application tries to call an abstract method. + * Normally, this error is caught by the compiler; this error can + * only occur at run time if the definition of some class has + * incompatibly changed since the currently executing method was last + * compiled. + * + * @author unascribed + * @since JDK1.0 + */ +public +class AbstractMethodError extends IncompatibleClassChangeError { + private static final long serialVersionUID = -1654391082989018462L; + + /** + * Constructs an AbstractMethodError with no detail message. + */ + public AbstractMethodError() { + super(); + } + + /** + * Constructs an AbstractMethodError with the specified + * detail message. + * + * @param s the detail message. + */ + public AbstractMethodError(String s) { + super(s); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/compact/src/main/java/java/lang/IncompatibleClassChangeError.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/lang/IncompatibleClassChangeError.java Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 emul/compact/src/main/java/java/lang/NoSuchFieldError.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/lang/NoSuchFieldError.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * Thrown if an application tries to access or modify a specified + * field of an object, and that object no longer has that field. + *

+ * Normally, this error is caught by the compiler; this error can + * only occur at run time if the definition of a class has + * incompatibly changed. + * + * @author unascribed + * @since JDK1.0 + */ +public +class NoSuchFieldError extends IncompatibleClassChangeError { + private static final long serialVersionUID = -3456430195886129035L; + + /** + * Constructs a NoSuchFieldError with no detail message. + */ + public NoSuchFieldError() { + super(); + } + + /** + * Constructs a NoSuchFieldError with the specified + * detail message. + * + * @param s the detail message. + */ + public NoSuchFieldError(String s) { + super(s); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/compact/src/main/java/java/lang/System.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/lang/System.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,36 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package java.lang; + +/** Poor man's re-implementation of most important System methods. + * + * @author Jaroslav Tulach + */ +public class System { + private System() { + } + + public static void arraycopy(Object value, int srcBegin, Object dst, int dstBegin, int count) { + org.apidesign.bck2brwsr.emul.lang.System.arraycopy(value, srcBegin, dst, dstBegin, count); + } + + public static long currentTimeMillis() { + return org.apidesign.bck2brwsr.emul.lang.System.currentTimeMillis(); + } + +} diff -r d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,185 @@ +/* + * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.ref; + + +/** + * Abstract base class for reference objects. This class defines the + * operations common to all reference objects. Because reference objects are + * implemented in close cooperation with the garbage collector, this class may + * not be subclassed directly. + * + * @author Mark Reinhold + * @since 1.2 + */ + +public abstract class Reference { + + /* A Reference instance is in one of four possible internal states: + * + * Active: Subject to special treatment by the garbage collector. Some + * time after the collector detects that the reachability of the + * referent has changed to the appropriate state, it changes the + * instance's state to either Pending or Inactive, depending upon + * whether or not the instance was registered with a queue when it was + * created. In the former case it also adds the instance to the + * pending-Reference list. Newly-created instances are Active. + * + * Pending: An element of the pending-Reference list, waiting to be + * enqueued by the Reference-handler thread. Unregistered instances + * are never in this state. + * + * Enqueued: An element of the queue with which the instance was + * registered when it was created. When an instance is removed from + * its ReferenceQueue, it is made Inactive. Unregistered instances are + * never in this state. + * + * Inactive: Nothing more to do. Once an instance becomes Inactive its + * state will never change again. + * + * The state is encoded in the queue and next fields as follows: + * + * Active: queue = ReferenceQueue with which instance is registered, or + * ReferenceQueue.NULL if it was not registered with a queue; next = + * null. + * + * Pending: queue = ReferenceQueue with which instance is registered; + * next = Following instance in queue, or this if at end of list. + * + * Enqueued: queue = ReferenceQueue.ENQUEUED; next = Following instance + * in queue, or this if at end of list. + * + * Inactive: queue = ReferenceQueue.NULL; next = this. + * + * With this scheme the collector need only examine the next field in order + * to determine whether a Reference instance requires special treatment: If + * the next field is null then the instance is active; if it is non-null, + * then the collector should treat the instance normally. + * + * To ensure that concurrent collector can discover active Reference + * objects without interfering with application threads that may apply + * the enqueue() method to those objects, collectors should link + * discovered objects through the discovered field. + */ + + private T referent; /* Treated specially by GC */ + + ReferenceQueue queue; + + Reference next; + transient private Reference discovered; /* used by VM */ + + + /* Object used to synchronize with the garbage collector. The collector + * must acquire this lock at the beginning of each collection cycle. It is + * therefore critical that any code holding this lock complete as quickly + * as possible, allocate no new objects, and avoid calling user code. + */ + static private class Lock { }; + private static Lock lock = new Lock(); + + + /* List of References waiting to be enqueued. The collector adds + * References to this list, while the Reference-handler thread removes + * them. This list is protected by the above lock object. + */ + private static Reference pending = null; + + + + /* -- Referent accessor and setters -- */ + + /** + * Returns this reference object's referent. If this reference object has + * been cleared, either by the program or by the garbage collector, then + * this method returns null. + * + * @return The object to which this reference refers, or + * null if this reference object has been cleared + */ + public T get() { + return this.referent; + } + + /** + * Clears this reference object. Invoking this method will not cause this + * object to be enqueued. + * + *

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

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

This method does not offer real-time guarantees: It schedules the + * timeout as if by invoking the {@link Object#wait(long)} method. + * + * @param timeout If positive, block for up to timeout + * milliseconds while waiting for a reference to be + * added to this queue. If zero, block indefinitely. + * + * @return A reference object, if one was available within the specified + * timeout period, otherwise null + * + * @throws IllegalArgumentException + * If the value of the timeout argument is negative + * + * @throws InterruptedException + * If the timeout wait is interrupted + */ + public Reference remove(long timeout) + throws IllegalArgumentException, InterruptedException + { + if (timeout < 0) { + throw new IllegalArgumentException("Negative timeout value"); + } + synchronized (lock) { + Reference r = reallyPoll(); + if (r != null) return r; + for (;;) { + lock.wait(timeout); + r = reallyPoll(); + if (r != null) return r; + if (timeout != 0) return null; + } + } + } + + /** + * Removes the next reference object in this queue, blocking until one + * becomes available. + * + * @return A reference object, blocking until one becomes available + * @throws InterruptedException If the wait is interrupted + */ + public Reference remove() throws InterruptedException { + return remove(0); + } + +} diff -r d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 emul/compact/src/main/java/java/util/AbstractQueue.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/AbstractQueue.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,192 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +package java.util; + +/** + * This class provides skeletal implementations of some {@link Queue} + * operations. The implementations in this class are appropriate when + * the base implementation does not allow null + * elements. Methods {@link #add add}, {@link #remove remove}, and + * {@link #element element} are based on {@link #offer offer}, {@link + * #poll poll}, and {@link #peek peek}, respectively, but throw + * exceptions instead of indicating failure via false or + * null returns. + * + *

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

+     *     String[] y = x.toArray(new String[0]);
+ * + * Note that {@code toArray(new Object[0])} is identical in function to + * {@code toArray()}. + * + * @param a the array into which the elements of the list are to + * be stored, if it is big enough; otherwise, a new array of the + * same runtime type is allocated for this purpose. + * @return an array containing the elements of the list + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in + * this list + * @throws NullPointerException if the specified array is null + */ + @SuppressWarnings("unchecked") + public T[] toArray(T[] a) { + if (a.length < size) + a = (T[])java.lang.reflect.Array.newInstance( + a.getClass().getComponentType(), size); + int i = 0; + Object[] result = a; + for (Node x = first; x != null; x = x.next) + result[i++] = x.item; + + if (a.length > size) + a[size] = null; + + return a; + } + + private static final long serialVersionUID = 876323262645176354L; + +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/compact/src/main/java/java/util/Objects.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/Objects.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2009, 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; + +/** + * This class consists of {@code static} utility methods for operating + * on objects. These utilities include {@code null}-safe or {@code + * null}-tolerant methods for computing the hash code of an object, + * returning a string for an object, and comparing two objects. + * + * @since 1.7 + */ +public final class Objects { + private Objects() { + throw new AssertionError("No java.util.Objects instances for you!"); + } + + /** + * Returns {@code true} if the arguments are equal to each other + * and {@code false} otherwise. + * Consequently, if both arguments are {@code null}, {@code true} + * is returned and if exactly one argument is {@code null}, {@code + * false} is returned. Otherwise, equality is determined by using + * the {@link Object#equals equals} method of the first + * argument. + * + * @param a an object + * @param b an object to be compared with {@code a} for equality + * @return {@code true} if the arguments are equal to each other + * and {@code false} otherwise + * @see Object#equals(Object) + */ + public static boolean equals(Object a, Object b) { + return (a == b) || (a != null && a.equals(b)); + } + + /** + * Returns {@code true} if the arguments are deeply equal to each other + * and {@code false} otherwise. + * + * Two {@code null} values are deeply equal. If both arguments are + * arrays, the algorithm in {@link Arrays#deepEquals(Object[], + * Object[]) Arrays.deepEquals} is used to determine equality. + * Otherwise, equality is determined by using the {@link + * Object#equals equals} method of the first argument. + * + * @param a an object + * @param b an object to be compared with {@code a} for deep equality + * @return {@code true} if the arguments are deeply equal to each other + * and {@code false} otherwise + * @see Arrays#deepEquals(Object[], Object[]) + * @see Objects#equals(Object, Object) + */ + public static boolean deepEquals(Object a, Object b) { + if (a == b) + return true; + else if (a == null || b == null) + return false; + else + return Arrays.deepEquals0(a, b); + } + + /** + * Returns the hash code of a non-{@code null} argument and 0 for + * a {@code null} argument. + * + * @param o an object + * @return the hash code of a non-{@code null} argument and 0 for + * a {@code null} argument + * @see Object#hashCode + */ + public static int hashCode(Object o) { + return o != null ? o.hashCode() : 0; + } + + /** + * Generates a hash code for a sequence of input values. The hash + * code is generated as if all the input values were placed into an + * array, and that array were hashed by calling {@link + * Arrays#hashCode(Object[])}. + * + *

This method is useful for implementing {@link + * Object#hashCode()} on objects containing multiple fields. For + * example, if an object that has three fields, {@code x}, {@code + * y}, and {@code z}, one could write: + * + *

+    * @Override public int hashCode() {
+    *     return Objects.hash(x, y, z);
+    * }
+    * 
+ * + * Warning: When a single object reference is supplied, the returned + * value does not equal the hash code of that object reference. This + * value can be computed by calling {@link #hashCode(Object)}. + * + * @param values the values to be hashed + * @return a hash value of the sequence of input values + * @see Arrays#hashCode(Object[]) + * @see List#hashCode + */ + public static int hash(Object... values) { + return Arrays.hashCode(values); + } + + /** + * Returns the result of calling {@code toString} for a non-{@code + * null} argument and {@code "null"} for a {@code null} argument. + * + * @param o an object + * @return the result of calling {@code toString} for a non-{@code + * null} argument and {@code "null"} for a {@code null} argument + * @see Object#toString + * @see String#valueOf(Object) + */ + public static String toString(Object o) { + return String.valueOf(o); + } + + /** + * Returns the result of calling {@code toString} on the first + * argument if the first argument is not {@code null} and returns + * the second argument otherwise. + * + * @param o an object + * @param nullDefault string to return if the first argument is + * {@code null} + * @return the result of calling {@code toString} on the first + * argument if it is not {@code null} and the second argument + * otherwise. + * @see Objects#toString(Object) + */ + public static String toString(Object o, String nullDefault) { + return (o != null) ? o.toString() : nullDefault; + } + + /** + * Returns 0 if the arguments are identical and {@code + * c.compare(a, b)} otherwise. + * Consequently, if both arguments are {@code null} 0 + * is returned. + * + *

Note that if one of the arguments is {@code null}, a {@code + * NullPointerException} may or may not be thrown depending on + * what ordering policy, if any, the {@link Comparator Comparator} + * chooses to have for {@code null} values. + * + * @param the type of the objects being compared + * @param a an object + * @param b an object to be compared with {@code a} + * @param c the {@code Comparator} to compare the first two arguments + * @return 0 if the arguments are identical and {@code + * c.compare(a, b)} otherwise. + * @see Comparable + * @see Comparator + */ + public static int compare(T a, T b, Comparator c) { + return (a == b) ? 0 : c.compare(a, b); + } + + /** + * Checks that the specified object reference is not {@code null}. This + * method is designed primarily for doing parameter validation in methods + * and constructors, as demonstrated below: + *

+     * public Foo(Bar bar) {
+     *     this.bar = Objects.requireNonNull(bar);
+     * }
+     * 
+ * + * @param obj the object reference to check for nullity + * @param the type of the reference + * @return {@code obj} if not {@code null} + * @throws NullPointerException if {@code obj} is {@code null} + */ + public static T requireNonNull(T obj) { + if (obj == null) + throw new NullPointerException(); + return obj; + } + + /** + * Checks that the specified object reference is not {@code null} and + * throws a customized {@link NullPointerException} if it is. This method + * is designed primarily for doing parameter validation in methods and + * constructors with multiple parameters, as demonstrated below: + *
+     * public Foo(Bar bar, Baz baz) {
+     *     this.bar = Objects.requireNonNull(bar, "bar must not be null");
+     *     this.baz = Objects.requireNonNull(baz, "baz must not be null");
+     * }
+     * 
+ * + * @param obj the object reference to check for nullity + * @param message detail message to be used in the event that a {@code + * NullPointerException} is thrown + * @param the type of the reference + * @return {@code obj} if not {@code null} + * @throws NullPointerException if {@code obj} is {@code null} + */ + public static T requireNonNull(T obj, String message) { + if (obj == null) + throw new NullPointerException(message); + return obj; + } +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/compact/src/main/java/java/util/PriorityQueue.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/PriorityQueue.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,731 @@ +/* + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util; + + +/** + * An unbounded priority {@linkplain Queue queue} based on a priority heap. + * The elements of the priority queue are ordered according to their + * {@linkplain Comparable natural ordering}, or by a {@link Comparator} + * provided at queue construction time, depending on which constructor is + * used. A priority queue does not permit {@code null} elements. + * A priority queue relying on natural ordering also does not permit + * insertion of non-comparable objects (doing so may result in + * {@code ClassCastException}). + * + *

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 {@code
+     * public int nextInt() {
+     *   return next(32);
+     * }}
+ * + * @return the next pseudorandom, uniformly distributed {@code int} + * value from this random number generator's sequence + */ + public int nextInt() { + return next(32); + } + + /** + * Returns a pseudorandom, uniformly distributed {@code int} value + * between 0 (inclusive) and the specified value (exclusive), drawn from + * this random number generator's sequence. The general contract of + * {@code nextInt} is that one {@code int} value in the specified range + * is pseudorandomly generated and returned. All {@code n} possible + * {@code int} values are produced with (approximately) equal + * probability. The method {@code nextInt(int n)} is implemented by + * class {@code Random} as if by: + *
 {@code
+     * public int nextInt(int n) {
+     *   if (n <= 0)
+     *     throw new IllegalArgumentException("n must be positive");
+     *
+     *   if ((n & -n) == n)  // i.e., n is a power of 2
+     *     return (int)((n * (long)next(31)) >> 31);
+     *
+     *   int bits, val;
+     *   do {
+     *       bits = next(31);
+     *       val = bits % n;
+     *   } while (bits - val + (n-1) < 0);
+     *   return val;
+     * }}
+ * + *

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

+ * prints the following output: + *

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

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

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

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

+ * prints the following output: + *

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

The returned iterator is fail-fast. + * + * @return an iterator over the elements in this list in proper sequence + */ + public synchronized Iterator iterator() { + return new Itr(); + } + + /** + * An optimized version of AbstractList.Itr + */ + private class Itr implements Iterator { + int cursor; // index of next element to return + int lastRet = -1; // index of last element returned; -1 if no such + int expectedModCount = modCount; + + public boolean hasNext() { + // Racy but within spec, since modifications are checked + // within or after synchronization in next/previous + return cursor != elementCount; + } + + public E next() { + synchronized (Vector.this) { + checkForComodification(); + int i = cursor; + if (i >= elementCount) + throw new NoSuchElementException(); + cursor = i + 1; + return elementData(lastRet = i); + } + } + + public void remove() { + if (lastRet == -1) + throw new IllegalStateException(); + synchronized (Vector.this) { + checkForComodification(); + Vector.this.remove(lastRet); + expectedModCount = modCount; + } + cursor = lastRet; + lastRet = -1; + } + + final void checkForComodification() { + if (modCount != expectedModCount) + throw new ConcurrentModificationException(); + } + } + + /** + * An optimized version of AbstractList.ListItr + */ + final class ListItr extends Itr implements ListIterator { + ListItr(int index) { + super(); + cursor = index; + } + + public boolean hasPrevious() { + return cursor != 0; + } + + public int nextIndex() { + return cursor; + } + + public int previousIndex() { + return cursor - 1; + } + + public E previous() { + synchronized (Vector.this) { + checkForComodification(); + int i = cursor - 1; + if (i < 0) + throw new NoSuchElementException(); + cursor = i; + return elementData(lastRet = i); + } + } + + public void set(E e) { + if (lastRet == -1) + throw new IllegalStateException(); + synchronized (Vector.this) { + checkForComodification(); + Vector.this.set(lastRet, e); + } + } + + public void add(E e) { + int i = cursor; + synchronized (Vector.this) { + checkForComodification(); + Vector.this.add(i, e); + expectedModCount = modCount; + } + cursor = i + 1; + lastRet = -1; + } + } +} diff -r d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 emul/compact/src/main/java/java/util/concurrent/TimeUnit.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/main/java/java/util/concurrent/TimeUnit.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,367 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +package java.util.concurrent; + +/** + * A TimeUnit represents time durations at a given unit of + * granularity and provides utility methods to convert across units, + * and to perform timing and delay operations in these units. A + * TimeUnit does not maintain time information, but only + * helps organize and use time representations that may be maintained + * separately across various contexts. A nanosecond is defined as one + * thousandth of a microsecond, a microsecond as one thousandth of a + * millisecond, a millisecond as one thousandth of a second, a minute + * as sixty seconds, an hour as sixty minutes, and a day as twenty four + * hours. + * + *

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

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

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

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

 {@code
+     * public synchronized Object poll(long timeout, TimeUnit unit)
+     *     throws InterruptedException {
+     *   while (empty) {
+     *     unit.timedWait(this, timeout);
+     *     ...
+     *   }
+     * }}
+ * + * @param obj the object to wait on + * @param timeout the maximum time to wait. If less than + * or equal to zero, do not wait at all. + * @throws InterruptedException if interrupted while waiting + */ + public void timedWait(Object obj, long timeout) + throws InterruptedException { + if (timeout > 0) { + long ms = toMillis(timeout); + int ns = excessNanos(timeout, ms); + obj.wait(ms, ns); + } + } + + /** + * Performs a timed {@link Thread#join(long, int) Thread.join} + * using this time unit. + * This is a convenience method that converts time arguments into the + * form required by the Thread.join method. + * + * @param thread the thread to wait for + * @param timeout the maximum time to wait. If less than + * or equal to zero, do not wait at all. + * @throws InterruptedException if interrupted while waiting + */ +// public void timedJoin(Thread thread, long timeout) +// throws InterruptedException { +// if (timeout > 0) { +// long ms = toMillis(timeout); +// int ns = excessNanos(timeout, ms); +// thread.join(ms, ns); +// } +// } + + /** + * Performs a {@link Thread#sleep(long, int) Thread.sleep} using + * this time unit. + * This is a convenience method that converts time arguments into the + * form required by the Thread.sleep method. + * + * @param timeout the minimum time to sleep. If less than + * or equal to zero, do not sleep at all. + * @throws InterruptedException if interrupted while sleeping + */ + public void sleep(long timeout) throws InterruptedException { + if (timeout > 0) { + long ms = toMillis(timeout); + int ns = excessNanos(timeout, ms); + Object o = new Object(); + synchronized (o) { + o.wait(ms, ns); + } + } + } + +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/CollectionsTest.java --- a/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/CollectionsTest.java Fri Jan 25 15:08:24 2013 +0100 +++ b/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/CollectionsTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -25,6 +25,8 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Vector; import org.apidesign.bck2brwsr.vmtest.Compare; import org.apidesign.bck2brwsr.vmtest.VMTest; import org.testng.annotations.Factory; @@ -92,10 +94,9 @@ map.put("nine", 9); map.put("ten", 10); - Map.Entry[] arr = map.entrySet().toArray(new Map.Entry[map.size()]); - Arrays.sort(arr, new C()); - - return Arrays.asList(arr).toString(); + List> arr = new Vector<>(); + arr.addAll(map.entrySet()); + return arr.toString(); } @Factory diff -r d443b6c05a01 -r 41e1dc88a399 emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/JFXIssuesTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/JFXIssuesTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,46 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.compact.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + + +public class JFXIssuesTest { + private abstract class Application { + public abstract int getID(); + } + + private class MyApplication extends Application { + + @Override + public int getID() { + return 1; + } + + } + + @Compare public boolean isClassAssignable() { + return Application.class.isAssignableFrom(MyApplication.class); + } + + @Factory public static Object[] create() { + return VMTest.create(JFXIssuesTest.class); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/RandomTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/RandomTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,40 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.compact.tck; + +import java.util.Random; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class RandomTest { + @Compare public boolean canInstantiateRandom() { + Random r = new Random(); + r.nextInt(); + return r != null; + } + + + @Factory public static Object[] create() { + return VMTest.create(RandomTest.class); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ReaderTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ReaderTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,61 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.compact.tck; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class ReaderTest { + @Compare public String readUTFString() throws IOException { + byte[] arr = { + (byte)-59, (byte)-67, (byte)108, (byte)117, (byte)-59, (byte)-91, + (byte)111, (byte)117, (byte)-60, (byte)-115, (byte)107, (byte)-61, + (byte)-67, (byte)32, (byte)107, (byte)-59, (byte)-81, (byte)-59, + (byte)-120 + }; + ByteArrayInputStream is = new ByteArrayInputStream(arr); + InputStreamReader r = new InputStreamReader(is, "UTF-8"); + + StringBuilder sb = new StringBuilder(); + for (;;) { + int ch = r.read(); + if (ch == -1) { + break; + } + sb.append((char)ch); + } + return sb.toString().toString(); + } + @Compare public String stringToBytes() throws UnsupportedEncodingException { + return Arrays.toString("\u017dlu\u0165ou\u010dk\u00fd k\u016f\u0148".getBytes("UTF-8")); + } + + @Factory public static Object[] create() { + return VMTest.create(ReaderTest.class); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ServiceLoaderTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ServiceLoaderTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,61 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.compact.tck; + +import java.io.IOException; +import java.util.ServiceLoader; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.openide.util.lookup.ServiceProvider; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class ServiceLoaderTest { + @Compare//(scripting = false) + public Object findsIOException() { +// delayStart(); + for (IOException e : ServiceLoader.load(IOException.class)) { + return "Found service: " + e.getClass().getName(); + } + return null; + } +/* + @org.apidesign.bck2brwsr.core.JavaScriptBody(args = { "a" }, body = "alert(a);") + private static void alert(String a) { + } + private void delayStart() { + for (int i = 0; i < 10; i++) { + alert("State: " + i); + for (int j = 0; j < 493208409; j++) ; + } + } +*/ + + @Factory + public static Object[] create() { + return VMTest.create(ServiceLoaderTest.class); + } + + + @ServiceProvider(service = IOException.class) + public static class MyException extends IOException { + } +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ZipArchive.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ZipArchive.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,154 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.compact.tck; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; +import java.util.zip.ZipEntry; +import org.apidesign.bck2brwsr.emul.zip.ZipInputStream; + +/** + * + * @author Jaroslav Tulach + */ +final class ZipArchive { + private final Map entries = new LinkedHashMap<>(); + + public static ZipArchive createZip(InputStream is) throws IOException { + ZipArchive a = new ZipArchive(); + readZip(is, a); + return a; + } + + public static ZipArchive createReal(InputStream is) throws IOException { + ZipArchive a = new ZipArchive(); + realZip(is, a); + return a; + } + + /** + * Registers entry name and data + */ + final void register(String entry, InputStream is) throws IOException { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + for (;;) { + int ch = is.read(); + if (ch == -1) { + break; + } + os.write(ch); + } + os.close(); + entries.put(entry, os.toByteArray()); + } + + @Override + public int hashCode() { + return entries.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ZipArchive other = (ZipArchive) obj; + if (!Objects.deepEquals(this.entries, other.entries)) { + return false; + } + return true; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (Map.Entry en : entries.entrySet()) { + String string = en.getKey(); + byte[] bs = en.getValue(); + sb.append(string).append(" = ").append(Arrays.toString(bs)).append("\n"); + } + return sb.toString(); + } + + public void assertEquals(ZipArchive zip, String msg) { + boolean ok = true; + StringBuilder sb = new StringBuilder(); + sb.append(msg); + for (Map.Entry en : entries.entrySet()) { + String string = en.getKey(); + byte[] bs = en.getValue(); + byte[] other = zip.entries.get(string); + sb.append("\n"); + if (other == null) { + sb.append("EXTRA ").append(string).append(" = ").append(Arrays.toString(bs)); + ok = false; + continue; + } + if (Arrays.equals(bs, other)) { + sb.append("OK ").append(string); + continue; + } else { + sb.append("DIFF ").append(string).append(" = ").append(Arrays.toString(bs)).append("\n"); + sb.append(" TO").append(string).append(" = ").append(Arrays.toString(other)).append("\n"); + ok = false; + continue; + } + } + for (Map.Entry entry : zip.entries.entrySet()) { + String string = entry.getKey(); + if (entries.get(string) == null) { + sb.append("MISS ").append(string).append(" = ").append(Arrays.toString(entry.getValue())); + ok = false; + } + } + if (!ok) { + assert false : sb.toString(); + } + } + + public static void readZip(InputStream is, ZipArchive data) throws IOException { + ZipInputStream zip = new org.apidesign.bck2brwsr.emul.zip.ZipInputStream(is); + for (;;) { + ZipEntry en = zip.getNextEntry(); + if (en == null) { + return; + } + data.register(en.getName(), zip); + } + } + + public static void realZip(InputStream is, ZipArchive data) throws IOException { + java.util.zip.ZipInputStream zip = new java.util.zip.ZipInputStream(is); + for (;;) { + ZipEntry en = zip.getNextEntry(); + if (en == null) { + return; + } + data.register(en.getName(), zip); + } + } + +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ZipCompatibilityTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ZipCompatibilityTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,42 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.compact.tck; + +import java.io.IOException; +import java.io.InputStream; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class ZipCompatibilityTest { + @Compare + public String testDemoStaticCalculator() throws IOException { + InputStream is = getClass().getResourceAsStream("demo.static.calculator-0.3-SNAPSHOT.jar"); + ZipArchive zip = ZipArchive.createZip(is); + return zip.toString(); + } + + @Factory + public static Object[] create() { + return VMTest.create(ZipCompatibilityTest.class); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ZipVsJzLibTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/compact/src/test/java/org/apidesign/bck2brwsr/compact/tck/ZipVsJzLibTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,39 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.compact.tck; + +import java.io.IOException; +import java.io.InputStream; +import org.testng.annotations.Test; + +/** + * + * @author Jaroslav Tulach + */ +public class ZipVsJzLibTest { + @Test public void r() throws IOException { + InputStream is = getClass().getResourceAsStream("demo.static.calculator-0.3-SNAPSHOT.jar"); + ZipArchive zip = ZipArchive.createZip(is); + + is = getClass().getResourceAsStream("demo.static.calculator-0.3-SNAPSHOT.jar"); + ZipArchive real = ZipArchive.createReal(is); + + real.assertEquals(zip, "Are they the same?"); + } + +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/compact/src/test/resources/org/apidesign/bck2brwsr/compact/tck/demo.static.calculator-0.3-SNAPSHOT.jar Binary file emul/compact/src/test/resources/org/apidesign/bck2brwsr/compact/tck/demo.static.calculator-0.3-SNAPSHOT.jar has changed diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/pom.xml --- a/emul/mini/pom.xml Fri Jan 25 15:08:24 2013 +0100 +++ b/emul/mini/pom.xml Sun Feb 17 17:58:34 2013 +0100 @@ -22,6 +22,12 @@ 0.3-SNAPSHOT jar + + org.testng + testng + 6.5.2 + test + diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/java/lang/ArrayStoreException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/java/lang/ArrayStoreException.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * Thrown to indicate that an attempt has been made to store the + * wrong type of object into an array of objects. For example, the + * following code generates an ArrayStoreException: + *

+ *     Object x[] = new String[3];
+ *     x[0] = new Integer(0);
+ * 
+ * + * @author unascribed + * @since JDK1.0 + */ +public +class ArrayStoreException extends RuntimeException { + private static final long serialVersionUID = -4522193890499838241L; + + /** + * Constructs an ArrayStoreException with no detail message. + */ + public ArrayStoreException() { + super(); + } + + /** + * Constructs an ArrayStoreException with the specified + * detail message. + * + * @param s the detail message. + */ + public ArrayStoreException(String s) { + super(s); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/java/lang/Character.java --- a/emul/mini/src/main/java/java/lang/Character.java Fri Jan 25 15:08:24 2013 +0100 +++ b/emul/mini/src/main/java/java/lang/Character.java Sun Feb 17 17:58:34 2013 +0100 @@ -2026,7 +2026,7 @@ */ public static boolean isJavaIdentifierPart(int codePoint) { return isJavaIdentifierStart(codePoint) || - ('0' <= codePoint && codePoint <= '9'); + ('0' <= codePoint && codePoint <= '9') || codePoint == '$'; } /** @@ -2193,6 +2193,10 @@ * @see Character#isDigit(int) * @since 1.5 */ + @JavaScriptBody(args = { "codePoint", "radix" }, body= + "var x = parseInt(String.fromCharCode(codePoint), radix);\n" + + "return isNaN(x) ? -1 : x;" + ) public static int digit(int codePoint, int radix) { throw new UnsupportedOperationException(); } diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/java/lang/Class.java --- a/emul/mini/src/main/java/java/lang/Class.java Fri Jan 25 15:08:24 2013 +0100 +++ b/emul/mini/src/main/java/java/lang/Class.java Sun Feb 17 17:58:34 2013 +0100 @@ -32,6 +32,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.TypeVariable; +import java.net.URL; import org.apidesign.bck2brwsr.core.JavaScriptBody; import org.apidesign.bck2brwsr.emul.reflect.MethodImpl; @@ -230,12 +231,14 @@ } @JavaScriptBody(args = {"n", "c" }, body = - "if (vm[c]) return vm[c].$class;\n" - + "if (vm.loadClass) {\n" - + " vm.loadClass(n);\n" - + " if (vm[c]) return vm[c].$class;\n" + "if (!vm[c]) {\n" + + " if (vm.loadClass) {\n" + + " vm.loadClass(n);\n" + + " }\n" + + " if (!vm[c]) return null;\n" + "}\n" - + "return null;" + + "vm[c](false);" + + "return vm[c].$class;" ) private static native Class loadCls(String n, String c); @@ -346,6 +349,9 @@ * @since JDK1.1 */ public boolean isInstance(Object obj) { + if (obj == null) { + return false; + } if (isArray()) { return isAssignableFrom(obj.getClass()); } @@ -397,10 +403,15 @@ return cmpType != null && getComponentType().isAssignableFrom(cmpType); } String prop = "$instOf_" + getName().replace('.', '_'); - return hasProperty(cls, prop); + return hasCnstrProperty(cls, prop); } - + @JavaScriptBody(args = { "who", "prop" }, body = + "if (who.cnstr.prototype[prop]) return true; else return false;" + ) + private static native boolean hasCnstrProperty(Object who, String prop); + + /** * Determines if the specified {@code Class} object represents an * interface type. @@ -605,7 +616,9 @@ * @see java.lang.reflect.Modifier * @since JDK1.1 */ - public native int getModifiers(); + public int getModifiers() { + return getAccess(); + } /** @@ -906,7 +919,50 @@ } return m; } - + + /** + * Returns an array of {@code Method} objects reflecting all the + * methods declared by the class or interface represented by this + * {@code Class} object. This includes public, protected, default + * (package) access, and private methods, but excludes inherited methods. + * The elements in the array returned are not sorted and are not in any + * particular order. This method returns an array of length 0 if the class + * or interface declares no methods, or if this {@code Class} object + * represents a primitive type, an array class, or void. The class + * initialization method {@code } is not included in the + * returned array. If the class declares multiple public member methods + * with the same parameter types, they are all included in the returned + * array. + * + *

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

    + * + *
  • invocation of + * {@link SecurityManager#checkMemberAccess + * s.checkMemberAccess(this, Member.DECLARED)} denies + * access to the declared methods within this class + * + *
  • the caller's class loader is not the same as or an + * ancestor of the class loader for the current class and + * invocation of {@link SecurityManager#checkPackageAccess + * s.checkPackageAccess()} denies access to the package + * of this class + * + *
+ * + * @since JDK1.1 + */ + public Method[] getDeclaredMethods() throws SecurityException { + throw new SecurityException(); + } + /** * Character.isDigit answers {@code true} to some non-ascii * digits. This one does not. @@ -1028,15 +1084,16 @@ * @since JDK1.1 */ public java.net.URL getResource(String name) { - name = resolveName(name); - ClassLoader cl = null; - if (cl==null) { - // A system class. - return ClassLoader.getSystemResource(name); - } - return cl.getResource(name); + InputStream is = getResourceAsStream(name); + return is == null ? null : newResourceURL(URL.class, "res:/" + name, is); } - + + @JavaScriptBody(args = { "url", "spec", "is" }, body = + "var u = url.cnstr(true);\n" + + "u.constructor.cons__VLjava_lang_String_2Ljava_io_InputStream_2.call(u, spec, is);\n" + + "return u;" + ) + private static native URL newResourceURL(Class url, String spec, InputStream is); /** * Add a package name prefix if the name is not absolute Remove leading "/" @@ -1092,6 +1149,48 @@ public ClassLoader getClassLoader() { throw new SecurityException(); } + + /** + * Determines the interfaces implemented by the class or interface + * represented by this object. + * + *

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

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

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

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

If this object represents a primitive type or void, the method + * returns an array of length 0. + * + * @return an array of interfaces implemented by this class. + */ + public native Class[] getInterfaces(); /** * Returns the {@code Class} representing the component type of an diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/java/lang/ClassLoader.java --- a/emul/mini/src/main/java/java/lang/ClassLoader.java Fri Jan 25 15:08:24 2013 +0100 +++ b/emul/mini/src/main/java/java/lang/ClassLoader.java Sun Feb 17 17:58:34 2013 +0100 @@ -703,7 +703,7 @@ public static Enumeration getSystemResources(String name) throws IOException { - ClassLoader system = getSystemClassLoader(); + ClassLoader system = null; // getSystemClassLoader(); if (system == null) { return getBootstrapResources(name); } @@ -874,7 +874,29 @@ } private static Enumeration getBootstrapResources(String name) { - throw new UnsupportedOperationException(); + URL u = Object.class.getResource("/" + name); + return new OneOrZeroEnum(u); + } + + private static class OneOrZeroEnum implements Enumeration { + private URL u; + + public OneOrZeroEnum(URL u) { + this.u = u; + } + + public boolean hasMoreElements() { + return u != null; + } + + public URL nextElement() { + URL r = u; + if (r == null) { + throw new NoSuchElementException(); + } + u = null; + return r; + } } private static class CompoundEnumeration implements Enumeration { diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/java/lang/Cloneable.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/java/lang/Cloneable.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1995, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * A class implements the Cloneable interface to + * indicate to the {@link java.lang.Object#clone()} method that it + * is legal for that method to make a + * field-for-field copy of instances of that class. + *

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

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

+ * Note that this interface does not contain the clone method. + * Therefore, it is not possible to clone an object merely by virtue of the + * fact that it implements this interface. Even if the clone method is invoked + * reflectively, there is no guarantee that it will succeed. + * + * @author unascribed + * @see java.lang.CloneNotSupportedException + * @see java.lang.Object#clone() + * @since JDK1.0 + */ +public interface Cloneable { +} diff -r d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/java/lang/Long.java --- a/emul/mini/src/main/java/java/lang/Long.java Fri Jan 25 15:08:24 2013 +0100 +++ b/emul/mini/src/main/java/java/lang/Long.java Sun Feb 17 17:58:34 2013 +0100 @@ -262,7 +262,7 @@ * @param i a {@code long} to be converted. * @return a string representation of the argument in base 10. */ - @JavaScriptBody(args = "i", body = "return i.toString();") + @JavaScriptBody(args = "i", body = "return i.toExactString();") public static String toString(long i) { if (i == Long.MIN_VALUE) return "-9223372036854775808"; diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/java/lang/Math.java --- a/emul/mini/src/main/java/java/lang/Math.java Fri Jan 25 15:08:24 2013 +0100 +++ b/emul/mini/src/main/java/java/lang/Math.java Sun Feb 17 17:58:34 2013 +0100 @@ -375,6 +375,68 @@ public static double floor(double a) { throw new UnsupportedOperationException(); } + /** + * Computes the remainder operation on two arguments as prescribed + * by the IEEE 754 standard. + * The remainder value is mathematically equal to + * f1 - f2 × n, + * where n is the mathematical integer closest to the exact + * mathematical value of the quotient {@code f1/f2}, and if two + * mathematical integers are equally close to {@code f1/f2}, + * then n is the integer that is even. If the remainder is + * zero, its sign is the same as the sign of the first argument. + * Special cases: + *

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

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

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

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

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

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

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

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

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

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

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

If the constructor completes normally, returns the newly + * created and initialized instance. + * + * @param initargs array of objects to be passed as arguments to + * the constructor call; values of primitive types are wrapped in + * a wrapper object of the appropriate type (e.g. a {@code float} + * in a {@link java.lang.Float Float}) + * + * @return a new object created by calling the constructor + * this object represents + * + * @exception IllegalAccessException if this {@code Constructor} object + * is enforcing Java language access control and the underlying + * constructor is inaccessible. + * @exception IllegalArgumentException if the number of actual + * and formal parameters differ; if an unwrapping + * conversion for primitive arguments fails; or if, + * after possible unwrapping, a parameter value + * cannot be converted to the corresponding formal + * parameter type by a method invocation conversion; if + * this constructor pertains to an enum type. + * @exception InstantiationException if the class that declares the + * underlying constructor represents an abstract class. + * @exception InvocationTargetException if the underlying constructor + * throws an exception. + * @exception ExceptionInInitializerError if the initialization provoked + * by this method fails. + */ + public T newInstance(Object ... initargs) + throws InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException + { + throw new SecurityException(); + } + + /** + * Returns {@code true} if this constructor was declared to take + * a variable number of arguments; returns {@code false} + * otherwise. + * + * @return {@code true} if an only if this constructor was declared to + * take a variable number of arguments. + * @since 1.5 + */ + public boolean isVarArgs() { + return (getModifiers() & Modifier.VARARGS) != 0; + } + + /** + * Returns {@code true} if this constructor is a synthetic + * constructor; returns {@code false} otherwise. + * + * @return true if and only if this constructor is a synthetic + * constructor as defined by + * The Java™ Language Specification. + * @since 1.5 + */ + public boolean isSynthetic() { + return Modifier.isSynthetic(getModifiers()); + } + + int getSlot() { + return slot; + } + + String getSignature() { + return signature; + } + + byte[] getRawAnnotations() { + return annotations; + } + + byte[] getRawParameterAnnotations() { + return parameterAnnotations; + } + + /** + * @throws NullPointerException {@inheritDoc} + * @since 1.5 + */ + public T getAnnotation(Class annotationClass) { + if (annotationClass == null) + throw new NullPointerException(); + + return null; // XXX (T) declaredAnnotations().get(annotationClass); + } + + /** + * @since 1.5 + */ + public Annotation[] getDeclaredAnnotations() { + return new Annotation[0]; // XXX AnnotationParser.toArray(declaredAnnotations()); + } + + /** + * Returns an array of arrays that represent the annotations on the formal + * parameters, in declaration order, of the method represented by + * this {@code Constructor} object. (Returns an array of length zero if the + * underlying method is parameterless. If the method has one or more + * parameters, a nested array of length zero is returned for each parameter + * with no annotations.) The annotation objects contained in the returned + * arrays are serializable. The caller of this method is free to modify + * the returned arrays; it will have no effect on the arrays returned to + * other callers. + * + * @return an array of arrays that represent the annotations on the formal + * parameters, in declaration order, of the method represented by this + * Constructor object + * @since 1.5 + */ + public Annotation[][] getParameterAnnotations() { + int numParameters = parameterTypes.length; + if (parameterAnnotations == null) + return new Annotation[numParameters][0]; + + return new Annotation[numParameters][0]; // XXX +/* + Annotation[][] result = AnnotationParser.parseParameterAnnotations( + parameterAnnotations, + sun.misc.SharedSecrets.getJavaLangAccess(). + getConstantPool(getDeclaringClass()), + getDeclaringClass()); + if (result.length != numParameters) { + Class declaringClass = getDeclaringClass(); + if (declaringClass.isEnum() || + declaringClass.isAnonymousClass() || + declaringClass.isLocalClass() ) + ; // Can't do reliable parameter counting + else { + if (!declaringClass.isMemberClass() || // top-level + // Check for the enclosing instance parameter for + // non-static member classes + (declaringClass.isMemberClass() && + ((declaringClass.getModifiers() & Modifier.STATIC) == 0) && + result.length + 1 != numParameters) ) { + throw new AnnotationFormatError( + "Parameter annotations don't match number of parameters"); + } + } + } + return result; + */ + } +} diff -r d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/java/lang/reflect/Method.java --- a/emul/mini/src/main/java/java/lang/reflect/Method.java Fri Jan 25 15:08:24 2013 +0100 +++ b/emul/mini/src/main/java/java/lang/reflect/Method.java Sun Feb 17 17:58:34 2013 +0100 @@ -533,7 +533,7 @@ + "} else {\n" + " p = args;\n" + "}\n" - + "return method.fld_data.apply(self, p);\n" + + "return method._data().apply(self, p);\n" ) private static native Object invoke0(boolean isStatic, Method m, Object self, Object[] args); @@ -648,8 +648,9 @@ @JavaScriptBody(args = { "ac" }, body = - "if (this.fld_data.anno) {" - + " return this.fld_data.anno['L' + ac.jvmName + ';'];" + "var a = this._data().anno;" + + "if (a) {" + + " return a['L' + ac.jvmName + ';'];" + "} else return null;" ) private Object getAnnotationData(Class annotationClass) { diff -r d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,407 @@ +/* + * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.reflect; + + +/** + * {@code Proxy} provides static methods for creating dynamic proxy + * classes and instances, and it is also the superclass of all + * dynamic proxy classes created by those methods. + * + *

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

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

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

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

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

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

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

Methods Duplicated in Multiple Proxy Interfaces

+ * + *

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

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

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

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

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

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

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

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

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

The reliability of this method is important for the ability + * to use it to make security decisions, so its implementation should + * not just test if the class in question extends {@code Proxy}. + * + * @param cl the class to test + * @return {@code true} if the class is a proxy class and + * {@code false} otherwise + * @throws NullPointerException if {@code cl} is {@code null} + */ + public static boolean isProxyClass(Class cl) { + if (cl == null) { + throw new NullPointerException(); + } + + return false; + } + + /** + * Returns the invocation handler for the specified proxy instance. + * + * @param proxy the proxy instance to return the invocation handler for + * @return the invocation handler for the proxy instance + * @throws IllegalArgumentException if the argument is not a + * proxy instance + */ + public static InvocationHandler getInvocationHandler(Object proxy) + throws IllegalArgumentException + { + /* + * Verify that the object is actually a proxy instance. + */ + if (!isProxyClass(proxy.getClass())) { + throw new IllegalArgumentException("not a proxy instance"); + } + + Proxy p = (Proxy) proxy; + return p.h; + } + + private static native Class defineClass0(ClassLoader loader, String name, + byte[] b, int off, int len); +} diff -r d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/java/net/URL.java --- a/emul/mini/src/main/java/java/net/URL.java Fri Jan 25 15:08:24 2013 +0100 +++ b/emul/mini/src/main/java/java/net/URL.java Sun Feb 17 17:58:34 2013 +0100 @@ -25,10 +25,12 @@ package java.net; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import org.apidesign.bck2brwsr.core.JavaScriptBody; + /** * Class URL represents a Uniform Resource * Locator, a pointer to a "resource" on the World @@ -212,6 +214,9 @@ * @serial */ private int hashCode = -1; + + /** input stream associated with the URL */ + private InputStream is; /** * Creates a URL object from the specified @@ -421,6 +426,11 @@ public URL(String spec) throws MalformedURLException { this(null, spec); } + + private URL(String spec, InputStream is) throws MalformedURLException { + this(null, spec); + this.is = is; + } /** * Creates a URL by parsing the given spec within a specified context. @@ -496,6 +506,17 @@ public URL(URL context, String spec, URLStreamHandler handler) throws MalformedURLException { + this(findContext(context), spec, handler != null); + } + + private URL(URL context, String spec, boolean ishandler) + throws MalformedURLException { + // Check for permission to specify a handler + if (ishandler) { + throw new SecurityException(); + } + URLStreamHandler handler = null; + String original = spec; int i, limit, c; int start = 0; @@ -503,10 +524,6 @@ boolean aRef=false; boolean isRelative = false; - // Check for permission to specify a handler - if (handler != null) { - throw new SecurityException(); - } try { limit = spec.length(); @@ -950,8 +967,14 @@ * @see java.net.URLConnection#getInputStream() */ public final InputStream openStream() throws java.io.IOException { - throw new IOException(); -// return openConnection().getInputStream(); + if (is != null) { + return is; + } + byte[] arr = (byte[]) getContent(new Class[] { byte[].class }); + if (arr == null) { + throw new IOException(); + } + return new ByteArrayInputStream(arr); } /** @@ -976,6 +999,17 @@ ) private static native String loadText(String url) throws IOException; + @JavaScriptBody(args = { "url", "arr" }, body = "" + + "var request = new XMLHttpRequest();\n" + + "request.open('GET', url, false);\n" + + "request.overrideMimeType('text\\/plain; charset=x-user-defined');\n" + + "request.send();\n" + + "var t = request.responseText;\n" + + "for (var i = 0; i < t.length; i++) arr.push(t.charCodeAt(i) & 0xff);\n" + + "return arr;\n" + ) + private static native Object loadBytes(String url, byte[] arr) throws IOException; + /** * Gets the contents of this URL. This method is a shorthand for: *

@@ -994,7 +1028,10 @@
     throws java.io.IOException {
         for (Class c : classes) {
             if (c == String.class) {
-                return getContent();
+                return loadText(toExternalForm());
+            }
+            if (c == byte[].class) {
+                return loadBytes(toExternalForm(), new byte[0]);
             }
         }
         return null;
@@ -1005,8 +1042,24 @@
         return universal;
     }
 
+    private static URL findContext(URL context) throws MalformedURLException {
+        if (context == null) {
+            String base = findBaseURL();
+            if (base != null) {
+                context = new URL(null, base, false);
+            }
+        }
+        return context;
+    }
+    
+    @JavaScriptBody(args = {}, body = 
+          "if (typeof window !== 'object') return null;\n"
+        + "if (!window.location) return null;\n"
+        + "if (!window.location.href) return null;\n"
+        + "return window.location.href;\n"
+    )
+    private static native String findBaseURL();
 }
-
 class Parts {
     String path, query, ref;
 
diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/java/util/zip/Adler32.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/emul/mini/src/main/java/java/util/zip/Adler32.java	Sun Feb 17 17:58:34 2013 +0100
@@ -0,0 +1,205 @@
+/* Adler32.java - Computes Adler32 data checksum of a data stream
+   Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.util.zip;
+
+/*
+ * Written using on-line Java Platform 1.2 API Specification, as well
+ * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
+ * The actual Adler32 algorithm is taken from RFC 1950.
+ * Status:  Believed complete and correct.
+ */
+
+/**
+ * Computes Adler32 checksum for a stream of data. An Adler32 
+ * checksum is not as reliable as a CRC32 checksum, but a lot faster to 
+ * compute.
+ *

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

+ *

+ * From that document: + *

+ * "ADLER32 (Adler-32 checksum) + * This contains a checksum value of the uncompressed data + * (excluding any dictionary data) computed according to Adler-32 + * algorithm. This algorithm is a 32-bit extension and improvement + * of the Fletcher algorithm, used in the ITU-T X.224 / ISO 8073 + * standard. + *

+ * Adler-32 is composed of two sums accumulated per byte: s1 is + * the sum of all bytes, s2 is the sum of all s1 values. Both sums + * are done modulo 65521. s1 is initialized to 1, s2 to zero. The + * Adler-32 checksum is stored as s2*65536 + s1 in most- + * significant-byte first (network) order." + *

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

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

+ * The modulo on unsigned long accumulators can be delayed for 5552 + * bytes, so the modulo operation time is negligible. If the bytes + * are a, b, c, the second sum is 3a + 2b + c + 3, and so is position + * and order sensitive, unlike the first sum, which is just a + * checksum. That 65521 is prime is important to avoid a possible + * large class of two-byte errors that leave the check unchanged. + * (The Fletcher checksum uses 255, which is not prime and which also + * makes the Fletcher check insensitive to single byte changes 0 <-> + * 255.) + *

+ * The sum s1 is initialized to 1 instead of zero to make the length + * of the sequence part of s2, so that the length does not have to be + * checked separately. (Any sequence of zeroes has a Fletcher + * checksum of zero.)" + * + * @author John Leuner, Per Bothner + * @since JDK 1.1 + * + * @see InflaterInputStream + * @see DeflaterOutputStream + */ +public class Adler32 implements Checksum +{ + + /** largest prime smaller than 65536 */ + private static final int BASE = 65521; + + private int checksum; //we do all in int. + + //Note that java doesn't have unsigned integers, + //so we have to be careful with what arithmetic + //we do. We return the checksum as a long to + //avoid sign confusion. + + /** + * Creates a new instance of the Adler32 class. + * The checksum starts off with a value of 1. + */ + public Adler32 () + { + reset(); + } + + /** + * Resets the Adler32 checksum to the initial value. + */ + public void reset () + { + checksum = 1; //Initialize to 1 + } + + /** + * Updates the checksum with the byte b. + * + * @param bval the data value to add. The high byte of the int is ignored. + */ + public void update (int bval) + { + //We could make a length 1 byte array and call update again, but I + //would rather not have that overhead + int s1 = checksum & 0xffff; + int s2 = checksum >>> 16; + + s1 = (s1 + (bval & 0xFF)) % BASE; + s2 = (s1 + s2) % BASE; + + checksum = (s2 << 16) + s1; + } + + /** + * Updates the checksum with the bytes taken from the array. + * + * @param buffer an array of bytes + */ + public void update (byte[] buffer) + { + update(buffer, 0, buffer.length); + } + + /** + * Updates the checksum with the bytes taken from the array. + * + * @param buf an array of bytes + * @param off the start of the data used for this update + * @param len the number of bytes to use for this update + */ + public void update (byte[] buf, int off, int len) + { + //(By Per Bothner) + int s1 = checksum & 0xffff; + int s2 = checksum >>> 16; + + while (len > 0) + { + // We can defer the modulo operation: + // s1 maximally grows from 65521 to 65521 + 255 * 3800 + // s2 maximally grows by 3800 * median(s1) = 2090079800 < 2^31 + int n = 3800; + if (n > len) + n = len; + len -= n; + while (--n >= 0) + { + s1 = s1 + (buf[off++] & 0xFF); + s2 = s2 + s1; + } + s1 %= BASE; + s2 %= BASE; + } + + /*Old implementation, borrowed from somewhere: + int n; + + while (len-- > 0) { + + s1 = (s1 + (bs[offset++] & 0xff)) % BASE; + s2 = (s2 + s1) % BASE; + }*/ + + checksum = (s2 << 16) | s1; + } + + /** + * Returns the Adler32 data checksum computed so far. + */ + public long getValue() + { + return (long) checksum & 0xffffffffL; + } +} diff -r d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,132 @@ +/* CRC32.java - Computes CRC32 data checksum of a data stream + Copyright (C) 1999. 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +/* + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * The actual CRC32 algorithm is taken from RFC 1952. + * Status: Believed complete and correct. + */ + +/** + * Computes CRC32 data checksum of a data stream. + * The actual CRC32 algorithm is described in RFC 1952 + * (GZIP file format specification version 4.3). + * Can be used to get the CRC32 over a stream if used with checked input/output + * streams. + * + * @see InflaterInputStream + * @see DeflaterOutputStream + * + * @author Per Bothner + * @date April 1, 1999. + */ +public class CRC32 implements Checksum +{ + /** The crc data checksum so far. */ + private int crc = 0; + + /** The fast CRC table. Computed once when the CRC32 class is loaded. */ + private static int[] crc_table = make_crc_table(); + + /** Make the table for a fast CRC. */ + private static int[] make_crc_table () + { + int[] crc_table = new int[256]; + for (int n = 0; n < 256; n++) + { + int c = n; + for (int k = 8; --k >= 0; ) + { + if ((c & 1) != 0) + c = 0xedb88320 ^ (c >>> 1); + else + c = c >>> 1; + } + crc_table[n] = c; + } + return crc_table; + } + + /** + * Returns the CRC32 data checksum computed so far. + */ + public long getValue () + { + return (long) crc & 0xffffffffL; + } + + /** + * Resets the CRC32 data checksum as if no update was ever called. + */ + public void reset () { crc = 0; } + + /** + * Updates the checksum with the int bval. + * + * @param bval (the byte is taken as the lower 8 bits of bval) + */ + + public void update (int bval) + { + int c = ~crc; + c = crc_table[(c ^ bval) & 0xff] ^ (c >>> 8); + crc = ~c; + } + + /** + * Adds the byte array to the data checksum. + * + * @param buf the buffer which contains the data + * @param off the offset in the buffer where the data starts + * @param len the length of the data + */ + public void update (byte[] buf, int off, int len) + { + int c = ~crc; + while (--len >= 0) + c = crc_table[(c ^ buf[off++]) & 0xff] ^ (c >>> 8); + crc = ~c; + } + + /** + * Adds the complete byte array to the data checksum. + */ + public void update (byte[] buf) { update(buf, 0, buf.length); } +} diff -r d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,310 @@ +/* + * 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 org.apidesign.bck2brwsr.emul.zip.Inflater impl; + + /** + * 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) { + if (getClass() == org.apidesign.bck2brwsr.emul.zip.Inflater.class) { + impl = null; + } else { + impl = new org.apidesign.bck2brwsr.emul.zip.Inflater(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) { + impl.setInput(b, off, 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) { + impl.setInput(b); + } + + /** + * 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) { + impl.setDictionary(b, off, len); + } + + /** + * 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) { + impl.setDictionary(b); + } + + /** + * 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() { + return impl.getRemaining(); + } + + /** + * 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() { + return impl.needsInput(); + } + + /** + * 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() { + return impl.needsDictionary(); + } + + /** + * 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() { + return impl.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 + { + return impl.inflate(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 impl.inflate(b); + } + + /** + * Returns the ADLER-32 value of the uncompressed data. + * @return the ADLER-32 value of the uncompressed data + */ + public int getAdler() { + return impl.getAdler(); + } + + /** + * 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 impl.getTotalIn(); + } + + /** + * 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() { + return impl.getBytesRead(); + } + + /** + * 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 impl.getTotalOut(); + } + + /** + * 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() { + return impl.getBytesWritten(); + } + + /** + * Resets inflater so that a new set of input data can be processed. + */ + public void reset() { + impl.reset(); + } + + /** + * 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() { + impl.end(); + } + + /** + * Closes the decompressor when garbage is collected. + */ + protected void finalize() { + end(); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,331 @@ +/* + * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util.zip; + +/** + * This class is used to represent a ZIP file entry. + * + * @author David Connelly + */ +public +class ZipEntry implements ZipConstants, Cloneable { + String name; // entry name + long time = -1; // modification time (in DOS time) + long crc = -1; // crc-32 of entry data + long size = -1; // uncompressed size of entry data + long csize = -1; // compressed size of entry data + int method = -1; // compression method + int flag = 0; // general purpose flag + byte[] extra; // optional extra field data for entry + String comment; // optional comment string for entry + + /** + * Compression method for uncompressed entries. + */ + public static final int STORED = 0; + + /** + * Compression method for compressed (deflated) entries. + */ + public static final int DEFLATED = 8; + + /** + * Creates a new zip entry with the specified name. + * + * @param name the entry name + * @exception NullPointerException if the entry name is null + * @exception IllegalArgumentException if the entry name is longer than + * 0xFFFF bytes + */ + public ZipEntry(String name) { + if (name == null) { + throw new NullPointerException(); + } + if (name.length() > 0xFFFF) { + throw new IllegalArgumentException("entry name too long"); + } + this.name = name; + } + + /** + * Creates a new zip entry with fields taken from the specified + * zip entry. + * @param e a zip Entry object + */ + public ZipEntry(ZipEntry e) { + name = e.name; + time = e.time; + crc = e.crc; + size = e.size; + csize = e.csize; + method = e.method; + flag = e.flag; + extra = e.extra; + comment = e.comment; + } + + /* + * Creates a new un-initialized zip entry + */ + ZipEntry() {} + + /** + * Returns the name of the entry. + * @return the name of the entry + */ + public String getName() { + return name; + } + + /** + * Sets the modification time of the entry. + * @param time the entry modification time in number of milliseconds + * since the epoch + * @see #getTime() + */ + public void setTime(long time) { + this.time = javaToDosTime(time); + } + + /** + * Returns the modification time of the entry, or -1 if not specified. + * @return the modification time of the entry, or -1 if not specified + * @see #setTime(long) + */ + public long getTime() { + return time != -1 ? dosToJavaTime(time) : -1; + } + + /** + * Sets the uncompressed size of the entry data. + * @param size the uncompressed size in bytes + * @exception IllegalArgumentException if the specified size is less + * than 0, is greater than 0xFFFFFFFF when + * ZIP64 format is not supported, + * or is less than 0 when ZIP64 is supported + * @see #getSize() + */ + public void setSize(long size) { + if (size < 0) { + throw new IllegalArgumentException("invalid entry size"); + } + this.size = size; + } + + /** + * Returns the uncompressed size of the entry data, or -1 if not known. + * @return the uncompressed size of the entry data, or -1 if not known + * @see #setSize(long) + */ + public long getSize() { + return size; + } + + /** + * Returns the size of the compressed entry data, or -1 if not known. + * In the case of a stored entry, the compressed size will be the same + * as the uncompressed size of the entry. + * @return the size of the compressed entry data, or -1 if not known + * @see #setCompressedSize(long) + */ + public long getCompressedSize() { + return csize; + } + + /** + * Sets the size of the compressed entry data. + * @param csize the compressed size to set to + * @see #getCompressedSize() + */ + public void setCompressedSize(long csize) { + this.csize = csize; + } + + /** + * Sets the CRC-32 checksum of the uncompressed entry data. + * @param crc the CRC-32 value + * @exception IllegalArgumentException if the specified CRC-32 value is + * less than 0 or greater than 0xFFFFFFFF + * @see #getCrc() + */ + public void setCrc(long crc) { + if (crc < 0 || crc > 0xFFFFFFFFL) { + throw new IllegalArgumentException("invalid entry crc-32"); + } + this.crc = crc; + } + + /** + * Returns the CRC-32 checksum of the uncompressed entry data, or -1 if + * not known. + * @return the CRC-32 checksum of the uncompressed entry data, or -1 if + * not known + * @see #setCrc(long) + */ + public long getCrc() { + return crc; + } + + /** + * Sets the compression method for the entry. + * @param method the compression method, either STORED or DEFLATED + * @exception IllegalArgumentException if the specified compression + * method is invalid + * @see #getMethod() + */ + public void setMethod(int method) { + if (method != STORED && method != DEFLATED) { + throw new IllegalArgumentException("invalid compression method"); + } + this.method = method; + } + + /** + * Returns the compression method of the entry, or -1 if not specified. + * @return the compression method of the entry, or -1 if not specified + * @see #setMethod(int) + */ + public int getMethod() { + return method; + } + + /** + * Sets the optional extra field data for the entry. + * @param extra the extra field data bytes + * @exception IllegalArgumentException if the length of the specified + * extra field data is greater than 0xFFFF bytes + * @see #getExtra() + */ + public void setExtra(byte[] extra) { + if (extra != null && extra.length > 0xFFFF) { + throw new IllegalArgumentException("invalid extra field length"); + } + this.extra = extra; + } + + /** + * Returns the extra field data for the entry, or null if none. + * @return the extra field data for the entry, or null if none + * @see #setExtra(byte[]) + */ + public byte[] getExtra() { + return extra; + } + + /** + * Sets the optional comment string for the entry. + * + *

ZIP entry comments have maximum length of 0xffff. If the length of the + * specified comment string is greater than 0xFFFF bytes after encoding, only + * the first 0xFFFF bytes are output to the ZIP file entry. + * + * @param comment the comment string + * + * @see #getComment() + */ + public void setComment(String comment) { + this.comment = comment; + } + + /** + * Returns the comment string for the entry, or null if none. + * @return the comment string for the entry, or null if none + * @see #setComment(String) + */ + public String getComment() { + return comment; + } + + /** + * Returns true if this is a directory entry. A directory entry is + * defined to be one whose name ends with a '/'. + * @return true if this is a directory entry + */ + public boolean isDirectory() { + return name.endsWith("/"); + } + + /** + * Returns a string representation of the ZIP entry. + */ + public String toString() { + return getName(); + } + + /* + * Converts DOS time to Java time (number of milliseconds since epoch). + */ + private static long dosToJavaTime(long dtime) { + return dtime; + /* XXX: + Date d = new Date((int)(((dtime >> 25) & 0x7f) + 80), + (int)(((dtime >> 21) & 0x0f) - 1), + (int)((dtime >> 16) & 0x1f), + (int)((dtime >> 11) & 0x1f), + (int)((dtime >> 5) & 0x3f), + (int)((dtime << 1) & 0x3e)); + return d.getTime(); + */ + } + + /* + * Converts Java time to DOS time. + */ + private static long javaToDosTime(long time) { + return time; + /* XXX: + Date d = new Date(time); + int year = d.getYear() + 1900; + if (year < 1980) { + return (1 << 21) | (1 << 16); + } + return (year - 1980) << 25 | (d.getMonth() + 1) << 21 | + d.getDate() << 16 | d.getHours() << 11 | d.getMinutes() << 5 | + d.getSeconds() >> 1; + */ + } + + /** + * Returns the hash code value for this entry. + */ + public int hashCode() { + return name.hashCode(); + } + + /** + * Returns a copy of this entry. + */ + public Object clone() { + try { + ZipEntry e = (ZipEntry)super.clone(); + e.extra = (extra == null) ? null : extra.clone(); + return e; + } catch (CloneNotSupportedException e) { + // This should never happen, since we are Cloneable + throw new IllegalStateException(); + } + } +} diff -r d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 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 d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,194 @@ +/* + * 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; + +/** + * 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 final org.apidesign.bck2brwsr.emul.zip.ZipInputStream impl; + + /** + * 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) { + super(in); + impl = new org.apidesign.bck2brwsr.emul.zip.ZipInputStream(in); + } + + /** + * 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 { + return impl.getNextEntry(); + } + + /** + * 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 { + impl.closeEntry(); + } + + /** + * 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 { + return impl.available(); + } + + /** + * 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 { + return impl.read(b, off, len); + } + + /** + * 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 { + return impl.skip(n); + } + + /** + * 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 { + impl.close(); + } + + /** + * 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); + } + + @Override + public int read() throws IOException { + return impl.read(); + } + + @Override + public boolean markSupported() { + return impl.markSupported(); + } + + @Override + public void mark(int readlimit) { + impl.mark(readlimit); + } + + @Override + public void reset() throws IOException { + impl.reset(); + } + + @Override + public int read(byte[] b) throws IOException { + return impl.read(b); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 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 Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,98 @@ + + + + + + + + +Provides classes for reading and writing the standard ZIP and GZIP +file formats. Also includes classes for compressing and decompressing +data using the DEFLATE compression algorithm, which is used by the +ZIP and GZIP file formats. Additionally, there are utility classes +for computing the CRC-32 and Adler-32 checksums of arbitrary +input streams. + + +

Package Specification

+ + + + + + + +@since JDK1.1 + + + + diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/lang/ManifestInputStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/lang/ManifestInputStream.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,228 @@ +/* + * Copyright (c) 1997, 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 org.apidesign.bck2brwsr.emul.lang; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/* + * A fast buffered input stream for parsing manifest files. + * + * Taken from java.util.jar.Manifest.FastInputStream and modified to be + * independent of other Manifest functionality. + */ +public abstract class ManifestInputStream extends FilterInputStream { + private byte[] buf; + private int count = 0; + private int pos = 0; + + protected ManifestInputStream(InputStream in) { + this(in, 8192); + } + + protected ManifestInputStream(InputStream in, int size) { + super(in); + buf = new byte[size]; + } + + public int read() throws IOException { + if (pos >= count) { + fill(); + if (pos >= count) { + return -1; + } + } + return buf[pos++] & 0xff; + } + + public int read(byte[] b, int off, int len) throws IOException { + int avail = count - pos; + if (avail <= 0) { + if (len >= buf.length) { + return in.read(b, off, len); + } + fill(); + avail = count - pos; + if (avail <= 0) { + return -1; + } + } + if (len > avail) { + len = avail; + } + System.arraycopy(buf, pos, b, off, len); + pos += len; + return len; + } + + /* + * Reads 'len' bytes from the input stream, or until an end-of-line + * is reached. Returns the number of bytes read. + */ + public int readLine(byte[] b, int off, int len) throws IOException { + byte[] tbuf = this.buf; + int total = 0; + while (total < len) { + int avail = count - pos; + if (avail <= 0) { + fill(); + avail = count - pos; + if (avail <= 0) { + return -1; + } + } + int n = len - total; + if (n > avail) { + n = avail; + } + int tpos = pos; + int maxpos = tpos + n; + while (tpos < maxpos && tbuf[tpos++] != '\n') { + ; + } + n = tpos - pos; + System.arraycopy(tbuf, pos, b, off, n); + off += n; + total += n; + pos = tpos; + if (tbuf[tpos - 1] == '\n') { + break; + } + } + return total; + } + + public byte peek() throws IOException { + if (pos == count) { + fill(); + } + if (pos == count) { + return -1; // nothing left in buffer + } + return buf[pos]; + } + + public int readLine(byte[] b) throws IOException { + return readLine(b, 0, b.length); + } + + public long skip(long n) throws IOException { + if (n <= 0) { + return 0; + } + long avail = count - pos; + if (avail <= 0) { + return in.skip(n); + } + if (n > avail) { + n = avail; + } + pos += n; + return n; + } + + public int available() throws IOException { + return (count - pos) + in.available(); + } + + public void close() throws IOException { + if (in != null) { + in.close(); + in = null; + buf = null; + } + } + + private void fill() throws IOException { + count = pos = 0; + int n = in.read(buf, 0, buf.length); + if (n > 0) { + count = n; + } + } + + protected abstract String putValue(String key, String value); + + public void readAttributes(byte[] lbuf) throws IOException { + ManifestInputStream is = this; + + String name = null; + String value = null; + byte[] lastline = null; + int len; + while ((len = is.readLine(lbuf)) != -1) { + boolean lineContinued = false; + if (lbuf[--len] != '\n') { + throw new IOException("line too long"); + } + if (len > 0 && lbuf[len - 1] == '\r') { + --len; + } + if (len == 0) { + break; + } + int i = 0; + if (lbuf[0] == ' ') { + if (name == null) { + throw new IOException("misplaced continuation line"); + } + lineContinued = true; + byte[] buf = new byte[lastline.length + len - 1]; + System.arraycopy(lastline, 0, buf, 0, lastline.length); + System.arraycopy(lbuf, 1, buf, lastline.length, len - 1); + if (is.peek() == ' ') { + lastline = buf; + continue; + } + value = new String(buf, 0, buf.length, "UTF8"); + lastline = null; + } else { + while (lbuf[i++] != ':') { + if (i >= len) { + throw new IOException("invalid header field"); + } + } + if (lbuf[i++] != ' ') { + throw new IOException("invalid header field"); + } + name = new String(lbuf, 0, 0, i - 2); + if (is.peek() == ' ') { + lastline = new byte[len - i]; + System.arraycopy(lbuf, i, lastline, 0, len - i); + continue; + } + value = new String(lbuf, i, len - i, "UTF8"); + } + try { + if ((putValue(name, value) != null) && (!lineContinued)) { + throw new IOException("Duplicate name in Manifest: " + name + ".\n" + "Ensure that the manifest does not " + "have duplicate entries, and\n" + "that blank lines separate " + "individual sections in both your\n" + "manifest and in the META-INF/MANIFEST.MF " + "entry in the jar file."); + } + } catch (IllegalArgumentException e) { + throw new IOException("invalid header field name: " + name); + } + } + } +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/lang/System.java --- a/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/lang/System.java Fri Jan 25 15:08:24 2013 +0100 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/lang/System.java Sun Feb 17 17:58:34 2013 +0100 @@ -17,6 +17,7 @@ */ package org.apidesign.bck2brwsr.emul.lang; +import java.lang.reflect.Method; import org.apidesign.bck2brwsr.core.JavaScriptBody; /** @@ -38,5 +39,27 @@ " }\n" + "}" ) - public static native void arraycopy(Object value, int srcBegin, Object dst, int dstBegin, int count); + public static void arraycopy(Object src, int srcBegin, Object dst, int dstBegin, int count) { + try { + Class system = Class.forName("java.lang.System"); + Method m = system.getMethod("arraycopy", Object.class, int.class, Object.class, int.class, int.class); + m.invoke(null, src, srcBegin, dst, dstBegin, count); + } catch (Exception ex) { + throw new IllegalStateException(ex); + } + } + + @JavaScriptBody(args = { "arr", "expectedSize" }, body = + "while (expectedSize-- > arr.length) { arr.push(0); }; return arr;" + ) + public static native byte[] expandArray(byte[] arr, int expectedSize); + + @JavaScriptBody(args = {}, body = "return new Date().getTime();") + public static native long currentTimeMillis(); + + public static long nanoTime() { + return 1000000L * currentTimeMillis(); + } + @JavaScriptBody(args = { "obj" }, body="return vm.java_lang_Object(false).hashCode__I.call(obj);") + public static native int identityHashCode(Object obj); } diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/AnnotationImpl.java --- a/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/AnnotationImpl.java Fri Jan 25 15:08:24 2013 +0100 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/AnnotationImpl.java Sun Feb 17 17:58:34 2013 +0100 @@ -18,6 +18,8 @@ package org.apidesign.bck2brwsr.emul.reflect; import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import org.apidesign.bck2brwsr.core.JavaScriptBody; /** @@ -25,39 +27,73 @@ * @author Jaroslav Tulach */ public final class AnnotationImpl implements Annotation { + private final Class type; + + public AnnotationImpl(Class type) { + this.type = type; + } + public Class annotationType() { - return getClass(); + return type; } - @JavaScriptBody(args = { "a", "n", "values" }, body = "" - + "function f(v, p) {\n" - + " var val = v;\n" - + " var prop = p;\n" + @JavaScriptBody(args = { "a", "n", "arr", "values" }, body = "" + + "function f(val, prop, clazz) {\n" + " return function() {\n" - + " return val[prop];\n" + + " if (clazz == null) return val[prop];\n" + + " if (clazz.isArray__Z()) {\n" + + " var valarr = val[prop];\n" + + " var cmp = clazz.getComponentType__Ljava_lang_Class_2();\n" + + " var retarr = vm.java_lang_reflect_Array(false).newInstance__Ljava_lang_Object_2Ljava_lang_Class_2I(cmp, valarr.length);\n" + + " for (var i = 0; i < valarr.length; i++) {\n" + + " retarr[i] = CLS.prototype.c__Ljava_lang_Object_2Ljava_lang_Class_2Ljava_lang_Object_2(cmp, valarr[i]);\n" + + " }\n" + + " return retarr;\n" + + " }\n" + + " return CLS.prototype.c__Ljava_lang_Object_2Ljava_lang_Class_2Ljava_lang_Object_2(clazz, val[prop]);\n" + " };\n" + "}\n" - + "var props = Object.getOwnPropertyNames(values);\n" - + "for (var i = 0; i < props.length; i++) {\n" - + " var p = props[i];\n" - + " a[p] = new f(values, p);\n" + + "for (var i = 0; i < arr.length; i += 3) {\n" + + " var m = arr[i];\n" + + " var p = arr[i + 1];\n" + + " var c = arr[i + 2];\n" + + " a[m] = f(values, p, c);\n" + "}\n" + "a['$instOf_' + n] = true;\n" + "return a;" ) - private static T create(AnnotationImpl a, String n, Object values) { - return null; + private static native T create( + AnnotationImpl a, String n, Object[] methodsAndProps, Object values + ); + + private static Object c(Class a, Object v) { + return create(a, v); } + public static T create(Class annoClass, Object values) { - return create(new AnnotationImpl(), annoClass.getName().replace('.', '_'), values); + return create(new AnnotationImpl(annoClass), + annoClass.getName().replace('.', '_'), + findProps(annoClass), values + ); } public static Annotation[] create(Object anno) { String[] names = findNames(anno); Annotation[] ret = new Annotation[names.length]; for (int i = 0; i < names.length; i++) { - String n = names[i].substring(1, names[i].length() - 1).replace('/', '_'); - ret[i] = create(new AnnotationImpl(), n, findData(anno, names[i])); + String annoNameSlash = names[i].substring(1, names[i].length() - 1); + Class annoClass; + try { + annoClass = (Class)Class.forName(annoNameSlash.replace('/', '.')); + } catch (ClassNotFoundException ex) { + throw new IllegalStateException("Can't find annotation class " + annoNameSlash); + } + ret[i] = create( + new AnnotationImpl(annoClass), + annoNameSlash.replace('/', '_'), + findProps(annoClass), + findData(anno, names[i]) + ); } return ret; } @@ -70,12 +106,25 @@ + "}" + "return arr;" ) - private static String[] findNames(Object anno) { - throw new UnsupportedOperationException(); - } + private static native String[] findNames(Object anno); @JavaScriptBody(args={ "anno", "p"}, body="return anno[p];") - private static Object findData(Object anno, String p) { - throw new UnsupportedOperationException(); + private static native Object findData(Object anno, String p); + + private static Object[] findProps(Class annoClass) { + final Method[] marr = MethodImpl.findMethods(annoClass, Modifier.PUBLIC); + Object[] arr = new Object[marr.length * 3]; + int pos = 0; + for (Method m : marr) { + arr[pos++] = MethodImpl.toSignature(m); + arr[pos++] = m.getName(); + final Class rt = m.getReturnType(); + if (rt.isArray()) { + arr[pos++] = rt.getComponentType().isAnnotation() ? rt : null; + } else { + arr[pos++] = rt.isAnnotation() ? rt : null; + } + } + return arr; } } diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/MethodImpl.java --- a/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/MethodImpl.java Fri Jan 25 15:08:24 2013 +0100 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/MethodImpl.java Sun Feb 17 17:58:34 2013 +0100 @@ -1,29 +1,23 @@ -/* - * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. */ package org.apidesign.bck2brwsr.emul.reflect; +import java.lang.reflect.Array; import java.lang.reflect.Method; import java.util.Enumeration; import org.apidesign.bck2brwsr.core.JavaScriptBody; @@ -41,7 +35,7 @@ throw new IllegalStateException(ex); } } - + protected abstract Method create(Class declaringClass, String name, Object data, String sig); @@ -55,8 +49,10 @@ + "var arr = new Array();\n" + "for (m in c) {\n" + " if (m.indexOf(prefix) === 0) {\n" + + " if (!c[m].cls) continue;\n" + " arr.push(m);\n" + " arr.push(c[m]);\n" + + " arr.push(c[m].cls.$class);\n" + " }" + "}\n" + "return arr;") @@ -66,9 +62,10 @@ public static Method findMethod( Class clazz, String name, Class... parameterTypes) { Object[] data = findMethodData(clazz, name + "__"); - BIG: for (int i = 0; i < data.length; i += 2) { - String sig = ((String) data[0]).substring(name.length() + 2); - Method tmp = INSTANCE.create(clazz, name, data[1], sig); + BIG: for (int i = 0; i < data.length; i += 3) { + String sig = ((String) data[i]).substring(name.length() + 2); + Class cls = (Class) data[i + 2]; + Method tmp = INSTANCE.create(cls, name, data[i + 1], sig); Class[] tmpParms = tmp.getParameterTypes(); if (parameterTypes.length != tmpParms.length) { continue; @@ -86,7 +83,7 @@ public static Method[] findMethods(Class clazz, int mask) { Object[] namesAndData = findMethodData(clazz, ""); int cnt = 0; - for (int i = 0; i < namesAndData.length; i += 2) { + for (int i = 0; i < namesAndData.length; i += 3) { String sig = (String) namesAndData[i]; Object data = namesAndData[i + 1]; int middle = sig.indexOf("__"); @@ -95,7 +92,8 @@ } String name = sig.substring(0, middle); sig = sig.substring(middle + 2); - final Method m = INSTANCE.create(clazz, name, data, sig); + Class cls = (Class) namesAndData[i + 2]; + final Method m = INSTANCE.create(cls, name, data, sig); if ((m.getModifiers() & mask) == 0) { continue; } @@ -107,6 +105,62 @@ } return arr; } + static String toSignature(Method m) { + StringBuilder sb = new StringBuilder(); + sb.append(m.getName()).append("__"); + appendType(sb, m.getReturnType()); + Class[] arr = m.getParameterTypes(); + for (int i = 0; i < arr.length; i++) { + appendType(sb, arr[i]); + } + return sb.toString(); + } + + private static void appendType(StringBuilder sb, Class type) { + if (type == Integer.TYPE) { + sb.append('I'); + return; + } + if (type == Long.TYPE) { + sb.append('J'); + return; + } + if (type == Double.TYPE) { + sb.append('D'); + return; + } + if (type == Float.TYPE) { + sb.append('F'); + return; + } + if (type == Byte.TYPE) { + sb.append('B'); + return; + } + if (type == Boolean.TYPE) { + sb.append('Z'); + return; + } + if (type == Short.TYPE) { + sb.append('S'); + return; + } + if (type == Void.TYPE) { + sb.append('V'); + return; + } + if (type == Character.TYPE) { + sb.append('C'); + return; + } + if (type.isArray()) { + sb.append("_3"); + appendType(sb, type.getComponentType()); + return; + } + sb.append('L').append(type.getName().replace('.', '_')); + sb.append("_2"); + } public static int signatureElements(String sig) { Enumeration en = signatureParser(sig); @@ -148,13 +202,19 @@ return Character.TYPE; case 'L': try { - int up = sig.indexOf("_2"); - String type = sig.substring(1, up); + int up = sig.indexOf("_2", pos); + String type = sig.substring(pos, up); pos = up + 2; - return Class.forName(type); + return Class.forName(type.replace('_', '.')); } catch (ClassNotFoundException ex) { - // should not happen + throw new IllegalStateException(ex); } + case '_': { + char nch = sig.charAt(pos++); + assert nch == '3' : "Can't find '3' at " + sig.substring(pos - 1); + final Class compType = nextElement(); + return Array.newInstance(compType, 0).getClass(); + } } throw new UnsupportedOperationException(sig + " at " + pos); } diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/TypeProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/TypeProvider.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,40 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.emul.reflect; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; + +/** + * + * @author Jaroslav Tulach + */ +public abstract class TypeProvider { + private TypeProvider() { + } + + public static TypeProvider getDefault() { + return null; + } + + public abstract TypeVariable>[] getTypeParameters(Constructor c); + public abstract Type[] getGenericParameterTypes(Constructor c); + public abstract Type[] getGenericExceptionTypes(Constructor c); + +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/Adler32.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/Adler32.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,139 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2000-2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package org.apidesign.bck2brwsr.emul.zip; + +final class Adler32 implements Checksum { + + // largest prime smaller than 65536 + static final private int BASE=65521; + // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + static final private int NMAX=5552; + + private long s1=1L; + private long s2=0L; + + public void reset(long init){ + s1=init&0xffff; + s2=(init>>16)&0xffff; + } + + public void reset(){ + s1=1L; + s2=0L; + } + + public long getValue(){ + return ((s2<<16)|s1); + } + + public void update(byte[] buf, int index, int len){ + + if(len==1){ + s1+=buf[index++]&0xff; s2+=s1; + s1%=BASE; + s2%=BASE; + return; + } + + int len1 = len/NMAX; + int len2 = len%NMAX; + while(len1-->0) { + int k=NMAX; + len-=k; + while(k-->0){ + s1+=buf[index++]&0xff; s2+=s1; + } + s1%=BASE; + s2%=BASE; + } + + int k=len2; + len-=k; + while(k-->0){ + s1+=buf[index++]&0xff; s2+=s1; + } + s1%=BASE; + s2%=BASE; + } + + public Adler32 copy(){ + Adler32 foo = new Adler32(); + foo.s1 = this.s1; + foo.s2 = this.s2; + return foo; + } + + // The following logic has come from zlib.1.2. + static long combine(long adler1, long adler2, long len2){ + long BASEL = (long)BASE; + long sum1; + long sum2; + long rem; // unsigned int + + rem = len2 % BASEL; + sum1 = adler1 & 0xffffL; + sum2 = rem * sum1; + sum2 %= BASEL; // MOD(sum2); + sum1 += (adler2 & 0xffffL) + BASEL - 1; + sum2 += ((adler1 >> 16) & 0xffffL) + ((adler2 >> 16) & 0xffffL) + BASEL - rem; + if (sum1 >= BASEL) sum1 -= BASEL; + if (sum1 >= BASEL) sum1 -= BASEL; + if (sum2 >= (BASEL << 1)) sum2 -= (BASEL << 1); + if (sum2 >= BASEL) sum2 -= BASEL; + return sum1 | (sum2 << 16); + } + +/* + private java.util.zip.Adler32 adler=new java.util.zip.Adler32(); + public void update(byte[] buf, int index, int len){ + if(buf==null) {adler.reset();} + else{adler.update(buf, index, len);} + } + public void reset(){ + adler.reset(); + } + public void reset(long init){ + if(init==1L){ + adler.reset(); + } + else{ + System.err.println("unsupported operation"); + } + } + public long getValue(){ + return adler.getValue(); + } +*/ +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/CRC32.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/CRC32.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,181 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package org.apidesign.bck2brwsr.emul.zip; + +import org.apidesign.bck2brwsr.emul.lang.System; + +final class CRC32 implements Checksum { + + /* + * The following logic has come from RFC1952. + */ + private int v = 0; + private static int[] crc_table = null; + static { + crc_table = new int[256]; + for (int n = 0; n < 256; n++) { + int c = n; + for (int k = 8; --k >= 0; ) { + if ((c & 1) != 0) + c = 0xedb88320 ^ (c >>> 1); + else + c = c >>> 1; + } + crc_table[n] = c; + } + } + + public void update (byte[] buf, int index, int len) { + int c = ~v; + while (--len >= 0) + c = crc_table[(c^buf[index++])&0xff]^(c >>> 8); + v = ~c; + } + + public void reset(){ + v = 0; + } + + public void reset(long vv){ + v = (int)(vv&0xffffffffL); + } + + public long getValue(){ + return (long)(v&0xffffffffL); + } + + // The following logic has come from zlib.1.2. + private static final int GF2_DIM = 32; + static long combine(long crc1, long crc2, long len2){ + long row; + long[] even = new long[GF2_DIM]; + long[] odd = new long[GF2_DIM]; + + // degenerate case (also disallow negative lengths) + if (len2 <= 0) + return crc1; + + // put operator for one zero bit in odd + odd[0] = 0xedb88320L; // CRC-32 polynomial + row = 1; + for (int n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + // put operator for two zero bits in even + gf2_matrix_square(even, odd); + + // put operator for four zero bits in odd + gf2_matrix_square(odd, even); + + // apply len2 zeros to crc1 (first square will put the operator for one + // zero byte, eight zero bits, in even) + do { + // apply zeros operator for this bit of len2 + gf2_matrix_square(even, odd); + if ((len2 & 1)!=0) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + // if no more bits set, then done + if (len2 == 0) + break; + + // another iteration of the loop with odd and even swapped + gf2_matrix_square(odd, even); + if ((len2 & 1)!=0) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + // if no more bits set, then done + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; + } + + private static long gf2_matrix_times(long[] mat, long vec){ + long sum = 0; + int index = 0; + while (vec!=0) { + if ((vec & 1)!=0) + sum ^= mat[index]; + vec >>= 1; + index++; + } + return sum; + } + + static final void gf2_matrix_square(long[] square, long[] mat) { + for (int n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); + } + + /* + private java.util.zip.CRC32 crc32 = new java.util.zip.CRC32(); + + public void update(byte[] buf, int index, int len){ + if(buf==null) {crc32.reset();} + else{crc32.update(buf, index, len);} + } + public void reset(){ + crc32.reset(); + } + public void reset(long init){ + if(init==0L){ + crc32.reset(); + } + else{ + System.err.println("unsupported operation"); + } + } + public long getValue(){ + return crc32.getValue(); + } +*/ + public CRC32 copy(){ + CRC32 foo = new CRC32(); + foo.v = this.v; + return foo; + } + + public static int[] getCRC32Table(){ + int[] tmp = new int[crc_table.length]; + System.arraycopy(crc_table, 0, tmp, 0, tmp.length); + return tmp; + } +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/Checksum.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/Checksum.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,43 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package org.apidesign.bck2brwsr.emul.zip; + +interface Checksum { + void update(byte[] buf, int index, int len); + void reset(); + void reset(long init); + long getValue(); + Checksum copy(); +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/FastJar.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/FastJar.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,175 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * Portions Copyrighted 2007 Sun Microsystems, Inc. + */ +package org.apidesign.bck2brwsr.emul.zip; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * + * @author Tomas Zezula + */ +public final class FastJar { + private final byte[] arr; + + public FastJar(byte[] arr) { + this.arr = arr; + } + + + private static final int GIVE_UP = 1<<16; + + public static final class Entry { + + public final String name; + final long offset; + private final long dosTime; + + Entry (String name, long offset, long time) { + assert name != null; + this.name = name; + this.offset = offset; + this.dosTime = time; + } +/* + public long getTime () { + Date d = new Date((int)(((dosTime >> 25) & 0x7f) + 80), + (int)(((dosTime >> 21) & 0x0f) - 1), + (int)((dosTime >> 16) & 0x1f), + (int)((dosTime >> 11) & 0x1f), + (int)((dosTime >> 5) & 0x3f), + (int)((dosTime << 1) & 0x3e)); + return d.getTime(); + } + */ + } + + public InputStream getInputStream (final Entry e) throws IOException { + return getInputStream(arr, e.offset); + } + + private static InputStream getInputStream (byte[] arr, final long offset) throws IOException { + ByteArrayInputStream is = new ByteArrayInputStream(arr); + is.skip(offset); + ZipInputStream in = new ZipInputStream (is); + ZipEntry e = in.getNextEntry(); + if (e != null && e.getCrc() == 0L && e.getMethod() == ZipEntry.STORED) { + int cp = arr.length - is.available(); + return new ByteArrayInputStream(arr, cp, (int)e.getSize()); + } + return in; + } + + public Entry[] list() throws IOException { + final int size = arr.length; + + int at = size - ZipInputStream.ENDHDR; + + byte[] data = new byte[ZipInputStream.ENDHDR]; + int giveup = 0; + + do { + org.apidesign.bck2brwsr.emul.lang.System.arraycopy(arr, at, data, 0, data.length); + at--; + giveup++; + if (giveup > GIVE_UP) { + throw new IOException (); + } + } while (getsig(data) != ZipInputStream.ENDSIG); + + + final long censize = endsiz(data); + final long cenoff = endoff(data); + at = (int) cenoff; + + Entry[] result = new Entry[0]; + int cenread = 0; + data = new byte[ZipInputStream.CENHDR]; + while (cenread < censize) { + org.apidesign.bck2brwsr.emul.lang.System.arraycopy(arr, at, data, 0, data.length); + at += data.length; + if (getsig(data) != ZipInputStream.CENSIG) { + throw new IOException("No central table"); //NOI18N + } + int cennam = cennam(data); + int cenext = cenext(data); + int cencom = cencom(data); + long lhoff = cenoff(data); + long centim = centim(data); + String name = new String(arr, at, cennam, "UTF-8"); + at += cennam; + int seekby = cenext+cencom; + int cendatalen = ZipInputStream.CENHDR + cennam + seekby; + cenread+=cendatalen; + result = addEntry(result, new Entry(name,lhoff, centim)); + at += seekby; + } + return result; + } + + private Entry[] addEntry(Entry[] result, Entry entry) { + Entry[] e = new Entry[result.length + 1]; + e[result.length] = entry; + org.apidesign.bck2brwsr.emul.lang.System.arraycopy(result, 0, e, 0, result.length); + return e; + } + + private static final long getsig(final byte[] b) throws IOException {return get32(b,0);} + private static final long endsiz(final byte[] b) throws IOException {return get32(b,ZipInputStream.ENDSIZ);} + private static final long endoff(final byte[] b) throws IOException {return get32(b,ZipInputStream.ENDOFF);} + private static final long cenlen(final byte[] b) throws IOException {return get32(b,ZipInputStream.CENLEN);} + private static final long censiz(final byte[] b) throws IOException {return get32(b,ZipInputStream.CENSIZ);} + private static final long centim(final byte[] b) throws IOException {return get32(b,ZipInputStream.CENTIM);} + private static final int cennam(final byte[] b) throws IOException {return get16(b,ZipInputStream.CENNAM);} + private static final int cenext(final byte[] b) throws IOException {return get16(b,ZipInputStream.CENEXT);} + private static final int cencom(final byte[] b) throws IOException {return get16(b,ZipInputStream.CENCOM);} + private static final long cenoff (final byte[] b) throws IOException {return get32(b,ZipInputStream.CENOFF);} + private static final int lochow(final byte[] b) throws IOException {return get16(b,ZipInputStream.LOCHOW);} + private static final int locname(final byte[] b) throws IOException {return get16(b,ZipInputStream.LOCNAM);} + private static final int locext(final byte[] b) throws IOException {return get16(b,ZipInputStream.LOCEXT);} + private static final long locsiz(final byte[] b) throws IOException {return get32(b,ZipInputStream.LOCSIZ);} + + private static final int get16(final byte[] b, int off) throws IOException { + final int b1 = b[off]; + final int b2 = b[off+1]; + return (b1 & 0xff) | ((b2 & 0xff) << 8); + } + + private static final long get32(final byte[] b, int off) throws IOException { + final int s1 = get16(b, off); + final int s2 = get16(b, off+2); + return s1 | ((long)s2 << 16); + } + +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/GZIPHeader.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/GZIPHeader.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,215 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package org.apidesign.bck2brwsr.emul.zip; + +import org.apidesign.bck2brwsr.emul.lang.System; +import java.io.UnsupportedEncodingException; + +/** + * @see "http://www.ietf.org/rfc/rfc1952.txt" + */ +final class GZIPHeader implements Cloneable { + + public static final byte OS_MSDOS = (byte) 0x00; + public static final byte OS_AMIGA = (byte) 0x01; + public static final byte OS_VMS = (byte) 0x02; + public static final byte OS_UNIX = (byte) 0x03; + public static final byte OS_ATARI = (byte) 0x05; + public static final byte OS_OS2 = (byte) 0x06; + public static final byte OS_MACOS = (byte) 0x07; + public static final byte OS_TOPS20 = (byte) 0x0a; + public static final byte OS_WIN32 = (byte) 0x0b; + public static final byte OS_VMCMS = (byte) 0x04; + public static final byte OS_ZSYSTEM = (byte) 0x08; + public static final byte OS_CPM = (byte) 0x09; + public static final byte OS_QDOS = (byte) 0x0c; + public static final byte OS_RISCOS = (byte) 0x0d; + public static final byte OS_UNKNOWN = (byte) 0xff; + + boolean text = false; + private boolean fhcrc = false; + long time; + int xflags; + int os = 255; + byte[] extra; + byte[] name; + byte[] comment; + int hcrc; + long crc; + boolean done = false; + long mtime = 0; + + public void setModifiedTime(long mtime) { + this.mtime = mtime; + } + + public long getModifiedTime() { + return mtime; + } + + public void setOS(int os) { + if((0<=os && os <=13) || os==255) + this.os=os; + else + throw new IllegalArgumentException("os: "+os); + } + + public int getOS(){ + return os; + } + + public void setName(String name) { + try{ + this.name=name.getBytes("ISO-8859-1"); + } + catch(UnsupportedEncodingException e){ + throw new IllegalArgumentException("name must be in ISO-8859-1 "+name); + } + } + + public String getName(){ + if(name==null) return ""; + try { + return new String(name, "ISO-8859-1"); + } + catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException(e.toString()); + } + } + + public void setComment(String comment) { + try{ + this.comment=comment.getBytes("ISO-8859-1"); + } + catch(UnsupportedEncodingException e){ + throw new IllegalArgumentException("comment must be in ISO-8859-1 "+name); + } + } + + public String getComment(){ + if(comment==null) return ""; + try { + return new String(comment, "ISO-8859-1"); + } + catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException(e.toString()); + } + } + + public void setCRC(long crc){ + this.crc = crc; + } + + public long getCRC(){ + return crc; + } +/* + void put(Deflate d){ + int flag = 0; + if(text){ + flag |= 1; // FTEXT + } + if(fhcrc){ + flag |= 2; // FHCRC + } + if(extra!=null){ + flag |= 4; // FEXTRA + } + if(name!=null){ + flag |= 8; // FNAME + } + if(comment!=null){ + flag |= 16; // FCOMMENT + } + int xfl = 0; + if(d.level == JZlib.Z_BEST_SPEED){ + xfl |= 4; + } + else if (d.level == JZlib.Z_BEST_COMPRESSION){ + xfl |= 2; + } + + d.put_short((short)0x8b1f); // ID1 ID2 + d.put_byte((byte)8); // CM(Compression Method) + d.put_byte((byte)flag); + d.put_byte((byte)mtime); + d.put_byte((byte)(mtime>>8)); + d.put_byte((byte)(mtime>>16)); + d.put_byte((byte)(mtime>>24)); + d.put_byte((byte)xfl); + d.put_byte((byte)os); + + if(extra!=null){ + d.put_byte((byte)extra.length); + d.put_byte((byte)(extra.length>>8)); + d.put_byte(extra, 0, extra.length); + } + + if(name!=null){ + d.put_byte(name, 0, name.length); + d.put_byte((byte)0); + } + + if(comment!=null){ + d.put_byte(comment, 0, comment.length); + d.put_byte((byte)0); + } + } +*/ + @Override + public Object clone() throws CloneNotSupportedException { + GZIPHeader gheader = (GZIPHeader)super.clone(); + byte[] tmp; + if(gheader.extra!=null){ + tmp=new byte[gheader.extra.length]; + System.arraycopy(gheader.extra, 0, tmp, 0, tmp.length); + gheader.extra = tmp; + } + + if(gheader.name!=null){ + tmp=new byte[gheader.name.length]; + System.arraycopy(gheader.name, 0, tmp, 0, tmp.length); + gheader.name = tmp; + } + + if(gheader.comment!=null){ + tmp=new byte[gheader.comment.length]; + System.arraycopy(gheader.comment, 0, tmp, 0, tmp.length); + gheader.comment = tmp; + } + + return gheader; + } +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/InfBlocks.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/InfBlocks.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,616 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package org.apidesign.bck2brwsr.emul.zip; + +import org.apidesign.bck2brwsr.emul.lang.System; + +final class InfBlocks{ + static final private int MANY=1440; + + // And'ing with mask[n] masks the lower n bits + static final private int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + // Table for deflate from PKZIP's appnote.txt. + static final int[] border = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + + static final private int Z_OK=0; + static final private int Z_STREAM_END=1; + static final private int Z_NEED_DICT=2; + static final private int Z_ERRNO=-1; + static final private int Z_STREAM_ERROR=-2; + static final private int Z_DATA_ERROR=-3; + static final private int Z_MEM_ERROR=-4; + static final private int Z_BUF_ERROR=-5; + static final private int Z_VERSION_ERROR=-6; + + static final private int TYPE=0; // get type bits (3, including end bit) + static final private int LENS=1; // get lengths for stored + static final private int STORED=2;// processing stored block + static final private int TABLE=3; // get table lengths + static final private int BTREE=4; // get bit lengths tree for a dynamic block + static final private int DTREE=5; // get length, distance trees for a dynamic block + static final private int CODES=6; // processing fixed or dynamic block + static final private int DRY=7; // output remaining window bytes + static final private int DONE=8; // finished last block, done + static final private int BAD=9; // ot a data error--stuck here + + int mode; // current inflate_block mode + + int left; // if STORED, bytes left to copy + + int table; // table lengths (14 bits) + int index; // index into blens (or border) + int[] blens; // bit lengths of codes + int[] bb=new int[1]; // bit length tree depth + int[] tb=new int[1]; // bit length decoding tree + + int[] bl=new int[1]; + int[] bd=new int[1]; + + int[][] tl=new int[1][]; + int[][] td=new int[1][]; + int[] tli=new int[1]; // tl_index + int[] tdi=new int[1]; // td_index + + private final InfCodes codes; // if CODES, current state + + int last; // true if this block is the last block + + // mode independent information + int bitk; // bits in bit buffer + int bitb; // bit buffer + int[] hufts; // single malloc for tree space + byte[] window; // sliding window + int end; // one byte after sliding window + int read; // window read pointer + int write; // window write pointer + private boolean check; + + private final InfTree inftree=new InfTree(); + + private final ZStream z; + + InfBlocks(ZStream z, int w){ + this.z=z; + this.codes=new InfCodes(this.z, this); + hufts=new int[MANY*3]; + window=new byte[w]; + end=w; + this.check = (z.istate.wrap==0) ? false : true; + mode = TYPE; + reset(); + } + + void reset(){ + if(mode==BTREE || mode==DTREE){ + } + if(mode==CODES){ + codes.free(z); + } + mode=TYPE; + bitk=0; + bitb=0; + read=write=0; + if(check){ + z.adler.reset(); + } + } + + int proc(int r){ + int t; // temporary storage + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + {p=z.next_in_index;n=z.avail_in;b=bitb;k=bitk;} + {q=write;m=(int)(q>> 1){ + case 0: // stored + {b>>>=(3);k-=(3);} + t = k & 7; // go to byte boundary + + {b>>>=(t);k-=(t);} + mode = LENS; // get length of stored block + break; + case 1: // fixed + InfTree.inflate_trees_fixed(bl, bd, tl, td, z); + codes.init(bl[0], bd[0], tl[0], 0, td[0], 0); + + {b>>>=(3);k-=(3);} + + mode = CODES; + break; + case 2: // dynamic + + {b>>>=(3);k-=(3);} + + mode = TABLE; + break; + case 3: // illegal + + {b>>>=(3);k-=(3);} + mode = BAD; + z.msg = "invalid block type"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + } + break; + case LENS: + + while(k<(32)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + }; + n--; + b|=(z.next_in[p++]&0xff)<>> 16) & 0xffff) != (b & 0xffff)){ + mode = BAD; + z.msg = "invalid stored block lengths"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + } + left = (b & 0xffff); + b = k = 0; // dump bits + mode = left!=0 ? STORED : (last!=0 ? DRY : TYPE); + break; + case STORED: + if (n == 0){ + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + } + + if(m==0){ + if(q==end&&read!=0){ + q=0; m=(int)(qn) t = n; + if(t>m) t = m; + System.arraycopy(z.next_in, p, window, q, t); + p += t; n -= t; + q += t; m -= t; + if ((left -= t) != 0) + break; + mode = last!=0 ? DRY : TYPE; + break; + case TABLE: + + while(k<(14)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + }; + n--; + b|=(z.next_in[p++]&0xff)< 29 || ((t >> 5) & 0x1f) > 29) + { + mode = BAD; + z.msg = "too many length or distance symbols"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + } + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if(blens==null || blens.length>>=(14);k-=(14);} + + index = 0; + mode = BTREE; + case BTREE: + while (index < 4 + (table >>> 10)){ + while(k<(3)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + }; + n--; + b|=(z.next_in[p++]&0xff)<>>=(3);k-=(3);} + } + + while(index < 19){ + blens[border[index++]] = 0; + } + + bb[0] = 7; + t = inftree.inflate_trees_bits(blens, bb, tb, hufts, z); + if (t != Z_OK){ + r = t; + if (r == Z_DATA_ERROR){ + blens=null; + mode = BAD; + } + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + } + + index = 0; + mode = DTREE; + case DTREE: + while (true){ + t = table; + if(!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))){ + break; + } + + int[] h; + int i, j, c; + + t = bb[0]; + + while(k<(t)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + }; + n--; + b|=(z.next_in[p++]&0xff)<>>=(t);k-=(t); + blens[index++] = c; + } + else { // c == 16..18 + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + + while(k<(t+i)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + }; + n--; + b|=(z.next_in[p++]&0xff)<>>=(t);k-=(t); + + j += (b & inflate_mask[i]); + + b>>>=(i);k-=(i); + + i = index; + t = table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)){ + blens=null; + mode = BAD; + z.msg = "invalid bit length repeat"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + } + + c = c == 16 ? blens[i-1] : 0; + do{ + blens[i++] = c; + } + while (--j!=0); + index = i; + } + } + + tb[0]=-1; + { + bl[0] = 9; // must be <= 9 for lookahead assumptions + bd[0] = 6; // must be <= 9 for lookahead assumptions + t = table; + t = inftree.inflate_trees_dynamic(257 + (t & 0x1f), + 1 + ((t >> 5) & 0x1f), + blens, bl, bd, tli, tdi, hufts, z); + + if (t != Z_OK){ + if (t == Z_DATA_ERROR){ + blens=null; + mode = BAD; + } + r = t; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(r); + } + codes.init(bl[0], bd[0], hufts, tli[0], hufts, tdi[0]); + } + mode = CODES; + case CODES: + bitb=b; bitk=k; + z.avail_in=n; z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + + if ((r = codes.proc(r)) != Z_STREAM_END){ + return inflate_flush(r); + } + r = Z_OK; + codes.free(z); + + p=z.next_in_index; n=z.avail_in;b=bitb;k=bitk; + q=write;m=(int)(q z.avail_out) n = z.avail_out; + if(n!=0 && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if(check && n>0){ + z.adler.update(window, q, n); + } + + // copy as far as end of window + System.arraycopy(window, q, z.next_out, p, n); + p += n; + q += n; + + // see if more to copy at beginning of window + if (q == end){ + // wrap pointers + q = 0; + if (write == end) + write = 0; + + // compute bytes to copy + n = write - q; + if (n > z.avail_out) n = z.avail_out; + if (n!=0 && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if(check && n>0){ + z.adler.update(window, q, n); + } + + // copy + System.arraycopy(window, q, z.next_out, p, n); + p += n; + q += n; + } + + // update pointers + z.next_out_index = p; + read = q; + + // done + return r; + } +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/InfCodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/InfCodes.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,612 @@ +/* -*-mode:java; c-basic-offset:2; -*- */ +/* +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package org.apidesign.bck2brwsr.emul.zip; + +import org.apidesign.bck2brwsr.emul.lang.System; + +final class InfCodes{ + + static final private int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + static final private int Z_OK=0; + static final private int Z_STREAM_END=1; + static final private int Z_NEED_DICT=2; + static final private int Z_ERRNO=-1; + static final private int Z_STREAM_ERROR=-2; + static final private int Z_DATA_ERROR=-3; + static final private int Z_MEM_ERROR=-4; + static final private int Z_BUF_ERROR=-5; + static final private int Z_VERSION_ERROR=-6; + + // waiting for "i:"=input, + // "o:"=output, + // "x:"=nothing + static final private int START=0; // x: set up for LEN + static final private int LEN=1; // i: get length/literal/eob next + static final private int LENEXT=2; // i: getting length extra (have base) + static final private int DIST=3; // i: get distance next + static final private int DISTEXT=4;// i: getting distance extra + static final private int COPY=5; // o: copying bytes in window, waiting for space + static final private int LIT=6; // o: got literal, waiting for output space + static final private int WASH=7; // o: got eob, possibly still output waiting + static final private int END=8; // x: got eob and all data flushed + static final private int BADCODE=9;// x: got error + + int mode; // current inflate_codes mode + + // mode dependent information + int len; + + int[] tree; // pointer into tree + int tree_index=0; + int need; // bits needed + + int lit; + + // if EXT or COPY, where and how much + int get; // bits to get for extra + int dist; // distance back to copy from + + byte lbits; // ltree bits decoded per branch + byte dbits; // dtree bits decoder per branch + int[] ltree; // literal/length/eob tree + int ltree_index; // literal/length/eob tree + int[] dtree; // distance tree + int dtree_index; // distance tree + + private final ZStream z; + private final InfBlocks s; + InfCodes(ZStream z, InfBlocks s){ + this.z=z; + this.s=s; + } + + void init(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index){ + mode=START; + lbits=(byte)bl; + dbits=(byte)bd; + ltree=tl; + ltree_index=tl_index; + dtree = td; + dtree_index=td_index; + tree=null; + } + + int proc(int r){ + int j; // temporary storage + int[] t; // temporary pointer + int tindex; // temporary pointer + int e; // extra bits or operation + int b=0; // bit buffer + int k=0; // bits in bit buffer + int p=0; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int f; // pointer to copy strings from + + // copy input/output information to locals (UPDATE macro restores) + p=z.next_in_index;n=z.avail_in;b=s.bitb;k=s.bitk; + q=s.write;m=q= 258 && n >= 10){ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + r = inflate_fast(lbits, dbits, + ltree, ltree_index, + dtree, dtree_index, + s, z); + + p=z.next_in_index;n=z.avail_in;b=s.bitb;k=s.bitk; + q=s.write;m=q>>=(tree[tindex+1]); + k-=(tree[tindex+1]); + + e=tree[tindex]; + + if(e == 0){ // literal + lit = tree[tindex+2]; + mode = LIT; + break; + } + if((e & 16)!=0 ){ // length + get = e & 15; + len = tree[tindex+2]; + mode = LENEXT; + break; + } + if ((e & 64) == 0){ // next table + need = e; + tree_index = tindex/3+tree[tindex+2]; + break; + } + if ((e & 32)!=0){ // end of block + mode = WASH; + break; + } + mode = BADCODE; // invalid code + z.msg = "invalid literal/length code"; + r = Z_DATA_ERROR; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(r); + + case LENEXT: // i: getting length extra (have base) + j = get; + + while(k<(j)){ + if(n!=0)r=Z_OK; + else{ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(r); + } + n--; b|=(z.next_in[p++]&0xff)<>=j; + k-=j; + + need = dbits; + tree = dtree; + tree_index=dtree_index; + mode = DIST; + case DIST: // i: get distance next + j = need; + + while(k<(j)){ + if(n!=0)r=Z_OK; + else{ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(r); + } + n--; b|=(z.next_in[p++]&0xff)<>=tree[tindex+1]; + k-=tree[tindex+1]; + + e = (tree[tindex]); + if((e & 16)!=0){ // distance + get = e & 15; + dist = tree[tindex+2]; + mode = DISTEXT; + break; + } + if ((e & 64) == 0){ // next table + need = e; + tree_index = tindex/3 + tree[tindex+2]; + break; + } + mode = BADCODE; // invalid code + z.msg = "invalid distance code"; + r = Z_DATA_ERROR; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(r); + + case DISTEXT: // i: getting distance extra + j = get; + + while(k<(j)){ + if(n!=0)r=Z_OK; + else{ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(r); + } + n--; b|=(z.next_in[p++]&0xff)<>=j; + k-=j; + + mode = COPY; + case COPY: // o: copying bytes in window, waiting for space + f = q - dist; + while(f < 0){ // modulo window size-"while" instead + f += s.end; // of "if" handles invalid distances + } + while (len!=0){ + + if(m==0){ + if(q==s.end&&s.read!=0){q=0;m=q 7){ // return unused byte, if any + k -= 8; + n++; + p--; // can always return one + } + + s.write=q; r=s.inflate_flush(r); + q=s.write;m=q= 258 && n >= 10 + // get literal/length code + while(k<(20)){ // max bits for literal/length code + n--; + b|=(z.next_in[p++]&0xff)<>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + s.window[q++] = (byte)tp[tp_index_t_3+2]; + m--; + continue; + } + do { + + b>>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + if((e&16)!=0){ + e &= 15; + c = tp[tp_index_t_3+2] + ((int)b & inflate_mask[e]); + + b>>=e; k-=e; + + // decode distance base of block to copy + while(k<(15)){ // max bits for distance code + n--; + b|=(z.next_in[p++]&0xff)<>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + if((e&16)!=0){ + // get extra bits to add to distance base + e &= 15; + while(k<(e)){ // get extra bits (up to 13) + n--; + b|=(z.next_in[p++]&0xff)<>=(e); k-=(e); + + // do the copy + m -= c; + if (q >= d){ // offset before dest + // just copy + r=q-d; + if(q-r>0 && 2>(q-r)){ + s.window[q++]=s.window[r++]; // minimum count is three, + s.window[q++]=s.window[r++]; // so unroll loop a little + c-=2; + } + else{ + System.arraycopy(s.window, r, s.window, q, 2); + q+=2; r+=2; c-=2; + } + } + else{ // else offset after destination + r=q-d; + do{ + r+=s.end; // force pointer in window + }while(r<0); // covers invalid distances + e=s.end-r; + if(c>e){ // if source crosses, + c-=e; // wrapped copy + if(q-r>0 && e>(q-r)){ + do{s.window[q++] = s.window[r++];} + while(--e!=0); + } + else{ + System.arraycopy(s.window, r, s.window, q, e); + q+=e; r+=e; e=0; + } + r = 0; // copy rest from start of window + } + + } + + // copy all or what's left + if(q-r>0 && c>(q-r)){ + do{s.window[q++] = s.window[r++];} + while(--c!=0); + } + else{ + System.arraycopy(s.window, r, s.window, q, c); + q+=c; r+=c; c=0; + } + break; + } + else if((e&64)==0){ + t+=tp[tp_index_t_3+2]; + t+=(b&inflate_mask[e]); + tp_index_t_3=(tp_index+t)*3; + e=tp[tp_index_t_3]; + } + else{ + z.msg = "invalid distance code"; + + c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_DATA_ERROR; + } + } + while(true); + break; + } + + if((e&64)==0){ + t+=tp[tp_index_t_3+2]; + t+=(b&inflate_mask[e]); + tp_index_t_3=(tp_index+t)*3; + if((e=tp[tp_index_t_3])==0){ + + b>>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + s.window[q++]=(byte)tp[tp_index_t_3+2]; + m--; + break; + } + } + else if((e&32)!=0){ + + c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_STREAM_END; + } + else{ + z.msg="invalid literal/length code"; + + c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_DATA_ERROR; + } + } + while(true); + } + while(m>=258 && n>= 10); + + // not enough input or output--restore pointers and return + c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_OK; + } +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/InfTree.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/InfTree.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,520 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package org.apidesign.bck2brwsr.emul.zip; + +import org.apidesign.bck2brwsr.emul.lang.System; + +final class InfTree{ + + static final private int MANY=1440; + + static final private int Z_OK=0; + static final private int Z_STREAM_END=1; + static final private int Z_NEED_DICT=2; + static final private int Z_ERRNO=-1; + static final private int Z_STREAM_ERROR=-2; + static final private int Z_DATA_ERROR=-3; + static final private int Z_MEM_ERROR=-4; + static final private int Z_BUF_ERROR=-5; + static final private int Z_VERSION_ERROR=-6; + + static final int fixed_bl = 9; + static final int fixed_bd = 5; + + static final int[] fixed_tl = { + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,192, + 80,7,10, 0,8,96, 0,8,32, 0,9,160, + 0,8,0, 0,8,128, 0,8,64, 0,9,224, + 80,7,6, 0,8,88, 0,8,24, 0,9,144, + 83,7,59, 0,8,120, 0,8,56, 0,9,208, + 81,7,17, 0,8,104, 0,8,40, 0,9,176, + 0,8,8, 0,8,136, 0,8,72, 0,9,240, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,200, + 81,7,13, 0,8,100, 0,8,36, 0,9,168, + 0,8,4, 0,8,132, 0,8,68, 0,9,232, + 80,7,8, 0,8,92, 0,8,28, 0,9,152, + 84,7,83, 0,8,124, 0,8,60, 0,9,216, + 82,7,23, 0,8,108, 0,8,44, 0,9,184, + 0,8,12, 0,8,140, 0,8,76, 0,9,248, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,196, + 81,7,11, 0,8,98, 0,8,34, 0,9,164, + 0,8,2, 0,8,130, 0,8,66, 0,9,228, + 80,7,7, 0,8,90, 0,8,26, 0,9,148, + 84,7,67, 0,8,122, 0,8,58, 0,9,212, + 82,7,19, 0,8,106, 0,8,42, 0,9,180, + 0,8,10, 0,8,138, 0,8,74, 0,9,244, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,204, + 81,7,15, 0,8,102, 0,8,38, 0,9,172, + 0,8,6, 0,8,134, 0,8,70, 0,9,236, + 80,7,9, 0,8,94, 0,8,30, 0,9,156, + 84,7,99, 0,8,126, 0,8,62, 0,9,220, + 82,7,27, 0,8,110, 0,8,46, 0,9,188, + 0,8,14, 0,8,142, 0,8,78, 0,9,252, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,194, + 80,7,10, 0,8,97, 0,8,33, 0,9,162, + 0,8,1, 0,8,129, 0,8,65, 0,9,226, + 80,7,6, 0,8,89, 0,8,25, 0,9,146, + 83,7,59, 0,8,121, 0,8,57, 0,9,210, + 81,7,17, 0,8,105, 0,8,41, 0,9,178, + 0,8,9, 0,8,137, 0,8,73, 0,9,242, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,202, + 81,7,13, 0,8,101, 0,8,37, 0,9,170, + 0,8,5, 0,8,133, 0,8,69, 0,9,234, + 80,7,8, 0,8,93, 0,8,29, 0,9,154, + 84,7,83, 0,8,125, 0,8,61, 0,9,218, + 82,7,23, 0,8,109, 0,8,45, 0,9,186, + 0,8,13, 0,8,141, 0,8,77, 0,9,250, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,198, + 81,7,11, 0,8,99, 0,8,35, 0,9,166, + 0,8,3, 0,8,131, 0,8,67, 0,9,230, + 80,7,7, 0,8,91, 0,8,27, 0,9,150, + 84,7,67, 0,8,123, 0,8,59, 0,9,214, + 82,7,19, 0,8,107, 0,8,43, 0,9,182, + 0,8,11, 0,8,139, 0,8,75, 0,9,246, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,206, + 81,7,15, 0,8,103, 0,8,39, 0,9,174, + 0,8,7, 0,8,135, 0,8,71, 0,9,238, + 80,7,9, 0,8,95, 0,8,31, 0,9,158, + 84,7,99, 0,8,127, 0,8,63, 0,9,222, + 82,7,27, 0,8,111, 0,8,47, 0,9,190, + 0,8,15, 0,8,143, 0,8,79, 0,9,254, + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,193, + + 80,7,10, 0,8,96, 0,8,32, 0,9,161, + 0,8,0, 0,8,128, 0,8,64, 0,9,225, + 80,7,6, 0,8,88, 0,8,24, 0,9,145, + 83,7,59, 0,8,120, 0,8,56, 0,9,209, + 81,7,17, 0,8,104, 0,8,40, 0,9,177, + 0,8,8, 0,8,136, 0,8,72, 0,9,241, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,201, + 81,7,13, 0,8,100, 0,8,36, 0,9,169, + 0,8,4, 0,8,132, 0,8,68, 0,9,233, + 80,7,8, 0,8,92, 0,8,28, 0,9,153, + 84,7,83, 0,8,124, 0,8,60, 0,9,217, + 82,7,23, 0,8,108, 0,8,44, 0,9,185, + 0,8,12, 0,8,140, 0,8,76, 0,9,249, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,197, + 81,7,11, 0,8,98, 0,8,34, 0,9,165, + 0,8,2, 0,8,130, 0,8,66, 0,9,229, + 80,7,7, 0,8,90, 0,8,26, 0,9,149, + 84,7,67, 0,8,122, 0,8,58, 0,9,213, + 82,7,19, 0,8,106, 0,8,42, 0,9,181, + 0,8,10, 0,8,138, 0,8,74, 0,9,245, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,205, + 81,7,15, 0,8,102, 0,8,38, 0,9,173, + 0,8,6, 0,8,134, 0,8,70, 0,9,237, + 80,7,9, 0,8,94, 0,8,30, 0,9,157, + 84,7,99, 0,8,126, 0,8,62, 0,9,221, + 82,7,27, 0,8,110, 0,8,46, 0,9,189, + 0,8,14, 0,8,142, 0,8,78, 0,9,253, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,195, + 80,7,10, 0,8,97, 0,8,33, 0,9,163, + 0,8,1, 0,8,129, 0,8,65, 0,9,227, + 80,7,6, 0,8,89, 0,8,25, 0,9,147, + 83,7,59, 0,8,121, 0,8,57, 0,9,211, + 81,7,17, 0,8,105, 0,8,41, 0,9,179, + 0,8,9, 0,8,137, 0,8,73, 0,9,243, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,203, + 81,7,13, 0,8,101, 0,8,37, 0,9,171, + 0,8,5, 0,8,133, 0,8,69, 0,9,235, + 80,7,8, 0,8,93, 0,8,29, 0,9,155, + 84,7,83, 0,8,125, 0,8,61, 0,9,219, + 82,7,23, 0,8,109, 0,8,45, 0,9,187, + 0,8,13, 0,8,141, 0,8,77, 0,9,251, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,199, + 81,7,11, 0,8,99, 0,8,35, 0,9,167, + 0,8,3, 0,8,131, 0,8,67, 0,9,231, + 80,7,7, 0,8,91, 0,8,27, 0,9,151, + 84,7,67, 0,8,123, 0,8,59, 0,9,215, + 82,7,19, 0,8,107, 0,8,43, 0,9,183, + 0,8,11, 0,8,139, 0,8,75, 0,9,247, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,207, + 81,7,15, 0,8,103, 0,8,39, 0,9,175, + 0,8,7, 0,8,135, 0,8,71, 0,9,239, + 80,7,9, 0,8,95, 0,8,31, 0,9,159, + 84,7,99, 0,8,127, 0,8,63, 0,9,223, + 82,7,27, 0,8,111, 0,8,47, 0,9,191, + 0,8,15, 0,8,143, 0,8,79, 0,9,255 + }; + static final int[] fixed_td = { + 80,5,1, 87,5,257, 83,5,17, 91,5,4097, + 81,5,5, 89,5,1025, 85,5,65, 93,5,16385, + 80,5,3, 88,5,513, 84,5,33, 92,5,8193, + 82,5,9, 90,5,2049, 86,5,129, 192,5,24577, + 80,5,2, 87,5,385, 83,5,25, 91,5,6145, + 81,5,7, 89,5,1537, 85,5,97, 93,5,24577, + 80,5,4, 88,5,769, 84,5,49, 92,5,12289, + 82,5,13, 90,5,3073, 86,5,193, 192,5,24577 + }; + + // Tables for deflate from PKZIP's appnote.txt. + static final int[] cplens = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 + }; + + // see note #13 above about 258 + static final int[] cplext = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112 // 112==invalid + }; + + static final int[] cpdist = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 + }; + + static final int[] cpdext = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + + // If BMAX needs to be larger than 16, then h and x[] should be uLong. + static final int BMAX=15; // maximum bit length of any code + + int[] hn = null; // hufts used in space + int[] v = null; // work area for huft_build + int[] c = null; // bit length count table + int[] r = null; // table entry for structure assignment + int[] u = null; // table stack + int[] x = null; // bit offsets, then code stack + + private int huft_build(int[] b, // code lengths in bits (all assumed <= BMAX) + int bindex, + int n, // number of codes (assumed <= 288) + int s, // number of simple-valued codes (0..s-1) + int[] d, // list of base values for non-simple codes + int[] e, // list of extra bits for non-simple codes + int[] t, // result: starting table + int[] m, // maximum lookup bits, returns actual + int[] hp,// space for trees + int[] hn,// hufts used in space + int[] v // working area: values in order of bit length + ){ + // Given a list of code lengths and a maximum table size, make a set of + // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + // if the given code set is incomplete (the tables are still built in this + // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of + // lengths), or Z_MEM_ERROR if not enough memory. + + int a; // counter for codes of length k + int f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + int i; // counter, current code + int j; // counter + int k; // number of bits in current code + int l; // bits per table (returned in m) + int mask; // (1 << w) - 1, to avoid cc -O bug on HP + int p; // pointer into c[], b[], or v[] + int q; // points to current table + int w; // bits before this table == (l * h) + int xp; // pointer into x + int y; // number of dummy codes added + int z; // number of entries in current table + + // Generate counts for each bit length + + p = 0; i = n; + do { + c[b[bindex+p]]++; p++; i--; // assume all entries <= BMAX + }while(i!=0); + + if(c[0] == n){ // null input--all zero length codes + t[0] = -1; + m[0] = 0; + return Z_OK; + } + + // Find minimum and maximum length, bound *m by those + l = m[0]; + for (j = 1; j <= BMAX; j++) + if(c[j]!=0) break; + k = j; // minimum code length + if(l < j){ + l = j; + } + for (i = BMAX; i!=0; i--){ + if(c[i]!=0) break; + } + g = i; // maximum code length + if(l > i){ + l = i; + } + m[0] = l; + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1){ + if ((y -= c[j]) < 0){ + return Z_DATA_ERROR; + } + } + if ((y -= c[i]) < 0){ + return Z_DATA_ERROR; + } + c[i] += y; + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = 1; xp = 2; + while (--i!=0) { // note that i == g from above + x[xp] = (j += c[p]); + xp++; + p++; + } + + // Make a table of values in order of bit lengths + i = 0; p = 0; + do { + if ((j = b[bindex+p]) != 0){ + v[x[j]++] = i; + } + p++; + } + while (++i < n); + n = x[g]; // set n to length of v + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = 0; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = 0; // just to keep compilers happy + q = 0; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++){ + a = c[k]; + while (a--!=0){ + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l){ + h++; + w += l; // previous table always l bits + // compute minimum size table less than or equal to l bits + z = g - w; + z = (z > l) ? l : z; // table size upper limit + if((f=1<<(j=k-w))>a+1){ // try a k-w bit table + // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = k; + if(j < z){ + while (++j < z){ // try smaller tables up to z bits + if((f <<= 1) <= c[++xp]) + break; // enough codes to use up j bits + f -= c[xp]; // else deduct codes from patterns + } + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (hn[0] + z > MANY){ // (note: doesn't matter for fixed) + return Z_DATA_ERROR; // overflow of MANY + } + u[h] = q = /*hp+*/ hn[0]; // DEBUG + hn[0] += z; + + // connect to last table, if there is one + if(h!=0){ + x[h]=i; // save pattern for backing up + r[0]=(byte)j; // bits in this table + r[1]=(byte)l; // bits to dump before this table + j=i>>>(w - l); + r[2] = (int)(q - u[h-1] - j); // offset to this table + System.arraycopy(r, 0, hp, (u[h-1]+j)*3, 3); // connect to last table + } + else{ + t[0] = q; // first table is returned result + } + } + + // set up table entry in r + r[1] = (byte)(k - w); + if (p >= n){ + r[0] = 128 + 64; // out of values--invalid code + } + else if (v[p] < s){ + r[0] = (byte)(v[p] < 256 ? 0 : 32 + 64); // 256 is end-of-block + r[2] = v[p++]; // simple code is just the value + } + else{ + r[0]=(byte)(e[v[p]-s]+16+64); // non-simple--look up in lists + r[2]=d[v[p++] - s]; + } + + // fill code-like entries with r + f=1<<(k-w); + for (j=i>>>w;j>>= 1){ + i ^= j; + } + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]){ + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; + } + + int inflate_trees_bits(int[] c, // 19 code lengths + int[] bb, // bits tree desired/actual depth + int[] tb, // bits tree result + int[] hp, // space for trees + ZStream z // for messages + ){ + int result; + initWorkArea(19); + hn[0]=0; + result = huft_build(c, 0, 19, 19, null, null, tb, bb, hp, hn, v); + + if(result == Z_DATA_ERROR){ + z.msg = "oversubscribed dynamic bit lengths tree"; + } + else if(result == Z_BUF_ERROR || bb[0] == 0){ + z.msg = "incomplete dynamic bit lengths tree"; + result = Z_DATA_ERROR; + } + return result; + } + + int inflate_trees_dynamic(int nl, // number of literal/length codes + int nd, // number of distance codes + int[] c, // that many (total) code lengths + int[] bl, // literal desired/actual bit depth + int[] bd, // distance desired/actual bit depth + int[] tl, // literal/length tree result + int[] td, // distance tree result + int[] hp, // space for trees + ZStream z // for messages + ){ + int result; + + // build literal/length tree + initWorkArea(288); + hn[0]=0; + result = huft_build(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v); + if (result != Z_OK || bl[0] == 0){ + if(result == Z_DATA_ERROR){ + z.msg = "oversubscribed literal/length tree"; + } + else if (result != Z_MEM_ERROR){ + z.msg = "incomplete literal/length tree"; + result = Z_DATA_ERROR; + } + return result; + } + + // build distance tree + initWorkArea(288); + result = huft_build(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v); + + if (result != Z_OK || (bd[0] == 0 && nl > 257)){ + if (result == Z_DATA_ERROR){ + z.msg = "oversubscribed distance tree"; + } + else if (result == Z_BUF_ERROR) { + z.msg = "incomplete distance tree"; + result = Z_DATA_ERROR; + } + else if (result != Z_MEM_ERROR){ + z.msg = "empty distance tree with lengths"; + result = Z_DATA_ERROR; + } + return result; + } + + return Z_OK; + } + + static int inflate_trees_fixed(int[] bl, //literal desired/actual bit depth + int[] bd, //distance desired/actual bit depth + int[][] tl,//literal/length tree result + int[][] td,//distance tree result + ZStream z //for memory allocation + ){ + bl[0]=fixed_bl; + bd[0]=fixed_bd; + tl[0]=fixed_tl; + td[0]=fixed_td; + return Z_OK; + } + + private void initWorkArea(int vsize){ + if(hn==null){ + hn=new int[1]; + v=new int[vsize]; + c=new int[BMAX+1]; + r=new int[3]; + u=new int[BMAX]; + x=new int[BMAX+1]; + } + if(v.length> 4) + 1; + if(w < 48) + w &= 15; + } + + if(w<8 ||w>15){ + inflateEnd(); + return Z_STREAM_ERROR; + } + if(blocks != null && wbits != w){ + blocks.free(); + blocks=null; + } + + // set window size + wbits=w; + + this.blocks=new InfBlocks(z, 1<>8))&0xff; + + if((wrap&1)==0 || // check if zlib header allowed + (((this.method << 8)+b) % 31)!=0){ + this.mode = BAD; + z.msg = "incorrect header check"; + // since zlib 1.2, it is allowted to inflateSync for this case. + /* + this.marker = 5; // can't try inflateSync + */ + break; + } + + if((this.method&0xf)!=Z_DEFLATED){ + this.mode = BAD; + z.msg="unknown compression method"; + // since zlib 1.2, it is allowted to inflateSync for this case. + /* + this.marker = 5; // can't try inflateSync + */ + break; + } + + if((this.method>>4)+8>this.wbits){ + this.mode = BAD; + z.msg="invalid window size"; + // since zlib 1.2, it is allowted to inflateSync for this case. + /* + this.marker = 5; // can't try inflateSync + */ + break; + } + + z.adler=new Adler32(); + + if((b&PRESET_DICT)==0){ + this.mode = BLOCKS; + break; + } + this.mode = DICT4; + case DICT4: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need=((z.next_in[z.next_in_index++]&0xff)<<24)&0xff000000L; + this.mode=DICT3; + case DICT3: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need+=((z.next_in[z.next_in_index++]&0xff)<<16)&0xff0000L; + this.mode=DICT2; + case DICT2: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need+=((z.next_in[z.next_in_index++]&0xff)<<8)&0xff00L; + this.mode=DICT1; + case DICT1: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need += (z.next_in[z.next_in_index++]&0xffL); + z.adler.reset(this.need); + this.mode = DICT0; + return Z_NEED_DICT; + case DICT0: + this.mode = BAD; + z.msg = "need dictionary"; + this.marker = 0; // can try inflateSync + return Z_STREAM_ERROR; + case BLOCKS: + r = this.blocks.proc(r); + if(r == Z_DATA_ERROR){ + this.mode = BAD; + this.marker = 0; // can try inflateSync + break; + } + if(r == Z_OK){ + r = f; + } + if(r != Z_STREAM_END){ + return r; + } + r = f; + this.was=z.adler.getValue(); + this.blocks.reset(); + if(this.wrap==0){ + this.mode=DONE; + break; + } + this.mode=CHECK4; + case CHECK4: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need=((z.next_in[z.next_in_index++]&0xff)<<24)&0xff000000L; + this.mode=CHECK3; + case CHECK3: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need+=((z.next_in[z.next_in_index++]&0xff)<<16)&0xff0000L; + this.mode = CHECK2; + case CHECK2: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need+=((z.next_in[z.next_in_index++]&0xff)<<8)&0xff00L; + this.mode = CHECK1; + case CHECK1: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + this.need+=(z.next_in[z.next_in_index++]&0xffL); + + if(flags!=0){ // gzip + this.need = ((this.need&0xff000000)>>24 | + (this.need&0x00ff0000)>>8 | + (this.need&0x0000ff00)<<8 | + (this.need&0x0000ffff)<<24)&0xffffffffL; + } + + if(((int)(this.was)) != ((int)(this.need))){ + z.msg = "incorrect data check"; + // chack is delayed + /* + this.mode = BAD; + this.marker = 5; // can't try inflateSync + break; + */ + } + else if(flags!=0 && gheader!=null){ + gheader.crc = this.need; + } + + this.mode = LENGTH; + case LENGTH: + if (wrap!=0 && flags!=0) { + + try { r=readBytes(4, r, f); } + catch(Return e){ return e.r; } + + if(z.msg!=null && z.msg.equals("incorrect data check")){ + this.mode = BAD; + this.marker = 5; // can't try inflateSync + break; + } + + if (this.need != (z.total_out & 0xffffffffL)) { + z.msg = "incorrect length check"; + this.mode = BAD; + break; + } + z.msg = null; + } + else { + if(z.msg!=null && z.msg.equals("incorrect data check")){ + this.mode = BAD; + this.marker = 5; // can't try inflateSync + break; + } + } + + this.mode = DONE; + case DONE: + return Z_STREAM_END; + case BAD: + return Z_DATA_ERROR; + + case FLAGS: + + try { r=readBytes(2, r, f); } + catch(Return e){ return e.r; } + + flags = ((int)this.need)&0xffff; + + if ((flags & 0xff) != Z_DEFLATED) { + z.msg = "unknown compression method"; + this.mode = BAD; + break; + } + if ((flags & 0xe000)!=0) { + z.msg = "unknown header flags set"; + this.mode = BAD; + break; + } + + if ((flags & 0x0200)!=0){ + checksum(2, this.need); + } + + this.mode = TIME; + + case TIME: + try { r=readBytes(4, r, f); } + catch(Return e){ return e.r; } + if(gheader!=null) + gheader.time = this.need; + if ((flags & 0x0200)!=0){ + checksum(4, this.need); + } + this.mode = OS; + case OS: + try { r=readBytes(2, r, f); } + catch(Return e){ return e.r; } + if(gheader!=null){ + gheader.xflags = ((int)this.need)&0xff; + gheader.os = (((int)this.need)>>8)&0xff; + } + if ((flags & 0x0200)!=0){ + checksum(2, this.need); + } + this.mode = EXLEN; + case EXLEN: + if ((flags & 0x0400)!=0) { + try { r=readBytes(2, r, f); } + catch(Return e){ return e.r; } + if(gheader!=null){ + gheader.extra = new byte[((int)this.need)&0xffff]; + } + if ((flags & 0x0200)!=0){ + checksum(2, this.need); + } + } + else if(gheader!=null){ + gheader.extra=null; + } + this.mode = EXTRA; + + case EXTRA: + if ((flags & 0x0400)!=0) { + try { + r=readBytes(r, f); + if(gheader!=null){ + byte[] foo = tmp_array; + tmp_array=null; + if(foo.length == gheader.extra.length){ + System.arraycopy(foo, 0, gheader.extra, 0, foo.length); + } + else{ + z.msg = "bad extra field length"; + this.mode = BAD; + break; + } + } + } + catch(Return e){ return e.r; } + } + else if(gheader!=null){ + gheader.extra=null; + } + this.mode = NAME; + case NAME: + if ((flags & 0x0800)!=0) { + try { + r=readString(r, f); + if(gheader!=null){ + gheader.name=tmp_array; + } + tmp_array=null; + } + catch(Return e){ return e.r; } + } + else if(gheader!=null){ + gheader.name=null; + } + this.mode = COMMENT; + case COMMENT: + if ((flags & 0x1000)!=0) { + try { + r=readString(r, f); + if(gheader!=null){ + gheader.comment=tmp_array; + } + tmp_array=null; + } + catch(Return e){ return e.r; } + } + else if(gheader!=null){ + gheader.comment=null; + } + this.mode = HCRC; + case HCRC: + if ((flags & 0x0200)!=0) { + try { r=readBytes(2, r, f); } + catch(Return e){ return e.r; } + if(gheader!=null){ + gheader.hcrc=(int)(this.need&0xffff); + } + if(this.need != (z.adler.getValue()&0xffffL)){ + this.mode = BAD; + z.msg = "header crc mismatch"; + this.marker = 5; // can't try inflateSync + break; + } + } + z.adler = new CRC32(); + + this.mode = BLOCKS; + break; + default: + return Z_STREAM_ERROR; + } + } + } + + int inflateSetDictionary(byte[] dictionary, int dictLength){ + if(z==null || (this.mode != DICT0 && this.wrap != 0)){ + return Z_STREAM_ERROR; + } + + int index=0; + int length = dictLength; + + if(this.mode==DICT0){ + long adler_need=z.adler.getValue(); + z.adler.reset(); + z.adler.update(dictionary, 0, dictLength); + if(z.adler.getValue()!=adler_need){ + return Z_DATA_ERROR; + } + } + + z.adler.reset(); + + if(length >= (1<0){ + if(z.avail_in==0){ throw new Return(r); }; r=f; + z.avail_in--; z.total_in++; + this.need = this.need | + ((z.next_in[z.next_in_index++]&0xff)<<((n-need_bytes)*8)); + need_bytes--; + } + if(n==2){ + this.need&=0xffffL; + } + else if(n==4) { + this.need&=0xffffffffL; + } + need_bytes=-1; + return r; + } + class Return extends Exception{ + int r; + Return(int r){this.r=r; } + } + + private byte[] tmp_array; + private int readString(int r, int f) throws Return{ + int b=0; + byte[] arr = new byte[4092]; + int at = 0; + do { + if(z.avail_in==0){ throw new Return(r); }; r=f; + z.avail_in--; z.total_in++; + b = z.next_in[z.next_in_index]; + if(b!=0) arr = append(arr, z.next_in[z.next_in_index], at++); + z.adler.update(z.next_in, z.next_in_index, 1); + z.next_in_index++; + }while(b!=0); + + tmp_array = copy(arr, at); + + return r; + } + + private int readBytes(int r, int f) throws Return{ + int b=0; + byte[] arr = new byte[4092]; + int at = 0; + while(this.need>0){ + if(z.avail_in==0){ throw new Return(r); }; r=f; + z.avail_in--; z.total_in++; + b = z.next_in[z.next_in_index]; + arr = append(arr, z.next_in[z.next_in_index], at++); + z.adler.update(z.next_in, z.next_in_index, 1); + z.next_in_index++; + this.need--; + } + + tmp_array = copy(arr, at); + + return r; + } + + private static byte[] copy(byte[] arr, int len) { + byte[] ret = new byte[len]; + org.apidesign.bck2brwsr.emul.lang.System.arraycopy(arr, 0, ret, 0, len); + return ret; + } + private static byte[] append(byte[] arr, byte b, int index) { + arr[index] = b; + return arr; + } + + private void checksum(int n, long v){ + for(int i=0; i>=8; + } + z.adler.update(crcbuf, 0, n); + } + + public GZIPHeader getGZIPHeader(){ + return gheader; + } + + boolean inParsingHeader(){ + switch(mode){ + case HEAD: + case DICT4: + case DICT3: + case DICT2: + case DICT1: + case FLAGS: + case TIME: + case OS: + case EXLEN: + case EXTRA: + case NAME: + case COMMENT: + case HCRC: + return true; + default: + return false; + } + } +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/Inflater.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/Inflater.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,338 @@ +/* + * 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 org.apidesign.bck2brwsr.emul.zip; + +import java.util.zip.*; +import java.io.IOException; + +/** + * 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 extends java.util.zip.Inflater { + private final boolean nowrap; + private JzLibInflater impl; + + /** + * 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) { + this.nowrap = nowrap; + reset(); + } + + /** + * 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(); + } + impl.setInput(b, off, len, 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 + * @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(); + } + byte[] arr; + if (off == 0) { + arr = b; + } else { + arr = new byte[len]; + org.apidesign.bck2brwsr.emul.lang.System.arraycopy(b, off, arr, 0, len); + } + impl.setDictionary(arr, len); + } + + /** + * 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) { + impl.setDictionary(b, 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() { + return impl.getAvailIn(); + } + + /** + * 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() { + return getRemaining() <= 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() { + return impl.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() { + return impl.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(); + } + impl.setOutput(b, off, len); + int err = impl.inflate(JzLibInflater.Z_NO_FLUSH); + return impl.next_out_index - off; + } + + /** + * 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() { + return (int) impl.getAdler(); + } + + /** + * 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() { + return impl.total_in; + } + + /** + * 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() { + return impl.total_out; + } + + /** + * Resets inflater so that a new set of input data can be processed. + */ + public void reset() { + impl = new JzLibInflater(15, nowrap); + } + + /** + * 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() { + impl.end(); + } + + /** + * Closes the decompressor when garbage is collected. + */ + protected void finalize() { + end(); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/JzLibInflater.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/JzLibInflater.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,137 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package org.apidesign.bck2brwsr.emul.zip; + +final class JzLibInflater extends ZStream{ + + static final private int MAX_WBITS=15; // 32K LZ77 window + static final private int DEF_WBITS=MAX_WBITS; + + public static final int Z_NO_FLUSH=0; + static final private int Z_PARTIAL_FLUSH=1; + static final private int Z_SYNC_FLUSH=2; + static final private int Z_FULL_FLUSH=3; + static final private int Z_FINISH=4; + + static final private int MAX_MEM_LEVEL=9; + + static final private int Z_OK=0; + static final private int Z_STREAM_END=1; + static final private int Z_NEED_DICT=2; + static final private int Z_ERRNO=-1; + static final private int Z_STREAM_ERROR=-2; + static final private int Z_DATA_ERROR=-3; + static final private int Z_MEM_ERROR=-4; + static final private int Z_BUF_ERROR=-5; + static final private int Z_VERSION_ERROR=-6; + + public JzLibInflater() { + super(); + init(); + } + + public JzLibInflater(int w) { + this(w, false); + } + + public JzLibInflater(int w, boolean nowrap) { + super(); + int ret = init(w, nowrap); + if(ret!=Z_OK) + throw new IllegalStateException(ret+": "+msg); + } + + private boolean finished = false; + + public int init(){ + return init(DEF_WBITS); + } + + public int init(boolean nowrap){ + return init(DEF_WBITS, nowrap); + } + + public int init(int w){ + return init(w, false); + } + + public int init(int w, boolean nowrap){ + finished = false; + istate=new Inflate(this); + return istate.inflateInit(nowrap?-w:w); + } + + public int inflate(int f){ + if(istate==null) return Z_STREAM_ERROR; + int ret = istate.inflate(f); + if(ret == Z_STREAM_END) + finished = true; + return ret; + } + + public int end(){ + finished = true; + if(istate==null) return Z_STREAM_ERROR; + int ret=istate.inflateEnd(); +// istate = null; + return ret; + } + + public int sync(){ + if(istate == null) + return Z_STREAM_ERROR; + return istate.inflateSync(); + } + + public int syncPoint(){ + if(istate == null) + return Z_STREAM_ERROR; + return istate.inflateSyncPoint(); + } + + public int setDictionary(byte[] dictionary, int dictLength){ + if(istate == null) + return Z_STREAM_ERROR; + return istate.inflateSetDictionary(dictionary, dictLength); + } + + public boolean finished(){ + return istate.mode==12 /*DONE*/; + } + + public boolean needDict() { + return istate == null ? false : istate.mode == Inflate.DICT0; + } +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/ZStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/ZStream.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,253 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2000-2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +package org.apidesign.bck2brwsr.emul.zip; + +import org.apidesign.bck2brwsr.emul.lang.System; + +/** + * ZStream + * + * @deprecated Not for public use in the future. + */ +@Deprecated +class ZStream{ + + static final private int MAX_WBITS=15; // 32K LZ77 window + static final private int DEF_WBITS=MAX_WBITS; + + static final private int Z_NO_FLUSH=0; + static final private int Z_PARTIAL_FLUSH=1; + static final private int Z_SYNC_FLUSH=2; + static final private int Z_FULL_FLUSH=3; + static final private int Z_FINISH=4; + + static final private int MAX_MEM_LEVEL=9; + + static final private int Z_OK=0; + static final private int Z_STREAM_END=1; + static final private int Z_NEED_DICT=2; + static final private int Z_ERRNO=-1; + static final private int Z_STREAM_ERROR=-2; + static final private int Z_DATA_ERROR=-3; + static final private int Z_MEM_ERROR=-4; + static final private int Z_BUF_ERROR=-5; + static final private int Z_VERSION_ERROR=-6; + + public byte[] next_in; // next input byte + public int next_in_index; + public int avail_in; // number of bytes available at next_in + public long total_in; // total nb of input bytes read so far + + public byte[] next_out; // next output byte should be put there + public int next_out_index; + public int avail_out; // remaining free space at next_out + public long total_out; // total nb of bytes output so far + + public String msg; + + Inflate istate; + + int data_type; // best guess about the data type: ascii or binary + + Checksum adler; + + public ZStream(){ + this(new Adler32()); + } + + public ZStream(Checksum adler){ + this.adler=adler; + } + + public int inflateInit(){ + return inflateInit(DEF_WBITS); + } + public int inflateInit(boolean nowrap){ + return inflateInit(DEF_WBITS, nowrap); + } + public int inflateInit(int w){ + return inflateInit(w, false); + } + + public int inflateInit(int w, boolean nowrap){ + istate=new Inflate(this); + return istate.inflateInit(nowrap?-w:w); + } + + public int inflate(int f){ + if(istate==null) return Z_STREAM_ERROR; + return istate.inflate(f); + } + public int inflateEnd(){ + if(istate==null) return Z_STREAM_ERROR; + int ret=istate.inflateEnd(); +// istate = null; + return ret; + } + + public int inflateSync(){ + if(istate == null) + return Z_STREAM_ERROR; + return istate.inflateSync(); + } + public int inflateSyncPoint(){ + if(istate == null) + return Z_STREAM_ERROR; + return istate.inflateSyncPoint(); + } + public int inflateSetDictionary(byte[] dictionary, int dictLength){ + if(istate == null) + return Z_STREAM_ERROR; + return istate.inflateSetDictionary(dictionary, dictLength); + } + public boolean inflateFinished(){ + return istate.mode==12 /*DONE*/; + } + + + public long getAdler(){ + return adler.getValue(); + } + + public void free(){ + next_in=null; + next_out=null; + msg=null; + } + + public void setOutput(byte[] buf){ + setOutput(buf, 0, buf.length); + } + + public void setOutput(byte[] buf, int off, int len){ + next_out = buf; + next_out_index = off; + avail_out = len; + } + + public void setInput(byte[] buf){ + setInput(buf, 0, buf.length, false); + } + + public void setInput(byte[] buf, boolean append){ + setInput(buf, 0, buf.length, append); + } + + public void setInput(byte[] buf, int off, int len, boolean append){ + if(len<=0 && append && next_in!=null) return; + + if(avail_in>0 && append){ + byte[] tmp = new byte[avail_in+len]; + System.arraycopy(next_in, next_in_index, tmp, 0, avail_in); + System.arraycopy(buf, off, tmp, avail_in, len); + next_in=tmp; + next_in_index=0; + avail_in+=len; + } + else{ + next_in=buf; + next_in_index=off; + avail_in=len; + } + } + + public byte[] getNextIn(){ + return next_in; + } + + public void setNextIn(byte[] next_in){ + this.next_in = next_in; + } + + public int getNextInIndex(){ + return next_in_index; + } + + public void setNextInIndex(int next_in_index){ + this.next_in_index = next_in_index; + } + + public int getAvailIn(){ + return avail_in; + } + + public void setAvailIn(int avail_in){ + this.avail_in = avail_in; + } + + public byte[] getNextOut(){ + return next_out; + } + + public void setNextOut(byte[] next_out){ + this.next_out = next_out; + } + + public int getNextOutIndex(){ + return next_out_index; + } + + public void setNextOutIndex(int next_out_index){ + this.next_out_index = next_out_index; + } + + public int getAvailOut(){ + return avail_out; + + } + + public void setAvailOut(int avail_out){ + this.avail_out = avail_out; + } + + public long getTotalOut(){ + return total_out; + } + + public long getTotalIn(){ + return total_in; + } + + public String getMessage(){ + return msg; + } + + /** + * Those methods are expected to be override by Inflater and Deflater. + * In the future, they will become abstract methods. + */ + public int end(){ return Z_OK; } + public boolean finished(){ return false; } +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/ZipConstants64.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/ZipConstants64.java Sun Feb 17 17:58:34 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 org.apidesign.bck2brwsr.emul.zip; + +/* + * This class defines the constants that are used by the classes + * which manipulate Zip64 files. + */ + +public 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 d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/ZipInputStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/ZipInputStream.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,468 @@ +/* + * 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 org.apidesign.bck2brwsr.emul.zip; + +import java.util.zip.*; +import java.io.InputStream; +import java.io.IOException; +import java.io.EOFException; +import java.io.PushbackInputStream; +import static org.apidesign.bck2brwsr.emul.zip.ZipConstants64.*; +import static java.util.zip.ZipInputStream.*; + +/** + * 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 { + private ZipEntry entry; + private int flag; + private CRC32 crc = new CRC32(); + private long remaining; + private byte[] tmpbuf = new byte[512]; + + private static final int STORED = ZipEntry.STORED; + private static final int DEFLATED = ZipEntry.DEFLATED; + + private boolean closed = false; + // this flag is set to true after EOF has reached for + // one entry + private boolean entryEOF = false; + + /** + * Check to make sure that this stream has not been closed + */ + private void ensureOpen() throws IOException { + if (closed) { + throw new IOException("Stream closed"); + } + } + + /** + * Creates a new ZIP input stream. + * + *

The UTF-8 {@link java.nio.charset.Charset charset} is used to + * decode the entry names. + * + * @param in the actual input stream + */ + public ZipInputStream(InputStream in) { +// this(in, "UTF-8"); + super(new PushbackInputStream(in, 512), new Inflater(true), 512); + //usesDefaultInflater = true; + if(in == null) { + throw new NullPointerException("in is null"); + } + } + + /** + * Creates a new ZIP input stream. + * + * @param in the actual input stream + * + * @param charset + * The {@linkplain java.nio.charset.Charset charset} to be + * used to decode the ZIP entry name (ignored if the + * language + * encoding bit of the ZIP entry's general purpose bit + * flag is set). + * + * @since 1.7 + * + public ZipInputStream(InputStream in, Charset charset) { + super(new PushbackInputStream(in, 512), new Inflater(true), 512); + usesDefaultInflater = true; + if(in == null) { + throw new NullPointerException("in is null"); + } + if (charset == null) + throw new NullPointerException("charset is null"); + this.zc = ZipCoder.get(charset); + } + */ + + /** + * Reads the next ZIP file entry and positions the stream at the + * beginning of the entry data. + * @return the next ZIP file entry, or null if there are no more entries + * @exception ZipException if a ZIP file error has occurred + * @exception IOException if an I/O error has occurred + */ + public ZipEntry getNextEntry() throws IOException { + ensureOpen(); + if (entry != null) { + closeEntry(); + } + crc.reset(); + inf.reset(); + if ((entry = readLOC()) == null) { + return null; + } + if (entry.getMethod() == STORED) { + remaining = entry.getSize(); + } + 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.getMethod()) { + 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.getCrc() != crc.getValue()) { + throw new ZipException( + "invalid entry CRC (expected 0x" + Long.toHexString(entry.getCrc()) + + " but got 0x" + Long.toHexString(crc.getValue()) + ")"); + } + return len; + default: + throw new ZipException("invalid compression method"); + } + } + + /** + * Skips specified number of bytes in the current ZIP entry. + * @param n the number of bytes to skip + * @return the actual number of bytes skipped + * @exception ZipException if a ZIP file error has occurred + * @exception IOException if an I/O error has occurred + * @exception IllegalArgumentException if n < 0 + */ + public long skip(long n) throws IOException { + if (n < 0) { + throw new IllegalArgumentException("negative skip length"); + } + ensureOpen(); + int max = (int)Math.min(n, Integer.MAX_VALUE); + int total = 0; + while (total < max) { + int len = max - total; + if (len > tmpbuf.length) { + len = tmpbuf.length; + } + len = read(tmpbuf, 0, len); + if (len == -1) { + entryEOF = true; + break; + } + total += len; + } + return total; + } + + /** + * Closes this input stream and releases any system resources associated + * with the stream. + * @exception IOException if an I/O error has occurred + */ + public void close() throws IOException { + if (!closed) { + super.close(); + closed = true; + } + } + + private byte[] b = new byte[256]; + + /* + * Reads local file (LOC) header for next entry. + */ + private ZipEntry readLOC() throws IOException { + try { + readFully(tmpbuf, 0, LOCHDR); + } catch (EOFException e) { + return null; + } + if (get32(tmpbuf, 0) != LOCSIG) { + return null; + } + // get flag first, we need check EFS. + flag = get16(tmpbuf, LOCFLG); + // get the entry name and create the ZipEntry first + int len = get16(tmpbuf, LOCNAM); + int blen = b.length; + if (len > blen) { + do + blen = blen * 2; + while (len > blen); + b = new byte[blen]; + } + readFully(b, 0, len); + // Force to use UTF-8 if the EFS bit is ON, even the cs is NOT UTF-8 + ZipEntry e = createZipEntry(((flag & EFS) != 0) + ? toStringUTF8(b, len) + : toString(b, len)); + // now get the remaining fields for the entry + if ((flag & 1) == 1) { + throw new ZipException("encrypted ZIP entry not supported"); + } + e.setMethod(get16(tmpbuf, LOCHOW)); + e.setTime(get32(tmpbuf, LOCTIM)); + if ((flag & 8) == 8) { + /* "Data Descriptor" present */ + if (e.getMethod() != DEFLATED) { + throw new ZipException( + "only DEFLATED entries can have EXT descriptor"); + } + } else { + e.setCrc(get32(tmpbuf, LOCCRC)); + e.setCompressedSize(get32(tmpbuf, LOCSIZ)); + e.setSize(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.getCompressedSize() == ZIP64_MAGICVAL || e.getCompressedSize() == 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.setSize(get64(bb, off)); + e.setCompressedSize(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.setCrc(sig); + e.setCompressedSize(get64(tmpbuf, ZIP64_EXTSIZ - ZIP64_EXTCRC)); + e.setSize(get64(tmpbuf, ZIP64_EXTLEN - ZIP64_EXTCRC)); + ((PushbackInputStream)in).unread( + tmpbuf, ZIP64_EXTHDR - ZIP64_EXTCRC - 1, ZIP64_EXTCRC); + } else { + e.setCrc(get32(tmpbuf, ZIP64_EXTCRC)); + e.setCompressedSize(get64(tmpbuf, ZIP64_EXTSIZ)); + e.setSize(get64(tmpbuf, ZIP64_EXTLEN)); + } + } else { + readFully(tmpbuf, 0, EXTHDR); + long sig = get32(tmpbuf, 0); + if (sig != EXTSIG) { // no EXTSIG present + e.setCrc(sig); + e.setCompressedSize(get32(tmpbuf, EXTSIZ - EXTCRC)); + e.setSize(get32(tmpbuf, EXTLEN - EXTCRC)); + ((PushbackInputStream)in).unread( + tmpbuf, EXTHDR - EXTCRC - 1, EXTCRC); + } else { + e.setCrc(get32(tmpbuf, EXTCRC)); + e.setCompressedSize(get32(tmpbuf, EXTSIZ)); + e.setSize(get32(tmpbuf, EXTLEN)); + } + } + } + if (e.getSize() != inf.getBytesWritten()) { + throw new ZipException( + "invalid entry size (expected " + e.getSize() + + " but got " + inf.getBytesWritten() + " bytes)"); + } + if (e.getCompressedSize() != inf.getBytesRead()) { + throw new ZipException( + "invalid entry compressed size (expected " + e.getCompressedSize() + + " but got " + inf.getBytesRead() + " bytes)"); + } + if (e.getCrc() != crc.getValue()) { + throw new ZipException( + "invalid entry CRC (expected 0x" + Long.toHexString(e.getCrc()) + + " but got 0x" + Long.toHexString(crc.getValue()) + ")"); + } + } + + /* + * Reads bytes, blocking until all bytes are read. + */ + private void readFully(byte[] b, int off, int len) throws IOException { + while (len > 0) { + int n = in.read(b, off, len); + if (n == -1) { + throw new EOFException(); + } + off += n; + len -= n; + } + } + + /* + * Fetches unsigned 16-bit value from byte array at specified offset. + * The bytes are assumed to be in Intel (little-endian) byte order. + */ + private static final int get16(byte b[], int off) { + return (b[off] & 0xff) | ((b[off+1] & 0xff) << 8); + } + + /* + * Fetches unsigned 32-bit value from byte array at specified offset. + * The bytes are assumed to be in Intel (little-endian) byte order. + */ + private static final long get32(byte b[], int off) { + return (get16(b, off) | ((long)get16(b, off+2) << 16)) & 0xffffffffL; + } + + /* + * Fetches signed 64-bit value from byte array at specified offset. + * The bytes are assumed to be in Intel (little-endian) byte order. + */ + private static final long get64(byte b[], int off) { + return get32(b, off) | (get32(b, off+4) << 32); + } + + private static String toStringUTF8(byte[] arr, int len) { + return new String(arr, 0, len); + } + + private static String toString(byte[] b, int len) { + return new String(b, 0, len); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_Number.js --- a/emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_Number.js Fri Jan 25 15:08:24 2013 +0100 +++ b/emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_Number.js Sun Feb 17 17:58:34 2013 +0100 @@ -4,6 +4,517 @@ Number.prototype.mul32 = function(x) { return (((this * (x >> 16)) << 16) + this * (x & 0xFFFF)) | 0; }; +Number.prototype.neg32 = function() { return (-this) | 0; }; Number.prototype.toInt8 = function() { return (this << 24) >> 24; }; -Number.prototype.toInt16 = function() { return (this << 16) >> 16; }; \ No newline at end of file +Number.prototype.toInt16 = function() { return (this << 16) >> 16; }; + +var __m32 = 0xFFFFFFFF; + +Number.prototype.next32 = function(low) { + if (this === 0) { + return low; + } + var l = new Number(low); + l.hi = this | 0; + return l; +}; + +Number.prototype.high32 = function() { + return this.hi ? this.hi : (Math.floor(this / (__m32+1))) | 0; +}; +Number.prototype.toInt32 = function() { return this | 0; }; +Number.prototype.toFP = function() { + return this.hi ? this.hi * (__m32+1) + this : this; +}; +Number.prototype.toLong = function() { + var hi = (this > __m32) ? (Math.floor(this / (__m32+1))) | 0 : 0; + return hi.next32(Math.floor(this % (__m32+1))); +}; + +Number.prototype.toExactString = function() { + if (this.hi) { + // check for Long.MIN_VALUE + if ((this.hi == (0x80000000 | 0)) && (this == 0)) { + return '-9223372036854775808'; + } + var res = 0; + var a = [ 6,9,2,7,6,9,4,9,2,4 ]; + var s = ''; + var digit; + var neg = this.hi < 0; + if (neg) { + var x = this.neg64(); + var hi = x.hi; + var low = x; + } else { + var hi = this.hi; + var low = this; + } + for (var i = 0; i < a.length; i++) { + res += hi * a[i]; + var low_digit = low % 10; + digit = (res % 10) + low_digit; + + low = Math.floor(low / 10); + res = Math.floor(res / 10); + + if (digit >= 10) { + digit -= 10; + res++; + } + s = String(digit).concat(s); + } + s = String(res).concat(s).replace(/^0+/, ''); + return (neg ? '-' : '').concat(s); + } + return String(this); +}; + +Number.prototype.add64 = function(x) { + var low = this + x; + carry = 0; + if (low > __m32) { + carry = 1; + low -= (__m32+1); + } + var hi = (this.high32() + x.high32() + carry) | 0; + return hi.next32(low); +}; + +Number.prototype.sub64 = function(x) { + var low = this - x; + carry = 0; + if (low < 0) { + carry = 1; + low += (__m32+1); + } + var hi = (this.high32() - x.high32() - carry) | 0; + return hi.next32(low); +}; + +Number.prototype.mul64 = function(x) { + var low = this.mul32(x); + low += (low < 0) ? (__m32+1) : 0; + // first count upper 32 bits of (this.low * x.low) + var hi_hi = 0; + var hi_low = 0; + var m = 1; + for (var i = 0; i < 32; i++) { + if (x & m) { + hi_hi += this >>> 16; + hi_low += this & 0xFFFF + } + hi_low >>= 1; + hi_low += (hi_hi & 1) ? 0x8000 : 0; + hi_hi >>= 1; + m <<= 1; + } + var hi = (hi_hi << 16) + hi_low; + + var m1 = this.high32().mul32(x); + var m2 = this.mul32(x.high32()); + hi = hi.add32(m1).add32(m2); + + return hi.next32(low); +}; + +Number.prototype.and64 = function(x) { + var low = this & x; + low += (low < 0) ? (__m32+1) : 0; + if (this.hi && x.hi) { + var hi = this.hi & x.hi; + return hi.next32(low); + }; + return low; +}; + +Number.prototype.or64 = function(x) { + var low = this | x; + low += (low < 0) ? (__m32+1) : 0; + if (this.hi || x.hi) { + var hi = this.hi | x.hi; + return hi.next32(low); + }; + return low; +}; + +Number.prototype.xor64 = function(x) { + var low = this ^ x; + low += (low < 0) ? (__m32+1) : 0; + if (this.hi || x.hi) { + var hi = this.hi ^ x.hi; + return hi.next32(low); + }; + return low; +}; + +Number.prototype.shl64 = function(x) { + if (x >= 32) { + var hi = this << (x - 32); + return hi.next32(0); + } else { + var hi = this.high32() << x; + var low_reminder = this >> (32 - x); + hi |= low_reminder; + var low = this << x; + low += (low < 0) ? (__m32+1) : 0; + return hi.next32(low); + } +}; + +Number.prototype.shr64 = function(x) { + if (x >= 32) { + var low = this.high32() >> (x - 32); + low += (low < 0) ? (__m32+1) : 0; + return low; + } else { + var low = this >> x; + var hi_reminder = this.high32() << (32 - x); + low |= hi_reminder; + low += (low < 0) ? (__m32+1) : 0; + var hi = this.high32() >> x; + return hi.next32(low); + } +}; + +Number.prototype.ushr64 = function(x) { + if (x >= 32) { + var low = this.high32() >>> (x - 32); + low += (low < 0) ? (__m32+1) : 0; + return low; + } else { + var low = this >>> x; + var hi_reminder = this.high32() << (32 - x); + low |= hi_reminder; + low += (low < 0) ? (__m32+1) : 0; + var hi = this.high32() >>> x; + return hi.next32(low); + } +}; + +Number.prototype.compare64 = function(x) { + if (this.high32() === x.high32()) { + return (this < x) ? -1 : ((this > x) ? 1 : 0); + } + return (this.high32() < x.high32()) ? -1 : 1; +}; + +Number.prototype.neg64 = function() { + var hi = this.high32(); + var low = this; + if ((hi === 0) && (low < 0)) { return -low; } + hi = ~hi; + low = ~low; + low += (low < 0) ? (__m32+1) : 0; + var ret = hi.next32(low); + return ret.add64(1); +}; + +(function(numberPrototype) { + function __Int64(hi32, lo32) { + this.hi32 = hi32 | 0; + this.lo32 = lo32 | 0; + + this.get32 = function(bitIndex) { + var v0; + var v1; + bitIndex += 32; + var selector = bitIndex >>> 5; + switch (selector) { + case 0: + v0 = 0; + v1 = this.lo32; + break; + case 1: + v0 = this.lo32; + v1 = this.hi32; + break; + case 2: + v0 = this.hi32; + v1 = 0; + break + default: + return 0; + } + + var shift = bitIndex & 31; + if (shift === 0) { + return v0; + } + + return (v1 << (32 - shift)) | (v0 >>> shift); + } + + this.get16 = function(bitIndex) { + return this.get32(bitIndex) & 0xffff; + } + + this.set16 = function(bitIndex, value) { + bitIndex += 32; + var shift = bitIndex & 15; + var svalue = (value & 0xffff) << shift; + var smask = 0xffff << shift; + var selector = bitIndex >>> 4; + switch (selector) { + case 0: + break; + case 1: + this.lo32 = (this.lo32 & ~(smask >>> 16)) + | (svalue >>> 16); + break; + case 2: + this.lo32 = (this.lo32 & ~smask) | svalue; + break; + case 3: + this.lo32 = (this.lo32 & ~(smask << 16)) + | (svalue << 16); + this.hi32 = (this.hi32 & ~(smask >>> 16)) + | (svalue >>> 16); + break; + case 4: + this.hi32 = (this.hi32 & ~smask) | svalue; + break; + case 5: + this.hi32 = (this.hi32 & ~(smask << 16)) + | (svalue << 16); + break; + } + } + + this.getDigit = function(index, shift) { + return this.get16((index << 4) - shift); + } + + this.getTwoDigits = function(index, shift) { + return this.get32(((index - 1) << 4) - shift); + } + + this.setDigit = function(index, shift, value) { + this.set16((index << 4) - shift, value); + } + + this.countSignificantDigits = function() { + var sd; + var remaining; + + if (this.hi32 === 0) { + if (this.lo32 === 0) { + return 0; + } + + sd = 2; + remaining = this.lo32; + } else { + sd = 4; + remaining = this.hi32; + } + + if (remaining < 0) { + return sd; + } + + return (remaining < 65536) ? sd - 1 : sd; + } + + this.toNumber = function() { + var lo32 = this.lo32; + if (lo32 < 0) { + lo32 += 0x100000000; + } + + return this.hi32.next32(lo32); + } + } + + function __countLeadingZeroes16(number) { + var nlz = 0; + + if (number < 256) { + nlz += 8; + number <<= 8; + } + + if (number < 4096) { + nlz += 4; + number <<= 4; + } + + if (number < 16384) { + nlz += 2; + number <<= 2; + } + + return (number < 32768) ? nlz + 1 : nlz; + } + + // q = u / v; r = u - q * v; + // v != 0 + function __div64(q, r, u, v) { + var m = u.countSignificantDigits(); + var n = v.countSignificantDigits(); + + q.hi32 = q.lo32 = 0; + + if (n === 1) { + // v has single digit + var vd = v.getDigit(0, 0); + var carry = 0; + for (var i = m - 1; i >= 0; --i) { + var ui = (carry << 16) | u.getDigit(i, 0); + if (ui < 0) { + ui += 0x100000000; + } + var qi = (ui / vd) | 0; + q.setDigit(i, 0, qi); + carry = ui - qi * vd; + } + + r.hi32 = 0; + r.lo32 = carry; + return; + } + + r.hi32 = u.hi32; + r.lo32 = u.lo32; + + if (m < n) { + return; + } + + // Normalize + var nrm = __countLeadingZeroes16(v.getDigit(n - 1, 0)); + + var vd1 = v.getDigit(n - 1, nrm); + var vd0 = v.getDigit(n - 2, nrm); + for (var j = m - n; j >= 0; --j) { + // Calculate qj estimate + var ud21 = r.getTwoDigits(j + n, nrm); + var ud2 = ud21 >>> 16; + if (ud21 < 0) { + ud21 += 0x100000000; + } + + var qest = (ud2 === vd1) ? 0xFFFF : ((ud21 / vd1) | 0); + var rest = ud21 - qest * vd1; + + // 0 <= (qest - qj) <= 2 + + // Refine qj estimate + var ud0 = r.getDigit(j + n - 2, nrm); + while ((qest * vd0) > ((rest * 0x10000) + ud0)) { + --qest; + rest += vd1; + } + + // 0 <= (qest - qj) <= 1 + + // Multiply and subtract + var carry = 0; + for (var i = 0; i < n; ++i) { + var vi = qest * v.getDigit(i, nrm); + var ui = r.getDigit(i + j, nrm) - carry - (vi & 0xffff); + r.setDigit(i + j, nrm, ui); + carry = (vi >>> 16) - (ui >> 16); + } + var uj = ud2 - carry; + + if (uj < 0) { + // qest - qj = 1 + + // Add back + --qest; + var carry = 0; + for (var i = 0; i < n; ++i) { + var ui = r.getDigit(i + j, nrm) + v.getDigit(i, nrm) + + carry; + r.setDigit(i + j, nrm, ui); + carry = ui >> 16; + } + uj += carry; + } + + q.setDigit(j, 0, qest); + r.setDigit(j + n, nrm, uj); + } + } + + numberPrototype.div64 = function(x) { + var negateResult = false; + var u, v; + + if ((this.high32() & 0x80000000) != 0) { + u = this.neg64(); + negateResult = !negateResult; + } else { + u = this; + } + + if ((x.high32() & 0x80000000) != 0) { + v = x.neg64(); + negateResult = !negateResult; + } else { + v = x; + } + + if ((v === 0) && (v.high32() === 0)) { + // TODO: throw + } + + if (u.high32() === 0) { + if (v.high32() === 0) { + var result = (u / v) | 0; + return negateResult ? result.neg64() : result; + } + + return 0; + } + + var u64 = new __Int64(u.high32(), u); + var v64 = new __Int64(v.high32(), v); + var q64 = new __Int64(0, 0); + var r64 = new __Int64(0, 0); + + __div64(q64, r64, u64, v64); + + var result = q64.toNumber(); + return negateResult ? result.neg64() : result; + } + + numberPrototype.mod64 = function(x) { + var negateResult = false; + var u, v; + + if ((this.high32() & 0x80000000) != 0) { + u = this.neg64(); + negateResult = !negateResult; + } else { + u = this; + } + + if ((x.high32() & 0x80000000) != 0) { + v = x.neg64(); + } else { + v = x; + } + + if ((v === 0) && (v.high32() === 0)) { + // TODO: throw + } + + if (u.high32() === 0) { + var result = (v.high32() === 0) ? (u % v) : u; + return negateResult ? result.neg64() : result; + } + + var u64 = new __Int64(u.high32(), u); + var v64 = new __Int64(v.high32(), v); + var q64 = new __Int64(0, 0); + var r64 = new __Int64(0, 0); + + __div64(q64, r64, u64, v64); + + var result = r64.toNumber(); + return negateResult ? result.neg64() : result; + } +})(Number.prototype); diff -r d443b6c05a01 -r 41e1dc88a399 emul/mini/src/test/java/org/apidesign/bck2brwsr/emul/reflect/MethodImplTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/mini/src/test/java/org/apidesign/bck2brwsr/emul/reflect/MethodImplTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,49 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.emul.reflect; + +import java.lang.reflect.Method; +import java.util.Enumeration; +import org.testng.annotations.Test; + +/** + * + * @author Jaroslav Tulach + */ +public class MethodImplTest { + + public MethodImplTest() { + } + + public static String[] arr(String... arr) { + return arr; + } + + @Test + public void testSignatureForMethodWithAnArray() throws NoSuchMethodException { + Method m = MethodImplTest.class.getMethod("arr", String[].class); + String sig = MethodImpl.toSignature(m); + int sep = sig.indexOf("__"); + assert sep > 0 : "Separator found " + sig; + + Enumeration en = MethodImpl.signatureParser(sig.substring(sep + 2)); + + assert en.nextElement() == m.getReturnType() : "Return type is the same"; + assert en.nextElement() == m.getParameterTypes()[0] : "1st param type is the same"; + } +} \ No newline at end of file diff -r d443b6c05a01 -r 41e1dc88a399 ide/editor/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ide/editor/pom.xml Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,198 @@ + + + 4.0.0 + + ide + org.apidesign.bck2brwsr + 0.3-SNAPSHOT + + + org.apidesign.bck2brwsr.ide.editor + editor + 0.3-SNAPSHOT + nbm + + Editor Support for Bck2Brwsr + + + UTF-8 + RELEASE72 + ${project.build.directory}/endorsed + + + + + + netbeans + NetBeans + http://bits.netbeans.org/maven2/ + + false + + + + + + + org.netbeans.api + org-netbeans-api-annotations-common + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-java-source + ${netbeans.version} + + + org.netbeans.api + org-netbeans-libs-javacapi + ${netbeans.version} + + + org.netbeans.api + org-netbeans-spi-java-hints + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-parsing-api + ${netbeans.version} + + + org.netbeans.api + org-netbeans-spi-editor-hints + ${netbeans.version} + + + org.netbeans.api + org-openide-util + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-java-lexer + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-lexer + ${netbeans.version} + + + org.apidesign.bck2brwsr + core + 0.3-SNAPSHOT + jar + test + + + org.netbeans.api + org-netbeans-modules-java-hints-test + ${netbeans.version} + test + + + org.netbeans.api + org-netbeans-libs-junit4 + ${netbeans.version} + test + + + org.netbeans.modules + org-netbeans-lib-nbjavac + ${netbeans.version} + test + + + org.testng + testng + test + + + + + + + org.codehaus.mojo + nbm-maven-plugin + 3.8 + true + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.5.1 + + 1.6 + 1.6 + + ${endorsed.dir} + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + true + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + endorsed + validate + + copy + + + + + ${endorsed.dir} + true + + + org.netbeans.api + org-netbeans-libs-javacapi + ${netbeans.version} + + + org.netbeans.external + nb-javac-api + ${netbeans.version} + + + org.netbeans.modules + org-netbeans-libs-javacimpl + ${netbeans.version} + + + org.netbeans.external + nb-javac-impl + ${netbeans.version} + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + -Djava.endorsed.dirs=${endorsed.dir} + + + + + diff -r d443b6c05a01 -r 41e1dc88a399 ide/editor/src/main/java/org/apidesign/bck2brwsr/ide/editor/JNIHelper.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ide/editor/src/main/java/org/apidesign/bck2brwsr/ide/editor/JNIHelper.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,80 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.ide.editor; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +/** + * JNI Helper. + * To facilitate lookup of methods by name and signature, instead of manually parsing signatures, + * constructs the map of all methods and uses Class.getName() to generate almost-correct signatures. + */ +class JNIHelper { + + static Method method(String clazz, String method, String signature) { + final Map methods = methodMap(JNIHelper.clazz(clazz)); + return methods.get(methodKey(method, signature)); + } + + static Class clazz(String clazz) { + try { + return Class.forName(clazz); + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException(e); + } + } + + static Map methodMap(final Class clazz) { + final Map map = new HashMap(); + final Method[] methods = clazz.getDeclaredMethods(); + for (int i = 0; i < methods.length; i++) { + final Method method = methods[i]; + map.put(methodKey(method.getName(), signature(method)), method); + } + return map; + } + + static String methodKey(String method, String signature) { + return method + '@' + signature; + } + + static String signature(final Method method) { + final Class[] parameterTypes = method.getParameterTypes(); + final StringBuilder b = new StringBuilder(); + for (int j = 0; j < parameterTypes.length; j++) { + b.append(signature(parameterTypes[j])); + } + return b.toString(); + } + + static String signature(final Class clazz) { + if (clazz == boolean.class) return "Z"; + else if (clazz == byte.class) return "B"; + else if (clazz == char.class) return "C"; + else if (clazz == double.class) return "D"; + else if (clazz == float.class) return "F"; + else if (clazz == int.class) return "I"; + else if (clazz == long.class) return "J"; + else if (clazz == short.class) return "S"; + else if (clazz == void.class) return "V"; + else if (clazz.isArray()) return clazz.getName().replace('.','/'); + else return "L" + clazz.getName().replace('.','/') + ";"; + } +} diff -r d443b6c05a01 -r 41e1dc88a399 ide/editor/src/main/java/org/apidesign/bck2brwsr/ide/editor/JSEmbeddingProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ide/editor/src/main/java/org/apidesign/bck2brwsr/ide/editor/JSEmbeddingProvider.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,188 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.ide.editor; + +import com.sun.source.tree.AnnotationTree; +import com.sun.source.tree.AssignmentTree; +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.LiteralTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.util.SourcePositions; +import com.sun.source.util.TreePath; +import com.sun.source.util.TreePathScanner; +import com.sun.source.util.Trees; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.lang.model.element.TypeElement; +import javax.swing.text.Document; +import org.netbeans.api.editor.mimelookup.MimeRegistration; +import org.netbeans.api.java.source.CompilationInfo; +import org.netbeans.api.java.source.JavaParserResultTask; +import org.netbeans.api.java.source.JavaSource; +import org.netbeans.api.lexer.Language; +import org.netbeans.api.lexer.TokenHierarchy; +import org.netbeans.api.lexer.TokenSequence; +import org.netbeans.modules.parsing.api.Snapshot; +import org.netbeans.modules.parsing.spi.Parser; +import org.netbeans.modules.parsing.spi.Scheduler; +import org.netbeans.modules.parsing.spi.SchedulerEvent; +import org.netbeans.modules.parsing.spi.SchedulerTask; +import org.netbeans.modules.parsing.spi.TaskFactory; +import org.openide.util.Exceptions; + +/** + * + * @author Tomas Zezula + */ +public final class JSEmbeddingProvider extends JavaParserResultTask { + + private static final int PRIORITY = 1000; + private static final String JS_ANNOTATION = "org.apidesign.bck2brwsr.core.JavaScriptBody"; //NOI18N + private static final String BODY = "body"; //NOI18N + private static final String JAVA_MIME_TYPE = "text/x-java"; //NOI18N + private static final String JAVASCRIPT_MIME_TYPE = "text/javascript"; //NOI18N + private final AtomicBoolean canceled = new AtomicBoolean(); + + private JSEmbeddingProvider() { + super(JavaSource.Phase.ELEMENTS_RESOLVED); + } + + @Override + public int getPriority() { + return PRIORITY; + } + + @Override + public void cancel() { + canceled.set(true); + } + + @Override + public Class getSchedulerClass() { + return Scheduler.EDITOR_SENSITIVE_TASK_SCHEDULER; + } + + @Override + public void run(Parser.Result t, SchedulerEvent se) { + canceled.set(false); + final CompilationInfo ci = CompilationInfo.get(t); + final CompilationUnitTree cu = ci.getCompilationUnit(); + final Trees trees = ci.getTrees(); + final SourcePositions sp = trees.getSourcePositions(); + final Finder f = new Finder(trees); + final List result = new ArrayList(); + f.scan(cu, result); + if (!result.isEmpty()) { + try { + final TokenHierarchy tk = TokenHierarchy.get(ci.getDocument()); + final Language java = Language.find(JAVA_MIME_TYPE); + final Language javaScript = Language.find(JAVASCRIPT_MIME_TYPE); + if (java != null && javaScript != null) { + final TokenSequence seq = tk.tokenSequence(java); + if (seq != null) { + for (LiteralTree lt : result) { + final int start = (int) sp.getStartPosition(cu, lt); + final int end = (int) sp.getEndPosition(cu, lt); + seq.move(start); + while (seq.moveNext() && seq.offset() < end) { + seq.createEmbedding(javaScript, 1, 1, true); + } + } + } + } + } catch (IOException ioe) { + Exceptions.printStackTrace(ioe); + } + } + } + + + + + private static final class Finder extends TreePathScanner> { + + private final Trees trees; + private CompilationUnitTree cu; + private boolean inEmbedding; + + Finder(final Trees trees) { + this.trees = trees; + } + + @Override + public Void visitCompilationUnit( + final CompilationUnitTree unit, + final List p) { + this.cu = unit; + return super.visitCompilationUnit(unit, p); + } + + + + @Override + public Void visitMethod( + final MethodTree m, + final List p) { + for (AnnotationTree a : m.getModifiers().getAnnotations()) { + final TypeElement ae = (TypeElement) trees.getElement(TreePath.getPath(cu, a.getAnnotationType())); + if (ae != null && JS_ANNOTATION.contentEquals(ae.getQualifiedName())) { + final List args = a.getArguments(); + for (ExpressionTree kvp : args) { + if (kvp instanceof AssignmentTree) { + final AssignmentTree assignemt = (AssignmentTree) kvp; + if (BODY.equals(assignemt.getVariable().toString())) { + inEmbedding = true; + try { + scan(assignemt.getExpression(), p); + } finally { + inEmbedding = false; + } + } + } + } + } + } + return null; + } + + @Override + public Void visitLiteral(LiteralTree node, List p) { + if (inEmbedding) { + p.add(node); + } + return super.visitLiteral(node, p); + } + + } + + @MimeRegistration( + service = TaskFactory.class, + mimeType = JAVA_MIME_TYPE) + public static final class Factory extends TaskFactory { + @Override + public Collection create(Snapshot snpsht) { + return Collections.singleton(new JSEmbeddingProvider()); + } + } + +} diff -r d443b6c05a01 -r 41e1dc88a399 ide/editor/src/main/java/org/apidesign/bck2brwsr/ide/editor/JSNI2JavaScriptBody.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ide/editor/src/main/java/org/apidesign/bck2brwsr/ide/editor/JSNI2JavaScriptBody.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,149 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.ide.editor; + +import com.sun.source.tree.AnnotationTree; +import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.LiteralTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.Tree.Kind; +import com.sun.source.tree.VariableTree; +import com.sun.source.util.TreePath; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.netbeans.api.java.lexer.JavaTokenId; +import static org.netbeans.api.java.lexer.JavaTokenId.BLOCK_COMMENT; +import static org.netbeans.api.java.lexer.JavaTokenId.JAVADOC_COMMENT; +import static org.netbeans.api.java.lexer.JavaTokenId.LINE_COMMENT; +import static org.netbeans.api.java.lexer.JavaTokenId.WHITESPACE; +import org.netbeans.api.java.source.CompilationInfo; +import org.netbeans.api.java.source.TreeMaker; +import org.netbeans.api.lexer.Token; +import org.netbeans.api.lexer.TokenSequence; +import org.netbeans.spi.editor.hints.ErrorDescription; +import org.netbeans.spi.editor.hints.Fix; +import org.netbeans.spi.java.hints.ErrorDescriptionFactory; +import org.netbeans.spi.java.hints.Hint; +import org.netbeans.spi.java.hints.HintContext; +import org.netbeans.spi.java.hints.JavaFix; +import org.netbeans.spi.java.hints.TriggerTreeKind; +import org.openide.util.NbBundle.Messages; + +@Hint(displayName = "#DN_JSNI2JavaScriptBody", description = "#DESC_JSNI2JavaScriptBody", category = "general") +@Messages({ + "DN_JSNI2JavaScriptBody=JSNI to @JavaScriptBody", + "DESC_JSNI2JavaScriptBody=JSNI to @JavaScriptBody" +}) +public class JSNI2JavaScriptBody { + + @TriggerTreeKind(Kind.METHOD) + @Messages("ERR_JSNI2JavaScriptBody=Can convert JSNI to @JavaScriptBody") + public static ErrorDescription computeWarning(final HintContext ctx) { + Token token = findBlockToken(ctx.getInfo(), ctx.getPath(), ctx); + + if (token == null) { + return null; + } + + Fix fix = new FixImpl(ctx.getInfo(), ctx.getPath()).toEditorFix(); + return ErrorDescriptionFactory.forName(ctx, ctx.getPath(), Bundle.ERR_JSNI2JavaScriptBody(), fix); + } + + private static Token findBlockToken(CompilationInfo info, TreePath path, HintContext ctx) { + int end = (int) info.getTrees().getSourcePositions().getEndPosition(path.getCompilationUnit(), path.getLeaf()); + TokenSequence ts = info.getTokenHierarchy().tokenSequence(JavaTokenId.language()); + + if (ts == null) return null; + + ts.move(end); + + if ((ctx != null && ctx.isCanceled()) || !ts.movePrevious() || ts.token().id() != JavaTokenId.SEMICOLON) return null; + + OUTER: while (ts.movePrevious()) { + if (ctx != null && ctx.isCanceled()) return null; + + switch (ts.token().id()) { + case WHITESPACE: break; + case LINE_COMMENT: break; + case JAVADOC_COMMENT: break; + case BLOCK_COMMENT: + final CharSequence tok = ts.token().text(); + final int l = tok.length(); + if (l > 4 + && tok.subSequence(0, 4).toString().equals("/*-{") // NOI18N + && tok.subSequence(l - 4, l).toString().equals("}-*/") // NOI18N + ) { + return ts.offsetToken(); + } + break; + default: + break OUTER; + } + } + + return null; + } + + private static final class FixImpl extends JavaFix { + + public FixImpl(CompilationInfo info, TreePath tp) { + super(info, tp); + } + + @Override + @Messages("FIX_JSNI2JavaScriptBody=Convert JSNI to @JavaScriptBody") + protected String getText() { + return Bundle.FIX_JSNI2JavaScriptBody(); + } + + @Override + protected void performRewrite(TransformationContext ctx) { + Token jsniComment = findBlockToken(ctx.getWorkingCopy(), ctx.getPath(), null); + + if (jsniComment == null) { + //XXX: warn? + return ; + } + + JsniCommentTokenizer tok = new JsniCommentTokenizer(); + ManglingSink ms = new ManglingSink(); + final CharSequence cmnt = jsniComment.text(); + tok.process(cmnt.subSequence(4, cmnt.length() - 4), ms); + + TreeMaker make = ctx.getWorkingCopy().getTreeMaker(); + MethodTree mt = (MethodTree) ctx.getPath().getLeaf(); + List params = new ArrayList(); + + for (VariableTree p : mt.getParameters()) { + params.add(make.Literal(p.getName().toString())); + } + + AnnotationTree jsBody = make.Annotation(make.QualIdent("org.apidesign.bck2brwsr.core.JavaScriptBody"), + Arrays.asList( + make.Assignment(make.Identifier("args"), make.NewArray(null, Collections.emptyList(), params)), + make.Assignment(make.Identifier("body"), make.Literal(ms.out.toString())) + ) + ); + + + ctx.getWorkingCopy().rewrite(mt.getModifiers(), make.addModifiersAnnotation(mt.getModifiers(), jsBody)); + } + } +} diff -r d443b6c05a01 -r 41e1dc88a399 ide/editor/src/main/java/org/apidesign/bck2brwsr/ide/editor/JsniCommentTokenizer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ide/editor/src/main/java/org/apidesign/bck2brwsr/ide/editor/JsniCommentTokenizer.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,70 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.ide.editor; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +final class JsniCommentTokenizer { + + /** + * Tokenize the contents of JSNI comment into the provided {@linkplain Sink}. + * @param in the contents of JSNI comment + * @param out the sink that consumes parsed tokens + */ + public void process(final CharSequence in, final Sink out) { + final Matcher member = Pattern.compile("@([^:]+)::([a-zA-Z_$][a-zA-Z\\d_$]*)").matcher(in); + final Matcher signature = Pattern.compile("\\(([^\\)]*)\\)").matcher(in); + + int i = 0; + while (true) { + if (member.find(i)) { + final int memberStart = member.start(); + final int memberEnd = member.end(); + if (memberStart > i) out.javascript(in.subSequence(i, memberStart).toString()); + + final String clazz = member.group(1); + final String name = member.group(2); + + if (in.charAt(memberEnd) == '(') { + if (!signature.find(memberEnd)) { + throw new IllegalStateException("Expected method signature"); + } + assert signature.start() == memberEnd; + out.method(clazz, name, signature.group(1)); + i = signature.end(); + } else { + out.field(clazz, name); + i = memberEnd; + } + } else { + out.javascript(in.subSequence(i, in.length()).toString()); + break; + } + } + } + + + static interface Sink { + void javascript(String s); + + void method(String clazz, String method, String signature); + + void field(String clazz, String field); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 ide/editor/src/main/java/org/apidesign/bck2brwsr/ide/editor/ManglingSink.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ide/editor/src/main/java/org/apidesign/bck2brwsr/ide/editor/ManglingSink.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,71 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.ide.editor; + +/** + * An implementation of {@linkplain JsniCommentTokenizer.Sink} that generates B2B + */ +class ManglingSink implements JsniCommentTokenizer.Sink { + + final StringBuilder out = new StringBuilder(); + + public void javascript(String s) { + out.append(s); + } + + public void method(String clazz, String method, String signature) { + out.append(mangle(clazz, method, signature)); + } + + public void field(String clazz, String field) { +// out.append(field); + out.append('_').append(field).append('(').append(')'); + } + + + @Override + public String toString() { + return out.toString(); + } + + + static String mangle(String clazz, String method, String signature) { + final StringBuilder builder = new StringBuilder(); + builder.append(method); + builder.append("__"); + builder.append(mangle(JNIHelper.signature(JNIHelper.method(clazz, method, signature).getReturnType()))); + builder.append(mangle(signature)); + return builder.toString(); + } + + + static String mangle(String txt) { + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < txt.length(); i++) { + final char ch = txt.charAt(i); + switch (ch) { + case '/': sb.append('_'); break; + case '_': sb.append("_1"); break; + case ';': sb.append("_2"); break; + case '[': sb.append("_3"); break; + default: sb.append(ch); break; + } + } + return sb.toString(); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 ide/editor/src/main/nbm/manifest.mf --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ide/editor/src/main/nbm/manifest.mf Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 +OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/jackpot30/test/hints/Bundle.properties diff -r d443b6c05a01 -r 41e1dc88a399 ide/editor/src/main/resources/org/netbeans/modules/jackpot30/test/hints/Bundle.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ide/editor/src/main/resources/org/netbeans/modules/jackpot30/test/hints/Bundle.properties Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,21 @@ +# +# Back 2 Browser Bytecode Translator +# Copyright (C) 2012 Jaroslav Tulach +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. Look for COPYING file in the top folder. +# If not, see http://opensource.org/licenses/GPL-2.0. +# + +OpenIDE-Module-Name=Bck2Brwsr Editor Support +OpenIDE-Module-Short-Description=Provides hints, coloring, etc. for the bck2brwsr.apidesign.org project +OpenIDE-Module-Display-Category=Java diff -r d443b6c05a01 -r 41e1dc88a399 ide/editor/src/test/java/org/apidesign/bck2brwsr/ide/editor/DejsniReaderTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ide/editor/src/test/java/org/apidesign/bck2brwsr/ide/editor/DejsniReaderTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,84 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.ide.editor; + +import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.netbeans.modules.java.hints.test.api.HintTest; +import org.openide.filesystems.FileUtil; +import org.testng.annotations.Test; + +public class DejsniReaderTest { + + @Test + public void test1() throws Exception { + String s = "class Test {\n" + + " /** javadoc */\n" + + " public native void test() /*-{\n" + + " // body\n" + + " }-*/;\n" + + "}\n"; + + String expected = " import org.apidesign.bck2brwsr.core.JavaScriptBody;\n" + + "class Test {\n" + + "\n" + + " /** javadoc */\n" + + " @JavaScriptBody(args = {}, body = \"\\n // body\\n \")\n" + + " public native void test();\n" + + "}\n"; + + HintTest.create() + .input(s) + .classpath(FileUtil.getArchiveRoot(JavaScriptBody.class.getProtectionDomain().getCodeSource().getLocation())) + .run(JSNI2JavaScriptBody.class) + .findWarning("2:23-2:27:verifier:" + Bundle.ERR_JSNI2JavaScriptBody()) + .applyFix() + .assertCompilable() + .assertOutput(expected); + } + + + @Test + public void test2() throws Exception { + String s = "class Test {\n" + + " /** javadoc */\n" + + " @SuppressWarnings(\"unused\")\n" + + " // comment\n" + + " public native void test() /*-{\n" + + " // body\n" + + " }-*/;\n" + + "}\n"; + + String expected = " import org.apidesign.bck2brwsr.core.JavaScriptBody;\n" + + "class Test {\n" + + "\n" + + " /** javadoc */\n" + + " @SuppressWarnings(\"unused\")\n" + + " // comment\n" + + " @JavaScriptBody(args = {}, body = \"\\n // body\\n \")\n" + + " public native void test();\n" + + "}\n"; + HintTest.create() + .input(s) + .classpath(FileUtil.getArchiveRoot(JavaScriptBody.class.getProtectionDomain().getCodeSource().getLocation())) + .run(JSNI2JavaScriptBody.class) + .findWarning("4:23-4:27:verifier:" + Bundle.ERR_JSNI2JavaScriptBody()) + .applyFix() + .assertCompilable() + .assertOutput(expected); + } +} \ No newline at end of file diff -r d443b6c05a01 -r 41e1dc88a399 ide/editor/src/test/java/org/apidesign/bck2brwsr/ide/editor/JSNI2JavaScriptBodyTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ide/editor/src/test/java/org/apidesign/bck2brwsr/ide/editor/JSNI2JavaScriptBodyTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,46 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.ide.editor; + +import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.netbeans.modules.java.hints.test.api.HintTest; +import org.openide.filesystems.FileUtil; +import org.testng.annotations.Test; + +public class JSNI2JavaScriptBodyTest { + + @Test + public void testFixWorking() throws Exception { + HintTest.create() + .input("package test;\n" + + "public class Test {\n" + + " public native void run(int a) /*-{ this.a = a; }-*/;\n" + + "}\n") + .classpath(FileUtil.getArchiveRoot(JavaScriptBody.class.getProtectionDomain().getCodeSource().getLocation())) + .run(JSNI2JavaScriptBody.class) + .findWarning("2:23-2:26:verifier:" + Bundle.ERR_JSNI2JavaScriptBody()) + .applyFix() + .assertCompilable() + .assertOutput("package test;\n" + + "import org.apidesign.bck2brwsr.core.JavaScriptBody;\n" + + "public class Test {\n" + + " @JavaScriptBody(args = {\"a\"}, body = \" this.a = a; \")\n" + + " public native void run(int a);\n" + + "}\n"); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 ide/editor/src/test/java/org/apidesign/bck2brwsr/ide/editor/JsniCommentTokenizerTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ide/editor/src/test/java/org/apidesign/bck2brwsr/ide/editor/JsniCommentTokenizerTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,154 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.ide.editor; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class JsniCommentTokenizerTest { + + private static class MockSink implements JsniCommentTokenizer.Sink { + final List out = new ArrayList(); + + public void javascript(String s) { + out.add("J " + s); + } + + public void method(String clazz, String method, String signature) { + out.add("M " + clazz + "|" + method + "|" + signature); + } + + public void field(String clazz, String field) { + out.add("F " + clazz + "|" + field); + } + } + + + @Test + public void testProcess_nop() throws IOException { + final String in = "foo bar"; + final List expected = new ArrayList(); + expected.add("J foo bar"); + + final JsniCommentTokenizer jsniCommentTokenizer = new JsniCommentTokenizer(); + final MockSink out = new MockSink(); + jsniCommentTokenizer.process(in, out); + + Assert.assertEquals(expected, out.out); + } + + @Test + public void testProcess_read_static_field() throws IOException { + final String in = " @com.google.gwt.examples.JSNIExample::myStaticField = val + \" and stuff\";"; + final List expected = new ArrayList(); + expected.add("J "); + expected.add("F com.google.gwt.examples.JSNIExample|myStaticField"); + expected.add("J = val + \" and stuff\";"); + + final JsniCommentTokenizer jsniCommentTokenizer = new JsniCommentTokenizer(); + final MockSink out = new MockSink(); + jsniCommentTokenizer.process(in, out); + + Assert.assertEquals(expected, out.out); + } + + @Test + public void testProcess_write_instance_field() throws IOException { + final String in = " x.@com.google.gwt.examples.JSNIExample::myInstanceField = val + \" and stuff\";"; + final List expected = new ArrayList(); + expected.add("J x."); + expected.add("F com.google.gwt.examples.JSNIExample|myInstanceField"); + expected.add("J = val + \" and stuff\";"); + + final JsniCommentTokenizer jsniCommentTokenizer = new JsniCommentTokenizer(); + final MockSink out = new MockSink(); + jsniCommentTokenizer.process(in, out); + + Assert.assertEquals(expected, out.out); + } + + @Test + public void testProcess_read_instance_field() throws IOException { + final String in = " var val = this.@com.google.gwt.examples.JSNIExample::myInstanceField;"; + final List expected = new ArrayList(); + expected.add("J var val = this."); + expected.add("F com.google.gwt.examples.JSNIExample|myInstanceField"); + expected.add("J ;"); + + final JsniCommentTokenizer jsniCommentTokenizer = new JsniCommentTokenizer(); + final MockSink out = new MockSink(); + jsniCommentTokenizer.process(in, out); + + Assert.assertEquals(expected, out.out); + } + + + @Test + public void testProcess_static_method() throws IOException { + final String in = " @com.google.gwt.examples.JSNIExample::staticFoo(Ljava/lang/String;)(s);"; + final List expected = new ArrayList(); + expected.add("J "); + expected.add("M com.google.gwt.examples.JSNIExample|staticFoo|Ljava/lang/String;"); + expected.add("J (s);"); + + final JsniCommentTokenizer jsniCommentTokenizer = new JsniCommentTokenizer(); + final MockSink out = new MockSink(); + jsniCommentTokenizer.process(in, out); + + Assert.assertEquals(expected, out.out); + } + + + @Test + public void testProcess_instance_method() throws IOException { + final String in = " x.@com.google.gwt.examples.JSNIExample::instanceFoo(Ljava/lang/String;)(s);"; + final List expected = new ArrayList(); + expected.add("J x."); + expected.add("M com.google.gwt.examples.JSNIExample|instanceFoo|Ljava/lang/String;"); + expected.add("J (s);"); + + final JsniCommentTokenizer jsniCommentTokenizer = new JsniCommentTokenizer(); + final MockSink out = new MockSink(); + jsniCommentTokenizer.process(in, out); + + Assert.assertEquals(expected, out.out); + } + + + @Test + public void testProcess_multiline() throws IOException { + final String in = + " x.@com.google.gwt.examples.JSNIExample::instanceFoo(Ljava/lang/String;)(s);" + + " @com.google.gwt.examples.JSNIExample::myStaticField = val + \" and stuff\";"; + final List expected = new ArrayList(); + expected.add("J x."); + expected.add("M com.google.gwt.examples.JSNIExample|instanceFoo|Ljava/lang/String;"); + expected.add("J (s); "); + expected.add("F com.google.gwt.examples.JSNIExample|myStaticField"); + expected.add("J = val + \" and stuff\";"); + + final JsniCommentTokenizer jsniCommentTokenizer = new JsniCommentTokenizer(); + final MockSink out = new MockSink(); + jsniCommentTokenizer.process(in, out); + + Assert.assertEquals(expected, out.out); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 ide/editor/src/test/java/org/apidesign/bck2brwsr/ide/editor/ManglingSinkTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ide/editor/src/test/java/org/apidesign/bck2brwsr/ide/editor/ManglingSinkTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,58 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.ide.editor; + +import org.testng.Assert; +import org.testng.annotations.Test; + + +public class ManglingSinkTest { + + @Test + public void testMangle_1() { + Assert.assertEquals( + "binarySearch__I_3BIIB", + ManglingSink.mangle("java.util.Arrays", "binarySearch", "[BIIB") + ); + } + + @Test + public void testMangle_2() { + Assert.assertEquals( + "sort__V_3I", + ManglingSink.mangle("java.util.Arrays", "sort", "[I") + ); + } + + @Test + public void testMangle_3() { + Assert.assertEquals( + "binarySearch__I_3Ljava_lang_Object_2IILjava_lang_Object_2", + ManglingSink.mangle("java.util.Arrays", "binarySearch", "[Ljava/lang/Object;IILjava/lang/Object;") + ); + } + + + @Test + public void testField() { + final ManglingSink manglingSink = new ManglingSink(); + manglingSink.field(null, "value"); + + Assert.assertEquals("_value()", manglingSink.toString()); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 ide/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ide/pom.xml Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,17 @@ + + + 4.0.0 + + bck2brwsr + org.apidesign + 0.3-SNAPSHOT + + org.apidesign.bck2brwsr + ide + 0.3-SNAPSHOT + pom + IDE Support + + editor + + diff -r d443b6c05a01 -r 41e1dc88a399 javap/src/main/java/org/apidesign/javap/AnnotationParser.java --- a/javap/src/main/java/org/apidesign/javap/AnnotationParser.java Fri Jan 25 15:08:24 2013 +0100 +++ b/javap/src/main/java/org/apidesign/javap/AnnotationParser.java Sun Feb 17 17:58:34 2013 +0100 @@ -35,22 +35,37 @@ */ public class AnnotationParser { private final boolean textual; + private final boolean iterateArray; - protected AnnotationParser(boolean textual) { + protected AnnotationParser(boolean textual, boolean iterateArray) { this.textual = textual; + this.iterateArray = iterateArray; } - protected void visitAnnotationStart(String type) throws IOException { + protected void visitAnnotationStart(String type, boolean top) throws IOException { } - protected void visitAnnotationEnd(String type) throws IOException { + protected void visitAnnotationEnd(String type, boolean top) throws IOException { } + + protected void visitValueStart(String attrName, char type) throws IOException { + } + + protected void visitValueEnd(String attrName, char type) throws IOException { + } + protected void visitAttr( String annoType, String attr, String attrType, String value ) throws IOException { } + protected void visitEnumAttr( + String annoType, String attr, String attrType, String value + ) throws IOException { + visitAttr(annoType, attr, attrType, value); + } + /** Initialize the parsing with constant pool from cd. * * @param attr the attribute defining annotations @@ -70,30 +85,32 @@ private void read(DataInputStream dis, ClassData cd) throws IOException { int cnt = dis.readUnsignedShort(); for (int i = 0; i < cnt; i++) { - readAnno(dis, cd); + readAnno(dis, cd, true); } } - private void readAnno(DataInputStream dis, ClassData cd) throws IOException { + private void readAnno(DataInputStream dis, ClassData cd, boolean top) throws IOException { int type = dis.readUnsignedShort(); String typeName = cd.StringValue(type); - visitAnnotationStart(typeName); + visitAnnotationStart(typeName, top); int cnt = dis.readUnsignedShort(); for (int i = 0; i < cnt; i++) { String attrName = cd.StringValue(dis.readUnsignedShort()); readValue(dis, cd, typeName, attrName); } - visitAnnotationEnd(typeName); + visitAnnotationEnd(typeName, top); if (cnt == 0) { visitAttr(typeName, null, null, null); } } - private void readValue(DataInputStream dis, ClassData cd, String typeName, String attrName) - throws IOException { + private void readValue( + DataInputStream dis, ClassData cd, String typeName, String attrName + ) throws IOException { char type = (char)dis.readByte(); + visitValueStart(attrName, type); if (type == '@') { - readAnno(dis, cd); + readAnno(dis, cd, false); } else if ("CFJZsSIDB".indexOf(type) >= 0) { // NOI18N int primitive = dis.readUnsignedShort(); String val = cd.stringValue(primitive, textual); @@ -112,13 +129,17 @@ } else if (type == '[') { int cnt = dis.readUnsignedShort(); for (int i = 0; i < cnt; i++) { - readValue(dis, cd, typeName, attrName); + readValue(dis, cd, typeName, iterateArray ? attrName : null); } } else if (type == 'e') { int enumT = dis.readUnsignedShort(); + String attrType = cd.stringValue(enumT, textual); int enumN = dis.readUnsignedShort(); + String val = cd.stringValue(enumN, textual); + visitEnumAttr(typeName, attrName, attrType, val); } else { throw new IOException("Unknown type " + type); } + visitValueEnd(attrName, type); } } diff -r d443b6c05a01 -r 41e1dc88a399 javaquery/api/pom.xml --- a/javaquery/api/pom.xml Fri Jan 25 15:08:24 2013 +0100 +++ b/javaquery/api/pom.xml Sun Feb 17 17:58:34 2013 +0100 @@ -43,24 +43,20 @@ org.netbeans.api org-openide-util-lookup + provided org.apidesign.bck2brwsr - core - 0.3-SNAPSHOT + emul + ${project.version} + rt jar - - - org.apidesign.bck2brwsr - emul.mini - 0.3-SNAPSHOT - jar - runtime + compile org.apidesign.bck2brwsr vm4brwsr - 0.3-SNAPSHOT + ${project.version} jar test diff -r d443b6c05a01 -r 41e1dc88a399 javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Canvas.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Canvas.java Fri Jan 25 15:08:24 2013 +0100 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Canvas.java Sun Feb 17 17:58:34 2013 +0100 @@ -47,7 +47,7 @@ @JavaScriptBody( args = {"el"}, - body = "var e = window.document.getElementById(el.fld_id);\n" + body = "var e = window.document.getElementById(el._id());\n" + "return e.getContext('2d');\n") private native static Object getContextImpl(Canvas el); diff -r d443b6c05a01 -r 41e1dc88a399 javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java Fri Jan 25 15:08:24 2013 +0100 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java Sun Feb 17 17:58:34 2013 +0100 @@ -41,21 +41,21 @@ @JavaScriptBody( args={"el", "property", "value"}, - body="var e = window.document.getElementById(el.fld_id);\n" + body="var e = window.document.getElementById(el._id());\n" + "e[property] = value;\n" ) static native void setAttribute(Element el, String property, Object value); @JavaScriptBody( args={"el", "property"}, - body="var e = window.document.getElementById(el.fld_id);\n" + body="var e = window.document.getElementById(el._id());\n" + "return e[property];\n" ) static native Object getAttribute(Element el, String property); @JavaScriptBody( args={"el"}, - body="return window.document.getElementById(el.fld_id);" + body="return window.document.getElementById(el._id());" ) static native Object getElementById(Element el); @@ -65,8 +65,8 @@ */ @JavaScriptBody( args={ "ev", "r" }, - body="var e = window.document.getElementById(this.fld_id);\n" - + "e[ev.fld_id] = function() { r.run__V(); };\n" + body="var e = window.document.getElementById(this._id());\n" + + "e[ev._id()] = function() { r.run__V(); };\n" ) final void on(OnEvent ev, Runnable r) { } diff -r d443b6c05a01 -r 41e1dc88a399 javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/GraphicsContext.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/GraphicsContext.java Fri Jan 25 15:08:24 2013 +0100 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/GraphicsContext.java Sun Feb 17 17:58:34 2013 +0100 @@ -32,7 +32,7 @@ } @JavaScriptBody(args = {"centerx", "centery", "radius", "startangle", "endangle", "ccw"}, - body = "this.fld_context.arc(centerx,centery, radius, startangle, endangle,ccw);") + body = "this._context().arc(centerx,centery, radius, startangle, endangle,ccw);") public native void arc(double centerX, double centerY, double startAngle, @@ -41,7 +41,7 @@ boolean ccw); @JavaScriptBody(args = {"x1", "y1", "x2", "y2", "r"}, - body = "this.fld_context.arcTo(x1,y1,x2,y2,r);") + body = "this._context().arcTo(x1,y1,x2,y2,r);") public native void arcTo(double x1, double y1, double x2, @@ -49,68 +49,68 @@ double r); @JavaScriptBody(args = {"x", "y"}, - body = "return this.fld_context.isPointInPath(x,y);") + body = "return this._context().isPointInPath(x,y);") public native boolean isPointInPath(double x, double y); - @JavaScriptBody(args = {}, body = "this.fld_context.fill();") + @JavaScriptBody(args = {}, body = "this._context().fill();") public native void fill(); - @JavaScriptBody(args = {}, body = "this.fld_context.stroke();") + @JavaScriptBody(args = {}, body = "this._context().stroke();") public native void stroke(); - @JavaScriptBody(args = {}, body = "this.fld_context.beginPath();") + @JavaScriptBody(args = {}, body = "this._context().beginPath();") public native void beginPath(); - @JavaScriptBody(args = {}, body = "this.fld_context.closePath();") + @JavaScriptBody(args = {}, body = "this._context().closePath();") public native void closePath(); - @JavaScriptBody(args = {}, body = "this.fld_context.clip();") + @JavaScriptBody(args = {}, body = "this._context().clip();") public native void clip(); - @JavaScriptBody(args = {"x", "y"}, body = "this.fld_context.moveTo(x,y);") + @JavaScriptBody(args = {"x", "y"}, body = "this._context().moveTo(x,y);") public native void moveTo(double x, double y); - @JavaScriptBody(args = {"x", "y"}, body = "this.fld_context.lineTo(x,y);") + @JavaScriptBody(args = {"x", "y"}, body = "this._context().lineTo(x,y);") public native void lineTo(double x, double y); - @JavaScriptBody(args = {"cpx", "cpy", "x", "y"}, body = "this.fld_context.quadraticCurveTo(cpx,cpy,x,y);") + @JavaScriptBody(args = {"cpx", "cpy", "x", "y"}, body = "this._context().quadraticCurveTo(cpx,cpy,x,y);") public native void quadraticCurveTo(double cpx, double cpy, double x, double y); @JavaScriptBody(args = {"cp1x", "cp1y", "cp2x", "cp2y", "x", "y"}, - body = "this.fld_context.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y);") + body = "this._context().bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y);") public native void bezierCurveTo(double cp1x, double cp1y, double cp2x, double cp2y, double x, double y); - @JavaScriptBody(args = {"x", "y", "width", "height"}, body = "this.fld_context.fillRect(x,y,width,height);") + @JavaScriptBody(args = {"x", "y", "width", "height"}, body = "this._context().fillRect(x,y,width,height);") public native void fillRect(double x, double y, double width, double height); - @JavaScriptBody(args = {"x", "y", "width", "height"}, body = "this.fld_context.strokeRect(x,y,width,height);") + @JavaScriptBody(args = {"x", "y", "width", "height"}, body = "this._context().strokeRect(x,y,width,height);") public native void strokeRect(double x, double y, double width, double height); - @JavaScriptBody(args = {"x", "y", "width", "height"}, body = "this.fld_context.clearRect(x,y,width,height);") + @JavaScriptBody(args = {"x", "y", "width", "height"}, body = "this._context().clearRect(x,y,width,height);") public native void clearRect(double x, double y, double width, double height); - @JavaScriptBody(args = {"x", "y", "width", "height"}, body = "this.fld_context.rectect(x,y,width,height);") + @JavaScriptBody(args = {"x", "y", "width", "height"}, body = "this._context().rectect(x,y,width,height);") public native void rect(double x, double y, double width, double height); - @JavaScriptBody(args = {}, body = "this.fld_context.save();") + @JavaScriptBody(args = {}, body = "this._context().save();") public native void save(); - @JavaScriptBody(args = {}, body = "this.fld_context.restore();") + @JavaScriptBody(args = {}, body = "this._context().restore();") public native void restore(); - @JavaScriptBody(args = {"angle"}, body = "this.fld_context.rotate(angle);") + @JavaScriptBody(args = {"angle"}, body = "this._context().rotate(angle);") public native void rotate(double angle); - @JavaScriptBody(args = {"a", "b", "c", "d", "e", "f"}, body = "this.fld_context.transform(a,b,c,d,e,f);") + @JavaScriptBody(args = {"a", "b", "c", "d", "e", "f"}, body = "this._context().transform(a,b,c,d,e,f);") public native void transform(double a, double b, double c, double d, double e, double f); - @JavaScriptBody(args = {"a", "b", "c", "d", "e", "f"}, body = "this.fld_context.setTransform(a,b,c,d,e,f);") + @JavaScriptBody(args = {"a", "b", "c", "d", "e", "f"}, body = "this._context().setTransform(a,b,c,d,e,f);") public native void setTransform(double a, double b, double c, double d, double e, double f); - @JavaScriptBody(args = {"x", "y"}, body = "this.fld_context.translate(x,y);") + @JavaScriptBody(args = {"x", "y"}, body = "this._context().translate(x,y);") public native void translate(double x, double y); - @JavaScriptBody(args = {"x", "y"}, body = "this.fld_context.scale(x,y);") + @JavaScriptBody(args = {"x", "y"}, body = "this._context().scale(x,y);") public native void scale(double x, double y); public void drawImage(Image image, double x, double y) { @@ -134,10 +134,10 @@ @JavaScriptBody(args = {"ctx", "img", "x", "y"}, body = "ctx.drawImage(img,x,y);") private native static void drawImageImpl(Object ctx, Object img, double x, double y); - @JavaScriptBody(args = {"style"}, body = "this.fld_context.fillStyle=style;") + @JavaScriptBody(args = {"style"}, body = "this._context().fillStyle=style;") public native void setFillStyle(String style); - @JavaScriptBody(args = {}, body = "return this.fld_context.fillStyle;") + @JavaScriptBody(args = {}, body = "return this._context().fillStyle;") public native String getFillStyle(); public void setFillStyle(LinearGradient style) { @@ -155,7 +155,7 @@ @JavaScriptBody(args = {"context","obj"}, body = "context.fillStyle=obj;") private native void setFillStyleImpl(Object context, Object obj); - @JavaScriptBody(args = {"style"}, body = "this.fld_context.strokeStyle=style;") + @JavaScriptBody(args = {"style"}, body = "this._context().strokeStyle=style;") public native void setStrokeStyle(String style); public void setStrokeStyle(LinearGradient style) { @@ -166,7 +166,7 @@ setStrokeStyleImpl(context, style.object()); } - @JavaScriptBody(args = {"style"}, body = "this.fld_context.fillStyle=style;") + @JavaScriptBody(args = {"style"}, body = "this._context().fillStyle=style;") public void setStrokeStyle(Pattern style) { setStrokeStyleImpl(context, style.object()); } @@ -174,79 +174,79 @@ @JavaScriptBody(args = {"context","obj"}, body = "context.strokeStyle=obj;") private native void setStrokeStyleImpl(Object context, Object obj); - @JavaScriptBody(args = {"color"}, body = "this.fld_context.shadowColor=color;") + @JavaScriptBody(args = {"color"}, body = "this._context().shadowColor=color;") public native void setShadowColor(String color); - @JavaScriptBody(args = {"blur"}, body = "this.fld_context.shadowBlur=blur;") + @JavaScriptBody(args = {"blur"}, body = "this._context().shadowBlur=blur;") public native void setShadowBlur(double blur); - @JavaScriptBody(args = {"x"}, body = "this.fld_context.shadowOffsetX=x;") + @JavaScriptBody(args = {"x"}, body = "this._context().shadowOffsetX=x;") public native void setShadowOffsetX(double x); - @JavaScriptBody(args = {"y"}, body = "this.fld_context.shadowOffsetY=y;") + @JavaScriptBody(args = {"y"}, body = "this._context().shadowOffsetY=y;") public native void setShadowOffsetY(double y); - @JavaScriptBody(args = {}, body = "return this.fld_context.strokeStyle;") + @JavaScriptBody(args = {}, body = "return this._context().strokeStyle;") public native String getStrokeStyle(); - @JavaScriptBody(args = {}, body = "return this.fld_context.shadowColor;") + @JavaScriptBody(args = {}, body = "return this._context().shadowColor;") public native String getShadowColor(); - @JavaScriptBody(args = {}, body = "return this.fld_context.shadowBlur;") + @JavaScriptBody(args = {}, body = "return this._context().shadowBlur;") public native double getShadowBlur(); - @JavaScriptBody(args = {}, body = "return this.fld_context.shadowOffsetX;") + @JavaScriptBody(args = {}, body = "return this._context().shadowOffsetX;") public native double getShadowOffsetX(); - @JavaScriptBody(args = {}, body = "return this.fld_context.shadowOffsetY;") + @JavaScriptBody(args = {}, body = "return this._context().shadowOffsetY;") public native double getShadowOffsetY(); - @JavaScriptBody(args = {}, body = "return this.fld_context.lineCap;") + @JavaScriptBody(args = {}, body = "return this._context().lineCap;") public native String getLineCap(); - @JavaScriptBody(args = {"style"}, body = "this.fld_context.lineCap=style;") + @JavaScriptBody(args = {"style"}, body = "this._context().lineCap=style;") public native void setLineCap(String style); - @JavaScriptBody(args = {}, body = "return this.fld_context.lineJoin;") + @JavaScriptBody(args = {}, body = "return this._context().lineJoin;") public native String getLineJoin(); - @JavaScriptBody(args = {"style"}, body = "this.fld_context.lineJoin=style;") + @JavaScriptBody(args = {"style"}, body = "this._context().lineJoin=style;") public native void setLineJoin(String style) ; - @JavaScriptBody(args = {}, body = "return this.fld_context.lineWidth;") + @JavaScriptBody(args = {}, body = "return this._context().lineWidth;") public native double getLineWidth(); - @JavaScriptBody(args = {"width"}, body = "this.fld_context.lineJoin=width;") + @JavaScriptBody(args = {"width"}, body = "this._context().lineJoin=width;") public native void setLineWidth(double width); - @JavaScriptBody(args = {}, body = "return this.fld_context.miterLimit;") + @JavaScriptBody(args = {}, body = "return this._context().miterLimit;") public native double getMiterLimit(); - @JavaScriptBody(args = {"limit"}, body = "this.fld_context.miterLimit=limit;") + @JavaScriptBody(args = {"limit"}, body = "this._context().miterLimit=limit;") public native void setMiterLimit(double limit); - @JavaScriptBody(args = {}, body = "return this.fld_context.font;") + @JavaScriptBody(args = {}, body = "return this._context().font;") public native String getFont(); - @JavaScriptBody(args = {"font"}, body = "this.fld_context.font=font;") + @JavaScriptBody(args = {"font"}, body = "this._context().font=font;") public native void setFont(String font); - @JavaScriptBody(args = {}, body = "return this.fld_context.textAlign;") + @JavaScriptBody(args = {}, body = "return this._context().textAlign;") public native String getTextAlign(); - @JavaScriptBody(args = {"textalign"}, body = "this.fld_context.textAlign=textalign;") + @JavaScriptBody(args = {"textalign"}, body = "this._context().textAlign=textalign;") public native void setTextAlign(String textAlign); - @JavaScriptBody(args = {}, body = "return this.fld_context.textBaseline;") + @JavaScriptBody(args = {}, body = "return this._context().textBaseline;") public native String getTextBaseline(); - @JavaScriptBody(args = {"textbaseline"}, body = "this.fld_context.textBaseline=textbaseline;") + @JavaScriptBody(args = {"textbaseline"}, body = "this._context().textBaseline=textbaseline;") public native void setTextBaseline(String textbaseline); - @JavaScriptBody(args = {"text", "x", "y"}, body = "this.fld_context.fillText(text,x,y);") + @JavaScriptBody(args = {"text", "x", "y"}, body = "this._context().fillText(text,x,y);") public native void fillText(String text, double x, double y); - @JavaScriptBody(args = {"text", "x", "y", "maxwidth"}, body = "this.fld_context.fillText(text,x,y,maxwidth);") + @JavaScriptBody(args = {"text", "x", "y", "maxwidth"}, body = "this._context().fillText(text,x,y,maxwidth);") public void fillText(String text, double x, double y, double maxWidth) { } @@ -255,13 +255,13 @@ } @JavaScriptBody(args = {"text"}, - body = "return this.fld_context.measureText(text);") + body = "return this._context().measureText(text);") private native Object measureTextImpl(String text); - @JavaScriptBody(args = {"text", "x", "y"}, body = "this.fld_context.strokeText(text,x,y);") + @JavaScriptBody(args = {"text", "x", "y"}, body = "this._context().strokeText(text,x,y);") public native void strokeText(String text, double x, double y); - @JavaScriptBody(args = {"text", "x", "y", "maxWidth"}, body = "this.fld_context.strokeText(text,x,y,maxWidth);") + @JavaScriptBody(args = {"text", "x", "y", "maxWidth"}, body = "this._context().strokeText(text,x,y,maxWidth);") public native void strokeText(String text, double x, double y, double maxWidth) ; public ImageData createImageData(double x, double y) { @@ -269,7 +269,7 @@ } @JavaScriptBody(args = {"x", "y"}, - body = "return this.fld_context.createImageData(x,y);") + body = "return this._context().createImageData(x,y);") private native Object createImageDataImpl(double x, double y); public ImageData createImageData(ImageData imageData) { @@ -281,7 +281,7 @@ } @JavaScriptBody(args = {"x", "y", "width", "height"}, - body = "return this.fld_context.getImageData(x,y,width,height);") + body = "return this._context().getImageData(x,y,width,height);") private native Object getImageDataImpl(double x, double y, double width, double height); public void putImageData(ImageData imageData, double x, double y) { @@ -289,7 +289,7 @@ } @JavaScriptBody(args = {"imageData", "x", "y"}, - body = "this.fld_context.putImageData(imageData,x,y);") + body = "this._context().putImageData(imageData,x,y);") private native void putImageDataImpl(Object imageData, double x, double y); public void putImageData(ImageData imageData, double x, double y, double dirtyx, double dirtyy, double dirtywidth, double dirtyheight) { @@ -297,20 +297,20 @@ } @JavaScriptBody(args = {"imageData", "x", "y", "dirtyx", "dirtyy", "dirtywidth", "dirtyheight"}, - body = "this.fld_context.putImageData(imageData,x,y, dirtyx, dirtyy, dirtywidth,dirtyheight);") + body = "this._context().putImageData(imageData,x,y, dirtyx, dirtyy, dirtywidth,dirtyheight);") private native void putImageDataImpl(Object imageData, double x, double y, double dirtyx, double dirtyy, double dirtywidth, double dirtyheight); - @JavaScriptBody(args = {"alpha"}, body = "this.fld_context.globalAlpha=alpha;") + @JavaScriptBody(args = {"alpha"}, body = "this._context().globalAlpha=alpha;") public native void setGlobalAlpha(double alpha) ; - @JavaScriptBody(args = {}, body = "return this.fld_context.globalAlpha;") + @JavaScriptBody(args = {}, body = "return this._context().globalAlpha;") public native double getGlobalAlpha(); - @JavaScriptBody(args = {"operation"}, body = "this.fld_context.globalCompositeOperation=operation;") - public native void setGlobalCompositeOperation(double alpha); + @JavaScriptBody(args = {"operation"}, body = "this._context().globalCompositeOperation=operation;") + public native void setGlobalCompositeOperation(String operation); - @JavaScriptBody(args = {}, body = "return this.fld_context.globalCompositeOperation;") - public native double getGlobalCompositeOperation(); + @JavaScriptBody(args = {}, body = "return this._context().globalCompositeOperation;") + public native String getGlobalCompositeOperation(); public LinearGradient createLinearGradient(double x0, double y0, double x1, double y1) { return new LinearGradient(createLinearGradientImpl(context, x0, y0, x1, y1)); diff -r d443b6c05a01 -r 41e1dc88a399 javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java Fri Jan 25 15:08:24 2013 +0100 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -128,6 +128,8 @@ sb = new StringBuilder(); } Bck2Brwsr.generate(sb, ProcessPageTest.class.getClassLoader(), names); + sb.append("var vm = this.bck2brwsr();\n"); + ScriptEngineManager sem = new ScriptEngineManager(); ScriptEngine js = sem.getEngineByExtension("js"); try { diff -r d443b6c05a01 -r 41e1dc88a399 javaquery/demo-calculator-dynamic/pom.xml --- a/javaquery/demo-calculator-dynamic/pom.xml Fri Jan 25 15:08:24 2013 +0100 +++ b/javaquery/demo-calculator-dynamic/pom.xml Sun Feb 17 17:58:34 2013 +0100 @@ -40,14 +40,54 @@ 1.7 + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + + true + lib/ + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.7 + + true + + + + maven-assembly-plugin + 2.4 + + + distro-assembly + package + + single + + + + src/main/assembly/bck2brwsr.xml + + + + + org.apidesign.bck2brwsr - emul.mini + emul 0.3-SNAPSHOT + rt org.apidesign.bck2brwsr @@ -60,5 +100,13 @@ 6.5.2 test + + org.apidesign.bck2brwsr + vm4brwsr + js + zip + 0.3-SNAPSHOT + provided + diff -r d443b6c05a01 -r 41e1dc88a399 javaquery/demo-calculator-dynamic/src/main/assembly/bck2brwsr.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/demo-calculator-dynamic/src/main/assembly/bck2brwsr.xml Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,62 @@ + + + + + bck2brwsr + + zip + dir + + public_html + + + false + runtime + lib + + *:jar + *:rt + + + + false + provided + + *:js + + true + / + + + + + ${project.build.directory}/${project.build.finalName}.jar + / + + + ${project.build.directory}/classes/org/apidesign/bck2brwsr/demo/calc/Calculator.xhtml + / + index.xhtml + + + + \ No newline at end of file diff -r d443b6c05a01 -r 41e1dc88a399 javaquery/demo-calculator-dynamic/src/main/resources/org/apidesign/bck2brwsr/demo/calc/Calculator.xhtml --- a/javaquery/demo-calculator-dynamic/src/main/resources/org/apidesign/bck2brwsr/demo/calc/Calculator.xhtml Fri Jan 25 15:08:24 2013 +0100 +++ b/javaquery/demo-calculator-dynamic/src/main/resources/org/apidesign/bck2brwsr/demo/calc/Calculator.xhtml Sun Feb 17 17:58:34 2013 +0100 @@ -78,9 +78,9 @@

- - + diff -r d443b6c05a01 -r 41e1dc88a399 javaquery/demo-calculator/nbactions.xml --- a/javaquery/demo-calculator/nbactions.xml Fri Jan 25 15:08:24 2013 +0100 +++ b/javaquery/demo-calculator/nbactions.xml Sun Feb 17 17:58:34 2013 +0100 @@ -22,8 +22,11 @@ run - process-classes - org.codehaus.mojo:exec-maven-plugin:1.2.1:exec + package + org.apidesign.bck2brwsr:mojo:0.3-SNAPSHOT:brwsr + + true + - + diff -r d443b6c05a01 -r 41e1dc88a399 javaquery/demo-calculator/pom.xml --- a/javaquery/demo-calculator/pom.xml Fri Jan 25 15:08:24 2013 +0100 +++ b/javaquery/demo-calculator/pom.xml Sun Feb 17 17:58:34 2013 +0100 @@ -16,34 +16,20 @@ - - org.apidesign.bck2brwsr - mojo - 0.3-SNAPSHOT - - - - j2js - - - - - org.codehaus.mojo - exec-maven-plugin - 1.2.1 + org.apidesign.bck2brwsr + mojo + 0.3-SNAPSHOT - exec + brwsr - xdg-open - - ${project.build.directory}/classes/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calculator.xhtml - + ${project.build.directory}/${project.build.finalName}-bck2brwsr/public_html/ + index.xhtml @@ -55,19 +41,67 @@ 1.7 + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + + true + lib/ + + + + + + maven-assembly-plugin + 2.4 + + + distro-assembly + package + + single + + + + src/main/assembly/bck2brwsr.xml + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.7 + + true + + org.apidesign.bck2brwsr - emul.mini + emul 0.3-SNAPSHOT + rt org.apidesign.bck2brwsr javaquery.api 0.3-SNAPSHOT + + org.apidesign.bck2brwsr + vm4brwsr + js + zip + 0.3-SNAPSHOT + provided + diff -r d443b6c05a01 -r 41e1dc88a399 javaquery/demo-calculator/src/main/assembly/bck2brwsr.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/demo-calculator/src/main/assembly/bck2brwsr.xml Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,62 @@ + + + + + bck2brwsr + + zip + dir + + public_html + + + false + runtime + lib + + *:jar + *:rt + + + + false + provided + + *:js + + true + / + + + + + ${project.build.directory}/${project.build.finalName}.jar + / + + + ${project.build.directory}/classes/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calculator.xhtml + / + index.xhtml + + + + \ No newline at end of file diff -r d443b6c05a01 -r 41e1dc88a399 javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calculator.xhtml --- a/javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calculator.xhtml Fri Jan 25 15:08:24 2013 +0100 +++ b/javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/demo/calc/staticcompilation/Calculator.xhtml Sun Feb 17 17:58:34 2013 +0100 @@ -77,78 +77,10 @@
- diff -r d443b6c05a01 -r 41e1dc88a399 launcher/pom.xml --- a/launcher/pom.xml Fri Jan 25 15:08:24 2013 +0100 +++ b/launcher/pom.xml Sun Feb 17 17:58:34 2013 +0100 @@ -23,6 +23,14 @@ 1.7 + + org.apache.maven.plugins + maven-javadoc-plugin + 2.8.1 + + org.apidesign.bck2brwsr.launcher.impl + + diff -r d443b6c05a01 -r 41e1dc88a399 launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java --- a/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java Fri Jan 25 15:08:24 2013 +0100 +++ b/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java Sun Feb 17 17:58:34 2013 +0100 @@ -39,6 +39,7 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; +import org.apidesign.bck2brwsr.launcher.InvocationContext.Resource; import org.apidesign.vm4brwsr.Bck2Brwsr; import org.glassfish.grizzly.PortRange; import org.glassfish.grizzly.http.server.HttpHandler; @@ -47,6 +48,7 @@ import org.glassfish.grizzly.http.server.Request; import org.glassfish.grizzly.http.server.Response; import org.glassfish.grizzly.http.server.ServerConfiguration; +import org.glassfish.grizzly.http.util.HttpStatus; /** * Lightweight server to launch Bck2Brwsr applications and tests. @@ -55,24 +57,23 @@ */ final class Bck2BrwsrLauncher extends Launcher implements Closeable { private static final Logger LOG = Logger.getLogger(Bck2BrwsrLauncher.class.getName()); - private static final MethodInvocation END = new MethodInvocation(null, null, null); - private Set loaders = new LinkedHashSet<>(); - private BlockingQueue methods = new LinkedBlockingQueue<>(); + private static final InvocationContext END = new InvocationContext(null, null, null); + private final Set loaders = new LinkedHashSet<>(); + private final BlockingQueue methods = new LinkedBlockingQueue<>(); private long timeOut; private final Res resources = new Res(); private final String cmd; private Object[] brwsr; private HttpServer server; private CountDownLatch wait; - + public Bck2BrwsrLauncher(String cmd) { this.cmd = cmd; } @Override - MethodInvocation addMethod(Class clazz, String method, String html) throws IOException { - loaders.add(clazz.getClassLoader()); - MethodInvocation c = new MethodInvocation(clazz.getName(), method, html); + InvocationContext runMethod(InvocationContext c) throws IOException { + loaders.add(c.clazz.getClassLoader()); methods.add(c); try { c.await(timeOut); @@ -94,9 +95,24 @@ if (!startpage.startsWith("/")) { startpage = "/" + startpage; } - HttpServer s = initServer(); + HttpServer s = initServer(".", true); + int last = startpage.lastIndexOf('/'); + String simpleName = startpage.substring(last); + s.getServerConfiguration().addHttpHandler(new Page(resources, startpage), simpleName); s.getServerConfiguration().addHttpHandler(new Page(resources, null), "/"); try { + launchServerAndBrwsr(s, simpleName); + } catch (URISyntaxException | InterruptedException ex) { + throw new IOException(ex); + } + } + + void showDirectory(File dir, String startpage) throws IOException { + if (!startpage.startsWith("/")) { + startpage = "/" + startpage; + } + HttpServer s = initServer(dir.getPath(), false); + try { launchServerAndBrwsr(s, startpage); } catch (URISyntaxException | InterruptedException ex) { throw new IOException(ex); @@ -122,37 +138,94 @@ } } - private HttpServer initServer() throws IOException { - HttpServer s = HttpServer.createSimpleServer(".", new PortRange(8080, 65535)); + private HttpServer initServer(String path, boolean addClasses) throws IOException { + HttpServer s = HttpServer.createSimpleServer(path, new PortRange(8080, 65535)); final ServerConfiguration conf = s.getServerConfiguration(); - conf.addHttpHandler(new VM(resources), "/vm.js"); - conf.addHttpHandler(new Classes(resources), "/classes/"); + if (addClasses) { + conf.addHttpHandler(new VM(resources), "/bck2brwsr.js"); + conf.addHttpHandler(new Classes(resources), "/classes/"); + } return s; } private void executeInBrowser() throws InterruptedException, URISyntaxException, IOException { wait = new CountDownLatch(1); - server = initServer(); - ServerConfiguration conf = server.getServerConfiguration(); + server = initServer(".", true); + final ServerConfiguration conf = server.getServerConfiguration(); + + class DynamicResourceHandler extends HttpHandler { + private final InvocationContext ic; + public DynamicResourceHandler(InvocationContext ic) { + if (ic == null || ic.resources.isEmpty()) { + throw new NullPointerException(); + } + this.ic = ic; + for (Resource r : ic.resources) { + conf.addHttpHandler(this, r.httpPath); + } + } + + public void close() { + conf.removeHttpHandler(this); + } + + @Override + public void service(Request request, Response response) throws Exception { + for (Resource r : ic.resources) { + if (r.httpPath.equals(request.getRequestURI())) { + LOG.log(Level.INFO, "Serving HttpResource for {0}", request.getRequestURI()); + response.setContentType(r.httpType); + copyStream(r.httpContent, response.getOutputStream(), null); + } + } + } + } + conf.addHttpHandler(new Page(resources, "org/apidesign/bck2brwsr/launcher/harness.xhtml" ), "/execute"); + conf.addHttpHandler(new HttpHandler() { int cnt; - List cases = new ArrayList<>(); + List cases = new ArrayList<>(); + DynamicResourceHandler prev; @Override public void service(Request request, Response response) throws Exception { String id = request.getParameter("request"); String value = request.getParameter("result"); + + InvocationContext mi = null; + int caseNmbr = -1; + if (id != null && value != null) { LOG.log(Level.INFO, "Received result for case {0} = {1}", new Object[]{id, value}); value = decodeURL(value); - cases.get(Integer.parseInt(id)).result(value, null); + int indx = Integer.parseInt(id); + cases.get(indx).result(value, null); + if (++indx < cases.size()) { + mi = cases.get(indx); + LOG.log(Level.INFO, "Re-executing case {0}", indx); + caseNmbr = indx; + } + } else { + if (!cases.isEmpty()) { + LOG.info("Re-executing test cases"); + mi = cases.get(0); + caseNmbr = 0; + } } - MethodInvocation mi = methods.take(); + if (prev != null) { + prev.close(); + prev = null; + } + + if (mi == null) { + mi = methods.take(); + caseNmbr = cnt++; + } if (mi == END) { response.getWriter().write(""); wait.countDown(); @@ -161,14 +234,18 @@ return; } + if (!mi.resources.isEmpty()) { + prev = new DynamicResourceHandler(mi); + } + cases.add(mi); - final String cn = mi.className; + final String cn = mi.clazz.getName(); final String mn = mi.methodName; - LOG.log(Level.INFO, "Request for {0} case. Sending {1}.{2}", new Object[]{cnt, cn, mn}); + LOG.log(Level.INFO, "Request for {0} case. Sending {1}.{2}", new Object[]{caseNmbr, cn, mn}); response.getWriter().write("{" + "className: '" + cn + "', " + "methodName: '" + mn + "', " - + "request: " + cnt + + "request: " + caseNmbr ); if (mi.html != null) { response.getWriter().write(", html: '"); @@ -176,7 +253,6 @@ response.getWriter().write("'"); } response.getWriter().write("}"); - cnt++; } }, "/data"); @@ -236,10 +312,10 @@ if (ch == '$' && params.length > 0) { int cnt = is.read() - '0'; if (cnt == 'U' - '0') { - os.write(baseURL.getBytes()); + os.write(baseURL.getBytes("UTF-8")); } if (cnt >= 0 && cnt < params.length) { - os.write(params[cnt].getBytes()); + os.write(params[cnt].getBytes("UTF-8")); } } else { os.write(ch); @@ -378,9 +454,9 @@ String r = resource; if (r == null) { r = request.getHttpHandlerPath(); - if (r.startsWith("/")) { - r = r.substring(1); - } + } + if (r.startsWith("/")) { + r = r.substring(1); } String[] replace = {}; if (r.endsWith(".html")) { @@ -411,14 +487,22 @@ StringBuilder sb = new StringBuilder(); Bck2Brwsr.generate(sb, loader); sb.append( - "function ldCls(res) {\n" - + " var request = new XMLHttpRequest();\n" - + " request.open('GET', '/classes/' + res, false);\n" - + " request.send();\n" - + " var arr = eval('(' + request.responseText + ')');\n" - + " return arr;\n" - + "}\n" - + "var vm = new bck2brwsr(ldCls);\n" + "(function WrapperVM(global) {" + + " function ldCls(res) {\n" + + " var request = new XMLHttpRequest();\n" + + " request.open('GET', '/classes/' + res, false);\n" + + " request.send();\n" + + " if (request.status !== 200) return null;\n" + + " var arr = eval('(' + request.responseText + ')');\n" + + " return arr;\n" + + " }\n" + + " var prevvm = global.bck2brwsr;\n" + + " global.bck2brwsr = function() {\n" + + " var args = Array.prototype.slice.apply(arguments);\n" + + " args.unshift(ldCls);\n" + + " return prevvm.apply(null, args);\n" + + " };\n" + + "})(this);\n" ); this.bck2brwsr = sb.toString(); } @@ -466,6 +550,7 @@ } w.append("\n]"); } catch (IOException ex) { + response.setStatus(HttpStatus.NOT_FOUND_404); response.setError(); response.setDetailMessage(ex.getMessage()); } diff -r d443b6c05a01 -r 41e1dc88a399 launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Console.java --- a/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Console.java Fri Jan 25 15:08:24 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,253 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.launcher; - -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.net.URL; -import java.util.Enumeration; -import org.apidesign.bck2brwsr.core.JavaScriptBody; - -/** - * - * @author Jaroslav Tulach - */ -public class Console { - static { - turnAssetionStatusOn(); - } - - @JavaScriptBody(args = {"id", "attr"}, body = - "return window.document.getElementById(id)[attr].toString();") - private static native Object getAttr(String id, String attr); - - @JavaScriptBody(args = {"id", "attr", "value"}, body = - "window.document.getElementById(id)[attr] = value;") - private static native void setAttr(String id, String attr, Object value); - - @JavaScriptBody(args = {}, body = "return; window.close();") - private static native void closeWindow(); - - private static void log(String newText) { - String id = "bck2brwsr.result"; - String attr = "value"; - setAttr(id, attr, getAttr(id, attr) + "\n" + newText); - setAttr(id, "scrollTop", getAttr(id, "scrollHeight")); - } - - public static void execute() throws Exception { - String clazz = (String) getAttr("clazz", "value"); - String method = (String) getAttr("method", "value"); - Object res = invokeMethod(clazz, method); - setAttr("bck2brwsr.result", "value", res); - } - - @JavaScriptBody(args = { "url", "callback", "arr" }, body = "" - + "var request = new XMLHttpRequest();\n" - + "request.open('GET', url, true);\n" - + "request.onreadystatechange = function() {\n" - + " if (this.readyState!==4) return;\n" - + " arr[0] = this.responseText;\n" - + " callback.run__V();\n" - + "};" - + "request.send();" - ) - private static native void loadText(String url, Runnable callback, String[] arr) throws IOException; - - public static void harness(String url) throws IOException { - log("Connecting to " + url); - Request r = new Request(url); - } - - private static class Request implements Runnable { - private final String[] arr = { null }; - private final String url; - - private Request(String url) throws IOException { - this.url = url; - loadText(url, this, arr); - } - - @Override - public void run() { - try { - String data = arr[0]; - log("\nGot \"" + data + "\""); - - if (data == null) { - log("Some error exiting"); - closeWindow(); - return; - } - - if (data.isEmpty()) { - log("No data, exiting"); - closeWindow(); - return; - } - - Case c = Case.parseData(data); - if (c.getHtmlFragment() != null) { - setAttr("bck2brwsr.fragment", "innerHTML", c.getHtmlFragment()); - } - log("Invoking " + c.getClassName() + '.' + c.getMethodName() + " as request: " + c.getRequestId()); - - Object result = invokeMethod(c.getClassName(), c.getMethodName()); - - setAttr("bck2brwsr.fragment", "innerHTML", ""); - log("Result: " + result); - - result = encodeURL("" + result); - - log("Sending back: " + url + "?request=" + c.getRequestId() + "&result=" + result); - String u = url + "?request=" + c.getRequestId() + "&result=" + result; - - loadText(u, this, arr); - - } catch (Exception ex) { - log(ex.getMessage()); - } - } - } - - private static String encodeURL(String r) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < r.length(); i++) { - int ch = r.charAt(i); - if (ch < 32 || ch == '%' || ch == '+') { - sb.append("%").append(("0" + Integer.toHexString(ch)).substring(0, 2)); - } else { - if (ch == 32) { - sb.append("+"); - } else { - sb.append((char)ch); - } - } - } - return sb.toString(); - } - - static String invoke(String clazz, String method) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException { - final Object r = invokeMethod(clazz, method); - return r == null ? "null" : r.toString().toString(); - } - - /** Helper method that inspects the classpath and loads given resource - * (usually a class file). Used while running tests in Rhino. - * - * @param name resource name to find - * @return the array of bytes in the given resource - * @throws IOException I/O in case something goes wrong - */ - public static byte[] read(String name) throws IOException { - URL u = null; - Enumeration en = Console.class.getClassLoader().getResources(name); - while (en.hasMoreElements()) { - u = en.nextElement(); - } - if (u == null) { - throw new IOException("Can't find " + name); - } - try (InputStream is = u.openStream()) { - byte[] arr; - arr = new byte[is.available()]; - int offset = 0; - while (offset < arr.length) { - int len = is.read(arr, offset, arr.length - offset); - if (len == -1) { - throw new IOException("Can't read " + name); - } - offset += len; - } - return arr; - } - } - - private static Object invokeMethod(String clazz, String method) - throws ClassNotFoundException, InvocationTargetException, - SecurityException, IllegalAccessException, IllegalArgumentException, - InstantiationException { - Method found = null; - Class c = Class.forName(clazz); - for (Method m : c.getMethods()) { - if (m.getName().equals(method)) { - found = m; - } - } - Object res; - if (found != null) { - try { - if ((found.getModifiers() & Modifier.STATIC) != 0) { - res = found.invoke(null); - } else { - res = found.invoke(c.newInstance()); - } - } catch (Throwable ex) { - res = ex.getClass().getName() + ":" + ex.getMessage(); - } - } else { - res = "Can't find method " + method + " in " + clazz; - } - return res; - } - - @JavaScriptBody(args = {}, body = "vm.desiredAssertionStatus = true;") - private static void turnAssetionStatusOn() { - } - - private static final class Case { - private final Object data; - - private Case(Object data) { - this.data = data; - } - - public static Case parseData(String s) { - return new Case(toJSON(s)); - } - - public String getMethodName() { - return value("methodName", data); - } - - public String getClassName() { - return value("className", data); - } - - public String getRequestId() { - return value("request", data); - } - - public String getHtmlFragment() { - return value("html", data); - } - - @JavaScriptBody(args = "s", body = "return eval('(' + s + ')');") - private static native Object toJSON(String s); - - @JavaScriptBody(args = {"p", "d"}, body = - "var v = d[p];\n" - + "if (typeof v === 'undefined') return null;\n" - + "return v.toString();" - ) - private static native String value(String p, Object d); - } -} diff -r d443b6c05a01 -r 41e1dc88a399 launcher/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,110 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.launcher; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** Represents individual method invocation, its context and its result. + * + * @author Jaroslav Tulach + */ +public final class InvocationContext { + final CountDownLatch wait = new CountDownLatch(1); + final Class clazz; + final String methodName; + private final Launcher launcher; + private String result; + private Throwable exception; + String html; + final List resources = new ArrayList<>(); + + InvocationContext(Launcher launcher, Class clazz, String methodName) { + this.launcher = launcher; + this.clazz = clazz; + this.methodName = methodName; + } + + /** An HTML fragment to be available for the execution. Useful primarily when + * executing in a browser via {@link Launcher#createBrowser(java.lang.String)}. + * @param html the html fragment + */ + public void setHtmlFragment(String html) { + this.html = html; + } + + /** HTTP resource to be available during execution. An invocation may + * perform an HTTP query and obtain a resource relative to the page. + */ + public void addHttpResource(String relativePath, String mimeType, InputStream content) { + if (relativePath == null || mimeType == null || content == null) { + throw new NullPointerException(); + } + resources.add(new Resource(content, mimeType, relativePath)); + } + + /** Invokes the associated method. + * @return the textual result of the invocation + */ + public String invoke() throws IOException { + launcher.runMethod(this); + return toString(); + } + + /** Obtains textual result of the invocation. + * @return text representing the exception or result value + */ + @Override + public String toString() { + if (exception != null) { + return exception.toString(); + } + return result; + } + + /** + * @param timeOut + * @throws InterruptedException + */ + void await(long timeOut) throws InterruptedException { + wait.await(timeOut, TimeUnit.MILLISECONDS); + } + + void result(String r, Throwable e) { + this.result = r; + this.exception = e; + wait.countDown(); + } + + + static final class Resource { + final InputStream httpContent; + final String httpType; + final String httpPath; + + Resource(InputStream httpContent, String httpType, String httpPath) { + this.httpContent = httpContent; + this.httpType = httpType; + this.httpPath = httpPath; + } + } +} diff -r d443b6c05a01 -r 41e1dc88a399 launcher/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java --- a/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java Fri Jan 25 15:08:24 2013 +0100 +++ b/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java Sun Feb 17 17:58:34 2013 +0100 @@ -17,12 +17,14 @@ */ package org.apidesign.bck2brwsr.launcher; +import org.apidesign.bck2brwsr.launcher.impl.Console; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Enumeration; import java.util.LinkedHashSet; import java.util.Set; +import java.util.logging.Level; import java.util.logging.Logger; import javax.script.Invocable; import javax.script.ScriptEngine; @@ -42,14 +44,18 @@ private Object console; - @Override MethodInvocation addMethod(Class clazz, String method, String html) { - loaders.add(clazz.getClassLoader()); - MethodInvocation mi = new MethodInvocation(clazz.getName(), method, html); + @Override InvocationContext runMethod(InvocationContext mi) { + loaders.add(mi.clazz.getClassLoader()); try { - mi.result(code.invokeMethod( + long time = System.currentTimeMillis(); + LOG.log(Level.FINE, "Invoking {0}.{1}", new Object[]{mi.clazz.getName(), mi.methodName}); + String res = code.invokeMethod( console, "invoke__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2", - mi.className, mi.methodName).toString(), null); + mi.clazz.getName(), mi.methodName).toString(); + time = System.currentTimeMillis() - time; + LOG.log(Level.FINE, "Resut of {0}.{1} = {2} in {3} ms", new Object[]{mi.clazz.getName(), mi.methodName, res, time}); + mi.result(res, null); } catch (ScriptException | NoSuchMethodException ex) { mi.result(null, ex); } @@ -83,7 +89,7 @@ ScriptEngine mach = sem.getEngineByExtension("js"); sb.append( - "\nvar vm = new bck2brwsr(org.apidesign.bck2brwsr.launcher.Console.read);" + "\nvar vm = new bck2brwsr(org.apidesign.bck2brwsr.launcher.impl.Console.read);" + "\nfunction initVM() { return vm; };" + "\n"); diff -r d443b6c05a01 -r 41e1dc88a399 launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java --- a/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java Fri Jan 25 15:08:24 2013 +0100 +++ b/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java Sun Feb 17 17:58:34 2013 +0100 @@ -18,12 +18,14 @@ package org.apidesign.bck2brwsr.launcher; import java.io.Closeable; +import java.io.File; import java.io.IOException; import java.net.URLClassLoader; import org.apidesign.vm4brwsr.Bck2Brwsr; /** An abstraction for executing tests in a Bck2Brwsr virtual machine. - * Either in JavaScript engine, or in external browser. + * Either in {@linkm Launcher#createJavaScript JavaScript engine}, + * or in {@linkm Launcher#createBrowser external browser}. * * @author Jaroslav Tulach */ @@ -31,33 +33,83 @@ Launcher() { } + + /** Initializes the launcher. This may mean starting a web browser or + * initializing execution engine. + * @throws IOException if something goes wrong + */ + public abstract void initialize() throws IOException; - abstract MethodInvocation addMethod(Class clazz, String method, String html) throws IOException; - - public abstract void initialize() throws IOException; + /** Shuts down the launcher. + * @throws IOException if something goes wrong + */ public abstract void shutdown() throws IOException; - public MethodInvocation invokeMethod(Class clazz, String method, String html) throws IOException { - return addMethod(clazz, method, html); + + + /** Builds an invocation context. The context can later be customized + * and {@link InvocationContext#invoke() invoked}. + * + * @param clazz the class to execute method from + * @param method the method to execute + * @return the context pointing to the selected method + */ + public InvocationContext createInvocation(Class clazz, String method) { + return new InvocationContext(this, clazz, method); } - + /** Creates launcher that uses internal JavaScript engine (Rhino). + * @return the launcher + */ public static Launcher createJavaScript() { final JSLauncher l = new JSLauncher(); l.addClassLoader(Bck2Brwsr.class.getClassLoader()); return l; } + /** Creates launcher that is using external browser. + * + * @param cmd null to use java.awt.Desktop to show the launcher + * or a string to execute in an external process (with a parameter to the URL) + * @return launcher executing in external browser. + */ public static Launcher createBrowser(String cmd) { final Bck2BrwsrLauncher l = new Bck2BrwsrLauncher(cmd); l.addClassLoader(Bck2Brwsr.class.getClassLoader()); l.setTimeout(180000); return l; } + + /** Starts an HTTP server which provides access to classes and resources + * available in the classes URL and shows a start page + * available as {@link ClassLoader#getResource(java.lang.String)} from the + * provide classloader. Opens a browser with URL showing the start page. + * + * @param classes classloader offering access to classes and resources + * @param startpage page to show in the browser + * @return interface that allows one to stop the server + * @throws IOException if something goes wrong + */ public static Closeable showURL(URLClassLoader classes, String startpage) throws IOException { Bck2BrwsrLauncher l = new Bck2BrwsrLauncher(null); l.addClassLoader(classes); l.showURL(startpage); return l; } + /** Starts an HTTP server which provides access to certain directory. + * The startpage should be relative location inside the root + * driecotry + * Opens a browser with URL showing the start page. + * + * @param directory the root directory on disk + * @praam startpage relative path from the root to the page + * @exception IOException if something goes wrong. + */ + public static Closeable showDir(File directory, String startpage) throws IOException { + Bck2BrwsrLauncher l = new Bck2BrwsrLauncher(null); + l.showDirectory(directory, startpage); + return l; + } + + abstract InvocationContext runMethod(InvocationContext c) throws IOException; } diff -r d443b6c05a01 -r 41e1dc88a399 launcher/src/main/java/org/apidesign/bck2brwsr/launcher/MethodInvocation.java --- a/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/MethodInvocation.java Fri Jan 25 15:08:24 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.launcher; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** - * - * @author Jaroslav Tulach - */ -public final class MethodInvocation { - final CountDownLatch wait = new CountDownLatch(1); - final String className; - final String methodName; - final String html; - private String result; - private Throwable exception; - - MethodInvocation(String className, String methodName, String html) { - this.className = className; - this.methodName = methodName; - this.html = html; - } - - void await(long timeOut) throws InterruptedException { - wait.await(timeOut, TimeUnit.MILLISECONDS); - } - - void result(String r, Throwable e) { - this.result = r; - this.exception = e; - wait.countDown(); - } - - @Override - public String toString() { - if (exception != null) { - return exception.toString(); - } - return result; - } - -} diff -r d443b6c05a01 -r 41e1dc88a399 launcher/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,255 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.launcher.impl; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.util.Enumeration; +import org.apidesign.bck2brwsr.core.JavaScriptBody; + +/** + * + * @author Jaroslav Tulach + */ +public class Console { + private Console() { + } + static { + turnAssetionStatusOn(); + } + + @JavaScriptBody(args = {"id", "attr"}, body = + "return window.document.getElementById(id)[attr].toString();") + private static native Object getAttr(String id, String attr); + + @JavaScriptBody(args = {"id", "attr", "value"}, body = + "window.document.getElementById(id)[attr] = value;") + private static native void setAttr(String id, String attr, Object value); + + @JavaScriptBody(args = {}, body = "return; window.close();") + private static native void closeWindow(); + + private static void log(String newText) { + String id = "bck2brwsr.result"; + String attr = "value"; + setAttr(id, attr, getAttr(id, attr) + "\n" + newText); + setAttr(id, "scrollTop", getAttr(id, "scrollHeight")); + } + + public static void execute() throws Exception { + String clazz = (String) getAttr("clazz", "value"); + String method = (String) getAttr("method", "value"); + Object res = invokeMethod(clazz, method); + setAttr("bck2brwsr.result", "value", res); + } + + @JavaScriptBody(args = { "url", "callback", "arr" }, body = "" + + "var request = new XMLHttpRequest();\n" + + "request.open('GET', url, true);\n" + + "request.onreadystatechange = function() {\n" + + " if (this.readyState!==4) return;\n" + + " arr[0] = this.responseText;\n" + + " callback.run__V();\n" + + "};" + + "request.send();" + ) + private static native void loadText(String url, Runnable callback, String[] arr) throws IOException; + + public static void harness(String url) throws IOException { + log("Connecting to " + url); + Request r = new Request(url); + } + + private static class Request implements Runnable { + private final String[] arr = { null }; + private final String url; + + private Request(String url) throws IOException { + this.url = url; + loadText(url, this, arr); + } + + @Override + public void run() { + try { + String data = arr[0]; + log("\nGot \"" + data + "\""); + + if (data == null) { + log("Some error exiting"); + closeWindow(); + return; + } + + if (data.isEmpty()) { + log("No data, exiting"); + closeWindow(); + return; + } + + Case c = Case.parseData(data); + if (c.getHtmlFragment() != null) { + setAttr("bck2brwsr.fragment", "innerHTML", c.getHtmlFragment()); + } + log("Invoking " + c.getClassName() + '.' + c.getMethodName() + " as request: " + c.getRequestId()); + + Object result = invokeMethod(c.getClassName(), c.getMethodName()); + + setAttr("bck2brwsr.fragment", "innerHTML", ""); + log("Result: " + result); + + result = encodeURL("" + result); + + log("Sending back: " + url + "?request=" + c.getRequestId() + "&result=" + result); + String u = url + "?request=" + c.getRequestId() + "&result=" + result; + + loadText(u, this, arr); + + } catch (Exception ex) { + log(ex.getClass().getName() + ":" + ex.getMessage()); + } + } + } + + private static String encodeURL(String r) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < r.length(); i++) { + int ch = r.charAt(i); + if (ch < 32 || ch == '%' || ch == '+') { + sb.append("%").append(("0" + Integer.toHexString(ch)).substring(0, 2)); + } else { + if (ch == 32) { + sb.append("+"); + } else { + sb.append((char)ch); + } + } + } + return sb.toString(); + } + + static String invoke(String clazz, String method) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException { + final Object r = invokeMethod(clazz, method); + return r == null ? "null" : r.toString().toString(); + } + + /** Helper method that inspects the classpath and loads given resource + * (usually a class file). Used while running tests in Rhino. + * + * @param name resource name to find + * @return the array of bytes in the given resource + * @throws IOException I/O in case something goes wrong + */ + public static byte[] read(String name) throws IOException { + URL u = null; + Enumeration en = Console.class.getClassLoader().getResources(name); + while (en.hasMoreElements()) { + u = en.nextElement(); + } + if (u == null) { + throw new IOException("Can't find " + name); + } + try (InputStream is = u.openStream()) { + byte[] arr; + arr = new byte[is.available()]; + int offset = 0; + while (offset < arr.length) { + int len = is.read(arr, offset, arr.length - offset); + if (len == -1) { + throw new IOException("Can't read " + name); + } + offset += len; + } + return arr; + } + } + + private static Object invokeMethod(String clazz, String method) + throws ClassNotFoundException, InvocationTargetException, + SecurityException, IllegalAccessException, IllegalArgumentException, + InstantiationException { + Method found = null; + Class c = Class.forName(clazz); + for (Method m : c.getMethods()) { + if (m.getName().equals(method)) { + found = m; + } + } + Object res; + if (found != null) { + try { + if ((found.getModifiers() & Modifier.STATIC) != 0) { + res = found.invoke(null); + } else { + res = found.invoke(c.newInstance()); + } + } catch (Throwable ex) { + res = ex.getClass().getName() + ":" + ex.getMessage(); + } + } else { + res = "Can't find method " + method + " in " + clazz; + } + return res; + } + + @JavaScriptBody(args = {}, body = "vm.desiredAssertionStatus = true;") + private static void turnAssetionStatusOn() { + } + + private static final class Case { + private final Object data; + + private Case(Object data) { + this.data = data; + } + + public static Case parseData(String s) { + return new Case(toJSON(s)); + } + + public String getMethodName() { + return value("methodName", data); + } + + public String getClassName() { + return value("className", data); + } + + public String getRequestId() { + return value("request", data); + } + + public String getHtmlFragment() { + return value("html", data); + } + + @JavaScriptBody(args = "s", body = "return eval('(' + s + ')');") + private static native Object toJSON(String s); + + @JavaScriptBody(args = {"p", "d"}, body = + "var v = d[p];\n" + + "if (typeof v === 'undefined') return null;\n" + + "return v.toString();" + ) + private static native String value(String p, Object d); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 launcher/src/main/resources/org/apidesign/bck2brwsr/launcher/harness.xhtml --- a/launcher/src/main/resources/org/apidesign/bck2brwsr/launcher/harness.xhtml Fri Jan 25 15:08:24 2013 +0100 +++ b/launcher/src/main/resources/org/apidesign/bck2brwsr/launcher/harness.xhtml Sun Feb 17 17:58:34 2013 +0100 @@ -24,7 +24,10 @@ Bck2Brwsr Harness - + +

Bck2Brwsr Execution Harness

@@ -34,7 +37,7 @@
diff -r d443b6c05a01 -r 41e1dc88a399 mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Bck2BrswrMojo.java --- a/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Bck2BrswrMojo.java Fri Jan 25 15:08:24 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,122 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.mojo; - -import org.apache.maven.plugin.AbstractMojo; - -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import org.apache.maven.artifact.Artifact; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.project.MavenProject; -import org.apidesign.vm4brwsr.Bck2Brwsr; - -/** Compiles classes into JavaScript. */ -@Mojo(name="j2js", defaultPhase=LifecyclePhase.PROCESS_CLASSES) -public class Bck2BrswrMojo extends AbstractMojo { - public Bck2BrswrMojo() { - } - /** Root of the class files */ - @Parameter(defaultValue="${project.build.directory}/classes") - private File classes; - /** File to generate. Defaults bootjava.js in the first non-empty - package under the classes directory */ - @Parameter - private File javascript; - - @Parameter(defaultValue="${project}") - private MavenProject prj; - - - - @Override - public void execute() throws MojoExecutionException { - if (!classes.isDirectory()) { - throw new MojoExecutionException("Can't find " + classes); - } - - if (javascript == null) { - javascript = new File(findNonEmptyFolder(classes), "bootjava.js"); - } - - List arr = new ArrayList(); - long newest = collectAllClasses("", classes, arr); - - if (javascript.lastModified() > newest) { - return; - } - - try { - URLClassLoader url = buildClassLoader(classes, prj.getDependencyArtifacts()); - FileWriter w = new FileWriter(javascript); - Bck2Brwsr.generate(w, url, arr.toArray(new String[0])); - w.close(); - } catch (IOException ex) { - throw new MojoExecutionException("Can't compile", ex); - } - } - - private static File findNonEmptyFolder(File dir) throws MojoExecutionException { - if (!dir.isDirectory()) { - throw new MojoExecutionException("Not a directory " + dir); - } - File[] arr = dir.listFiles(); - if (arr.length == 1 && arr[0].isDirectory()) { - return findNonEmptyFolder(arr[0]); - } - return dir; - } - - private static long collectAllClasses(String prefix, File toCheck, List arr) { - File[] files = toCheck.listFiles(); - if (files != null) { - long newest = 0L; - for (File f : files) { - long lastModified = collectAllClasses(prefix + f.getName() + "/", f, arr); - if (newest < lastModified) { - newest = lastModified; - } - } - return newest; - } else if (toCheck.getName().endsWith(".class")) { - arr.add(prefix.substring(0, prefix.length() - 7)); - return toCheck.lastModified(); - } else { - return 0L; - } - } - - private static URLClassLoader buildClassLoader(File root, Collection deps) throws MalformedURLException { - List arr = new ArrayList(); - arr.add(root.toURI().toURL()); - for (Artifact a : deps) { - arr.add(a.getFile().toURI().toURL()); - } - return new URLClassLoader(arr.toArray(new URL[0]), Bck2BrswrMojo.class.getClassLoader()); - } -} diff -r d443b6c05a01 -r 41e1dc88a399 mojo/src/main/java/org/apidesign/bck2brwsr/mojo/BrswrMojo.java --- a/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/BrswrMojo.java Fri Jan 25 15:08:24 2013 +0100 +++ b/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/BrswrMojo.java Sun Feb 17 17:58:34 2013 +0100 @@ -37,7 +37,7 @@ import org.apidesign.bck2brwsr.launcher.Launcher; /** Executes given HTML page in a browser. */ -@Mojo(name="brwsr", defaultPhase=LifecyclePhase.DEPLOY) +@Mojo(name="brwsr", defaultPhase=LifecyclePhase.NONE) public class BrswrMojo extends AbstractMojo { public BrswrMojo() { } @@ -51,21 +51,28 @@ /** Root of the class files */ @Parameter(defaultValue="${project.build.directory}/classes") private File classes; + + /** Root of all pages, and files, etc. */ + @Parameter + private File directory; @Override public void execute() throws MojoExecutionException { if (startpage == null) { throw new MojoExecutionException("You have to provide a start page"); } - + try { - URLClassLoader url = buildClassLoader(classes, prj.getDependencyArtifacts()); - Closeable httpServer; - try { - httpServer = Launcher.showURL(url, startpage()); - } catch (Exception ex) { - throw new MojoExecutionException("Can't open " + startpage(), ex); + if (directory != null) { + httpServer = Launcher.showDir(directory, startpage); + } else { + URLClassLoader url = buildClassLoader(classes, prj.getDependencyArtifacts()); + try { + httpServer = Launcher.showURL(url, startpage()); + } catch (Exception ex) { + throw new MojoExecutionException("Can't open " + startpage(), ex); + } } System.in.read(); httpServer.close(); diff -r d443b6c05a01 -r 41e1dc88a399 mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Java2JavaScript.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Java2JavaScript.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,122 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.mojo; + +import org.apache.maven.plugin.AbstractMojo; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.MavenProject; +import org.apidesign.vm4brwsr.Bck2Brwsr; + +/** Compiles classes into JavaScript. */ +@Mojo(name="j2js", defaultPhase=LifecyclePhase.PROCESS_CLASSES) +public class Java2JavaScript extends AbstractMojo { + public Java2JavaScript() { + } + /** Root of the class files */ + @Parameter(defaultValue="${project.build.directory}/classes") + private File classes; + /** File to generate. Defaults bootjava.js in the first non-empty + package under the classes directory */ + @Parameter + private File javascript; + + @Parameter(defaultValue="${project}") + private MavenProject prj; + + + + @Override + public void execute() throws MojoExecutionException { + if (!classes.isDirectory()) { + throw new MojoExecutionException("Can't find " + classes); + } + + if (javascript == null) { + javascript = new File(findNonEmptyFolder(classes), "bootjava.js"); + } + + List arr = new ArrayList(); + long newest = collectAllClasses("", classes, arr); + + if (javascript.lastModified() > newest) { + return; + } + + try { + URLClassLoader url = buildClassLoader(classes, prj.getDependencyArtifacts()); + FileWriter w = new FileWriter(javascript); + Bck2Brwsr.generate(w, url, arr.toArray(new String[0])); + w.close(); + } catch (IOException ex) { + throw new MojoExecutionException("Can't compile", ex); + } + } + + private static File findNonEmptyFolder(File dir) throws MojoExecutionException { + if (!dir.isDirectory()) { + throw new MojoExecutionException("Not a directory " + dir); + } + File[] arr = dir.listFiles(); + if (arr.length == 1 && arr[0].isDirectory()) { + return findNonEmptyFolder(arr[0]); + } + return dir; + } + + private static long collectAllClasses(String prefix, File toCheck, List arr) { + File[] files = toCheck.listFiles(); + if (files != null) { + long newest = 0L; + for (File f : files) { + long lastModified = collectAllClasses(prefix + f.getName() + "/", f, arr); + if (newest < lastModified) { + newest = lastModified; + } + } + return newest; + } else if (toCheck.getName().endsWith(".class")) { + arr.add(prefix.substring(0, prefix.length() - 7)); + return toCheck.lastModified(); + } else { + return 0L; + } + } + + private static URLClassLoader buildClassLoader(File root, Collection deps) throws MalformedURLException { + List arr = new ArrayList(); + arr.add(root.toURI().toURL()); + for (Artifact a : deps) { + arr.add(a.getFile().toURI().toURL()); + } + return new URLClassLoader(arr.toArray(new URL[0]), Java2JavaScript.class.getClassLoader()); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 mojo/src/main/resources/META-INF/maven/archetype-metadata.xml --- a/mojo/src/main/resources/META-INF/maven/archetype-metadata.xml Fri Jan 25 15:08:24 2013 +0100 +++ b/mojo/src/main/resources/META-INF/maven/archetype-metadata.xml Sun Feb 17 17:58:34 2013 +0100 @@ -30,6 +30,7 @@ src/main/resources **/*.xhtml + **/*.html @@ -44,5 +45,11 @@ nbactions.xml + + + + bck2brwsr-assembly.xml + + \ No newline at end of file diff -r d443b6c05a01 -r 41e1dc88a399 mojo/src/main/resources/archetype-resources/bck2brwsr-assembly.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mojo/src/main/resources/archetype-resources/bck2brwsr-assembly.xml Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,61 @@ + + + + + bck2brwsr + + zip + + public_html + + + false + runtime + lib + + *:jar + *:rt + + + + false + provided + + *:js + + true + / + + + + + ${project.build.directory}/${project.build.finalName}.jar + / + + + ${project.build.directory}/classes/${package.replace('.','/')}/index.html + / + index.html + + + + \ No newline at end of file diff -r d443b6c05a01 -r 41e1dc88a399 mojo/src/main/resources/archetype-resources/pom.xml --- a/mojo/src/main/resources/archetype-resources/pom.xml Fri Jan 25 15:08:24 2013 +0100 +++ b/mojo/src/main/resources/archetype-resources/pom.xml Sun Feb 17 17:58:34 2013 +0100 @@ -10,6 +10,32 @@ ${artifactId} + + + java.net + Java.net + https://maven.java.net/content/repositories/snapshots/ + + true + + + + netbeans + NetBeans + http://bits.netbeans.org/maven2/ + + + + + java.net + Local Maven repository of releases + https://maven.java.net/content/repositories/snapshots/ + + true + + + + UTF-8 @@ -27,7 +53,7 @@ - ${package.replace('.','/')}/index.xhtml + ${package.replace('.','/')}/index.html @@ -39,14 +65,46 @@ 1.7 + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + + true + lib/ + + + + + + maven-assembly-plugin + 2.4 + + + distro-assembly + package + + single + + + + bck2brwsr-assembly.xml + + + + + org.apidesign.bck2brwsr - emul.mini + emul 0.3-SNAPSHOT + rt org.apidesign.bck2brwsr @@ -60,7 +118,15 @@ test - ${project.groupId} + org.apidesign.bck2brwsr + vm4brwsr + js + zip + 0.3-SNAPSHOT + provided + + + org.apidesign.bck2brwsr vmtest 0.3-SNAPSHOT test diff -r d443b6c05a01 -r 41e1dc88a399 mojo/src/main/resources/archetype-resources/src/main/java/App.java --- a/mojo/src/main/resources/archetype-resources/src/main/java/App.java Fri Jan 25 15:08:24 2013 +0100 +++ b/mojo/src/main/resources/archetype-resources/src/main/java/App.java Sun Feb 17 17:58:34 2013 +0100 @@ -9,7 +9,7 @@ /** Edit the index.xhtml file. Use 'id' to name certain HTML elements. * Use this class to define behavior of the elements. */ -@Page(xhtml="index.xhtml", className="Index", properties={ +@Page(xhtml="index.html", className="Index", properties={ @Property(name="name", type=String.class) }) public class App { diff -r d443b6c05a01 -r 41e1dc88a399 mojo/src/main/resources/archetype-resources/src/main/resources/index.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mojo/src/main/resources/archetype-resources/src/main/resources/index.html Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,22 @@ + + + + + Bck2Brwsr's Hello World + + +

Loading Bck2Brwsr's Hello World...

+ Your name: + +

+ + +

+ + + + + diff -r d443b6c05a01 -r 41e1dc88a399 mojo/src/main/resources/archetype-resources/src/main/resources/index.xhtml --- a/mojo/src/main/resources/archetype-resources/src/main/resources/index.xhtml Fri Jan 25 15:08:24 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ - - - - - Bck2Brwsr's Hello World - - -

Loading Bck2Brwsr's Hello World...

- Your name: - -

- - -

- - - - - - diff -r d443b6c05a01 -r 41e1dc88a399 pom.xml --- a/pom.xml Fri Jan 25 15:08:24 2013 +0100 +++ b/pom.xml Sun Feb 17 17:58:34 2013 +0100 @@ -6,6 +6,11 @@ 0.3-SNAPSHOT pom Back 2 Browser + + net.java + jvnet-parent + 3 + vm emul @@ -17,6 +22,7 @@ benchmarks launcher vmtest + ide @@ -29,6 +35,11 @@ API Design http://apidesign.org + + scm:hg:http://source.apidesign.org/hg/bck2brwsr + scm:hg:https://source.apidesign.org/hg/bck2brwsr + http://source.apidesign.org/hg/bck2brwsr + netbeans @@ -82,6 +93,15 @@ + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.13 + + + diff -r d443b6c05a01 -r 41e1dc88a399 vm/pom.xml --- a/vm/pom.xml Fri Jan 25 15:08:24 2013 +0100 +++ b/vm/pom.xml Sun Feb 17 17:58:34 2013 +0100 @@ -12,7 +12,7 @@ 0.3-SNAPSHOT jar - Java VM for Browser + Virtual Machine for Browser http://bck2brwsr.apidesign.org @@ -68,6 +68,45 @@ 1.7 + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + + generate-js + process-classes + + java + + + + + org.apidesign.vm4brwsr.Main + + ${project.build.directory}/bck2brwsr.js + org/apidesign/vm4brwsr/Bck2Brwsr + + + + + maven-assembly-plugin + 2.4 + + + js + package + + single + + + + src/main/assembly/bck2brwsr.xml + + + + + @@ -85,19 +124,20 @@ ${project.groupId} core - 0.3-SNAPSHOT + ${project.version} jar ${project.groupId} emul.mini - 0.3-SNAPSHOT - test + ${project.version} + compile ${project.groupId} javap - 0.3-SNAPSHOT + ${project.version} + compile diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/main/assembly/bck2brwsr.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm/src/main/assembly/bck2brwsr.xml Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,36 @@ + + + + + js + + zip + + / + + + ${project.build.directory}/bck2brwsr.js + / + + + + \ No newline at end of file diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java Sun Feb 17 17:58:34 2013 +0100 @@ -40,6 +40,16 @@ * In this scenario, when a request for an unknown class is made, the loader * function is asked for its byte code and the system dynamically transforms * it to JavaScript. + *

+ * Instead of a loader function, one can also provide a URL to a JAR file. + * The bck2brwsr system will do its best to download the file + * and provide loader function for it automatically. + *

+ * One can provide as many loader functions and JAR URL references as necessary. + * Then the initialization code would look like:

+ * var vm = bck2brwsr(url1, url2, fnctn1, url3, functn2);
+ * 
+ * The provided URLs and loader functions will be consulted one by one. * * @author Jaroslav Tulach */ diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Sun Feb 17 17:58:34 2013 +0100 @@ -113,12 +113,7 @@ final String className = className(jc); out.append("\n\n").append(assignClass(className)); out.append("function CLS() {"); - out.append("\n if (!CLS.prototype.$instOf_").append(className).append(") {"); - for (FieldData v : jc.getFields()) { - if (v.isStatic()) { - out.append("\n CLS.").append(v.getName()).append(initField(v)); - } - } + out.append("\n if (!CLS.$class) {"); if (proto == null) { String sc = jc.getSuperClassName(); // with _ out.append("\n var pp = "). @@ -134,6 +129,18 @@ out.append("\n var c = ").append(proto[0]).append(";"); out.append("\n var sprcls = null;"); } + for (FieldData v : jc.getFields()) { + if (v.isStatic()) { + out.append("\n CLS.").append(v.getName()).append(initField(v)); + } else { + out.append("\n c._").append(v.getName()).append(" = function (v) {") + .append(" if (arguments.length == 1) this.fld_"). + append(className).append('_').append(v.getName()) + .append(" = v; return this.fld_"). + append(className).append('_').append(v.getName()) + .append("; };"); + } + } for (MethodData m : jc.getMethods()) { byte[] onlyArr = m.findAnnotationData(true); String[] only = findAnnotation(onlyArr, jc, @@ -168,12 +175,14 @@ out.append("\n };"); } out.append(prefix).append(mn).append(".access = " + m.getAccess()).append(";"); + out.append(prefix).append(mn).append(".cls = CLS;"); } out.append("\n c.constructor = CLS;"); out.append("\n c.$instOf_").append(className).append(" = true;"); for (String superInterface : jc.getSuperInterfaces()) { out.append("\n c.$instOf_").append(superInterface.replace('/', '_')).append(" = true;"); } + out.append("\n CLS.$class = 'temp';"); out.append("\n CLS.$class = "); out.append(accessClass("java_lang_Class(true);")); out.append("\n CLS.$class.jvmName = '").append(jc.getClassName()).append("';"); @@ -206,6 +215,7 @@ } if (!v.isStatic()) { out.append("\n this.fld_"). + append(className).append('_'). append(v.getName()).append(initField(v)); } } @@ -270,6 +280,7 @@ int lastStackFrame = -1; TrapData[] previousTrap = null; + boolean wide = false; out.append("\n var gt = 0;\n for(;;) switch(gt) {\n"); for (int i = 0; i < byteCodes.length; i++) { @@ -295,7 +306,7 @@ out.append("try {"); previousTrap = trap.current(); } - final int c = readByte(byteCodes, i); + final int c = readUByte(byteCodes, i); switch (c) { case opc_aload_0: emit(out, "var @1 = @2;", smapper.pushA(), lmapper.getA(0)); @@ -358,61 +369,91 @@ emit(out, "var @1 = @2;", smapper.pushD(), lmapper.getD(3)); break; case opc_iload: { - final int indx = readByte(byteCodes, ++i); + ++i; + final int indx = wide ? readUShort(byteCodes, i++) + : readUByte(byteCodes, i); + wide = false; emit(out, "var @1 = @2;", smapper.pushI(), lmapper.getI(indx)); break; } case opc_lload: { - final int indx = readByte(byteCodes, ++i); + ++i; + final int indx = wide ? readUShort(byteCodes, i++) + : readUByte(byteCodes, i); + wide = false; emit(out, "var @1 = @2;", smapper.pushL(), lmapper.getL(indx)); break; } case opc_fload: { - final int indx = readByte(byteCodes, ++i); + ++i; + final int indx = wide ? readUShort(byteCodes, i++) + : readUByte(byteCodes, i); + wide = false; emit(out, "var @1 = @2;", smapper.pushF(), lmapper.getF(indx)); break; } case opc_dload: { - final int indx = readByte(byteCodes, ++i); + ++i; + final int indx = wide ? readUShort(byteCodes, i++) + : readUByte(byteCodes, i); + wide = false; emit(out, "var @1 = @2;", smapper.pushD(), lmapper.getD(indx)); break; } case opc_aload: { - final int indx = readByte(byteCodes, ++i); + ++i; + final int indx = wide ? readUShort(byteCodes, i++) + : readUByte(byteCodes, i); + wide = false; emit(out, "var @1 = @2;", smapper.pushA(), lmapper.getA(indx)); break; } case opc_istore: { - final int indx = readByte(byteCodes, ++i); + ++i; + final int indx = wide ? readUShort(byteCodes, i++) + : readUByte(byteCodes, i); + wide = false; emit(out, "var @1 = @2;", lmapper.setI(indx), smapper.popI()); break; } case opc_lstore: { - final int indx = readByte(byteCodes, ++i); + ++i; + final int indx = wide ? readUShort(byteCodes, i++) + : readUByte(byteCodes, i); + wide = false; emit(out, "var @1 = @2;", lmapper.setL(indx), smapper.popL()); break; } case opc_fstore: { - final int indx = readByte(byteCodes, ++i); + ++i; + final int indx = wide ? readUShort(byteCodes, i++) + : readUByte(byteCodes, i); + wide = false; emit(out, "var @1 = @2;", lmapper.setF(indx), smapper.popF()); break; } case opc_dstore: { - final int indx = readByte(byteCodes, ++i); + ++i; + final int indx = wide ? readUShort(byteCodes, i++) + : readUByte(byteCodes, i); + wide = false; emit(out, "var @1 = @2;", lmapper.setD(indx), smapper.popD()); break; } case opc_astore: { - final int indx = readByte(byteCodes, ++i); + ++i; + final int indx = wide ? readUShort(byteCodes, i++) + : readUByte(byteCodes, i); + wide = false; emit(out, "var @1 = @2;", lmapper.setA(indx), smapper.popA()); break; @@ -481,7 +522,7 @@ emit(out, "@1 = @1.add32(@2);", smapper.getI(1), smapper.popI()); break; case opc_ladd: - emit(out, "@1 += @2;", smapper.getL(1), smapper.popL()); + emit(out, "@1 = @1.add64(@2);", smapper.getL(1), smapper.popL()); break; case opc_fadd: emit(out, "@1 += @2;", smapper.getF(1), smapper.popF()); @@ -493,7 +534,7 @@ emit(out, "@1 = @1.sub32(@2);", smapper.getI(1), smapper.popI()); break; case opc_lsub: - emit(out, "@1 -= @2;", smapper.getL(1), smapper.popL()); + emit(out, "@1 = @1.sub64(@2);", smapper.getL(1), smapper.popL()); break; case opc_fsub: emit(out, "@1 -= @2;", smapper.getF(1), smapper.popF()); @@ -505,7 +546,7 @@ emit(out, "@1 = @1.mul32(@2);", smapper.getI(1), smapper.popI()); break; case opc_lmul: - emit(out, "@1 *= @2;", smapper.getL(1), smapper.popL()); + emit(out, "@1 = @1.mul64(@2);", smapper.getL(1), smapper.popL()); break; case opc_fmul: emit(out, "@1 *= @2;", smapper.getF(1), smapper.popF()); @@ -518,7 +559,7 @@ smapper.getI(1), smapper.popI()); break; case opc_ldiv: - emit(out, "@1 = Math.floor(@1 / @2);", + emit(out, "@1 = @1.div64(@2);", smapper.getL(1), smapper.popL()); break; case opc_fdiv: @@ -531,7 +572,8 @@ emit(out, "@1 %= @2;", smapper.getI(1), smapper.popI()); break; case opc_lrem: - emit(out, "@1 %= @2;", smapper.getL(1), smapper.popL()); + emit(out, "@1 = @1.mod64(@2);", + smapper.getL(1), smapper.popL()); break; case opc_frem: emit(out, "@1 %= @2;", smapper.getF(1), smapper.popF()); @@ -543,25 +585,25 @@ emit(out, "@1 &= @2;", smapper.getI(1), smapper.popI()); break; case opc_land: - emit(out, "@1 &= @2;", smapper.getL(1), smapper.popL()); + emit(out, "@1 = @1.and64(@2);", smapper.getL(1), smapper.popL()); break; case opc_ior: emit(out, "@1 |= @2;", smapper.getI(1), smapper.popI()); break; case opc_lor: - emit(out, "@1 |= @2;", smapper.getL(1), smapper.popL()); + emit(out, "@1 = @1.or64(@2);", smapper.getL(1), smapper.popL()); break; case opc_ixor: emit(out, "@1 ^= @2;", smapper.getI(1), smapper.popI()); break; case opc_lxor: - emit(out, "@1 ^= @2;", smapper.getL(1), smapper.popL()); + emit(out, "@1 = @1.xor64(@2);", smapper.getL(1), smapper.popL()); break; case opc_ineg: - emit(out, "@1 = -@1;", smapper.getI(0)); + emit(out, "@1 = @1.neg32();", smapper.getI(0)); break; case opc_lneg: - emit(out, "@1 = -@1;", smapper.getL(0)); + emit(out, "@1 = @1.neg64();", smapper.getL(0)); break; case opc_fneg: emit(out, "@1 = -@1;", smapper.getF(0)); @@ -573,23 +615,28 @@ emit(out, "@1 <<= @2;", smapper.getI(1), smapper.popI()); break; case opc_lshl: - emit(out, "@1 <<= @2;", smapper.getL(1), smapper.popI()); + emit(out, "@1 = @1.shl64(@2);", smapper.getL(1), smapper.popI()); break; case opc_ishr: emit(out, "@1 >>= @2;", smapper.getI(1), smapper.popI()); break; case opc_lshr: - emit(out, "@1 >>= @2;", smapper.getL(1), smapper.popI()); + emit(out, "@1 = @1.shr64(@2);", smapper.getL(1), smapper.popI()); break; case opc_iushr: emit(out, "@1 >>>= @2;", smapper.getI(1), smapper.popI()); break; case opc_lushr: - emit(out, "@1 >>>= @2;", smapper.getL(1), smapper.popI()); + emit(out, "@1 = @1.ushr64(@2);", smapper.getL(1), smapper.popI()); break; case opc_iinc: { - final int varIndx = readByte(byteCodes, ++i); - final int incrBy = byteCodes[++i]; + ++i; + final int varIndx = wide ? readUShort(byteCodes, i++) + : readUByte(byteCodes, i); + ++i; + final int incrBy = wide ? readShort(byteCodes, i++) + : byteCodes[i]; + wide = false; if (incrBy == 1) { emit(out, "@1++;", lmapper.getI(varIndx)); } else { @@ -627,14 +674,14 @@ emit(out, "var @2 = @1;", smapper.popI(), smapper.pushD()); break; case opc_l2i: - emit(out, "var @2 = @1;", smapper.popL(), smapper.pushI()); + emit(out, "var @2 = @1.toInt32();", smapper.popL(), smapper.pushI()); break; // max int check? case opc_l2f: - emit(out, "var @2 = @1;", smapper.popL(), smapper.pushF()); + emit(out, "var @2 = @1.toFP();", smapper.popL(), smapper.pushF()); break; case opc_l2d: - emit(out, "var @2 = @1;", smapper.popL(), smapper.pushD()); + emit(out, "var @2 = @1.toFP();", smapper.popL(), smapper.pushD()); break; case opc_f2d: emit(out, "var @2 = @1;", smapper.popF(), smapper.pushD()); @@ -647,7 +694,7 @@ smapper.popF(), smapper.pushI()); break; case opc_f2l: - emit(out, "var @2 = Math.floor(@1);", + emit(out, "var @2 = Math.floor(@1).toLong();", smapper.popF(), smapper.pushL()); break; case opc_d2i: @@ -655,7 +702,7 @@ smapper.popD(), smapper.pushI()); break; case opc_d2l: - emit(out, "var @2 = Math.floor(@1);", + emit(out, "var @2 = Math.floor(@1).toLong();", smapper.popD(), smapper.pushL()); break; case opc_i2b: @@ -713,7 +760,7 @@ emit(out, "var @1 = 5;", smapper.pushI()); break; case opc_ldc: { - int indx = readByte(byteCodes, ++i); + int indx = readUByte(byteCodes, ++i); String v = encodeConstant(indx); int type = VarType.fromConstantType(jc.getTag(indx)); emit(out, "var @1 = @2;", smapper.pushT(type), v); @@ -725,11 +772,19 @@ i += 2; String v = encodeConstant(indx); int type = VarType.fromConstantType(jc.getTag(indx)); - emit(out, "var @1 = @2;", smapper.pushT(type), v); + if (type == VarType.LONG) { + final Long lv = new Long(v); + final int low = (int)(lv.longValue() & 0xFFFFFFFF); + final int hi = (int)(lv.longValue() >> 32); + emit(out, "var @1 = 0x@3.next32(0x@2);", smapper.pushL(), + Integer.toHexString(low), Integer.toHexString(hi)); + } else { + emit(out, "var @1 = @2;", smapper.pushT(type), v); + } break; } case opc_lcmp: - emit(out, "var @3 = (@2 == @1) ? 0 : ((@2 < @1) ? -1 : 1);", + emit(out, "var @3 = @2.compare64(@1);", smapper.popL(), smapper.popL(), smapper.pushI()); break; case opc_fcmpl: @@ -896,7 +951,7 @@ break; } case opc_newarray: - int atype = readByte(byteCodes, ++i); + int atype = readUByte(byteCodes, ++i); String jvmType; switch (atype) { case 4: jvmType = "[Z"; break; @@ -929,7 +984,7 @@ int type = readIntArg(byteCodes, i); i += 2; String typeName = jc.getClassName(type); - int dim = readByte(byteCodes, ++i); + int dim = readUByte(byteCodes, ++i); StringBuilder dims = new StringBuilder(); dims.append('['); for (int d = 0; d < dim; d++) { @@ -1004,16 +1059,16 @@ break; } case opc_dup2: { - if (smapper.get(0).isCategory2()) { - final Variable v = smapper.get(0); + final Variable vi1 = smapper.get(0); + + if (vi1.isCategory2()) { emit(out, "var @1 = @2;", - smapper.pushT(v.getType()), v); + smapper.pushT(vi1.getType()), vi1); } else { - final Variable v1 = smapper.get(0); - final Variable v2 = smapper.get(1); + final Variable vi2 = smapper.get(1); emit(out, "var @1 = @2, @3 = @4;", - smapper.pushT(v2.getType()), v2, - smapper.pushT(v1.getType()), v1); + smapper.pushT(vi2.getType()), vi2, + smapper.pushT(vi1.getType()), vi1); } break; } @@ -1028,10 +1083,11 @@ vo1, vi1, vo2, vi2, vo3, vo1); break; } - case opc_dup_x2: { - if (smapper.get(1).isCategory2()) { - final Variable vi1 = smapper.pop(); - final Variable vi2 = smapper.pop(); + case opc_dup2_x1: { + final Variable vi1 = smapper.pop(); + final Variable vi2 = smapper.pop(); + + if (vi1.isCategory2()) { final Variable vo3 = smapper.pushT(vi1.getType()); final Variable vo2 = smapper.pushT(vi2.getType()); final Variable vo1 = smapper.pushT(vi1.getType()); @@ -1039,8 +1095,32 @@ emit(out, "var @1 = @2, @3 = @4, @5 = @6;", vo1, vi1, vo2, vi2, vo3, vo1); } else { - final Variable vi1 = smapper.pop(); - final Variable vi2 = smapper.pop(); + final Variable vi3 = smapper.pop(); + final Variable vo5 = smapper.pushT(vi2.getType()); + final Variable vo4 = smapper.pushT(vi1.getType()); + final Variable vo3 = smapper.pushT(vi3.getType()); + final Variable vo2 = smapper.pushT(vi2.getType()); + final Variable vo1 = smapper.pushT(vi1.getType()); + + emit(out, "var @1 = @2, @3 = @4, @5 = @6,", + vo1, vi1, vo2, vi2, vo3, vi3); + emit(out, " @1 = @2, @3 = @4;", + vo4, vo1, vo5, vo2); + } + break; + } + case opc_dup_x2: { + final Variable vi1 = smapper.pop(); + final Variable vi2 = smapper.pop(); + + if (vi2.isCategory2()) { + final Variable vo3 = smapper.pushT(vi1.getType()); + final Variable vo2 = smapper.pushT(vi2.getType()); + final Variable vo1 = smapper.pushT(vi1.getType()); + + emit(out, "var @1 = @2, @3 = @4, @5 = @6;", + vo1, vi1, vo2, vi2, vo3, vo1); + } else { final Variable vi3 = smapper.pop(); final Variable vo4 = smapper.pushT(vi1.getType()); final Variable vo3 = smapper.pushT(vi3.getType()); @@ -1052,6 +1132,76 @@ } break; } + case opc_dup2_x2: { + final Variable vi1 = smapper.pop(); + final Variable vi2 = smapper.pop(); + + if (vi1.isCategory2()) { + if (vi2.isCategory2()) { + final Variable vo3 = smapper.pushT(vi1.getType()); + final Variable vo2 = smapper.pushT(vi2.getType()); + final Variable vo1 = smapper.pushT(vi1.getType()); + + emit(out, "var @1 = @2, @3 = @4, @5 = @6;", + vo1, vi1, vo2, vi2, vo3, vo1); + } else { + final Variable vi3 = smapper.pop(); + final Variable vo4 = smapper.pushT(vi1.getType()); + final Variable vo3 = smapper.pushT(vi3.getType()); + final Variable vo2 = smapper.pushT(vi2.getType()); + final Variable vo1 = smapper.pushT(vi1.getType()); + + emit(out, "var @1 = @2, @3 = @4, @5 = @6, @7 = @8;", + vo1, vi1, vo2, vi2, vo3, vi3, vo4, vo1); + } + } else { + final Variable vi3 = smapper.pop(); + + if (vi3.isCategory2()) { + final Variable vo5 = smapper.pushT(vi2.getType()); + final Variable vo4 = smapper.pushT(vi1.getType()); + final Variable vo3 = smapper.pushT(vi3.getType()); + final Variable vo2 = smapper.pushT(vi2.getType()); + final Variable vo1 = smapper.pushT(vi1.getType()); + + emit(out, "var @1 = @2, @3 = @4, @5 = @6,", + vo1, vi1, vo2, vi2, vo3, vi3); + emit(out, " @1 = @2, @3 = @4;", + vo4, vo1, vo5, vo2); + } else { + final Variable vi4 = smapper.pop(); + final Variable vo6 = smapper.pushT(vi2.getType()); + final Variable vo5 = smapper.pushT(vi1.getType()); + final Variable vo4 = smapper.pushT(vi4.getType()); + final Variable vo3 = smapper.pushT(vi3.getType()); + final Variable vo2 = smapper.pushT(vi2.getType()); + final Variable vo1 = smapper.pushT(vi1.getType()); + + emit(out, "var @1 = @2, @3 = @4, @5 = @6, @7 = @8,", + vo1, vi1, vo2, vi2, vo3, vi3, vo4, vi4); + emit(out, " @1 = @2, @3 = @4;", + vo5, vo1, vo6, vo2); + } + } + break; + } + case opc_swap: { + final Variable vi1 = smapper.get(0); + final Variable vi2 = smapper.get(1); + + if (vi1.getType() == vi2.getType()) { + final Variable tmp = smapper.pushT(vi1.getType()); + + emit(out, "var @1 = @2, @2 = @3, @3 = @1;", + tmp, vi1, vi2); + smapper.pop(1); + } else { + smapper.pop(2); + smapper.pushT(vi1.getType()); + smapper.pushT(vi2.getType()); + } + break; + } case opc_bipush: emit(out, "var @1 = @2;", smapper.pushI(), Integer.toString(byteCodes[++i])); @@ -1066,8 +1216,26 @@ int indx = readIntArg(byteCodes, i); String[] fi = jc.getFieldInfoName(indx); final int type = VarType.fromFieldType(fi[2].charAt(0)); - emit(out, "var @2 = @1.fld_@3;", - smapper.popA(), smapper.pushT(type), fi[1]); + final String mangleClass = mangleSig(fi[0]); + final String mangleClassAccess = accessClass(mangleClass); + emit(out, "var @2 = @4(false)._@3.call(@1);", + smapper.popA(), + smapper.pushT(type), fi[1], mangleClassAccess + ); + i += 2; + break; + } + case opc_putfield: { + int indx = readIntArg(byteCodes, i); + String[] fi = jc.getFieldInfoName(indx); + final int type = VarType.fromFieldType(fi[2].charAt(0)); + final String mangleClass = mangleSig(fi[0]); + final String mangleClassAccess = accessClass(mangleClass); + emit(out, "@4(false)._@3.call(@2, @1);", + smapper.popT(type), + smapper.popA(), fi[1], + mangleClassAccess + ); i += 2; break; } @@ -1082,15 +1250,6 @@ addReference(fi[0]); break; } - case opc_putfield: { - int indx = readIntArg(byteCodes, i); - String[] fi = jc.getFieldInfoName(indx); - final int type = VarType.fromFieldType(fi[2].charAt(0)); - emit(out, "@2.fld_@3 = @1;", - smapper.popT(type), smapper.popA(), fi[1]); - i += 2; - break; - } case opc_putstatic: { int indx = readIntArg(byteCodes, i); String[] fi = jc.getFieldInfoName(indx); @@ -1121,7 +1280,7 @@ int indx = readIntArg(byteCodes, i); final String type = jc.getClassName(indx); if (!type.startsWith("[")) { - emit(out, "var @2 = @1.$instOf_@3 ? 1 : 0;", + emit(out, "var @2 = @1 != null && @1.$instOf_@3 ? 1 : 0;", smapper.popA(), smapper.pushI(), type.replace('/', '_')); } else { @@ -1154,7 +1313,12 @@ break; } + case opc_wide: + wide = true; + break; + default: { + wide = false; emit(out, "throw 'unknown bytecode @1';", Integer.toString(c)); } @@ -1162,7 +1326,7 @@ if (debug(" //")) { for (int j = prev; j <= i; j++) { out.append(" "); - final int cc = readByte(byteCodes, j); + final int cc = readUByte(byteCodes, j); out.append(Integer.toString(cc)); } } @@ -1191,17 +1355,27 @@ final int indxLo = byteCodes[offsetInstruction + 2]; return (indxHi & 0xffffff00) | (indxLo & 0xff); } - private int readInt4(byte[] byteCodes, int offsetInstruction) { - final int d = byteCodes[offsetInstruction + 0] << 24; - final int c = byteCodes[offsetInstruction + 1] << 16; - final int b = byteCodes[offsetInstruction + 2] << 8; - final int a = byteCodes[offsetInstruction + 3]; + private int readInt4(byte[] byteCodes, int offset) { + final int d = byteCodes[offset + 0] << 24; + final int c = byteCodes[offset + 1] << 16; + final int b = byteCodes[offset + 2] << 8; + final int a = byteCodes[offset + 3]; return (d & 0xff000000) | (c & 0xff0000) | (b & 0xff00) | (a & 0xff); } - private int readByte(byte[] byteCodes, int offsetInstruction) { - return byteCodes[offsetInstruction] & 0xff; + private int readUByte(byte[] byteCodes, int offset) { + return byteCodes[offset] & 0xff; } - + + private int readUShort(byte[] byteCodes, int offset) { + return ((byteCodes[offset] & 0xff) << 8) + | (byteCodes[offset + 1] & 0xff); + } + + private int readShort(byte[] byteCodes, int offset) { + return (byteCodes[offset] << 8) + | (byteCodes[offset + 1] & 0xff); + } + private static void countArgs(String descriptor, char[] returnType, StringBuilder sig, StringBuilder cnt) { int i = 0; Boolean count = null; @@ -1279,6 +1453,10 @@ } } + static String mangleSig(String sig) { + return mangleSig(sig, 0, sig.length()); + } + private static String mangleSig(String txt, int first, int last) { StringBuilder sb = new StringBuilder(); for (int i = first; i < last; i++) { @@ -1443,7 +1621,7 @@ final String jvmType = "Lorg/apidesign/bck2brwsr/core/JavaScriptBody;"; class P extends AnnotationParser { public P() { - super(false); + super(false, true); } int cnt; @@ -1499,7 +1677,7 @@ final String[] values = new String[attrNames.length]; final boolean[] found = { false }; final String jvmType = "L" + className.replace('.', '/') + ";"; - AnnotationParser ap = new AnnotationParser(false) { + AnnotationParser ap = new AnnotationParser(false, true) { @Override protected void visitAttr(String type, String attr, String at, String value) { if (type.equals(jvmType)) { @@ -1536,35 +1714,71 @@ return " = null;"; } - private static void generateAnno(ClassData cd, final Appendable out, byte[] data) throws IOException { - AnnotationParser ap = new AnnotationParser(true) { - int anno; - int cnt; + private void generateAnno(ClassData cd, final Appendable out, byte[] data) throws IOException { + AnnotationParser ap = new AnnotationParser(true, false) { + int[] cnt = new int[32]; + int depth; @Override - protected void visitAnnotationStart(String type) throws IOException { - if (anno++ > 0) { + protected void visitAnnotationStart(String attrType, boolean top) throws IOException { + final String slashType = attrType.substring(1, attrType.length() - 1); + requireReference(slashType); + + if (cnt[depth]++ > 0) { out.append(","); } - out.append('"').append(type).append("\" : {\n"); - cnt = 0; + if (top) { + out.append('"').append(attrType).append("\" : "); + } + out.append("{\n"); + cnt[++depth] = 0; } @Override - protected void visitAnnotationEnd(String type) throws IOException { + protected void visitAnnotationEnd(String type, boolean top) throws IOException { out.append("\n}\n"); + depth--; + } + + @Override + protected void visitValueStart(String attrName, char type) throws IOException { + if (cnt[depth]++ > 0) { + out.append(",\n"); + } + cnt[++depth] = 0; + if (attrName != null) { + out.append(attrName).append(" : "); + } + if (type == '[') { + out.append("["); + } + } + + @Override + protected void visitValueEnd(String attrName, char type) throws IOException { + if (type == '[') { + out.append("]"); + } + depth--; } @Override protected void visitAttr(String type, String attr, String attrType, String value) throws IOException { - if (attr == null) { + if (attr == null && value == null) { return; } - if (cnt++ > 0) { - out.append(",\n"); - } - out.append(attr).append("__").append(attrType).append(" : ").append(value); + out.append(value); + } + + @Override + protected void visitEnumAttr(String type, String attr, String attrType, String value) + throws IOException { + final String slashType = attrType.substring(1, attrType.length() - 1); + requireReference(slashType); + + out.append(accessClass(slashType.replace('/', '_'))) + .append("(false).constructor.").append(value); } }; ap.parse(data, cd); diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/main/java/org/apidesign/vm4brwsr/ParseMan.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/ParseMan.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,57 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import java.io.IOException; +import java.io.InputStream; +import org.apidesign.bck2brwsr.emul.lang.ManifestInputStream; + +/** + * + * @author Jaroslav Tulach + */ +final class ParseMan extends ManifestInputStream { + private String cp; + private String mc; + + public ParseMan(InputStream is) throws IOException { + super(is); + readAttributes(new byte[512]); + } + + @Override + protected String putValue(String key, String value) { + if ("Class-Path".equals(key)) { + cp = value; + } + if ("Main-Class".equals(key)) { + mc = value; + } + return null; + } + + String getMainClass() { + return mc; + } + + @Override + public String toString() { + return cp; + } + +} diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/main/java/org/apidesign/vm4brwsr/VM.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Sun Feb 17 17:58:34 2013 +0100 @@ -31,7 +31,12 @@ static { // uses VMLazy to load dynamic classes - VMLazy.init(); + boolean assertsOn = false; + assert assertsOn = true; + if (assertsOn) { + VMLazy.init(); + Zips.init(); + } } @Override @@ -43,8 +48,7 @@ new VM(out).doCompile(l, names); } protected void doCompile(Bck2Brwsr.Resources l, StringArray names) throws IOException { - out.append("(function VM(global) {"); - out.append("\n var vm = {};"); + out.append("(function VM(global) {var fillInVMSkeleton = function(vm) {"); StringArray processed = new StringArray(); StringArray initCode = new StringArray(); for (String baseClass : names.toArray()) { @@ -112,25 +116,31 @@ } } out.append( - " global.bck2brwsr = function() {\n" - + " var args = arguments;\n" + " return vm;\n" + + " };\n" + + " global.bck2brwsr = function() {\n" + + " var args = Array.prototype.slice.apply(arguments);\n" + + " var vm = fillInVMSkeleton({});\n" + " var loader = {};\n" + " loader.vm = vm;\n" + " loader.loadClass = function(name) {\n" + " var attr = name.replace__Ljava_lang_String_2CC('.','_');\n" + " var fn = vm[attr];\n" + " if (fn) return fn(false);\n" - + " if (!args[0]) throw 'bck2brwsr initialized without loader function, cannot load ' + name;\n" + " return vm.org_apidesign_vm4brwsr_VMLazy(false).\n" + " load__Ljava_lang_Object_2Ljava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2(loader, name, args);\n" + " }\n" - + " if (args[0]) {\n" - + " vm.loadClass = loader.loadClass;\n" - + " vm.loadBytes = function(name) {\n" - + " if (!args[0]) throw 'bck2brwsr initialized without loader function, cannot load ' + name;\n" - + " return args[0](name);\n" - + " }\n" + + " if (vm.loadClass) {\n" + + " throw 'Cannot initialize the bck2brwsr VM twice!';\n" + " }\n" + + " vm.loadClass = loader.loadClass;\n" + + " vm.loadBytes = function(name) {\n" + + " return vm.org_apidesign_vm4brwsr_VMLazy(false).\n" + + " loadBytes___3BLjava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2(loader, name, args);\n" + + " }\n" + + " vm.java_lang_reflect_Array(false);\n" + + " vm.org_apidesign_vm4brwsr_VMLazy(false).\n" + + " loadBytes___3BLjava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2(loader, null, args);\n" + " return loader;\n" + " };\n"); out.append("}(this));"); diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java Sun Feb 17 17:58:34 2013 +0100 @@ -20,6 +20,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Array; import org.apidesign.bck2brwsr.core.JavaScriptBody; /** @@ -38,23 +39,19 @@ static void init() { } - @JavaScriptBody(args={"l", "res", "args" }, body = "" - + "\ntry {" - + "\n return args[0](res.toString());" - + "\n} catch (x) {" - + "\n throw Object.getOwnPropertyNames(l.vm).toString() + x.toString();" - + "\n}") - private static native byte[] read(Object l, String res, Object[] args); - static Object load(Object loader, String name, Object[] arguments) throws IOException, ClassNotFoundException { return new VMLazy(loader, arguments).load(name, false); } + static byte[] loadBytes(Object loader, String name, Object[] arguments) throws Exception { + return Zips.loadFromCp(arguments, name); + } + private Object load(String name, boolean instance) throws IOException, ClassNotFoundException { String res = name.replace('.', '/') + ".class"; - byte[] arr = read(loader, res, args); + byte[] arr = Zips.loadFromCp(args, res); if (arr == null) { throw new ClassNotFoundException(name); } @@ -117,8 +114,8 @@ body = "var cls = n.replace__Ljava_lang_String_2CC('/','_').toString();" + "\nvar dot = n.replace__Ljava_lang_String_2CC('/','.').toString();" - + "\nvar lazy = this.fld_lazy;" - + "\nvar loader = lazy.fld_loader;" + + "\nvar lazy = this._lazy();" + + "\nvar loader = lazy._loader();" + "\nvar vm = loader.vm;" + "\nif (vm[cls]) return false;" + "\nvm[cls] = function() {" diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/main/java/org/apidesign/vm4brwsr/VarType.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/VarType.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/VarType.java Sun Feb 17 17:58:34 2013 +0100 @@ -45,13 +45,13 @@ return VarType.DOUBLE; case RuntimeConstants.ITEM_Long: return VarType.LONG; + case RuntimeConstants.ITEM_Null: + case RuntimeConstants.ITEM_InitObject: case RuntimeConstants.ITEM_Object: + case RuntimeConstants.ITEM_NewObject: return VarType.REFERENCE; case RuntimeConstants.ITEM_Bogus: - case RuntimeConstants.ITEM_Null: - case RuntimeConstants.ITEM_InitObject: - case RuntimeConstants.ITEM_NewObject: /* unclear how to handle for now */ default: throw new IllegalStateException("Unhandled stack map type"); diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/main/java/org/apidesign/vm4brwsr/Zips.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/Zips.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,187 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.apidesign.bck2brwsr.emul.zip.FastJar; + +/** Conversion from classpath to load function. + * + * @author Jaroslav Tulach + */ +final class Zips { + private final FastJar fj; + + private Zips(String path, byte[] zipData) throws IOException { + long bef = timeNow(); + fj = new FastJar(zipData); + for (FastJar.Entry e : fj.list()) { + putRes(e.name, e); + } + log("Iterating thru " + path + " took " + (timeNow() - bef) + "ms"); + } + + public static void init() { + } + @JavaScriptBody(args = { "arr" }, body = "return arr.length;") + private static native int length(Object arr); + @JavaScriptBody(args = { "arr", "index" }, body = "return arr[index];") + private static native Object at(Object arr, int index); + @JavaScriptBody(args = { "arr", "index", "value" }, body = "arr[index] = value; return value;") + private static native Object set(Object arr, int index, Object value); + + public static byte[] loadFromCp(Object classpath, String res) + throws IOException, ClassNotFoundException { + for (int i = 0; i < length(classpath); i++) { + Object c = at(classpath, i); + if (c instanceof String) { + try { + String url = (String)c; + final Zips z = toZip(url); + c = set(classpath, i, z); + final byte[] man = z.findRes("META-INF/MANIFEST.MF"); + if (man != null) { + String mainClass = processClassPathAttr(man, url, classpath); +// if (mainClass != null) { +// Class.forName(mainClass); +// } + } + } catch (IOException ex) { + set(classpath, i, ex); + log("Cannot load " + c + " - " + ex.getClass().getName() + ":" + ex.getMessage()); + } + } + if (res != null) { + byte[] checkRes; + if (c instanceof Zips) { + checkRes = ((Zips)c).findRes(res); + } else { + checkRes = callFunction(c, res); + } + if (checkRes != null) { + return checkRes; + } + } + } + return null; + } + + @JavaScriptBody(args = { "fn", "res" }, body = + "if (typeof fn === 'function') return fn(res);\n" + + "return null;" + ) + private static native byte[] callFunction(Object fn, String res); + + @JavaScriptBody(args = { "msg" }, body = "console.log(msg.toString());") + private static native void log(String msg); + + private byte[] findRes(String res) throws IOException { + Object arr = findResImpl(res); + if (arr instanceof FastJar.Entry) { + long bef = timeNow(); + InputStream zip = fj.getInputStream((FastJar.Entry)arr); + arr = readFully(new byte[512], zip); + putRes(res, arr); + log("Reading " + res + " took " + (timeNow() - bef) + "ms"); + } + return (byte[]) arr; + } + + @JavaScriptBody(args = { "res" }, body = "var r = this[res]; return r ? r : null;") + private native Object findResImpl(String res); + + @JavaScriptBody(args = { "res", "arr" }, body = "this[res] = arr;") + private native void putRes(String res, Object arr); + + private static Zips toZip(String path) throws IOException { + URL u = new URL(path); + byte[] zipData = (byte[]) u.getContent(new Class[] { byte[].class }); + return new Zips(path, zipData); + } + + private static String processClassPathAttr(final byte[] man, String url, Object classpath) throws IOException { + try (ParseMan is = new ParseMan(new ByteArrayInputStream(man))) { + String cp = is.toString(); + if (cp != null) { + cp = cp.trim(); + for (int p = 0; p < cp.length();) { + int n = cp.indexOf(' ', p); + if (n == -1) { + n = cp.length(); + } + String el = cp.substring(p, n); + URL u = new URL(new URL(url), el); + classpath = addToArray(classpath, u.toString()); + p = n + 1; + } + } + return is.getMainClass(); + } + } + + private static Object addToArray(Object arr, String value) { + final int last = length(arr); + Object ret = enlargeArray(arr, last + 1); + set(ret, last, value); + return ret; + } + + @JavaScriptBody(args = { "arr", "len" }, body = "while (arr.length < len) arr.push(null); return arr;") + private static native Object enlargeArray(Object arr, int len); + @JavaScriptBody(args = { "arr", "len" }, body = "while (arr.length < len) arr.push(0);") + private static native void enlargeBytes(byte[] arr, int len); + + @JavaScriptBody(args = { "arr", "len" }, body = "arr.splice(len, arr.length - len);") + private static native void sliceArray(byte[] arr, int len); + + private static Object readFully(byte[] arr, InputStream zip) throws IOException { + int offset = 0; + for (;;) { + int len = zip.read(arr, offset, arr.length - offset); + if (len == -1) { + break; + } + offset += len; + if (offset == arr.length) { + enlargeBytes(arr, arr.length + 4096); + } + } + sliceArray(arr, offset); + return arr; + } + + private static long timeNow() { + double time = m(); + if (time >= 0) { + return (long)time; + } + return org.apidesign.bck2brwsr.emul.lang.System.currentTimeMillis(); + } + @JavaScriptBody(args = {}, body = + "if (typeof window.performance === 'undefined') return -1;\n" + + "if (typeof window.performance.now === 'undefined') return -1;\n" + + "return window.performance.now();" + ) + private static native double m(); + + +} diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/test/java/org/apidesign/vm4brwsr/ArrayTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/ArrayTest.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/ArrayTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -17,7 +17,6 @@ */ package org.apidesign.vm4brwsr; -import javax.script.Invocable; import static org.testng.Assert.*; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -76,18 +75,13 @@ assertExec("Returns 'false'", Array.class, "instanceOfArray__ZLjava_lang_Object_2", Double.valueOf(0), "non-array"); } - private static CharSequence codeSeq; - private static Invocable code; + private static TestVM code; @BeforeClass public void compileTheCode() throws Exception { - StringBuilder sb = new StringBuilder(); - code = StaticMethodTest.compileClass(sb, - "org/apidesign/vm4brwsr/Array" - ); - codeSeq = sb; + code = TestVM.compileClass("org/apidesign/vm4brwsr/Array"); } private static void assertExec(String msg, Class clazz, String method, Object expRes, Object... args) throws Exception { - StaticMethodTest.assertExec(code, codeSeq, msg, clazz, method, expRes, args); + code.assertExec(msg, clazz, method, expRes, args); } } diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -17,7 +17,6 @@ */ package org.apidesign.vm4brwsr; -import javax.script.Invocable; import org.testng.annotations.Test; import static org.testng.Assert.*; import org.testng.annotations.BeforeClass; @@ -87,12 +86,27 @@ @Test public void jsAnnotation() throws Exception { assertExec("Check class annotation", Classes.class, "getMarker__I", Double.valueOf(10)); } + @Test public void jsArrayAnnotation() throws Exception { + assertExec("Check array annotation", Classes.class, "getMarkerNicknames__Ljava_lang_String_2", Classes.getMarkerNicknames()); + } + @Test public void jsEnumAnnotation() throws Exception { + assertExec("Check enum annotation", Classes.class, "getMarkerE__Ljava_lang_String_2", Classes.getMarkerE()); + } + @Test public void jsRetentionAnnotation() throws Exception { + assertExec("Check enum annotation", Classes.class, "getRetention__Ljava_lang_String_2", Classes.getRetention()); + } @Test public void jsStringAnnotation() throws Exception { assertExec("Check class annotation", Classes.class, "getNamer__Ljava_lang_String_2Z", "my text", true); } @Test public void jsStringAnnotationFromArray() throws Exception { assertExec("Check class annotation", Classes.class, "getNamer__Ljava_lang_String_2Z", "my text", false); } + @Test public void jsInnerAnnotation() throws Exception { + assertExec("Check inner annotation", Classes.class, "getInnerNamer__I", Double.valueOf(Classes.getInnerNamer())); + } + @Test public void jsInnerAnnotationFromArray() throws Exception { + assertExec("Check inner annotation", Classes.class, "getInnerNamers__I", Double.valueOf(Classes.getInnerNamers())); + } @Test public void javaInvokeMethod() throws Exception { assertEquals(Classes.reflectiveMethodCall(true, "name"), "java.io.IOException", "Calls the name() method via reflection"); } @@ -102,6 +116,14 @@ "java.io.IOException", true, "name" ); } + + @Test public void jsMethodDeclaredInObject() throws Exception { + assertExec("Defined in Object", Classes.class, + "objectName__Ljava_lang_String_2", + "java.lang.Object" + ); + } + @Test public void jsInvokeParamMethod() throws Exception { assertExec("sums two numbers via reflection", Classes.class, "reflectiveSum__III", Double.valueOf(5), 2, 3 @@ -149,14 +171,12 @@ 0.0, "java.lang.String" ); } - /* @Test public void isInterface() throws Exception { assertExec("Calls Class.isInterface", Classes.class, "isInterface__ZLjava_lang_String_2", 1.0, "java.lang.Runnable" ); } - */ @Test public void integerType() throws Exception { assertExec("Computes the type", Classes.class, "intType__Ljava_lang_String_2", @@ -164,22 +184,23 @@ ); } - private static CharSequence codeSeq; - private static Invocable code; + private static TestVM code; @BeforeClass public void compileTheCode() throws Exception { - if (codeSeq == null) { - StringBuilder sb = new StringBuilder(); - code = StaticMethodTest.compileClass(sb, "org/apidesign/vm4brwsr/Classes"); - codeSeq = sb; - } + code = TestVM.compileClass("org/apidesign/vm4brwsr/Classes"); } private void assertExec( String msg, Class clazz, String method, Object expRes, Object... args ) throws Exception { - StaticMethodTest.assertExec(code, codeSeq, msg, clazz, method, expRes, args); + code.assertExec(msg, clazz, method, expRes, args); + } + @Test public void isClassAssignable() throws Exception { + assertExec("isAssignable works on subclasses", Classes.class, + "isClassAssignable__Z", + true + ); } } diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/test/java/org/apidesign/vm4brwsr/Classes.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java Sun Feb 17 17:58:34 2013 +0100 @@ -19,6 +19,8 @@ import java.io.IOException; import java.lang.annotation.Annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; import java.net.MalformedURLException; import org.apidesign.bck2brwsr.core.JavaScriptBody; @@ -27,8 +29,11 @@ * * @author Jaroslav Tulach */ -@ClassesMarker(number = 10) -@ClassesNamer(name = "my text") +@ClassesMarker(number = 10, nicknames = { "Ten", "Deset" }, count = ClassesMarker.E.TWO, subs = { + @ClassesMarker.Anno(Integer.SIZE), + @ClassesMarker.Anno(Integer.MIN_VALUE) +}) +@ClassesNamer(name = "my text", anno = @ClassesMarker.Anno(333)) public class Classes { public static String nameOfIO() { return nameFor(IOException.class); @@ -38,6 +43,8 @@ return c.getName(); } + private static final Class PRELOAD = Runnable.class; + public static boolean isInterface(String s) throws ClassNotFoundException { return Class.forName(s).isInterface(); } @@ -55,7 +62,7 @@ return new IOException().getClass().getName().toString(); } - @ClassesMarker(number = 1) + @ClassesMarker(number = 1, nicknames = { "One", "Jedna" } ) public static String name() { return IOException.class.getName().toString(); } @@ -65,6 +72,11 @@ public static String canonicalName() { return IOException.class.getCanonicalName(); } + + public static String objectName() throws NoSuchMethodException { + return IOException.class.getMethod("wait").getDeclaringClass().getName(); + } + public static boolean newInstance() throws Exception { IOException ioe = IOException.class.newInstance(); if (ioe instanceof IOException) { @@ -85,8 +97,45 @@ return -2; } ClassesMarker cm = Classes.class.getAnnotation(ClassesMarker.class); + assert cm instanceof Object : "Is object " + cm; + assert cm instanceof Annotation : "Is annotation " + cm; + assert !((Object)cm instanceof Class) : "Is not Class " + cm; return cm == null ? -1 : cm.number(); } + public static String getMarkerNicknames() { + ClassesMarker cm = Classes.class.getAnnotation(ClassesMarker.class); + if (cm == null) { + return null; + } + + final Object[] arr = cm.nicknames(); + assert arr instanceof Object[] : "Instance of Object array: " + arr; + assert arr instanceof String[] : "Instance of String array: " + arr; + assert !(arr instanceof Integer[]) : "Not instance of Integer array: " + arr; + + StringBuilder sb = new StringBuilder(); + for (String s : cm.nicknames()) { + sb.append(s).append("\n"); + } + return sb.toString().toString(); + } + @Retention(RetentionPolicy.CLASS) + @interface Ann { + } + + public static String getRetention() throws Exception { + Retention r = Ann.class.getAnnotation(Retention.class); + assert r != null : "Annotation is present"; + assert r.value() == RetentionPolicy.CLASS : "Policy value is OK: " + r.value(); + return r.annotationType().getName(); + } + public static String getMarkerE() { + ClassesMarker cm = Classes.class.getAnnotation(ClassesMarker.class); + if (cm == null) { + return null; + } + return cm.count().name(); + } public static String getNamer(boolean direct) { if (direct) { ClassesNamer cm = Classes.class.getAnnotation(ClassesNamer.class); @@ -99,6 +148,20 @@ } return null; } + public static int getInnerNamer() { + ClassesNamer cm = Classes.class.getAnnotation(ClassesNamer.class); + assert cm != null : "ClassesNamer is present"; + return cm.anno().value(); + } + public static int getInnerNamers() { + ClassesMarker cm = Classes.class.getAnnotation(ClassesMarker.class); + assert cm != null : "ClassesNamer is present"; + int sum = 0; + for (ClassesMarker.Anno anno : cm.subs()) { + sum += anno.value(); + } + return sum; + } public static String intType() { return Integer.TYPE.getName(); @@ -151,4 +214,20 @@ Method m = StaticMethod.class.getMethod("sum", int.class, int.class); return (int) m.invoke(null, a, b); } + + private abstract class Application { + public abstract int getID(); + } + + private class MyApplication extends Application { + @Override + public int getID() { + return 1; + } + } + + public static boolean isClassAssignable() { + return Application.class.isAssignableFrom(MyApplication.class); + } + } diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/test/java/org/apidesign/vm4brwsr/ClassesMarker.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/ClassesMarker.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/ClassesMarker.java Sun Feb 17 17:58:34 2013 +0100 @@ -27,4 +27,16 @@ @Retention(RetentionPolicy.RUNTIME) public @interface ClassesMarker { int number(); + String[] nicknames(); + E count() default E.ONE; + + enum E { + ONE, TWO; + } + + Anno[] subs() default {}; + + public @interface Anno { + int value(); + } } diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/test/java/org/apidesign/vm4brwsr/ClassesNamer.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/ClassesNamer.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/ClassesNamer.java Sun Feb 17 17:58:34 2013 +0100 @@ -27,4 +27,5 @@ @Retention(RetentionPolicy.RUNTIME) public @interface ClassesNamer { String name(); + ClassesMarker.Anno anno(); } diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/test/java/org/apidesign/vm4brwsr/ExceptionsTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/ExceptionsTest.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/ExceptionsTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -17,7 +17,6 @@ */ package org.apidesign.vm4brwsr; -import javax.script.Invocable; import javax.script.ScriptException; import static org.testng.Assert.*; import org.testng.annotations.BeforeClass; @@ -81,8 +80,7 @@ } @Test public void testThreeCalls() throws Exception { - Object vm = code.invokeFunction("bck2brwsr"); - Object clazz = code.invokeMethod(vm, "loadClass", Exceptions.class.getName()); + Object clazz = code.loadClass("loadClass", Exceptions.class.getName()); String method = "readCounter__ILjava_lang_String_2"; @@ -104,18 +102,13 @@ } } - private static CharSequence codeSeq; - private static Invocable code; + private static TestVM code; @BeforeClass public void compileTheCode() throws Exception { - StringBuilder sb = new StringBuilder(); - code = StaticMethodTest.compileClass(sb, - "org/apidesign/vm4brwsr/Exceptions" - ); - codeSeq = sb; + code = TestVM.compileClass("org/apidesign/vm4brwsr/Exceptions"); } private static void assertExec(String msg, Class clazz, String method, Object expRes, Object... args) throws Exception { - StaticMethodTest.assertExec(code, codeSeq, msg, clazz, method, expRes, args); + code.assertExec(msg, clazz, method, expRes, args); } } diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/test/java/org/apidesign/vm4brwsr/Instance.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/Instance.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/Instance.java Sun Feb 17 17:58:34 2013 +0100 @@ -68,12 +68,12 @@ GetByte i = new InstanceSub(7, 2.2d); return i.getByte(); } - public static boolean instanceOf(boolean sub) { + public static boolean instanceOf(int sub) { Instance i = createInstance(sub); return isInstanceSubOf(i); } public static int castsWork(boolean interfc) { - Instance i = createInstance(true); + Instance i = createInstance(2); if (interfc) { GetByte b = (GetByte)i; } else { @@ -85,11 +85,16 @@ private static boolean isInstanceSubOf(Instance instance) { return instance instanceof InstanceSub; } - private static Instance createInstance(boolean sub) { - return sub ? new InstanceSub(3, 0) : new Instance(); + private static Instance createInstance(int type) { + switch (type) { + case 0: return null; + case 1: return new Instance(); + case 2: return new InstanceSub(3, 0); + } + throw new IllegalArgumentException(); } private static boolean isNull() { - return createInstance(true) == null; + return createInstance(2) == null; } @JavaScriptBody(args = "obj", body = "return obj.constructor;") diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/test/java/org/apidesign/vm4brwsr/InstanceTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/InstanceTest.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/InstanceTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -17,7 +17,6 @@ */ package org.apidesign.vm4brwsr; -import javax.script.Invocable; import org.testng.annotations.Test; import org.testng.annotations.BeforeClass; @@ -80,16 +79,23 @@ @Test public void isInstanceOf() throws Exception { assertExec( "Yes, we are instance", - Instance.class, "instanceOf__ZZ", - Double.valueOf(1.0), true + Instance.class, "instanceOf__ZI", + Double.valueOf(1.0), 2 ); } @Test public void notInstanceOf() throws Exception { assertExec( "No, we are not an instance", - Instance.class, "instanceOf__ZZ", - Double.valueOf(0.0), false + Instance.class, "instanceOf__ZI", + Double.valueOf(0.0), 1 + ); + } + @Test public void nullInstanceOf() throws Exception { + assertExec( + "No, null is not an instance", + Instance.class, "instanceOf__ZI", + Double.valueOf(0.0), 0 ); } @@ -144,22 +150,17 @@ return "org/apidesign/vm4brwsr/Instance"; } - private static CharSequence codeSeq; - private static Invocable code; + private static TestVM code; @BeforeClass public void compileTheCode() throws Exception { - if (codeSeq == null) { - StringBuilder sb = new StringBuilder(); - code = StaticMethodTest.compileClass(sb, startCompilationWith()); - codeSeq = sb; - } + code = TestVM.compileClass(startCompilationWith()); } private void assertExec( String msg, Class clazz, String method, Object expRes, Object... args ) throws Exception { - StaticMethodTest.assertExec(code, codeSeq, msg, clazz, method, expRes, args); + code.assertExec(msg, clazz, method, expRes, args); } } diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/test/java/org/apidesign/vm4brwsr/NumberTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/NumberTest.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/NumberTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -17,8 +17,6 @@ */ package org.apidesign.vm4brwsr; -import javax.script.Invocable; -import javax.script.ScriptException; import static org.testng.Assert.*; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -143,45 +141,29 @@ s ); } - - private static CharSequence codeSeq; - private static Invocable code; + + private static TestVM code; @BeforeClass public void compileTheCode() throws Exception { - if (codeSeq == null) { - StringBuilder sb = new StringBuilder(); - code = StaticMethodTest.compileClass(sb, "org/apidesign/vm4brwsr/Numbers"); - codeSeq = sb; - } + code = TestVM.compileClass("org/apidesign/vm4brwsr/Numbers"); } private static void assertExec( - String msg, Class clazz, String method, Object expRes, Object... args) throws Exception { - - Object ret = null; - try { - ret = code.invokeFunction("bck2brwsr"); - ret = code.invokeMethod(ret, "loadClass", clazz.getName()); - ret = code.invokeMethod(ret, method, args); - } catch (ScriptException ex) { - fail("Execution failed in\n" + StaticMethodTest.dumpJS(codeSeq), ex); - } catch (NoSuchMethodException ex) { - fail("Cannot find method in\n" + StaticMethodTest.dumpJS(codeSeq), ex); - } - if (ret == null && expRes == null) { - return; - } - if (expRes.equals(ret)) { + String msg, Class clazz, String method, Object expRes, Object... args) throws Exception + { + Object ret = code.execCode(msg, clazz, method, expRes, args); + if (ret == null) { return; } if (expRes instanceof Double && ret instanceof Double) { double expD = ((Double)expRes).doubleValue(); double retD = ((Double)ret).doubleValue(); - assertEquals(retD, expD, 0.000004, msg + " was " + ret + "\n" + StaticMethodTest.dumpJS(codeSeq)); + assertEquals(retD, expD, 0.000004, msg + " " + + code.toString()); return; } - assertEquals(ret, expRes, msg + "was: " + ret + "\n" + StaticMethodTest.dumpJS(codeSeq)); + assertEquals(ret, expRes, msg + " " + code.toString()); } } diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/test/java/org/apidesign/vm4brwsr/StaticMethodTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/StaticMethodTest.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/StaticMethodTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -17,16 +17,6 @@ */ package org.apidesign.vm4brwsr; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.Enumeration; -import javax.script.Invocable; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; import static org.testng.Assert.*; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -78,6 +68,89 @@ 3.0d, 1l ); } + + @Test public void rintNegativeUp() throws Exception { + final double cnts = -453904.634; + assertExec( + "Should round up to end with 5", + Math.class, "rint__DD", + -453905.0, cnts + ); + } + + @Test public void rintNegativeDown() throws Exception { + final double cnts = -453904.434; + assertExec( + "Should round up to end with 4", + Math.class, "rint__DD", + -453904.0, cnts + ); + } + + @Test public void rintPositiveUp() throws Exception { + final double cnts = 453904.634; + assertExec( + "Should round up to end with 5", + Math.class, "rint__DD", + 453905.0, cnts + ); + } + @Test public void rintPositiveDown() throws Exception { + final double cnts = 453904.434; + assertExec( + "Should round up to end with 4", + Math.class, "rint__DD", + 453904.0, cnts + ); + } + @Test public void rintOneHalf() throws Exception { + final double cnts = 1.5; + assertExec( + "Should round up to end with 2", + Math.class, "rint__DD", + 2.0, cnts + ); + } + @Test public void rintNegativeOneHalf() throws Exception { + final double cnts = -1.5; + assertExec( + "Should round up to end with 2", + Math.class, "rint__DD", + -2.0, cnts + ); + } + @Test public void rintTwoAndHalf() throws Exception { + final double cnts = 2.5; + assertExec( + "Should round up to end with 2", + Math.class, "rint__DD", + 2.0, cnts + ); + } + @Test public void rintNegativeTwoOneHalf() throws Exception { + final double cnts = -2.5; + assertExec( + "Should round up to end with 2", + Math.class, "rint__DD", + -2.0, cnts + ); + } + + @Test public void ieeeReminder1() throws Exception { + assertExec( + "Same result 1", + Math.class, "IEEEremainder__DDD", + Math.IEEEremainder(10.0, 4.5), 10.0, 4.5 + ); + } + + @Test public void ieeeReminder2() throws Exception { + assertExec( + "Same result 1", + Math.class, "IEEEremainder__DDD", + Math.IEEEremainder(Integer.MAX_VALUE, -4.5), Integer.MAX_VALUE, -4.5 + ); + } @Test public void divAndRound() throws Exception { assertExec( @@ -248,97 +321,18 @@ ); } - private static CharSequence codeSeq; - private static Invocable code; + private static TestVM code; @BeforeClass public void compileTheCode() throws Exception { StringBuilder sb = new StringBuilder(); - code = compileClass(sb, "org/apidesign/vm4brwsr/StaticMethod"); - codeSeq = sb; - } - - - private static void assertExec( - String msg, Class clazz, String method, - Object expRes, Object... args - ) throws Exception { - assertExec(code, codeSeq, msg, clazz, method, expRes, args); - } - static void assertExec( - Invocable toRun, CharSequence theCode, - String msg, Class clazz, String method, - Object expRes, Object... args - ) throws Exception { - Object ret = null; - try { - ret = toRun.invokeFunction("bck2brwsr"); - ret = toRun.invokeMethod(ret, "loadClass", clazz.getName()); - ret = toRun.invokeMethod(ret, method, args); - } catch (ScriptException ex) { - fail("Execution failed in\n" + dumpJS(theCode), ex); - } catch (NoSuchMethodException ex) { - fail("Cannot find method in\n" + dumpJS(theCode), ex); - } - if (ret == null && expRes == null) { - return; - } - if (expRes != null && expRes.equals(ret)) { - return; - } - assertEquals(ret, expRes, msg + "was: " + ret + "\n" + dumpJS(theCode)); - + code = TestVM.compileClass(sb, "org/apidesign/vm4brwsr/StaticMethod"); } - static Invocable compileClass(StringBuilder sb, String... names) throws ScriptException, IOException { - return compileClass(sb, null, names); - } - static Invocable compileClass( - StringBuilder sb, ScriptEngine[] eng, String... names - ) throws ScriptException, IOException { - if (sb == null) { - sb = new StringBuilder(); - } - Bck2Brwsr.generate(sb, new EmulationResources(), names); - ScriptEngineManager sem = new ScriptEngineManager(); - ScriptEngine js = sem.getEngineByExtension("js"); - if (eng != null) { - eng[0] = js; - } - try { - Object res = js.eval(sb.toString()); - assertTrue(js instanceof Invocable, "It is invocable object: " + res); - return (Invocable)js; - } catch (Exception ex) { - if (sb.length() > 2000) { - sb = dumpJS(sb); - } - fail("Could not evaluate:\n" + sb, ex); - return null; - } - } - static StringBuilder dumpJS(CharSequence sb) throws IOException { - File f = File.createTempFile("execution", ".js"); - FileWriter w = new FileWriter(f); - w.append(sb); - w.close(); - return new StringBuilder(f.getPath()); - } - private static class EmulationResources implements Bck2Brwsr.Resources { - @Override - public InputStream get(String name) throws IOException { - Enumeration en = StaticMethodTest.class.getClassLoader().getResources(name); - URL u = null; - while (en.hasMoreElements()) { - u = en.nextElement(); - } - if (u == null) { - throw new IOException("Can't find " + name); - } - if (u.toExternalForm().contains("rt.jar!")) { - throw new IOException("No emulation for " + u); - } - return u.openStream(); - } + private void assertExec( + String msg, Class clazz, String method, + Object ret, Object... args + ) throws Exception { + code.assertExec(msg, clazz, method, ret, args); } } diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/test/java/org/apidesign/vm4brwsr/StringSample.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/StringSample.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/StringSample.java Sun Feb 17 17:58:34 2013 +0100 @@ -17,6 +17,8 @@ */ package org.apidesign.vm4brwsr; +import java.io.UnsupportedEncodingException; + /** * * @author Jaroslav Tulach @@ -68,6 +70,15 @@ return chars('a', (char)30, 'b') instanceof String; } + public static String getBytes(String s) throws UnsupportedEncodingException { + byte[] arr = s.getBytes("UTF-8"); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < arr.length; i++) { + sb.append(arr[i]).append(" "); + } + return sb.toString().toString(); + } + public static String insertBuffer() { StringBuilder sb = new StringBuilder(); sb.append("Jardo!"); diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/test/java/org/apidesign/vm4brwsr/StringTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/StringTest.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/StringTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -17,7 +17,6 @@ */ package org.apidesign.vm4brwsr; -import javax.script.Invocable; import org.testng.annotations.Test; import static org.testng.Assert.*; import org.testng.annotations.BeforeClass; @@ -75,6 +74,16 @@ ); } + @Test public void getBytes() throws Exception { + final String horse = "\u017dlu\u0165ou\u010dk\u00fd k\u016f\u0148"; + final String expected = StringSample.getBytes(horse); + assertExec( + "Bytes look simplar", + StringSample.class, "getBytes__Ljava_lang_String_2Ljava_lang_String_2", + expected, horse + ); + } + @Test(timeOut=10000) public void toStringConcatenation() throws Exception { assertExec( "Five executions should generate 5Hello World!", @@ -184,23 +193,20 @@ } - private static CharSequence codeSeq; - private static Invocable code; + private static TestVM code; @BeforeClass public void compileTheCode() throws Exception { - StringBuilder sb = new StringBuilder(); - code = StaticMethodTest.compileClass(sb, + code = TestVM.compileClass( "org/apidesign/vm4brwsr/StringSample", "java/lang/String" ); - codeSeq = sb; } private static void assertExec(String msg, Class clazz, String method, Object expRes, Object... args ) throws Exception { - StaticMethodTest.assertExec(code, codeSeq, msg, clazz, method, expRes, args); + code.assertExec(msg, clazz, method, expRes, args); } } diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/test/java/org/apidesign/vm4brwsr/SystemTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/SystemTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,56 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import org.testng.annotations.BeforeClass; +import static org.testng.Assert.*; +import org.testng.annotations.Test; + +/** + * + * @author Jaroslav Tulach + */ +public class SystemTest { + private static TestVM code; + + @Test public void verifyJSTime() throws Exception { + long now = System.currentTimeMillis(); + + Object js = code.execCode("Get js time", + org.apidesign.bck2brwsr.emul.lang.System.class, "currentTimeMillis__J", + null + ); + + assertTrue(js instanceof Double, "Double " + js); + long time = ((Double)js).longValue(); + + long later = System.currentTimeMillis(); + + assertTrue(now <= time, "Lower bound is OK: " + now + " <= " + time); + assertTrue(time <= later, "Upper bound is OK: " + time + " <= " + later); + } + + + @BeforeClass + public static void compileTheCode() throws Exception { + code = TestVM.compileClass( + "org/apidesign/bck2brwsr/emul/lang/System"); + } + +} + diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/test/java/org/apidesign/vm4brwsr/TestVM.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/TestVM.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,170 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; +import javax.script.Invocable; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import static org.testng.Assert.*; + +final class TestVM { + private final Invocable code; + private final CharSequence codeSeq; + private final Object bck2brwsr; + + + private TestVM(Invocable code, CharSequence codeSeq) throws ScriptException, NoSuchMethodException { + this.code = code; + this.codeSeq = codeSeq; + this.bck2brwsr = code.invokeFunction("bck2brwsr"); + } + + + public Object execCode( + String msg, Class clazz, String method, + Object expRes, Object... args + ) throws Exception { + Object ret = null; + try { + ret = code.invokeMethod(bck2brwsr, "loadClass", clazz.getName()); + ret = code.invokeMethod(ret, method, args); + } catch (ScriptException ex) { + fail("Execution failed in " + dumpJS(codeSeq), ex); + } catch (NoSuchMethodException ex) { + fail("Cannot find method in " + dumpJS(codeSeq), ex); + } + if (ret == null && expRes == null) { + return null; + } + if (expRes != null && expRes.equals(ret)) { + return null; + } + if (expRes instanceof Number) { + // in case of Long it is necessary convert it to number + // since the Long is represented by two numbers in JavaScript + try { + ret = code.invokeMethod(ret, "toFP"); + ret = code.invokeFunction("Number", ret); + } catch (ScriptException ex) { + fail("Conversion to number failed in " + dumpJS(codeSeq), ex); + } catch (NoSuchMethodException ex) { + fail("Cannot find global Number(x) function in " + dumpJS(codeSeq), ex); + } + } + return ret; + } + + void assertExec( + String msg, Class clazz, String method, Object expRes, Object... args + ) throws Exception { + Object ret = execCode(msg, clazz, method, expRes, args); + if (ret == null) { + return; + } + if (expRes != null && expRes.equals(ret)) { + return; + } + assertEquals(ret, expRes, msg + "was: " + ret + "\n" + dumpJS(codeSeq)); + } + + static TestVM compileClass(String... names) throws ScriptException, IOException { + return compileClass(null, names); + } + + static TestVM compileClass(StringBuilder sb, String... names) throws ScriptException, IOException { + return compileClass(sb, null, names); + } + + static TestVM compileClass(StringBuilder sb, ScriptEngine[] eng, String... names) throws ScriptException, IOException { + if (sb == null) { + sb = new StringBuilder(); + } + Bck2Brwsr.generate(sb, new EmulationResources(), names); + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine js = sem.getEngineByExtension("js"); + if (eng != null) { + eng[0] = js; + } + try { + Object res = js.eval(sb.toString()); + assertTrue(js instanceof Invocable, "It is invocable object: " + res); + return new TestVM((Invocable) js, sb); + } catch (Exception ex) { + if (sb.length() > 2000) { + sb = dumpJS(sb); + } + fail("Could not evaluate:\n" + sb, ex); + return null; + } + } + + Object loadClass(String loadClass, String name) throws ScriptException, NoSuchMethodException { + return code.invokeMethod(bck2brwsr, "loadClass", Exceptions.class.getName()); + } + + Object invokeMethod(Object obj, String method, Object... params) throws ScriptException, NoSuchMethodException { + return code.invokeMethod(obj, method, params); + } + + Object invokeFunction(String methodName, Object... args) throws ScriptException, NoSuchMethodException { + return code.invokeFunction(methodName, args); + } + + static StringBuilder dumpJS(CharSequence sb) throws IOException { + File f = File.createTempFile("execution", ".js"); + FileWriter w = new FileWriter(f); + w.append(sb); + w.close(); + return new StringBuilder(f.getPath()); + } + + @Override + public String toString() { + try { + return dumpJS(codeSeq).toString(); + } catch (IOException ex) { + return ex.toString(); + } + } + + + private static class EmulationResources implements Bck2Brwsr.Resources { + @Override + public InputStream get(String name) throws IOException { + Enumeration en = StaticMethodTest.class.getClassLoader().getResources(name); + URL u = null; + while (en.hasMoreElements()) { + u = en.nextElement(); + } + if (u == null) { + throw new IOException("Can't find " + name); + } + if (u.toExternalForm().contains("rt.jar!")) { + throw new IOException("No emulation for " + u); + } + return u.openStream(); + } + } +} diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/test/java/org/apidesign/vm4brwsr/VMLazyTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/VMLazyTest.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/VMLazyTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -17,7 +17,6 @@ */ package org.apidesign.vm4brwsr; -import javax.script.Invocable; import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.ScriptException; @@ -30,10 +29,7 @@ * @author Jaroslav Tulach */ public class VMLazyTest { - - - private static CharSequence codeSeq; - private static Invocable code; + private static TestVM code; @BeforeClass public void compileTheCode() throws Exception { @@ -50,11 +46,10 @@ sb.append("\n}"); ScriptEngine[] arr = { null }; - code = StaticMethodTest.compileClass(sb, arr, - "org/apidesign/vm4brwsr/VM" + code = TestVM.compileClass(sb, arr, + new String[]{"org/apidesign/vm4brwsr/VM", "org/apidesign/vm4brwsr/StaticMethod"} ); arr[0].getContext().setAttribute("loader", new BytesLoader(), ScriptContext.ENGINE_SCOPE); - codeSeq = sb; } @Test public void invokeStaticMethod() throws Exception { @@ -83,9 +78,9 @@ try { ret = code.invokeFunction(methodName, args); } catch (ScriptException ex) { - fail("Execution failed in\n" + StaticMethodTest.dumpJS(codeSeq), ex); + fail("Execution failed in\n" + code.toString(), ex); } catch (NoSuchMethodException ex) { - fail("Cannot find method in\n" + StaticMethodTest.dumpJS(codeSeq), ex); + fail("Cannot find method in\n" + code.toString(), ex); } if (ret == null && expRes == null) { return; @@ -93,6 +88,6 @@ if (expRes.equals(ret)) { return; } - assertEquals(ret, expRes, msg + "was: " + ret + "\n" + StaticMethodTest.dumpJS(codeSeq)); + assertEquals(ret, expRes, msg + "was: " + ret + "\n" + code.toString()); } } diff -r d443b6c05a01 -r 41e1dc88a399 vm/src/test/java/org/apidesign/vm4brwsr/VMinVMTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/VMinVMTest.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/VMinVMTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -21,7 +21,6 @@ import java.io.FileWriter; import java.io.IOException; import static org.testng.Assert.*; -import javax.script.Invocable; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -30,9 +29,7 @@ * @author Jaroslav Tulach */ public class VMinVMTest { - - private static CharSequence codeSeq; - private static Invocable code; + private static TestVM code; @Test public void compareGeneratedCodeForArrayClass() throws Exception { compareCode("org/apidesign/vm4brwsr/Array.class"); @@ -44,11 +41,7 @@ @BeforeClass public void compileTheCode() throws Exception { - StringBuilder sb = new StringBuilder(); - code = StaticMethodTest.compileClass(sb, - "org/apidesign/vm4brwsr/VMinVM" - ); - codeSeq = sb; + code = TestVM.compileClass("org/apidesign/vm4brwsr/VMinVM"); } private void compareCode(final String nm) throws Exception, IOException { @@ -73,7 +66,7 @@ } } w.append("\n];\n"); - w.append(codeSeq); + w.append(code.toString()); w.close(); throw new Exception(ex.getMessage() + " file: " + f, ex); } @@ -83,11 +76,11 @@ if (!ret1.toString().equals(ret)) { StringBuilder msg = new StringBuilder("Difference found between "); - msg.append(StaticMethodTest.dumpJS(ret1)); + msg.append(TestVM.dumpJS(ret1)); msg.append(" "); - msg.append(StaticMethodTest.dumpJS((CharSequence) ret)); + msg.append(TestVM.dumpJS((CharSequence) ret)); msg.append(" compiled by "); - msg.append(StaticMethodTest.dumpJS(codeSeq)); + msg.append(code.toString()); fail(msg.toString()); } } diff -r d443b6c05a01 -r 41e1dc88a399 vmtest/pom.xml --- a/vmtest/pom.xml Fri Jan 25 15:08:24 2013 +0100 +++ b/vmtest/pom.xml Sun Feb 17 17:58:34 2013 +0100 @@ -58,5 +58,10 @@ launcher ${project.version}
+ + org.netbeans.api + org-openide-util-lookup + provided +
diff -r d443b6c05a01 -r 41e1dc88a399 vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/Http.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/Http.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,56 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vmtest; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Exposes HTTP page or pages to the running {@link BrwsrTest}, so it can access under + * the relative path. + * + * @author Jaroslav Tulach + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface Http { + /** Set of pages to make available */ + public Resource[] value(); + + /** Exposes an HTTP page to the running {@link BrwsrTest}, so it can access + * under the relative path. + * + * @author Jaroslav Tulach + */ + @Retention(RetentionPolicy.RUNTIME) + @Target({}) + public @interface Resource { + /** path on the server that the test can use to access the exposed resource */ + String path(); + /** the content of the HttpResource */ + String content(); + /** resource relative to the class that should be used instead of content. + * Leave content equal to empty string. + */ + String resource() default ""; + /** mime type of the resource */ + String mimeType(); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java --- a/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java Sun Feb 17 17:58:34 2013 +0100 @@ -17,16 +17,18 @@ */ package org.apidesign.bck2brwsr.vmtest.impl; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.Map; -import java.util.WeakHashMap; import org.apidesign.bck2brwsr.launcher.Launcher; -import org.apidesign.bck2brwsr.launcher.MethodInvocation; +import org.apidesign.bck2brwsr.launcher.InvocationContext; +import org.apidesign.bck2brwsr.vmtest.HtmlFragment; +import org.apidesign.bck2brwsr.vmtest.Http; import org.testng.ITest; import org.testng.annotations.Test; @@ -39,22 +41,38 @@ private final Launcher l; private final String type; private final boolean fail; + private final HtmlFragment html; + private final Http.Resource[] http; Object value; - private final String html; - Bck2BrwsrCase(Method m, String type, Launcher l, boolean fail, String html) { + Bck2BrwsrCase(Method m, String type, Launcher l, boolean fail, HtmlFragment html, Http.Resource[] http) { this.l = l; this.m = m; this.type = type; this.fail = fail; this.html = html; + this.http = http; } @Test(groups = "run") public void executeCode() throws Throwable { if (l != null) { - MethodInvocation c = l.invokeMethod(m.getDeclaringClass(), m.getName(), html); - String res = c.toString(); + InvocationContext c = l.createInvocation(m.getDeclaringClass(), m.getName()); + if (html != null) { + c.setHtmlFragment(html.value()); + } + if (http != null) { + for (Http.Resource r : http) { + if (!r.content().isEmpty()) { + InputStream is = new ByteArrayInputStream(r.content().getBytes("UTF-8")); + c.addHttpResource(r.path(), r.mimeType(), is); + } else { + InputStream is = m.getDeclaringClass().getResourceAsStream(r.resource()); + c.addHttpResource(r.path(), r.mimeType(), is); + } + } + } + String res = c.invoke(); value = res; if (fail) { int idx = res.indexOf(':'); @@ -94,6 +112,9 @@ } catch (InvocationTargetException ex) { Throwable t = ex.getTargetException(); value = t.getClass().getName() + ":" + t.getMessage(); + if (t instanceof AssertionError) { + throw t; + } } } } diff -r d443b6c05a01 -r 41e1dc88a399 vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/CompareCase.java --- a/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/CompareCase.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/CompareCase.java Sun Feb 17 17:58:34 2013 +0100 @@ -111,17 +111,17 @@ if (c == null) { return; } - final Bck2BrwsrCase real = new Bck2BrwsrCase(m, "Java", null, false, null); + final Bck2BrwsrCase real = new Bck2BrwsrCase(m, "Java", null, false, null, null); ret.add(real); if (c.scripting()) { - final Bck2BrwsrCase js = new Bck2BrwsrCase(m, "JavaScript", l.javaScript(), false, null); + final Bck2BrwsrCase js = new Bck2BrwsrCase(m, "JavaScript", l.javaScript(), false, null, null); ret.add(js); ret.add(new CompareCase(m, real, js)); } for (String b : brwsr) { final Launcher s = l.brwsr(b); ret.add(s); - final Bck2BrwsrCase cse = new Bck2BrwsrCase(m, b, s, false, null); + final Bck2BrwsrCase cse = new Bck2BrwsrCase(m, b, s, false, null, null); ret.add(cse); ret.add(new CompareCase(m, real, cse)); } @@ -135,16 +135,25 @@ if (f == null) { f = m.getDeclaringClass().getAnnotation(HtmlFragment.class); } - String html = f == null ? null : f.value(); + Http.Resource[] r = {}; + Http h = m.getAnnotation(Http.class); + if (h == null) { + h = m.getDeclaringClass().getAnnotation(Http.class); + if (h != null) { + r = h.value(); + } + } else { + r = h.value(); + } if (brwsr.length == 0) { final Launcher s = l.brwsr(null); ret.add(s); - ret.add(new Bck2BrwsrCase(m, "Brwsr", s, true, html)); + ret.add(new Bck2BrwsrCase(m, "Brwsr", s, true, f, r)); } else { for (String b : brwsr) { final Launcher s = l.brwsr(b); ret.add(s); - ret.add(new Bck2BrwsrCase(m, b, s, true, html)); + ret.add(new Bck2BrwsrCase(m, b, s, true, f, r)); } } } diff -r d443b6c05a01 -r 41e1dc88a399 vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/GenerateZip.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/GenerateZip.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,36 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vmtest.impl; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** Annotation to generate a ZIP or JAR file during build. + * + * @author Jaroslav Tulach + */ +@Retention(RetentionPolicy.SOURCE) +@interface GenerateZip { + String name(); + + /** manifest for the file */ + String manifest() default ""; + + /** Array of names (at odd positions) and their content (on even) */ + String[] contents(); +} diff -r d443b6c05a01 -r 41e1dc88a399 vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/GenerateZipProcessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/GenerateZipProcessor.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,101 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vmtest.impl; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.Filer; +import javax.annotation.processing.Processor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic; +import javax.tools.FileObject; +import javax.tools.StandardLocation; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author Jaroslav Tulach + */ +@ServiceProvider(service = Processor.class) +@SupportedAnnotationTypes("org.apidesign.bck2brwsr.vmtest.impl.GenerateZip") +public class GenerateZipProcessor extends AbstractProcessor { + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + for (Element e : roundEnv.getElementsAnnotatedWith(GenerateZip.class)) { + GenerateZip gz = e.getAnnotation(GenerateZip.class); + if (gz == null) { + continue; + } + PackageElement pe = findPackage(e); + try { + generateJar(pe, gz, e); + } catch (IOException ex) { + processingEnv.getMessager().printMessage( + Diagnostic.Kind.ERROR, + "Can't generate JAR " + gz.name() + ": " + ex.getMessage() + ); + } + } + return true; + } + + private static PackageElement findPackage(Element e) { + while (e.getKind() != ElementKind.PACKAGE) { + e = e.getEnclosingElement(); + } + return (PackageElement)e; + } + + private void generateJar(PackageElement pe, GenerateZip gz, Element e) throws IOException { + final Filer filer = processingEnv.getFiler(); + FileObject res = filer.createResource( + StandardLocation.CLASS_OUTPUT, + pe.getQualifiedName().toString(), + gz.name(), e + ); + OutputStream os = res.openOutputStream(); + JarOutputStream jar; + if (gz.manifest().isEmpty()) { + jar = new JarOutputStream(os); + } else { + Manifest mf = new Manifest(new ByteArrayInputStream(gz.manifest().getBytes("UTF-8"))); + jar = new JarOutputStream(os, mf); + } + String[] arr = gz.contents(); + for (int i = 0; i < arr.length; i += 2) { + JarEntry je = new JarEntry(arr[i]); + jar.putNextEntry(je); + jar.write(arr[i + 1].getBytes("UTF-8")); + jar.closeEntry(); + } + jar.close(); + } + +} diff -r d443b6c05a01 -r 41e1dc88a399 vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/LaunchSetup.java --- a/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/LaunchSetup.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/LaunchSetup.java Sun Feb 17 17:58:34 2013 +0100 @@ -31,13 +31,19 @@ public final class LaunchSetup { static LaunchSetup INSTANCE = new LaunchSetup(); - private final Launcher js = Launcher.createJavaScript(); + private Launcher js; private final Map brwsrs = new LinkedHashMap<>(); private LaunchSetup() { } - public Launcher javaScript() { + public Launcher javaScript() { + return js(true); + } + private synchronized Launcher js(boolean create) { + if (js == null && create) { + js = Launcher.createJavaScript(); + } return js; } @@ -52,7 +58,9 @@ @BeforeGroups("run") public void initializeLauncher() throws IOException { - js.initialize(); + if (js(false) != null) { + js(true).initialize(); + } for (Launcher launcher : brwsrs.values()) { launcher.initialize(); } @@ -60,7 +68,9 @@ @AfterGroups("run") public void shutDownLauncher() throws IOException, InterruptedException { - js.shutdown(); + if (js(false) != null) { + js(true).shutdown(); + } for (Launcher launcher : brwsrs.values()) { launcher.shutdown(); } diff -r d443b6c05a01 -r 41e1dc88a399 vmtest/src/test/java/org/apidesign/bck2brwsr/tck/AssertionTest.java --- a/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/AssertionTest.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/AssertionTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -28,8 +28,12 @@ public class AssertionTest { @Compare public Object checkAssert() throws ClassNotFoundException { - assert false : "Is assertion status on?"; - return null; + try { + assert false : "Is assertion status on?"; + return null; + } catch (AssertionError ex) { + return ex.getClass().getName(); + } } @Factory diff -r d443b6c05a01 -r 41e1dc88a399 vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java --- a/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -17,6 +17,7 @@ */ package org.apidesign.bck2brwsr.tck; +import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import org.apidesign.bck2brwsr.vmtest.Compare; @@ -46,6 +47,19 @@ return "Ahoj".equals(null); } + @Compare public int highByteLenght() { + byte[] arr= { 77,97,110,105,102,101,115,116,45,86,101,114,115,105,111,110 }; + return new String(arr, 0).length(); + } + + @Compare public String highByte() { + byte[] arr= { 77,97,110,105,102,101,115,116,45,86,101,114,115,105,111,110 }; + StringBuilder sb = new StringBuilder(); + sb.append("pref:"); + sb.append(new String(arr, 0)); + return sb.toString(); + } + @Compare public static Object compareURLs() throws MalformedURLException { return new URL("http://apidesign.org:8080/wiki/").toExternalForm().toString(); } @@ -120,6 +134,21 @@ NullField nf = new NullField(); return ("" + nf.name).toString(); } + @Compare + public String toUTFString() throws UnsupportedEncodingException { + byte[] arr = { + (byte) -59, (byte) -67, (byte) 108, (byte) 117, (byte) -59, (byte) -91, + (byte) 111, (byte) 117, (byte) -60, (byte) -115, (byte) 107, (byte) -61, + (byte) -67, (byte) 32, (byte) 107, (byte) -59, (byte) -81, (byte) -59, + (byte) -120 + }; + return new String(arr, "utf-8"); + } + + @Compare + public int stringToBytesLenght() throws UnsupportedEncodingException { + return "\u017dlu\u0165ou\u010dk\u00fd k\u016f\u0148".getBytes("utf8").length; + } @Factory public static Object[] create() { diff -r d443b6c05a01 -r 41e1dc88a399 vmtest/src/test/java/org/apidesign/bck2brwsr/tck/HttpResourceTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/HttpResourceTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,106 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import java.io.InputStream; +import java.net.URL; +import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.apidesign.bck2brwsr.vmtest.BrwsrTest; +import org.apidesign.bck2brwsr.vmtest.Http; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class HttpResourceTest { + + @Http({ + @Http.Resource(path = "/xhr", content = "Hello Brwsr!", mimeType = "text/plain") + }) + @BrwsrTest + + + public String testReadContentViaXHR() throws Exception { + String msg = read("/xhr"); + assert "Hello Brwsr!".equals(msg) : "The message was " + msg; + return msg; + } + + @Http({ + @Http.Resource(path = "/url", content = "Hello via URL!", mimeType = "text/plain") + }) + @BrwsrTest + public String testReadContentViaURL() throws Exception { + URL url = new URL("http:/url"); + String msg = (String) url.getContent(); + assert "Hello via URL!".equals(msg) : "The message was " + msg; + return msg; + } + @Http({ + @Http.Resource(path = "/url", content = "Hello via URL!", mimeType = "text/plain") + }) + @BrwsrTest + public String testReadContentViaURLWithStringParam() throws Exception { + URL url = new URL("http:/url"); + String msg = (String) url.getContent(new Class[] { String.class }); + assert "Hello via URL!".equals(msg) : "The message was " + msg; + return msg; + } + + @Http({ + @Http.Resource(path = "/bytes", content = "", resource = "0xfe", mimeType = "x-application/binary") + }) + @BrwsrTest + public void testReadByte() throws Exception { + URL url = new URL("http:/bytes"); + final Object res = url.getContent(new Class[] { byte[].class }); + assert res instanceof byte[] : "Expecting byte[]: " + res; + byte[] arr = (byte[]) res; + assert arr.length == 1 : "One byte " + arr.length; + assert arr[0] == 0xfe : "It is 0xfe: " + Integer.toHexString(arr[0]); + } + + @Http({ + @Http.Resource(path = "/bytes", content = "", resource = "0xfe", mimeType = "x-application/binary") + }) + @BrwsrTest + public void testReadByteViaInputStream() throws Exception { + URL url = new URL("http:/bytes"); + InputStream is = url.openStream(); + byte[] arr = new byte[10]; + int len = is.read(arr); + assert len == 1 : "One byte " + len; + assert arr[0] == 0xfe : "It is 0xfe: " + Integer.toHexString(arr[0]); + } + + @JavaScriptBody(args = { "url" }, body = + "var req = new XMLHttpRequest();\n" + + "req.open('GET', url, false);\n" + + "req.send();\n" + + "return req.responseText;" + ) + private static native String read(String url); + + + @Factory + public static Object[] create() { + return VMTest.create(HttpResourceTest.class); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 vmtest/src/test/java/org/apidesign/bck2brwsr/tck/InheritanceA.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/InheritanceA.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,34 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +/** + * + * @author Jaroslav Tulach + */ +public class InheritanceA { + private String name; + + public void setA(String n) { + this.name = n; + } + + public String getA() { + return name; + } +} diff -r d443b6c05a01 -r 41e1dc88a399 vmtest/src/test/java/org/apidesign/bck2brwsr/tck/InheritanceB.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/InheritanceB.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,34 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +/** + * + * @author Jaroslav Tulach + */ +public class InheritanceB extends InheritanceA { + private String name; + + public void setB(String n) { + this.name = n; + } + + public String getB() { + return name; + } +} diff -r d443b6c05a01 -r 41e1dc88a399 vmtest/src/test/java/org/apidesign/bck2brwsr/tck/InheritanceTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/InheritanceTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,41 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class InheritanceTest { + + @Compare public String checkFieldsIndependent() throws ClassNotFoundException { + InheritanceB ib = new InheritanceB(); + ib.setA("A"); + ib.setB("B"); + return "A: " + ib.getA() + " B: " + ib.getB(); + } + + @Factory + public static Object[] create() { + return VMTest.create(InheritanceTest.class); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 vmtest/src/test/java/org/apidesign/bck2brwsr/tck/IntegerArithmeticTest.java --- a/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/IntegerArithmeticTest.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/IntegerArithmeticTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -47,6 +47,10 @@ return x % y; } + private static int neg(int x) { + return (-x); + } + @Compare public int addOverflow() { return add(Integer.MAX_VALUE, 1); } @@ -91,6 +95,18 @@ return mod(1, 2); } + @Compare public int negate() { + return neg(123456); + } + + @Compare public int negateMaxInt() { + return neg(Integer.MAX_VALUE); + } + + @Compare public int negateMinInt() { + return neg(Integer.MIN_VALUE); + } + @Compare public int sumTwoDimensions() { int[][] matrix = createMatrix(4, 3); matrix[0][0] += 10; diff -r d443b6c05a01 -r 41e1dc88a399 vmtest/src/test/java/org/apidesign/bck2brwsr/tck/LongArithmeticTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/LongArithmeticTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,334 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class LongArithmeticTest { + + private static long add(long x, long y) { + return (x + y); + } + + private static long sub(long x, long y) { + return (x - y); + } + + private static long mul(long x, long y) { + return (x * y); + } + + private static long div(long x, long y) { + return (x / y); + } + + private static long mod(long x, long y) { + return (x % y); + } + + private static long neg(long x) { + return (-x); + } + + private static long shl(long x, int b) { + return (x << b); + } + + private static long shr(long x, int b) { + return (x >> b); + } + + private static long ushr(long x, int b) { + return (x >>> b); + } + + private static long and(long x, long y) { + return (x & y); + } + + private static long or(long x, long y) { + return (x | y); + } + + private static long xor(long x, long y) { + return (x ^ y); + } + + public static int compare(long x, long y, int zero) { + final int xyResult = compareL(x, y, zero); + final int yxResult = compareL(y, x, zero); + + return ((xyResult + yxResult) == 0) ? xyResult : -2; + } + + private static int compareL(long x, long y, int zero) { + int result = -2; + int trueCount = 0; + + x += zero; + if (x == y) { + result = 0; + ++trueCount; + } + + x += zero; + if (x < y) { + result = -1; + ++trueCount; + } + + x += zero; + if (x > y) { + result = 1; + ++trueCount; + } + + return (trueCount == 1) ? result : -2; + } + + @Compare public long conversion() { + return Long.MAX_VALUE; + } + + @Compare public long negate1() { + return neg(0x00fa37d7763e0ca1l); + } + + @Compare public long negate2() { + return neg(0x80fa37d7763e0ca1l); + } + + @Compare public long negate3() { + return neg(0xfffffffffffffeddl); + } + + @Compare public long addOverflow() { + return add(Long.MAX_VALUE, 1l); + } + + @Compare public long subUnderflow() { + return sub(Long.MIN_VALUE, 1l); + } + + @Compare public long addMaxLongAndMaxLong() { + return add(Long.MAX_VALUE, Long.MAX_VALUE); + } + + @Compare public long subMinLongAndMinLong() { + return sub(Long.MIN_VALUE, Long.MIN_VALUE); + } + + @Compare public long subMinLongAndMaxLong() { + return sub(Long.MIN_VALUE, Long.MAX_VALUE); + } + + @Compare public long multiplyMaxLong() { + return mul(Long.MAX_VALUE, 2l); + } + + @Compare public long multiplyMaxLongAndMaxLong() { + return mul(Long.MAX_VALUE, Long.MAX_VALUE); + } + + @Compare public long multiplyMinLong() { + return mul(Long.MIN_VALUE, 2l); + } + + @Compare public long multiplyMinLongAndMinLong() { + return mul(Long.MIN_VALUE, Long.MIN_VALUE); + } + + @Compare public long multiplyPrecision() { + return mul(0x00fa37d7763e0ca1l, 0xa7b3432fff00123el); + } + + @Compare public long divideSmallPositiveNumbers() { + return div(0xabcdef, 0x123); + } + + @Compare public long divideSmallNegativeNumbers() { + return div(-0xabcdef, -0x123); + } + + @Compare public long divideSmallMixedNumbers() { + return div(0xabcdef, -0x123); + } + + @Compare public long dividePositiveNumbersOneDigitDenom() { + return div(0xabcdef0102ffffl, 0x654); + } + + @Compare public long divideNegativeNumbersOneDigitDenom() { + return div(-0xabcdef0102ffffl, -0x654); + } + + @Compare public long divideMixedNumbersOneDigitDenom() { + return div(-0xabcdef0102ffffl, 0x654); + } + + @Compare public long dividePositiveNumbersMultiDigitDenom() { + return div(0x7ffefc003322aabbl, 0x89ab1000l); + } + + @Compare public long divideNegativeNumbersMultiDigitDenom() { + return div(-0x7ffefc003322aabbl, -0x123489ab1001l); + } + + @Compare public long divideMixedNumbersMultiDigitDenom() { + return div(0x7ffefc003322aabbl, -0x38f49b0b7574e36l); + } + + @Compare public long divideWithOverflow() { + return div(0x8000fffe0000l, 0x8000ffffl); + } + + @Compare public long divideWithCorrection() { + return div(0x7fff800000000000l, 0x800000000001l); + } + + @Compare public long moduloSmallPositiveNumbers() { + return mod(0xabcdef, 0x123); + } + + @Compare public long moduloSmallNegativeNumbers() { + return mod(-0xabcdef, -0x123); + } + + @Compare public long moduloSmallMixedNumbers() { + return mod(0xabcdef, -0x123); + } + + @Compare public long moduloPositiveNumbersOneDigitDenom() { + return mod(0xabcdef0102ffffl, 0x654); + } + + @Compare public long moduloNegativeNumbersOneDigitDenom() { + return mod(-0xabcdef0102ffffl, -0x654); + } + + @Compare public long moduloMixedNumbersOneDigitDenom() { + return mod(-0xabcdef0102ffffl, 0x654); + } + + @Compare public long moduloPositiveNumbersMultiDigitDenom() { + return mod(0x7ffefc003322aabbl, 0x89ab1000l); + } + + @Compare public long moduloNegativeNumbersMultiDigitDenom() { + return mod(-0x7ffefc003322aabbl, -0x123489ab1001l); + } + + @Compare public long moduloMixedNumbersMultiDigitDenom() { + return mod(0x7ffefc003322aabbl, -0x38f49b0b7574e36l); + } + + @Compare public long moduloWithOverflow() { + return mod(0x8000fffe0000l, 0x8000ffffl); + } + + @Compare public long moduloWithCorrection() { + return mod(0x7fff800000000000l, 0x800000000001l); + } + + @Compare public long shiftL1() { + return shl(0x00fa37d7763e0ca1l, 5); + } + + @Compare public long shiftL2() { + return shl(0x00fa37d7763e0ca1l, 32); + } + + @Compare public long shiftL3() { + return shl(0x00fa37d7763e0ca1l, 45); + } + + @Compare public long shiftR1() { + return shr(0x00fa37d7763e0ca1l, 5); + } + + @Compare public long shiftR2() { + return shr(0x00fa37d7763e0ca1l, 32); + } + + @Compare public long shiftR3() { + return shr(0x00fa37d7763e0ca1l, 45); + } + + @Compare public long uShiftR1() { + return ushr(0x00fa37d7763e0ca1l, 5); + } + + @Compare public long uShiftR2() { + return ushr(0x00fa37d7763e0ca1l, 45); + } + + @Compare public long uShiftR3() { + return ushr(0xf0fa37d7763e0ca1l, 5); + } + + @Compare public long uShiftR4() { + return ushr(0xf0fa37d7763e0ca1l, 45); + } + + @Compare public long and1() { + return and(0x00fa37d7763e0ca1l, 0xa7b3432fff00123el); + } + + @Compare public long or1() { + return or(0x00fa37d7763e0ca1l, 0xa7b3432fff00123el); + } + + @Compare public long xor1() { + return xor(0x00fa37d7763e0ca1l, 0xa7b3432fff00123el); + } + + @Compare public long xor2() { + return xor(0x00fa37d7763e0ca1l, 0x00000000ff00123el); + } + + @Compare public long xor3() { + return xor(0x00000000763e0ca1l, 0x00000000ff00123el); + } + + @Compare public int compareSameNumbers() { + return compare(0x0000000000000000l, 0x0000000000000000l, 0); + } + + @Compare public int comparePositiveNumbers() { + return compare(0x0000000000200000l, 0x0000000010000000l, 0); + } + + @Compare public int compareNegativeNumbers() { + return compare(0xffffffffffffffffl, 0xffffffff00000000l, 0); + } + + @Compare public int compareMixedNumbers() { + return compare(0x8000000000000000l, 0x7fffffffffffffffl, 0); + } + + @Factory + public static Object[] create() { + return VMTest.create(LongArithmeticTest.class); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java --- a/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java Fri Jan 25 15:08:24 2013 +0100 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -17,12 +17,12 @@ */ package org.apidesign.bck2brwsr.tck; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; import org.apidesign.bck2brwsr.core.JavaScriptBody; import org.apidesign.bck2brwsr.vmtest.Compare; import org.apidesign.bck2brwsr.vmtest.VMTest; @@ -49,6 +49,14 @@ return long.class.toString(); } + @Compare public boolean isRunnableInterface() { + return Runnable.class.isInterface(); + } + + @Compare public String isRunnableHasRunMethod() throws NoSuchMethodException { + return Runnable.class.getMethod("run").getName(); + } + @Compare public String namesOfMethods() { StringBuilder sb = new StringBuilder(); String[] arr = new String[20]; @@ -61,6 +69,19 @@ } return sb.toString(); } + + @Compare public String namesOfDeclaringClassesOfMethods() { + StringBuilder sb = new StringBuilder(); + String[] arr = new String[20]; + int i = 0; + for (Method m : StaticUse.class.getMethods()) { + arr[i++] = m.getName() + "@" + m.getDeclaringClass().getName(); + } + for (String s : sort(arr, i)) { + sb.append(s).append("\n"); + } + return sb.toString(); + } @Compare public String cannotCallNonStaticMethodWithNull() throws Exception { StaticUse.class.getMethod("instanceMethod").invoke(null); @@ -71,6 +92,35 @@ return StaticUse.class.getMethod("instanceMethod").getReturnType(); } + @Retention(RetentionPolicy.RUNTIME) + @interface Ann { + } + + @Compare public String annoClass() throws Exception { + Retention r = Ann.class.getAnnotation(Retention.class); + assert r != null : "Annotation is present"; + assert r.value() == RetentionPolicy.RUNTIME : "Policy value is OK: " + r.value(); + return r.annotationType().getName(); + } + + @Compare public boolean isAnnotation() { + return Ann.class.isAnnotation(); + } + @Compare public boolean isNotAnnotation() { + return String.class.isAnnotation(); + } + @Compare public boolean isNotAnnotationEnum() { + return E.class.isAnnotation(); + } + enum E { A, B }; + @Compare public boolean isEnum() { + return E.A.getClass().isEnum(); + } + + @Compare public boolean isNotEnum() { + return "".getClass().isEnum(); + } + @Compare public String newInstanceFails() throws InstantiationException { try { return "success: " + StaticUse.class.newInstance(); diff -r d443b6c05a01 -r 41e1dc88a399 vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/CRC32Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/CRC32Test.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,41 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vmtest.impl; + +import java.io.UnsupportedEncodingException; +import java.util.zip.CRC32; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class CRC32Test { + + @Compare public long crc1() throws UnsupportedEncodingException { + CRC32 crc = new CRC32(); + crc.update("Hello World!".getBytes("UTF-8")); + return crc.getValue(); + } + + @Factory public static Object[] create() { + return VMTest.create(CRC32Test.class); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipEntryTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipEntryTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,67 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vmtest.impl; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import org.apidesign.bck2brwsr.emul.zip.FastJar; +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +/** + * + * @author Jaroslav Tulach + */ +@GenerateZip(name = "five.zip", contents = { + "1.txt", "one", + "2.txt", "duo", + "3.txt", "three", + "4.txt", "four", + "5.txt", "five" +}) +public class ZipEntryTest { + @Test + public void readEntriesEffectively() throws IOException { + InputStream is = ZipEntryTest.class.getResourceAsStream("five.zip"); + byte[] arr = new byte[is.available()]; + int len = is.read(arr); + assertEquals(len, arr.length, "Read fully"); + + FastJar fj = new FastJar(arr); + FastJar.Entry[] entrs = fj.list(); + + assertEquals(5, entrs.length, "Five entries"); + + for (int i = 1; i <= 5; i++) { + FastJar.Entry en = entrs[i - 1]; + assertEquals(en.name, i + ".txt"); +// assertEquals(cis.cnt, 0, "Content of the file should be skipped, not read"); + } + + assertContent("three", fj.getInputStream(entrs[3 - 1]), "read OK"); + assertContent("five", fj.getInputStream(entrs[5 - 1]), "read OK"); + } + + private static void assertContent(String exp, InputStream is, String msg) throws IOException { + byte[] arr = new byte[512]; + int len = is.read(arr); + String s = new String(arr, 0, len); + assertEquals(exp, s, msg); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipFileTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipFileTest.java Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,108 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vmtest.impl; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.apidesign.bck2brwsr.vmtest.BrwsrTest; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.Http; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +@GenerateZip(name = "readAnEntry.zip", contents = { + "my/main/file.txt", "Hello World!" +}) +public class ZipFileTest { + + @Compare public String readAnEntry() throws IOException { + InputStream is = ZipFileTest.class.getResourceAsStream("readAnEntry.zip"); + ZipInputStream zip = new ZipInputStream(is); + ZipEntry entry = zip.getNextEntry(); + assertEquals(entry.getName(), "my/main/file.txt", "Correct entry"); + + byte[] arr = new byte[4096]; + int len = zip.read(arr); + + assertEquals(zip.getNextEntry(), null, "No next entry"); + + final String ret = new String(arr, 0, len, "UTF-8"); + return ret; + } + + @JavaScriptBody(args = { "res", "path" }, body = + "var myvm = bck2brwsr.apply(null, path);\n" + + "var cls = myvm.loadClass('java.lang.String');\n" + + "return cls.getClass__Ljava_lang_Class_2().getResourceAsStream__Ljava_io_InputStream_2Ljava_lang_String_2(res);\n" + ) + private static native Object loadVMResource(String res, String...path); + + @Http({ + @Http.Resource(path = "/readAnEntry.jar", mimeType = "x-application/zip", content = "", resource="readAnEntry.zip") + }) + @BrwsrTest public void canVmLoadResourceFromZip() throws IOException { + Object res = loadVMResource("/my/main/file.txt", "/readAnEntry.jar"); + assert res instanceof InputStream : "Got array of bytes: " + res; + InputStream is = (InputStream)res; + + byte[] arr = new byte[4096]; + int len = is.read(arr); + + final String ret = new String(arr, 0, len, "UTF-8"); + + assertEquals(ret, "Hello World!", "Can read the bytes"); + } + + @GenerateZip(name = "cpattr.zip", contents = { + "META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n" + + "Created-By: hand\n" + + "Class-Path: realJar.jar\n\n\n" + }) + @Http({ + @Http.Resource(path = "/readComplexEntry.jar", mimeType = "x-application/zip", content = "", resource="cpattr.zip"), + @Http.Resource(path = "/realJar.jar", mimeType = "x-application/zip", content = "", resource="readAnEntry.zip"), + }) + @BrwsrTest public void understandsClassPathAttr() throws IOException { + Object res = loadVMResource("/my/main/file.txt", "/readComplexEntry.jar"); + assert res instanceof InputStream : "Got array of bytes: " + res; + InputStream is = (InputStream)res; + + byte[] arr = new byte[4096]; + int len = is.read(arr); + + final String ret = new String(arr, 0, len, "UTF-8"); + + assertEquals(ret, "Hello World!", "Can read the bytes from secondary JAR"); + } + + private static void assertEquals(Object real, Object exp, String msg) { + assert Objects.equals(exp, real) : msg + " exp: " + exp + " real: " + real; + } + + @Factory public static Object[] create() { + return VMTest.create(ZipFileTest.class); + } +} diff -r d443b6c05a01 -r 41e1dc88a399 vmtest/src/test/resources/org/apidesign/bck2brwsr/tck/0xfe --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/resources/org/apidesign/bck2brwsr/tck/0xfe Sun Feb 17 17:58:34 2013 +0100 @@ -0,0 +1,1 @@ +þ \ No newline at end of file