/*
GNU GENERAL LICENSE
Copyright (C) 2006 The Lobo Project. Copyright (C) 2014 - 2017 Lobo Evolution
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; either
verion 3 of the License, or (at your option) any later version.
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 License for more details.
You should have received a copy of the GNU General Public
along with this program. If not, see <http://www.gnu.org/licenses/>.
Contact info: lobochief@users.sourceforge.net; ivan.difrancesco@yahoo.it
*/
package org.lobobrowser.js;
import java.lang.reflect.Method;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lobobrowser.html.info.PropertyInfo;
import org.mozilla.javascript.EvaluatorException;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
/**
* The Class JavaObjectWrapper.
*/
public class JavaObjectWrapper extends ScriptableObject {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;
/** The Constant logger. */
private static final Logger logger = LogManager.getLogger(JavaObjectWrapper.class.getName());
/** The Constant loggableInfo. */
private static final boolean loggableInfo = logger.isEnabled(Level.INFO);
/** The delegate. */
private final Object delegate;
/** The class wrapper. */
private final JavaClassWrapper classWrapper;
/**
* Instantiates a new java object wrapper.
*
* @param classWrapper
* the class wrapper
* @throws InstantiationException
* the instantiation exception
* @throws IllegalAccessException
* the illegal access exception
*/
public JavaObjectWrapper(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.
Object delegate = this.classWrapper.newInstance();
this.delegate = delegate;
}
/**
* Instantiates a new java object wrapper.
*
* @param classWrapper
* the class wrapper
* @param delegate
* the delegate
*/
public JavaObjectWrapper(JavaClassWrapper classWrapper, 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;
}
/**
* Gets the java object.
*
* @return the java object
*/
public Object getJavaObject() {
// Cannot retain delegate with a strong reference.
return this.delegate;
}
/*
* (non-Javadoc)
*
* @see org.mozilla.javascript.ScriptableObject#getClassName()
*/
@Override
public String getClassName() {
return this.classWrapper.getClassName();
}
/*
* (non-Javadoc)
*
* @see org.mozilla.javascript.ScriptableObject#get(int,
* org.mozilla.javascript.Scriptable)
*/
@Override
public Object get(int index, Scriptable start) {
PropertyInfo pinfo = this.classWrapper.getIntegerIndexer();
if (pinfo == null) {
return super.get(index, start);
} else {
try {
Method getter = pinfo.getGetter();
if (getter == null) {
throw new EvaluatorException("Indexer is write-only");
}
// Cannot retain delegate with a strong reference.
Object javaObject = this.getJavaObject();
if (javaObject == null) {
throw new IllegalStateException("Java object (class=" + this.classWrapper + ") is null.");
}
Object raw = getter.invoke(javaObject, new Object[] { new Integer(index) });
if (raw != null) {
return JavaScript.getInstance().getJavascriptObject(raw, this.getParentScope());
}
} catch (Exception err) {
err.getCause();
}
}
return Scriptable.NOT_FOUND;
}
/*
* (non-Javadoc)
*
* @see org.mozilla.javascript.ScriptableObject#get(java.lang.String,
* org.mozilla.javascript.Scriptable)
*/
@Override
public Object get(String name, Scriptable start) {
PropertyInfo pinfo = this.classWrapper.getProperty(name);
if (pinfo != null) {
Method getter = pinfo.getGetter();
if (getter == null) {
throw new EvaluatorException("Property '" + name + "' is not readable");
}
try {
// Cannot retain delegate with a strong reference.
Object javaObject = this.getJavaObject();
if (javaObject == null) {
throw new IllegalStateException("Java object (class=" + this.classWrapper + ") is null.");
}
Object val = getter.invoke(javaObject, (Object[]) null);
return JavaScript.getInstance().getJavascriptObject(val, start.getParentScope());
} catch (Exception err) {
err.getCause();
return new Object();
}
} else {
Function f = this.classWrapper.getFunction(name);
if (f != null) {
return f;
} else {
// Should check properties set in context
// first. Consider element IDs should not
// override Window variables set by user.
Object result = super.get(name, start);
if (result != Scriptable.NOT_FOUND) {
return result;
}
PropertyInfo ni = this.classWrapper.getNameIndexer();
if (ni != null) {
Method getter = ni.getGetter();
if (getter != null) {
// Cannot retain delegate with a strong reference.
Object javaObject = this.getJavaObject();
if (javaObject == null) {
throw new IllegalStateException("Java object (class=" + this.classWrapper + ") is null.");
}
try {
Object val = getter.invoke(javaObject, new Object[] { name });
if (val == null) {
// There might not be an indexer setter.
return super.get(name, start);
} else {
return JavaScript.getInstance().getJavascriptObject(val, start.getParentScope());
}
} catch (Exception err) {
err.getCause();
}
}
}
return Scriptable.NOT_FOUND;
}
}
}
/*
* (non-Javadoc)
*
* @see org.mozilla.javascript.ScriptableObject#put(int,
* org.mozilla.javascript.Scriptable, java.lang.Object)
*/
@Override
public void put(int index, Scriptable start, Object value) {
PropertyInfo pinfo = this.classWrapper.getIntegerIndexer();
if (pinfo == null) {
super.put(index, start, value);
} else {
try {
Method setter = pinfo.getSetter();
if (setter == null) {
throw new EvaluatorException("Indexer is read-only");
}
Object actualValue;
actualValue = JavaScript.getInstance().getJavaObject(value, pinfo.getPropertyType());
setter.invoke(this.getJavaObject(), new Object[] { new Integer(index), actualValue });
} catch (Exception err) {
err.getCause();
}
}
}
/*
* (non-Javadoc)
*
* @see org.mozilla.javascript.ScriptableObject#put(java.lang.String,
* org.mozilla.javascript.Scriptable, java.lang.Object)
*/
@Override
public void put(String name, Scriptable start, Object value) {
if (value instanceof org.mozilla.javascript.Undefined) {
super.put(name, start, value);
} else {
PropertyInfo pinfo = this.classWrapper.getProperty(name);
if (pinfo != null) {
Method setter = pinfo.getSetter();
if (setter == null) {
throw new EvaluatorException(
"Property '" + name + "' is not settable in " + this.classWrapper.getClassName() + ".");
}
try {
Object actualValue;
actualValue = JavaScript.getInstance().getJavaObject(value, pinfo.getPropertyType());
setter.invoke(this.getJavaObject(), new Object[] { actualValue });
} catch (IllegalArgumentException iae) {
iae.getCause();
} catch (Exception err) {
err.getCause();
}
} else {
PropertyInfo ni = this.classWrapper.getNameIndexer();
if (ni != null) {
Method setter = ni.getSetter();
if (setter != null) {
try {
Object actualValue;
actualValue = JavaScript.getInstance().getJavaObject(value, ni.getPropertyType());
setter.invoke(this.getJavaObject(), new Object[] { name, actualValue });
} catch (Exception err) {
err.getCause();
}
} else {
super.put(name, start, value);
}
} else {
super.put(name, start, value);
}
}
}
}
/**
* Gets the constructor.
*
* @param className
* the class name
* @param classWrapper
* the class wrapper
* @param scope
* the scope
* @return the constructor
*/
public static Function getConstructor(String className, JavaClassWrapper classWrapper, Scriptable scope) {
return new JavaConstructorObject(className, classWrapper);
}
/**
* Gets the constructor.
*
* @param className
* the class name
* @param classWrapper
* the class wrapper
* @param scope
* the scope
* @param instantiator
* the instantiator
* @return the constructor
*/
public static Function getConstructor(String className, JavaClassWrapper classWrapper, Scriptable scope,
JavaInstantiator instantiator) {
return new JavaConstructorObject(className, classWrapper, instantiator);
}
/*
* (non-Javadoc)
*
* @see
* org.mozilla.javascript.ScriptableObject#getDefaultValue(java.lang.Class)
*/
@Override
public java.lang.Object getDefaultValue(Class hint) {
if (loggableInfo) {
logger.info("getDefaultValue(): hint=" + hint + ",this=" + this.getJavaObject());
}
if ((hint == null) || String.class.equals(hint)) {
Object javaObject = this.getJavaObject();
if (javaObject == null) {
throw new IllegalStateException("Java object (class=" + this.classWrapper + ") is null.");
}
return javaObject.toString();
} else if (Number.class.isAssignableFrom(hint)) {
Object javaObject = this.getJavaObject();
if (javaObject instanceof Number) {
return javaObject;
} else if (javaObject instanceof String) {
return Double.valueOf((String) javaObject);
} else {
return super.getDefaultValue(hint);
}
} else {
return super.getDefaultValue(hint);
}
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
Object javaObject = this.getJavaObject();
String type = javaObject == null ? "<null>" : javaObject.getClass().getName();
return "JavaObjectWrapper[object=" + this.getJavaObject() + ",type=" + type + "]";
}
}