All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mozilla.javascript.NativeJavaObject Maven / Gradle / Ivy

Go to download

Rhino is an open-source implementation of JavaScript written entirely in Java. It is typically embedded into Java applications to provide scripting to end users.

The newest version!
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.javascript;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;

/**
 * This class reflects non-Array Java objects into the JavaScript environment. It reflect fields
 * directly, and uses NativeJavaMethod objects to reflect (possibly overloaded) methods. It also
 * provides iterator support for all iterable objects.
 *
 * 

* * @author Mike Shaver * @see NativeJavaArray * @see NativeJavaPackage * @see NativeJavaClass */ public class NativeJavaObject implements Scriptable, SymbolScriptable, Wrapper, Serializable { private static final long serialVersionUID = -6948590651130498591L; static void init(ScriptableObject scope, boolean sealed) { JavaIterableIterator.init(scope, sealed); } public NativeJavaObject() {} public NativeJavaObject(Scriptable scope, Object javaObject, Class staticType) { this(scope, javaObject, staticType, false); } public NativeJavaObject( Scriptable scope, Object javaObject, Class staticType, boolean isAdapter) { this.parent = scope; this.javaObject = javaObject; this.staticType = staticType; this.isAdapter = isAdapter; initMembers(); } protected void initMembers() { Class dynamicType; if (javaObject != null) { dynamicType = javaObject.getClass(); } else { dynamicType = staticType; } members = JavaMembers.lookupClass(parent, dynamicType, staticType, isAdapter); fieldAndMethods = members.getFieldAndMethodsObjects(this, javaObject, false); } @Override public boolean has(String name, Scriptable start) { return members.has(name, false); } @Override public boolean has(int index, Scriptable start) { return false; } @Override public boolean has(Symbol key, Scriptable start) { if (SymbolKey.ITERATOR.equals(key) && javaObject instanceof Iterable) { return true; } return false; } @Override public Object get(String name, Scriptable start) { if (fieldAndMethods != null) { Object result = fieldAndMethods.get(name); if (result != null) { return result; } } // TODO: passing 'this' as the scope is bogus since it has // no parent scope return members.get(this, name, javaObject, false); } @Override public Object get(Symbol key, Scriptable start) { if (SymbolKey.ITERATOR.equals(key) && javaObject instanceof Iterable) { return symbol_iterator; } // Native Java objects have no Symbol members return Scriptable.NOT_FOUND; } @Override public Object get(int index, Scriptable start) { throw members.reportMemberNotFound(Integer.toString(index)); } @Override public void put(String name, Scriptable start, Object value) { // We could be asked to modify the value of a property in the // prototype. Since we can't add a property to a Java object, // we modify it in the prototype rather than copy it down. if (prototype == null || members.has(name, false)) members.put(this, name, javaObject, value, false); else prototype.put(name, prototype, value); } @Override public void put(Symbol symbol, Scriptable start, Object value) { // We could be asked to modify the value of a property in the // prototype. Since we can't add a property to a Java object, // we modify it in the prototype rather than copy it down. String name = symbol.toString(); if (prototype == null || members.has(name, false)) { members.put(this, name, javaObject, value, false); } else if (prototype instanceof SymbolScriptable) { ((SymbolScriptable) prototype).put(symbol, prototype, value); } } @Override public void put(int index, Scriptable start, Object value) { throw members.reportMemberNotFound(Integer.toString(index)); } @Override public boolean hasInstance(Scriptable value) { // This is an instance of a Java class, so always return false return false; } @Override public void delete(String name) {} @Override public void delete(Symbol key) {} @Override public void delete(int index) {} @Override public Scriptable getPrototype() { if (prototype == null && javaObject instanceof String) { return TopLevel.getBuiltinPrototype( ScriptableObject.getTopLevelScope(parent), TopLevel.Builtins.String); } return prototype; } /** Sets the prototype of the object. */ @Override public void setPrototype(Scriptable m) { prototype = m; } /** Returns the parent (enclosing) scope of the object. */ @Override public Scriptable getParentScope() { return parent; } /** Sets the parent (enclosing) scope of the object. */ @Override public void setParentScope(Scriptable m) { parent = m; } @Override public Object[] getIds() { return members.getIds(false); } /** * @deprecated Use {@link Context#getWrapFactory()} together with calling {@link * WrapFactory#wrap(Context, Scriptable, Object, Class)} */ @Deprecated public static Object wrap(Scriptable scope, Object obj, Class staticType) { Context cx = Context.getContext(); return cx.getWrapFactory().wrap(cx, scope, obj, staticType); } @Override public Object unwrap() { return javaObject; } @Override public String getClassName() { return "JavaObject"; } @Override public Object getDefaultValue(Class hint) { Object value; if (hint == null) { if (javaObject instanceof Boolean) { hint = ScriptRuntime.BooleanClass; } if (javaObject instanceof Number) { hint = ScriptRuntime.NumberClass; } } if (hint == null || hint == ScriptRuntime.StringClass) { value = javaObject.toString(); } else { String converterName; if (hint == ScriptRuntime.BooleanClass) { converterName = "booleanValue"; } else if (hint == ScriptRuntime.NumberClass) { converterName = "doubleValue"; } else { throw Context.reportRuntimeErrorById("msg.default.value"); } Object converterObject = get(converterName, this); if (converterObject instanceof Function) { Function f = (Function) converterObject; value = f.call( Context.getContext(), f.getParentScope(), this, ScriptRuntime.emptyArgs); } else { if (hint == ScriptRuntime.NumberClass && javaObject instanceof Boolean) { boolean b = ((Boolean) javaObject).booleanValue(); value = b ? ScriptRuntime.wrapNumber(1.0) : ScriptRuntime.zeroObj; } else { value = javaObject.toString(); } } } return value; } /** * Determine whether we can/should convert between the given type and the desired one. This * should be superceded by a conversion-cost calculation function, but for now I'll hide behind * precedent. */ public static boolean canConvert(Object fromObj, Class to) { int weight = getConversionWeight(fromObj, to); return (weight < CONVERSION_NONE); } private static final int JSTYPE_UNDEFINED = 0; // undefined type private static final int JSTYPE_NULL = 1; // null private static final int JSTYPE_BOOLEAN = 2; // boolean private static final int JSTYPE_NUMBER = 3; // number private static final int JSTYPE_STRING = 4; // string private static final int JSTYPE_JAVA_CLASS = 5; // JavaClass private static final int JSTYPE_JAVA_OBJECT = 6; // JavaObject private static final int JSTYPE_JAVA_ARRAY = 7; // JavaArray private static final int JSTYPE_OBJECT = 8; // Scriptable private static final int JSTYPE_BIGINT = 9; // BigInt static final byte CONVERSION_TRIVIAL = 1; static final byte CONVERSION_NONTRIVIAL = 0; static final byte CONVERSION_NONE = 99; /** * Derive a ranking based on how "natural" the conversion is. The special value CONVERSION_NONE * means no conversion is possible, and CONVERSION_NONTRIVIAL signals that more type conformance * testing is required. Based on "preferred method * conversions" from Live Connect 3 */ static int getConversionWeight(Object fromObj, Class to) { int fromCode = getJSTypeCode(fromObj); switch (fromCode) { case JSTYPE_UNDEFINED: if (to == ScriptRuntime.StringClass || to == ScriptRuntime.ObjectClass) { return 1; } break; case JSTYPE_NULL: if (!to.isPrimitive()) { return 1; } break; case JSTYPE_BOOLEAN: // "boolean" is #1 if (to == Boolean.TYPE) { return 1; } else if (to == ScriptRuntime.BooleanClass) { return 2; } else if (to == ScriptRuntime.ObjectClass) { return 3; } else if (to == ScriptRuntime.StringClass) { return 4; } break; case JSTYPE_NUMBER: case JSTYPE_BIGINT: if (to.isPrimitive()) { if (to == Double.TYPE) { return 1; } else if (to != Boolean.TYPE) { return 1 + getSizeRank(to); } } else { if (to == ScriptRuntime.StringClass) { // native numbers are #1-8 return 9; } else if (to == ScriptRuntime.BigIntegerClass) { return 10; } else if (to == ScriptRuntime.ObjectClass) { return 11; } else if (ScriptRuntime.NumberClass.isAssignableFrom(to)) { // "double" is #1 return 2; } } break; case JSTYPE_STRING: if (to == ScriptRuntime.StringClass) { return 1; } else if (to.isInstance(fromObj)) { return 2; } else if (to.isPrimitive()) { if (to == Character.TYPE) { return 3; } else if (to != Boolean.TYPE) { return 4; } } break; case JSTYPE_JAVA_CLASS: if (to == ScriptRuntime.ClassClass) { return 1; } else if (to == ScriptRuntime.ObjectClass) { return 3; } else if (to == ScriptRuntime.StringClass) { return 4; } break; case JSTYPE_JAVA_OBJECT: case JSTYPE_JAVA_ARRAY: Object javaObj = fromObj; if (javaObj instanceof Wrapper) { javaObj = ((Wrapper) javaObj).unwrap(); } if (to.isInstance(javaObj)) { return CONVERSION_NONTRIVIAL; } if (to == ScriptRuntime.StringClass) { return 2; } else if (to.isPrimitive() && to != Boolean.TYPE) { return (fromCode == JSTYPE_JAVA_ARRAY) ? CONVERSION_NONE : 2 + getSizeRank(to); } break; case JSTYPE_OBJECT: // Other objects takes #1-#3 spots if (to != ScriptRuntime.ObjectClass && to.isInstance(fromObj)) { // No conversion required, but don't apply for java.lang.Object return 1; } if (to.isArray()) { if (fromObj instanceof NativeArray) { // This is a native array conversion to a java array // Array conversions are all equal, and preferable to object // and string conversion, per LC3. return 2; } } else if (to == ScriptRuntime.ObjectClass) { return 3; } else if (to == ScriptRuntime.StringClass) { return 4; } else if (to == ScriptRuntime.DateClass) { if (fromObj instanceof NativeDate) { // This is a native date to java date conversion return 1; } } else if (to.isInterface()) { if (fromObj instanceof NativeFunction) { // See comments in createInterfaceAdapter return 1; } if (fromObj instanceof NativeObject) { return 2; } return 12; } else if (to.isPrimitive() && to != Boolean.TYPE) { return 4 + getSizeRank(to); } break; } return CONVERSION_NONE; } static int getSizeRank(Class aType) { if (aType == Double.TYPE) { return 1; } else if (aType == Float.TYPE) { return 2; } else if (aType == Long.TYPE) { return 3; } else if (aType == Integer.TYPE) { return 4; } else if (aType == Short.TYPE) { return 5; } else if (aType == Character.TYPE) { return 6; } else if (aType == Byte.TYPE) { return 7; } else if (aType == Boolean.TYPE) { return CONVERSION_NONE; } else { return 8; } } private static int getJSTypeCode(Object value) { if (value == null) { return JSTYPE_NULL; } else if (value == Undefined.instance) { return JSTYPE_UNDEFINED; } else if (value instanceof CharSequence) { return JSTYPE_STRING; } else if (value instanceof BigInteger) { return JSTYPE_BIGINT; } else if (value instanceof Number) { return JSTYPE_NUMBER; } else if (value instanceof Boolean) { return JSTYPE_BOOLEAN; } else if (value instanceof Scriptable) { if (value instanceof NativeJavaClass) { return JSTYPE_JAVA_CLASS; } else if (value instanceof NativeJavaArray) { return JSTYPE_JAVA_ARRAY; } else if (value instanceof Wrapper) { return JSTYPE_JAVA_OBJECT; } else { return JSTYPE_OBJECT; } } else if (value instanceof Class) { return JSTYPE_JAVA_CLASS; } else { Class valueClass = value.getClass(); if (valueClass.isArray()) { return JSTYPE_JAVA_ARRAY; } return JSTYPE_JAVA_OBJECT; } } /** * Not intended for public use. Callers should use the public API Context.toType. * * @deprecated as of 1.5 Release 4 * @see org.mozilla.javascript.Context#jsToJava(Object, Class) */ @Deprecated public static Object coerceType(Class type, Object value) { return coerceTypeImpl(type, value); } /** Type-munging for field setting and method invocation. Conforms to LC3 specification */ static Object coerceTypeImpl(Class type, Object value) { if (value != null && value.getClass() == type) { return value; } int jsTypeCode = getJSTypeCode(value); switch (jsTypeCode) { case JSTYPE_NULL: // raise error if type.isPrimitive() if (type.isPrimitive()) { reportConversionError(value, type); } return null; case JSTYPE_UNDEFINED: if (type == ScriptRuntime.StringClass || type == ScriptRuntime.ObjectClass) { return "undefined"; } reportConversionError("undefined", type); break; case JSTYPE_BOOLEAN: // Under LC3, only JS Booleans can be coerced into a Boolean value if (type == Boolean.TYPE || type == ScriptRuntime.BooleanClass || type == ScriptRuntime.ObjectClass) { return value; } else if (type == ScriptRuntime.StringClass) { return value.toString(); } else { reportConversionError(value, type); } break; case JSTYPE_NUMBER: case JSTYPE_BIGINT: if (type == ScriptRuntime.StringClass) { return ScriptRuntime.toString(value); } else if (type == ScriptRuntime.ObjectClass) { Context context = Context.getCurrentContext(); if ((context != null) && context.hasFeature(Context.FEATURE_INTEGER_WITHOUT_DECIMAL_PLACE)) { // to process numbers like 2.0 as 2 without decimal place long roundedValue = Math.round(toDouble(value)); if (roundedValue == toDouble(value)) { return coerceToNumber(Long.TYPE, value); } } return coerceToNumber( jsTypeCode == JSTYPE_BIGINT ? BigInteger.class : Double.TYPE, value); } else if ((type.isPrimitive() && type != Boolean.TYPE) || ScriptRuntime.NumberClass.isAssignableFrom(type)) { return coerceToNumber(type, value); } else { reportConversionError(value, type); } break; case JSTYPE_STRING: if (type == ScriptRuntime.StringClass || type.isInstance(value)) { return value.toString(); } else if (type == Character.TYPE || type == ScriptRuntime.CharacterClass) { // Special case for converting a single char string to a // character // Placed here because it applies *only* to JS strings, // not other JS objects converted to strings if (((CharSequence) value).length() == 1) { return Character.valueOf(((CharSequence) value).charAt(0)); } return coerceToNumber(type, value); } else if ((type.isPrimitive() && type != Boolean.TYPE) || ScriptRuntime.NumberClass.isAssignableFrom(type)) { return coerceToNumber(type, value); } else { reportConversionError(value, type); } break; case JSTYPE_JAVA_CLASS: if (value instanceof Wrapper) { value = ((Wrapper) value).unwrap(); } if (type == ScriptRuntime.ClassClass || type == ScriptRuntime.ObjectClass) { return value; } else if (type == ScriptRuntime.StringClass) { return value.toString(); } else { reportConversionError(value, type); } break; case JSTYPE_JAVA_OBJECT: case JSTYPE_JAVA_ARRAY: if (value instanceof Wrapper) { value = ((Wrapper) value).unwrap(); } if (type.isPrimitive()) { if (type == Boolean.TYPE) { reportConversionError(value, type); } return coerceToNumber(type, value); } if (type == ScriptRuntime.StringClass) { return value.toString(); } if (type.isInstance(value)) { return value; } reportConversionError(value, type); break; case JSTYPE_OBJECT: if (type == ScriptRuntime.StringClass) { return ScriptRuntime.toString(value); } else if (type.isPrimitive()) { if (type == Boolean.TYPE) { reportConversionError(value, type); } return coerceToNumber(type, value); } else if (type.isInstance(value)) { return value; } else if (type == ScriptRuntime.DateClass && value instanceof NativeDate) { double time = ((NativeDate) value).getJSTimeValue(); // XXX: This will replace NaN by 0 return new Date((long) time); } else if (type.isArray() && value instanceof NativeArray) { // Make a new java array, and coerce the JS array components // to the target (component) type. NativeArray array = (NativeArray) value; long length = array.getLength(); Class arrayType = type.getComponentType(); Object Result = Array.newInstance(arrayType, (int) length); for (int i = 0; i < length; ++i) { try { Array.set(Result, i, coerceTypeImpl(arrayType, array.get(i, array))); } catch (EvaluatorException ee) { reportConversionError(value, type); } } return Result; } else if (value instanceof Wrapper) { value = ((Wrapper) value).unwrap(); if (type.isInstance(value)) return value; reportConversionError(value, type); } else if (type.isInterface() && (value instanceof NativeObject || (value instanceof Callable && value instanceof ScriptableObject))) { // Try to use function/object as implementation of Java interface. return createInterfaceAdapter(type, (ScriptableObject) value); } else { reportConversionError(value, type); } break; } return value; } protected static Object createInterfaceAdapter(Class type, ScriptableObject so) { // XXX: Currently only instances of ScriptableObject are // supported since the resulting interface proxies should // be reused next time conversion is made and generic // Callable has no storage for it. Weak references can // address it but for now use this restriction. Object key = Kit.makeHashKeyFromPair(COERCED_INTERFACE_KEY, type); Object old = so.getAssociatedValue(key); if (old != null) { // Function was already wrapped return old; } Context cx = Context.getContext(); Object glue = InterfaceAdapter.create(cx, type, so); // Store for later retrieval glue = so.associateValue(key, glue); return glue; } private static Object coerceToNumber(Class type, Object value) { Class valueClass = value.getClass(); // Character if (type == Character.TYPE || type == ScriptRuntime.CharacterClass) { if (valueClass == ScriptRuntime.CharacterClass) { return value; } return Character.valueOf( (char) toInteger( value, ScriptRuntime.CharacterClass, Character.MIN_VALUE, Character.MAX_VALUE)); } // Double, Float if (type == ScriptRuntime.ObjectClass || type == ScriptRuntime.DoubleClass || type == Double.TYPE) { if (valueClass == ScriptRuntime.DoubleClass) { return value; } return Double.valueOf(toDouble(value)); } if (type == ScriptRuntime.BigIntegerClass) { if (valueClass == ScriptRuntime.BigIntegerClass) { return value; } return ScriptRuntime.toBigInt(value); } if (type == ScriptRuntime.FloatClass || type == Float.TYPE) { if (valueClass == ScriptRuntime.FloatClass) { return value; } double number = toDouble(value); if (Double.isInfinite(number) || Double.isNaN(number) || number == 0.0) { return Float.valueOf((float) number); } double absNumber = Math.abs(number); if (absNumber < Float.MIN_VALUE) { return Float.valueOf((number > 0.0) ? +0.0f : -0.0f); } else if (absNumber > Float.MAX_VALUE) { return Float.valueOf( (number > 0.0) ? Float.POSITIVE_INFINITY : Float.NEGATIVE_INFINITY); } else { return Float.valueOf((float) number); } } // Integer, Long, Short, Byte if (type == ScriptRuntime.IntegerClass || type == Integer.TYPE) { if (valueClass == ScriptRuntime.IntegerClass) { return value; } return Integer.valueOf( (int) toInteger( value, ScriptRuntime.IntegerClass, Integer.MIN_VALUE, Integer.MAX_VALUE)); } if (type == ScriptRuntime.LongClass || type == Long.TYPE) { if (valueClass == ScriptRuntime.LongClass) { return value; } /* Long values cannot be expressed exactly in doubles. * We thus use the largest and smallest double value that * has a value expressible as a long value. We build these * numerical values from their hexidecimal representations * to avoid any problems caused by attempting to parse a * decimal representation. */ final double max = Double.longBitsToDouble(0x43dfffffffffffffL); final double min = Double.longBitsToDouble(0xc3e0000000000000L); return Long.valueOf(toInteger(value, ScriptRuntime.LongClass, min, max)); } if (type == ScriptRuntime.ShortClass || type == Short.TYPE) { if (valueClass == ScriptRuntime.ShortClass) { return value; } return Short.valueOf( (short) toInteger( value, ScriptRuntime.ShortClass, Short.MIN_VALUE, Short.MAX_VALUE)); } if (type == ScriptRuntime.ByteClass || type == Byte.TYPE) { if (valueClass == ScriptRuntime.ByteClass) { return value; } return Byte.valueOf( (byte) toInteger( value, ScriptRuntime.ByteClass, Byte.MIN_VALUE, Byte.MAX_VALUE)); } return Double.valueOf(toDouble(value)); } private static double toDouble(Object value) { if (value instanceof Number) { return ((Number) value).doubleValue(); } else if (value instanceof String) { return ScriptRuntime.toNumber((String) value); } else if (value instanceof Scriptable) { if (value instanceof Wrapper) { // XXX: optimize tail-recursion? return toDouble(((Wrapper) value).unwrap()); } return ScriptRuntime.toNumber(value); } else { Method meth; try { meth = value.getClass().getMethod("doubleValue", (Class[]) null); } catch (NoSuchMethodException e) { meth = null; } catch (SecurityException e) { meth = null; } if (meth != null) { try { return ((Number) meth.invoke(value, (Object[]) null)).doubleValue(); } catch (IllegalAccessException e) { // XXX: ignore, or error message? reportConversionError(value, Double.TYPE); } catch (InvocationTargetException e) { // XXX: ignore, or error message? reportConversionError(value, Double.TYPE); } } return ScriptRuntime.toNumber(value.toString()); } } private static long toInteger(Object value, Class type, double min, double max) { double d = toDouble(value); if (Double.isInfinite(d) || Double.isNaN(d)) { // Convert to string first, for more readable message reportConversionError(ScriptRuntime.toString(value), type); } if (d > 0.0) { d = Math.floor(d); } else { d = Math.ceil(d); } if (d < min || d > max) { // Convert to string first, for more readable message reportConversionError(ScriptRuntime.toString(value), type); } return (long) d; } static void reportConversionError(Object value, Class type) { // It uses String.valueOf(value), not value.toString() since // value can be null, bug 282447. throw Context.reportRuntimeErrorById( "msg.conversion.not.allowed", String.valueOf(value), JavaMembers.javaSignature(type)); } private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeBoolean(isAdapter); if (isAdapter) { if (adapter_writeAdapterObject == null) { throw new IOException(); } Object[] args = {javaObject, out}; try { adapter_writeAdapterObject.invoke(null, args); } catch (Exception ex) { throw new IOException(); } } else { out.writeObject(javaObject); } if (staticType != null) { out.writeObject(staticType.getName()); } else { out.writeObject(null); } } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); isAdapter = in.readBoolean(); if (isAdapter) { if (adapter_readAdapterObject == null) throw new ClassNotFoundException(); Object[] args = {this, in}; try { javaObject = adapter_readAdapterObject.invoke(null, args); } catch (Exception ex) { throw new IOException(); } } else { javaObject = in.readObject(); } String className = (String) in.readObject(); if (className != null) { staticType = Class.forName(className); } else { staticType = null; } initMembers(); } private static Callable symbol_iterator = (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) -> { if (!(thisObj instanceof NativeJavaObject)) { throw ScriptRuntime.typeErrorById("msg.incompat.call", SymbolKey.ITERATOR); } Object javaObject = ((NativeJavaObject) thisObj).javaObject; if (!(javaObject instanceof Iterable)) { throw ScriptRuntime.typeErrorById("msg.incompat.call", SymbolKey.ITERATOR); } return new JavaIterableIterator(scope, (Iterable) javaObject); }; private static final class JavaIterableIterator extends ES6Iterator { private static final long serialVersionUID = 1L; private static final String ITERATOR_TAG = "JavaIterableIterator"; static void init(ScriptableObject scope, boolean sealed) { ES6Iterator.init(scope, sealed, new JavaIterableIterator(), ITERATOR_TAG); } /** Only for constructing the prototype object. */ private JavaIterableIterator() { super(); } JavaIterableIterator(Scriptable scope, Iterable iterable) { super(scope, ITERATOR_TAG); this.iterator = iterable.iterator(); } @Override public String getClassName() { return "Java Iterable Iterator"; } @Override protected boolean isDone(Context cx, Scriptable scope) { return !iterator.hasNext(); } @Override protected Object nextValue(Context cx, Scriptable scope) { if (!iterator.hasNext()) { return Undefined.instance; } Object obj = iterator.next(); return cx.getWrapFactory().wrap(cx, this, obj, obj == null ? null : obj.getClass()); } @Override protected String getTag() { return ITERATOR_TAG; } private Iterator iterator; } /** The prototype of this object. */ protected Scriptable prototype; /** The parent scope of this object. */ protected Scriptable parent; protected transient Object javaObject; protected transient Class staticType; protected transient JavaMembers members; private transient Map fieldAndMethods; protected transient boolean isAdapter; private static final Object COERCED_INTERFACE_KEY = "Coerced Interface"; private static Method adapter_writeAdapterObject; private static Method adapter_readAdapterObject; static { // Reflection in java is verbose Class[] sig2 = new Class[2]; Class cl = Kit.classOrNull("org.mozilla.javascript.JavaAdapter"); if (cl != null) { try { sig2[0] = ScriptRuntime.ObjectClass; sig2[1] = Kit.classOrNull("java.io.ObjectOutputStream"); adapter_writeAdapterObject = cl.getMethod("writeAdapterObject", sig2); sig2[0] = ScriptRuntime.ScriptableClass; sig2[1] = Kit.classOrNull("java.io.ObjectInputStream"); adapter_readAdapterObject = cl.getMethod("readAdapterObject", sig2); } catch (NoSuchMethodException e) { adapter_writeAdapterObject = null; adapter_readAdapterObject = null; } } } @Override public boolean equals(Object obj) { return obj != null && obj.getClass().equals(getClass()) && Objects.equals(((NativeJavaObject) obj).javaObject, javaObject); } @Override public int hashCode() { return javaObject == null ? 0 : javaObject.hashCode(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy