com.gargoylesoftware.htmlunit.javascript.ScriptableWrapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of htmlunit Show documentation
Show all versions of htmlunit Show documentation
A headless browser intended for use in testing web-based applications.
/*
* Copyright (c) 2002-2021 Gargoyle Software Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gargoylesoftware.htmlunit.javascript;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.ArrayUtils;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;
import net.sourceforge.htmlunit.corejs.javascript.Context;
import net.sourceforge.htmlunit.corejs.javascript.FunctionObject;
import net.sourceforge.htmlunit.corejs.javascript.Scriptable;
import net.sourceforge.htmlunit.corejs.javascript.ScriptableObject;
/**
* Simple wrapper to make "normal" object scriptable according to specific configuration
* and allowing use of index properties.
* TODO: Configuration of the
* properties and functions should occur from the XML configuration according to
* the browser to simulate.
*
* @author Marc Guillemot
* @author Ronald Brill
*/
public class ScriptableWrapper extends ScriptableObject {
private static final Class>[] METHOD_PARAMS_INT = {Integer.TYPE};
private static final Class>[] METHOD_PARAMS_STRING = {String.class};
private final Map properties_ = new HashMap<>();
private Method getByIndexMethod_;
private final Object javaObject_;
private final String jsClassName_;
private Method getByNameFallback_;
/**
* Constructs a wrapper for the java object.
* @param scope the scope of the executing script
* @param javaObject the javaObject to wrap
* @param staticType the static type of the object
*/
public ScriptableWrapper(final Scriptable scope, final Object javaObject,
final Class> staticType) {
javaObject_ = javaObject;
setParentScope(scope);
// all these information should come from the XML js configuration file
// just for a first time...
if (NodeList.class.equals(staticType)
|| NamedNodeMap.class.equals(staticType)) {
try {
jsClassName_ = staticType.getSimpleName();
// is there a better way that would avoid to keep local
// information?
// it seems that defineProperty with only accepts delegate if
// its method takes a ScriptableObject
// as parameter.
final Method length = javaObject.getClass().getMethod("getLength", ArrayUtils.EMPTY_CLASS_ARRAY);
properties_.put("length", length);
final Method item = javaObject.getClass().getMethod("item", METHOD_PARAMS_INT);
defineProperty("item", new MethodWrapper("item", staticType, METHOD_PARAMS_INT), 0);
final Method toString = getClass().getMethod("jsToString", ArrayUtils.EMPTY_CLASS_ARRAY);
defineProperty("toString", new FunctionObject("toString", toString, this), 0);
getByIndexMethod_ = item;
if (NamedNodeMap.class.equals(staticType)) {
final Method getNamedItem = javaObject.getClass().getMethod("getNamedItem", METHOD_PARAMS_STRING);
defineProperty("getNamedItem",
new MethodWrapper("getNamedItem", staticType, METHOD_PARAMS_STRING), 0);
getByNameFallback_ = getNamedItem;
}
}
catch (final Exception e) {
throw new RuntimeException("Method not found", e);
}
}
else {
throw new RuntimeException("Unknown type: " + staticType.getName());
}
}
/**
* {@inheritDoc}
* @see ScriptableObject#get(java.lang.String,net.sourceforge.htmlunit.corejs.javascript.Scriptable)
*/
@Override
public Object get(final String name, final Scriptable start) {
final Method propertyGetter = properties_.get(name);
final Object response;
if (propertyGetter != null) {
response = invoke(propertyGetter);
}
else {
final Object fromSuper = super.get(name, start);
if (fromSuper != Scriptable.NOT_FOUND) {
response = fromSuper;
}
else {
final Object byName = invoke(getByNameFallback_, new Object[] {name});
if (byName != null) {
response = byName;
}
else {
response = Scriptable.NOT_FOUND;
}
}
}
return Context.javaToJS(response, ScriptableObject
.getTopLevelScope(start));
}
/**
* {@inheritDoc}
* @see ScriptableObject#has(java.lang.String, net.sourceforge.htmlunit.corejs.javascript.Scriptable)
*/
@Override
public boolean has(final String name, final Scriptable start) {
return properties_.containsKey(name) || super.has(name, start);
}
/**
* Invokes the method on the wrapped object.
* @param method the method to invoke
* @return the invocation result
*/
protected Object invoke(final Method method) {
return invoke(method, ArrayUtils.EMPTY_OBJECT_ARRAY);
}
/**
* Invokes the method on the wrapped object.
* @param method the method to invoke
* @param args the argument to pass to the method
* @return the invocation result
*/
protected Object invoke(final Method method, final Object[] args) {
try {
return method.invoke(javaObject_, args);
}
catch (final Exception e) {
throw new RuntimeException(
"Invocation of method on java object failed", e);
}
}
/**
* {@inheritDoc}
* @see ScriptableObject#get(int, net.sourceforge.htmlunit.corejs.javascript.Scriptable)
*/
@Override
public Object get(final int index, final Scriptable start) {
if (getByIndexMethod_ != null) {
final Object byIndex = invoke(getByIndexMethod_, new Object[] {Integer.valueOf(index)});
return Context.javaToJS(byIndex, ScriptableObject.getTopLevelScope(start));
}
return super.get(index, start);
}
/**
* {@inheritDoc}
* @see ScriptableObject#getDefaultValue(java.lang.Class)
*/
@Override
public Object getDefaultValue(final Class> hint) {
if (String.class.equals(hint) || hint == null) {
return jsToString();
}
return super.getDefaultValue(hint);
}
/**
* To use as "toString" function in JavaScript.
* @return the string representation
*/
public String jsToString() {
return "[object " + getClassName() + "]";
}
/**
* {@inheritDoc}
* @see ScriptableObject#getClassName()
*/
@Override
public String getClassName() {
return jsClassName_;
}
/**
* Gets the java object made availabe to JavaScript through this wrapper.
* @return the wrapped object
*/
public Object getWrappedObject() {
return javaObject_;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy