org.cobraparser.js.JavaObjectWrapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Cobra Show documentation
Show all versions of Cobra Show documentation
Cobra is the rendering engine designed for LoboBrowser
/*
GNU LESSER GENERAL PUBLIC LICENSE
Copyright (C) 2006 The Lobo Project
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Contact info: [email protected]
*/
package org.cobraparser.js;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.cobraparser.html.js.Window;
import org.mozilla.javascript.EvaluatorException;
import org.mozilla.javascript.ExternalArrayData;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.WrappedException;
public class JavaObjectWrapper extends ScriptableObject {
private static final long serialVersionUID = -2669458528000105312L;
private static final Logger logger = Logger.getLogger(JavaObjectWrapper.class.getName());
private static final boolean loggableInfo = logger.isLoggable(Level.INFO);
private final Object delegate;
private final JavaClassWrapper classWrapper;
@Override
public void setParentScope(final Scriptable m) {
// Don't allow Window's parent scope to be changed. Fixes GH #29
if (classWrapper.getCanonicalClassName().equals(Window.class.getCanonicalName())) {
return;
}
if (m == this) {
// TODO: This happens when running jQuery 2
super.setParentScope(null);
} else {
super.setParentScope(m);
}
}
public JavaObjectWrapper(final JavaClassWrapper classWrapper) throws InstantiationException, IllegalAccessException {
this.classWrapper = classWrapper;
// Retaining a strong reference, but note
// that the object wrapper map uses weak keys
// and weak values.
final Object delegate = this.classWrapper.newInstance();
this.delegate = delegate;
setupProperties();
}
public JavaObjectWrapper(final JavaClassWrapper classWrapper, final Object delegate) {
if (delegate == null) {
throw new IllegalArgumentException("Argument delegate cannot be null.");
}
this.classWrapper = classWrapper;
// Retaining a strong reference, but note
// that the object wrapper map uses weak keys
// and weak values.
this.delegate = delegate;
setupProperties();
}
private void setupProperties() {
final PropertyInfo integerIndexer = classWrapper.getIntegerIndexer();
if (integerIndexer != null) {
setExternalArrayData(new ExternalArrayData() {
@Override
public int getArrayLength() {
try {
// TODO: Some length() methods are returning integer while others return length. A good test case is http://web-platform.test:8000/dom/nodes/Element-classlist.html
// Check if length() methods can be converted to return a single type.
final Object lengthObj = classWrapper.getProperty("length").getGetter().invoke(delegate, (Object[]) null);
if (lengthObj instanceof Long) {
final long lengthLong = (long) lengthObj;
final int lengthInt = (int) lengthLong;
// TODO: Check for overflow when casting to int and throw an exception
return lengthInt;
} else if (lengthObj instanceof Integer) {
return (int) lengthObj;
} else {
// TODO: Throw exception
throw new RuntimeException("Can't represent length as an integer type");
}
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return 0;
}
}
@Override
public Object getArrayElement(final int index) {
if (index < 0) {
// TODO: The interface's javadoc says that this method is only called for indices are within range.
// Need to check if negative values are considered in range. Negative indices are being used in
// one of the web-platform-tests
return org.mozilla.javascript.Undefined.instance;
}
try {
final Object result = JavaScript.getInstance().getJavascriptObject(
integerIndexer.getGetter().invoke(delegate, new Object[] { index }), null);
return result;
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException("Error accessing a indexed element");
}
}
@Override
public void setArrayElement(final int index, final Object value) {
// TODO: Can this be supported? Needs a setter.
throw new UnsupportedOperationException("Writing to an indexed object");
}
});
}
classWrapper.getProperties().forEach((name, property) -> {
// TODO: Don't setup properties if getter is null? Are write-only properties supported in JS?
defineProperty(name, null, property.getGetter(), property.getSetter(), 0);
});
classWrapper.getStaticFinalProperties().forEach((name, field) -> {
try {
defineProperty(name, field.get(null), READONLY);
} catch (final Exception e) {
throw new RuntimeException(e);
}
});
}
/**
* Returns the Java object.
*
* @return An object or null
if garbage collected.
*/
public Object getJavaObject() {
// Cannot retain delegate with a strong reference.
return this.delegate;
}
@Override
public String getClassName() {
return this.classWrapper.getClassName();
}
/*
@Override
public Object get(final int index, final Scriptable start) {
final PropertyInfo pinfo = this.classWrapper.getIntegerIndexer();
if (pinfo == null) {
return super.get(index, start);
} else {
try {
final Method getter = pinfo.getGetter();
if (getter == null) {
throw new EvaluatorException("Indexer is write-only");
}
// Cannot retain delegate with a strong reference.
final Object javaObject = this.getJavaObject();
if (javaObject == null) {
throw new IllegalStateException("Java object (class=" + this.classWrapper + ") is null.");
}
final Object raw = getter.invoke(javaObject, new Object[] { new Integer(index) });
if (raw == null) {
// Return this instead of null.
return Scriptable.NOT_FOUND;
}
return JavaScript.getInstance().getJavascriptObject(raw, this.getParentScope());
} catch (final Exception err) {
throw new WrappedException(err);
}
}
}*/
@Override
public Object get(final String name, final Scriptable start) {
final PropertyInfo pinfo = this.classWrapper.getProperty(name);
if (pinfo != null) {
final Method getter = pinfo.getGetter();
if (getter == null) {
throw new EvaluatorException("Property '" + name + "' is not readable");
}
// Cannot retain delegate with a strong reference.
final Object javaObject = this.getJavaObject();
if (javaObject == null) {
throw new IllegalStateException("Java object (class=" + this.classWrapper + ") is null.");
}
final Object val = AccessController.doPrivileged(new PrivilegedAction