/*
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 java.util.HashMap;
import java.util.Map;
import org.lobobrowser.html.info.PropertyInfo;
import org.mozilla.javascript.Function;
/**
* The Class JavaClassWrapper.
*/
public class JavaClassWrapper {
/** The java class. */
private final Class javaClass;
/** The functions. */
private final Map<String, JavaFunctionObject> functions = new HashMap<String, JavaFunctionObject>();
/** The properties. */
private final Map<String, PropertyInfo> properties = new HashMap<String, PropertyInfo>();
/** The name indexer. */
private PropertyInfo nameIndexer;
/** The integer indexer. */
private PropertyInfo integerIndexer;
/**
* Instantiates a new java class wrapper.
*
* @param class1
* the class1
*/
public JavaClassWrapper(Class class1) {
super();
this.javaClass = class1;
this.scanMethods();
}
/**
* New instance.
*
* @return the object
* @throws InstantiationException
* the instantiation exception
* @throws IllegalAccessException
* the illegal access exception
*/
public Object newInstance() throws InstantiationException,
IllegalAccessException {
return this.javaClass.newInstance();
}
/** Gets the class name.
*
* @return the class name
*/
public String getClassName() {
String className = this.javaClass.getName();
int lastDotIdx = className.lastIndexOf('.');
return lastDotIdx == -1 ? className : className
.substring(lastDotIdx + 1);
}
/**
* Gets the function.
*
* @param name
* the name
* @return the function
*/
public Function getFunction(String name) {
return this.functions.get(name);
}
/**
* Gets the property.
*
* @param name
* the name
* @return the property
*/
public PropertyInfo getProperty(String name) {
return this.properties.get(name);
}
/**
* Scan methods.
*/
private void scanMethods() {
Method[] methods = this.javaClass.getMethods();
int len = methods.length;
for (int i = 0; i < len; i++) {
Method method = methods[i];
String name = method.getName();
if (isPropertyMethod(name, method)) {
this.ensurePropertyKnown(name, method);
} else {
if (isNameIndexer(name, method)) {
this.updateNameIndexer(name, method);
} else if (this.isIntegerIndexer(name, method)) {
this.updateIntegerIndexer(name, method);
}
JavaFunctionObject f = this.functions.get(name);
if (f == null) {
f = new JavaFunctionObject(name);
this.functions.put(name, f);
}
f.addMethod(method);
}
}
}
/**
* Checks if is name indexer.
*
* @param name
* the name
* @param method
* the method
* @return true, if is name indexer
*/
private boolean isNameIndexer(String name, Method method) {
return ("namedItem".equals(name) && (method.getParameterTypes().length == 1))
|| ("setNamedItem".equals(name) && (method.getParameterTypes().length == 2));
}
/**
* Checks if is integer indexer.
*
* @param name
* the name
* @param method
* the method
* @return true, if is integer indexer
*/
private boolean isIntegerIndexer(String name, Method method) {
return ("item".equals(name) && (method.getParameterTypes().length == 1))
|| ("setItem".equals(name) && (method.getParameterTypes().length == 2));
}
/**
* Update name indexer.
*
* @param methodName
* the method name
* @param method
* the method
*/
private void updateNameIndexer(String methodName, Method method) {
boolean getter = true;
if (methodName.startsWith("set")) {
getter = false;
}
PropertyInfo indexer = this.nameIndexer;
if (indexer == null) {
indexer = new PropertyInfo("$item", Object.class);
this.nameIndexer = indexer;
}
if (getter) {
indexer.setGetter(method);
} else {
indexer.setSetter(method);
}
}
/**
* Update integer indexer.
*
* @param methodName
* the method name
* @param method
* the method
*/
private void updateIntegerIndexer(String methodName, Method method) {
boolean getter = true;
if (methodName.startsWith("set")) {
getter = false;
}
PropertyInfo indexer = this.integerIndexer;
if (indexer == null) {
Class pt = getter ? method.getReturnType() : method
.getParameterTypes()[1];
indexer = new PropertyInfo("$item", pt);
this.integerIndexer = indexer;
}
if (getter) {
indexer.setGetter(method);
} else {
indexer.setSetter(method);
}
}
/** Gets the integer indexer.
*
* @return the integer indexer
*/
public PropertyInfo getIntegerIndexer() {
return this.integerIndexer;
}
/** Gets the name indexer.
*
* @return the name indexer
*/
public PropertyInfo getNameIndexer() {
return this.nameIndexer;
}
/**
* Checks if is property method.
*
* @param name
* the name
* @param method
* the method
* @return true, if is property method
*/
private boolean isPropertyMethod(String name, Method method) {
if (name.startsWith("get") || name.startsWith("is")) {
return method.getParameterTypes().length == 0;
} else if (name.startsWith("set")) {
return method.getParameterTypes().length == 1;
} else {
return false;
}
}
/**
* Property uncapitalize.
*
* @param text
* the text
* @return the string
*/
private String propertyUncapitalize(String text) {
try {
if ((text.length() > 1) && Character.isUpperCase(text.charAt(1))) {
// If second letter is capitalized, don't uncapitalize,
// e.g. getURL.
return text;
}
return Character.toLowerCase(text.charAt(0)) + text.substring(1);
} catch (IndexOutOfBoundsException iob) {
return text;
}
}
/**
* Ensure property known.
*
* @param methodName
* the method name
* @param method
* the method
*/
private void ensurePropertyKnown(String methodName, Method method) {
String capPropertyName;
String propertyName;
boolean getter = false;
if (methodName.startsWith("get")) {
capPropertyName = methodName.substring(3);
propertyName = propertyUncapitalize(capPropertyName);
getter = true;
} else if (methodName.startsWith("set")) {
capPropertyName = methodName.substring(3);
propertyName = propertyUncapitalize(capPropertyName);
} else if (methodName.startsWith("is")) {
capPropertyName = methodName.substring(2);
propertyName = propertyUncapitalize(capPropertyName);
getter = true;
} else {
throw new IllegalArgumentException("methodName=" + methodName);
}
PropertyInfo pinfo = this.properties.get(propertyName);
if (pinfo == null) {
Class pt = getter ? method.getReturnType() : method
.getParameterTypes()[0];
pinfo = new PropertyInfo(propertyName, pt);
this.properties.put(propertyName, pinfo);
}
if (getter) {
pinfo.setGetter(method);
} else {
pinfo.setSetter(method);
}
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return this.javaClass.getName();
}
}