/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source 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 version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source 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, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
* Free SoftwareFoundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.es.wrapper;
import com.caucho.es.ESArrayWrapper;
import com.caucho.es.ESBase;
import com.caucho.es.ESBeanWrapper;
import com.caucho.es.Global;
import com.caucho.java.JavaCompiler;
import com.caucho.loader.SimpleLoader;
import com.caucho.server.util.CauchoSystem;
import com.caucho.util.CharBuffer;
import com.caucho.util.IntMap;
import com.caucho.vfs.JarPath;
import com.caucho.vfs.MergePath;
import com.caucho.vfs.Path;
import com.caucho.vfs.Vfs;
import com.caucho.vfs.WriteStream;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Logger;
public class Wrapper {
private static final Integer LOCK = new Integer(0);
private static final Logger log
= Logger.getLogger(Wrapper.class.getName());
private String name;
private String javaClassName;
private Class cl;
private boolean isPublic;
private Path dest;
private WriteStream os;
private ClassLoader loader;
private JavaCompiler compiler;
private ESBeanInfo beanInfo;
private IntMap hasDispatch;
private IntMap staticHasDispatch;
private IntMap setDispatch;
private IntMap staticSetDispatch;
private IntMap methodDispatch;
private IntMap staticMethodDispatch;
private HashMap namedProperties;
private ArrayList overloadDispatch;
private int depth;
private boolean isNewline;
private Class esBase;
/**
* Creates the instance of the wrapper generator.
*
* @param resin the global parent object
* @param cl the class to wrap.
*/
private Wrapper(Global resin, Class cl)
{
name = cl.getName().replace('/', '.');
MergePath mergePath = new MergePath();
mergePath.addClassPath(cl.getClassLoader());
Path destClass = mergePath.lookup(name.replace('.', '/') + ".class");
// technically, need to resort to dynamic. This is a cheat.
if (! destClass.exists() && cl.getInterfaces().length > 0) {
cl = cl.getInterfaces()[0];
name = cl.getName().replace('/', '.');
}
javaClassName = toJavaClassName(name);
CharBuffer cb = new CharBuffer();
for (int i = 0; i < name.length(); i++) {
char ch = name.charAt(i);
if (ch == '$')
cb.append("_0");
else if (ch == '_')
cb.append("__");
else
cb.append(ch);
}
name = "_jsbean." + cb + "_es";
this.cl = cl;
isPublic = Modifier.isPublic(cl.getModifiers());
//this.loader = resin.getParentLoader();
loader = cl.getClassLoader();
compiler = JavaCompiler.create(loader);
//compiler.setEncoding("utf8");
Path workPath = CauchoSystem.getWorkPath();
dest = workPath.lookup(name.replace('.', '/') + ".java");
hasDispatch = new IntMap();
staticHasDispatch = new IntMap();
setDispatch = new IntMap();
staticSetDispatch = new IntMap();
methodDispatch = new IntMap();
staticMethodDispatch = new IntMap();
namedProperties = new HashMap();
overloadDispatch = new ArrayList();
try {
esBase = Class.forName("com.caucho.es.ESBase");
} catch (Exception e) {
}
}
public static ESBase []bean(Global resin, Class cl)
throws Throwable
{
Wrapper wrapper = null;
if (cl.isArray()) {
ESBase arrayWrapper = ESArrayWrapper.wrapper(resin, cl);
return new ESBase[] { arrayWrapper.getProperty("CONSTRUCTOR"),
arrayWrapper };
}
synchronized (LOCK) {
wrapper = new Wrapper(resin, cl);
ESBeanWrapper beanWrapper = wrapper.wrap();
beanWrapper.n = 0;
return new ESBase[] { beanWrapper.wrapStatic(), beanWrapper };
}
}
/**
* Creates the wrapper for a class
*/
private ESBeanWrapper wrap() throws Throwable
{
dest.getParent().mkdirs();
Path workPath = CauchoSystem.getWorkPath();
Path destClass = workPath.lookup(name.replace('.', '/') + ".class");
ClassLoader beanLoader;
beanLoader = SimpleLoader.create(loader,
CauchoSystem.getWorkPath(),
name);
ESBeanWrapper wrapper;
try {
Class cl = CauchoSystem.loadClass(name, false, beanLoader);
wrapper = (ESBeanWrapper) cl.newInstance();
if (! wrapper.isModified() && wrapper.getVersionId() == CauchoSystem.getVersionId())
return wrapper;
} catch (Throwable e) {
}
destClass.remove();
os = dest.openWrite();
beanInfo = ESIntrospector.getBeanInfo(cl);
try {
printHeader();
printConstructors();
printHasProperty();
printSetProperty();
printKeys();
printDeletes();
printMethods();
printInit();
printFooter();
} finally {
os.close();
}
compiler.compile(name.replace('.', '/') + ".java", null);
beanLoader = SimpleLoader.create(loader,
CauchoSystem.getWorkPath(),
name);
try {
Class cl = CauchoSystem.loadClass(name, false, beanLoader);
wrapper = (ESBeanWrapper) cl.newInstance();
} catch (NoClassDefFoundError e) {
e.printStackTrace();
throw e;
}
return wrapper;
}
private long getSourceLastModified(Class cl)
{
long lastModified = 0;
String classPath;
URL resource = null;
String clName = cl.getName().replace('.', '/') + ".class";
String name;
if (loader != null)
resource = loader.getResource(clName);
String fileName = resource != null ? resource.toExternalForm() : null;
// XXX: need to implement jar: filesystem
if (resource == null ||
fileName.startsWith("systemresource:") ||
fileName.startsWith("jar:"))
return getClassPathLastModified(cl);
Path path = Vfs.lookup(fileName);
if (path != null && path.canRead())
return path.getLastModified();
else
return 0;
}
private long getClassPathLastModified(Class cl)
{
String clName = cl.getName().replace('.', '/') + ".class";
String classPath = System.getProperty("java.class.path");
char sep = CauchoSystem.getPathSeparatorChar();
int head = 0;
int tail;
for (; (tail = classPath.indexOf(sep, head)) >= 0; head = tail + 1) {
String name = classPath.substring(head, tail);
Path path = Vfs.lookupNative(name);
if (name.endsWith(".jar") || name.endsWith(".zip"))
path = JarPath.create(path);
if (path != null && path.lookup(clName).canRead()) {
return path.lookup(clName).getLastModified();
}
}
String name = classPath.substring(head);
Path path = Vfs.lookupNative(name);
if (name.endsWith(".jar") || name.endsWith(".zip"))
path = JarPath.create(path);
if (path != null && path.lookup(clName).canRead())
return path.lookup(clName).getLastModified();
return 0;
}
private void printHeader() throws IOException
{
int p = name.lastIndexOf('.');
String pkg = name.substring(0, p);
String clName = name.substring(p + 1);
println("package " + pkg + ";");
println("import com.caucho.es.*;");
Iterator iter = beanInfo.getNonPkgClasses().iterator();
while (iter.hasNext()) {
String name = (String) iter.next();
println("import " + name + ";");
}
println();
println("public class " + clName + " extends ESBeanWrapper {");
pushDepth();
if (isPublic)
println("private " + javaClassName + " _value;");
println();
println("public long getVersionId() { return " +
CauchoSystem.getVersionId() + "L; }");
println();
println("protected ESBeanWrapper dup()");
println("{");
println(" return new " + clName + "();");
println("}");
println();
println("public ESBeanWrapper wrap(Object value)");
println("{");
pushDepth();
println("if (value == null) throw new NullPointerException();");
println(name + " child = new " + name + "();");
println("child.value = value;");
if (isPublic)
println("child._value = (" + javaClassName + ") value;");
println("child.hasDispatch = instanceHasDispatch;");
println("child.setDispatch = instanceSetDispatch;");
println("child.methodDispatch = instanceMethodDispatch;");
println("child.n = -1;");
println("return child;");
popDepth();
println("}");
println();
println("public ESBeanWrapper wrapStatic()");
println("{");
pushDepth();
println(name + " child = new " + name + "();");
println("child.hasDispatch = staticHasDispatch;");
println("child.setDispatch = staticSetDispatch;");
println("child.methodDispatch = staticMethodDispatch;");
println("child.n = -2;");
println("child.name = \"" + javaClassName + "\";");
println("try {");
println(" child.value = Class.forName(child.name);");
println("} catch (Exception e) {}");
println("return child;");
popDepth();
println("}");
println();
println("public Class getJavaType()");
println("{");
pushDepth();
println("return value.getClass();");
popDepth();
println("}");
}
private void printConstructors() throws IOException
{
println();
println("public ESBase construct(Call call, int length)");
println(" throws Throwable");
println("{");
pushDepth();
println("if (n != -2)");
println(" throw new ESException(\"can't create `" + javaClassName + "'\");");
println();
if (printMethodConstructor()) {
popDepth();
println("}");
return;
}
ArrayList overload = beanInfo.getConstructors();
if (Modifier.isAbstract(cl.getModifiers()))
overload = null;
if (overload == null || overload.size() == 0) {
println(" throw new ESException(\"can't create `" + javaClassName + "'\");");
}
else {
Constructor last = null;
for (int i = 0; i < overload.size(); i++) {
if (overload.get(i) instanceof Constructor)
last = (Constructor) overload.get(i);
}
for (int i = 0; i < overload.size(); i++) {
Object o = overload.get(i);
if (! (o instanceof Constructor))
continue;
Constructor constructor = (Constructor) o;
if (constructor != last) {
println("if (length <= " + i + ")");
print(" ");
}
print("return wrap(new " + javaClassName + "(");
Class []param = constructor.getParameterTypes();
for (int j = 0; j < param.length; j++) {
if (j > 0)
print(", ");
printArgToJava(param[j], j);
}
println("));");
}
if (last == null)
println("throw new ESException(\"can't create `" + javaClassName + "'\");");
}
popDepth();
println("}");
}
private boolean printMethodConstructor() throws IOException
{
ArrayList overload = (ArrayList) beanInfo._staticMethodMap.get("create");
if (overload != null) {
printMethod(Integer.MIN_VALUE, "create", overload, null);
return true;
}
else
return false;
}
/**
* Print the code for accessing properties.
*/
private void printHasProperty() throws IOException
{
println();
println("public ESBase hasProperty(ESString name)");
println(" throws Throwable");
println("{");
pushDepth();
println("ESBase temp;");
println("switch (hasDispatch.get(name)) {");
PropertyDescriptor []props = beanInfo.getPropertyDescriptors();
int index = 1;
for (int i = 0; i < props.length; i++) {
if (props[i] instanceof NamedPropertyDescriptor)
index = doHasNamedProperty(index, (NamedPropertyDescriptor) props[i]);
else if (props[i] instanceof ESIndexedPropertyDescriptor)
index = doHasIndexProperty(index,
(ESIndexedPropertyDescriptor) props[i]);
else if (props[i] instanceof ESPropertyDescriptor)
index = doHasProperty(index, (ESPropertyDescriptor) props[i]);
else
throw new RuntimeException();
}
println("default:");
println(" return ESBase.esEmpty;");
println("}");
popDepth();
println("}");
}
private int doHasIndexProperty(int i, ESIndexedPropertyDescriptor prop)
throws IOException
{
Named named = new Named(prop.getName(), namedProperties.size());
int n = named.n;
namedProperties.put(prop.getName(), named);
hasDispatch.put(prop.getName(), i);
println("case " + i + ":");
pushDepth();
println("if (name" + n + " == null) {");
println(" name" + n + " = new " + name + "();");
println(" name" + n + ".value = value;");
if (isPublic)
println(" name" + n + "._value = _value;");
println(" name" + n + ".hasDispatch = has" + n + ";");
println(" name" + n + ".setDispatch = set" + n + ";");
println(" name" + n + ".delId = " + n + ";");
println("}");
println("return name" + n + ";");
popDepth();
i += 1;
ESMethodDescriptor md = prop.getESReadMethod();
if (md == null)
return i;
println("case " + i + ":");
pushDepth();
named.get = i;
ESMethodDescriptor size = prop.getESSizeMethod();
if (size != null) {
println("if (name.equals(LENGTH)) {");
pushDepth();
Method method = size.getMethod();
Class resultClass = method.getReturnType();
print("return ");
startJavaToES(resultClass);
startProp(size);
print(")");
endJavaToES(resultClass);
println(";");
popDepth();
println("} else {");
pushDepth();
}
Method method = md.getMethod();
Class resultClass = method.getReturnType();
print("return ");
startJavaToES(resultClass);
int p = startProp(md);
if (p > 0)
print(", ");
print("name.toInt32())");
endJavaToES(resultClass);
println(";");
if (size != null) {
popDepth();
println("}");
}
popDepth();
return i + 1;
}
private int doHasNamedProperty(int i, NamedPropertyDescriptor prop)
throws IOException
{
Named named = new Named(prop.getName(), namedProperties.size());
int n = named.n;
namedProperties.put(prop.getName(), named);
hasDispatch.put(prop.getName(), i);
println("case " + i + ":");
pushDepth();
println("if (name" + n + " == null) {");
println(" name" + n + " = new " + name + "();");
println(" name" + n + ".value = value;");
if (isPublic)
println(" name" + n + "._value = _value;");
println(" name" + n + ".hasDispatch = has" + n + ";");
println(" name" + n + ".setDispatch = set" + n + ";");
println(" name" + n + ".delId = " + n + ";");
println("}");
println("return name" + n + ";");
popDepth();
i += 1;
ESMethodDescriptor md = prop.getNamedReadMethod();
if (md == null)
return i;
println("case " + i + ":");
pushDepth();
named.get = i;
Method method = md.getMethod();
if (Modifier.isStatic(method.getModifiers()) && ! md.isStaticVirtual())
staticHasDispatch.put(prop.getName(), i - 1);
Class resultClass = method.getReturnType();
print("return ");
startJavaToES(resultClass);
int p = startProp(md);
if (p > 0)
print(", ");
print("name.toJavaString())");
endJavaToES(resultClass);
println(";");
popDepth();
return i + 1;
}
private int doHasProperty(int i, ESPropertyDescriptor prop)
throws IOException
{
Field field = prop.getESField();
ESMethodDescriptor md = prop.getESReadMethod();
if (field != null && ! Modifier.isPublic(field.getModifiers()))
field = null;
if (md == null && field == null)
return i;
hasDispatch.put(prop.getName(), i);
println("case " + i + ":");
pushDepth();
if (field != null) {
Class resultClass = field.getType();
print("return ");
startJavaToES(resultClass);
if (isPublic && field.getDeclaringClass().getName().equals(cl.getName()))
print("_value.");
else if (Modifier.isStatic(field.getModifiers()))
print(toJavaClassName(field.getDeclaringClass().getName()) + ".");
else
print("((" + toJavaClassName(field.getDeclaringClass().getName()) + ") value).");
print(field.getName());
endJavaToES(resultClass);
println(";");
popDepth();
if (Modifier.isStatic(field.getModifiers()))
staticHasDispatch.put(prop.getName(), i);
return i + 1;
}
Method method = md.getMethod();
if (Modifier.isStatic(method.getModifiers()) && ! md.isStaticVirtual())
staticHasDispatch.put(prop.getName(), i);
print("return ");
Class resultClass = method.getReturnType();
startJavaToES(resultClass);
int p = startProp(md);
print(")");
endJavaToES(resultClass);
println(";");
popDepth();
return i + 1;
}
private void printSetProperty() throws IOException
{
println();
println("public void setProperty(ESString name, ESBase newValue)");
println(" throws Throwable");
println("{");
pushDepth();
println("ESBase temp;");
println("switch (setDispatch.get(name)) {");
PropertyDescriptor []props = beanInfo.getPropertyDescriptors();
int index = 0;
for (int i = 0; i < props.length; i++) {
if (props[i] instanceof NamedPropertyDescriptor) {
index = doSetNamedProperty(index,
(NamedPropertyDescriptor) props[i]);
}
else if (props[i] instanceof ESIndexedPropertyDescriptor) {
index = doSetIndexProperty(index,
(ESIndexedPropertyDescriptor) props[i]);
}
else if (props[i] instanceof ESPropertyDescriptor)
index = doSetProperty(index, (ESPropertyDescriptor) props[i]);
else
throw new RuntimeException();
}
println("default:");
println(" return;");
println("}");
popDepth();
println("}");
}
private int doSetNamedProperty(int i, NamedPropertyDescriptor prop)
throws IOException
{
Named named = (Named) namedProperties.get(prop.getName());
if (named == null)
return i;
int n = named.n;
ESMethodDescriptor md = prop.getNamedWriteMethod();
if (md == null)
return i;
println("case " + i + ":");
pushDepth();
named.set = i;
int p = startProp(md);
Class []param = md.getParameterTypes();
if (p != 0)
print(", ");
print("name.toJavaString(), ");
printValueToJava(param[1], "newValue");
println(");");
println("return;");
popDepth();
return i + 1;
}
private int doSetIndexProperty(int i, ESIndexedPropertyDescriptor prop)
throws IOException
{
Named named = (Named) namedProperties.get(prop.getName());
if (named == null)
return i;
int n = named.n;
ESMethodDescriptor md = prop.getESWriteMethod();
if (md == null)
return i;
println("case " + i + ":");
pushDepth();
named.set = i;
int p = startProp(md);
Class []param = md.getParameterTypes();
if (p != 0)
print(", ");
print("name.toInt32(), ");
printValueToJava(param[1], "newValue");
println(");");
println("return;");
popDepth();
return i + 1;
}
private int doSetProperty(int i, ESPropertyDescriptor prop)
throws IOException
{
ESMethodDescriptor md = prop.getESWriteMethod();
Field field = prop.getESField();
if (field != null && Modifier.isFinal(field.getModifiers()))
field = null;
if (md == null && field == null)
return i;
println("case " + i + ":");
pushDepth();
setDispatch.put(prop.getName(), i);
if (field != null) {
Class resultClass = field.getType();
if (isPublic)
print("_value.");
else
print("((" + field.getDeclaringClass().getName() + ") value).");
print(field.getName());
print(" = ");
printValueToJava(resultClass, "newValue");
println(";");
println("return;");
popDepth();
return i + 1;
}
Method method = md.getMethod();
if (Modifier.isStatic(method.getModifiers()) && ! md.isStaticVirtual())
staticSetDispatch.put(prop.getName(), i);
Class []param = md.getParameterTypes();
int p = startProp(md);
if (p != 0)
print(", ");
printValueToJava(param[0], "newValue");
println(");");
println("return;");
popDepth();
return i + 1;
}
private void printKeys() throws IOException
{
println();
println("public java.util.Iterator keys()");
println(" throws Throwable");
println("{");
pushDepth();
println("switch (delId) {");
ESMethodDescriptor md = beanInfo.iterator;
if (md != null) {
println("case -1:");
print(" return Call.toESIterator(");
startProp(md);
println("));");
}
PropertyDescriptor []props = beanInfo.getPropertyDescriptors();
for (int i = 0; i < props.length; i++) {
if (props[i] instanceof NamedPropertyDescriptor)
printNamedKey((NamedPropertyDescriptor) props[i]);
}
println("default:");
println(" return super.keys();");
println("}");
popDepth();
println("}");
}
private void printNamedKey(NamedPropertyDescriptor prop)
throws IOException
{
ESMethodDescriptor md = prop.getNamedIteratorMethod();
if (md == null)
return;
Named named = (Named) namedProperties.get(prop.getName());
println("case " + named.n + ":");
pushDepth();
print("return Call.toESIterator(");
int p = startProp(md);
println("));");
popDepth();
}
private void printDeletes() throws IOException
{
println();
println("public ESBase delete(ESString key)");
println(" throws Throwable");
println("{");
pushDepth();
println("switch (delId) {");
PropertyDescriptor []props = beanInfo.getPropertyDescriptors();
for (int i = 0; i < props.length; i++) {
if (props[i] instanceof NamedPropertyDescriptor)
printNamedDelete((NamedPropertyDescriptor) props[i]);
}
println("default:");
println(" return ESBoolean.FALSE;");
println("}");
popDepth();
println("}");
}
private void printNamedDelete(NamedPropertyDescriptor prop)
throws IOException
{
ESMethodDescriptor md = prop.getNamedRemoveMethod();
if (md == null)
return;
Named named = (Named) namedProperties.get(prop.getName());
println("case " + named.n + ":");
pushDepth();
int p = startProp(md);
if (p > 0)
print(", ");
println("key.toJavaString());");
println("return ESBoolean.TRUE;");
popDepth();
}
/**
* Prints all the accessible methods in this object.
*/
private void printMethods() throws IOException
{
println();
println("public ESBase call(Call call, int length, int n)");
println(" throws Throwable");
println("{");
pushDepth();
println("ESBase temp;");
println("switch (n) {");
ArrayList overload = (ArrayList) beanInfo._methodMap.get("call");
if (overload != null)
printMethod(-1, "call", overload, null);
// Print the constructor (code -2)
ArrayList create = (ArrayList) beanInfo._staticMethodMap.get("create");
ArrayList call = (ArrayList) beanInfo._staticMethodMap.get("call");
if (create != null)
printMethod(-2, "create", create, null);
else if (call != null)
printMethod(-2, "call", create, null);
else {
println("case -2:");
println(" return construct(call, length);");
}
Iterator iter = beanInfo._methodMap.entrySet().iterator();
int i = 0;
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
overload = (ArrayList) entry.getValue();
String name = (String) entry.getKey();
i = printMethod(i, name, overload, methodDispatch);
}
iter = beanInfo._staticMethodMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
overload = (ArrayList) entry.getValue();
String name = (String) entry.getKey();
i = printMethod(i, name, overload, staticMethodDispatch);
}
println("}");
println("return ESBase.esUndefined;");
popDepth();
println("}");
}
/**
* Prints a method in a method dispatch.
*/
private int printMethod(int i, String name, ArrayList overload,
IntMap dispatch)
throws IOException
{
ESMethodDescriptor []last = null;
if (overload == null)
return i;
for (int j = 0; j < overload.size(); j++) {
last = (ESMethodDescriptor []) overload.get(j);
}
if (last == null) {
return i;
}
if (i > -100) {
println("case " + i + ":");
pushDepth();
if (dispatch != null)
dispatch.put(name, i++);
}
if (overload.size() > 2) {
ESMethodDescriptor []mds = (ESMethodDescriptor []) overload.get(2);
for (int j = 0; mds != null && j < mds.length; j++) {
Class []cl = mds[j].getParameterTypes();
int p = cl.length - 2;
if (cl[0].getName().equals("com.caucho.es.Call") &&
cl[1].getName().equals("int")) {
printMethod(mds[j], dispatch == null);
popDepth();
return i;
}
}
}
for (int j = 0; j < overload.size(); j++) {
Object o = overload.get(j);
if (o == null)
continue;
ESMethodDescriptor []mds = (ESMethodDescriptor []) o;
if (mds != last) {
println("if (length <= " + j + ") {");
pushDepth();
}
if (mds.length == 1)
printMethod(mds[0], dispatch == null);
else {
String var = "dispatch" + overloadDispatch.size();
overloadDispatch.add(mds);
println("switch (" + var + ".select(call, length)) {");
for (int k = 0; k < mds.length; k++) {
println("case " + k + ":");
pushDepth();
printMethod(mds[k], dispatch == null);
popDepth();
}
println("default:");
println(" throw new ESException(\"no matching method " + mds[0].getName() + "\");");
println("}");
}
if (mds != last) {
popDepth();
println("}");
}
}
if (i > -100) {
popDepth();
}
return i;
}
/**
* Print a single method based on a method descriptor. The arguments
* are assumed to come from a Call object.
*
* @param md the method descriptor.
*/
private void printMethod(ESMethodDescriptor md, boolean isProp)
throws IOException
{
boolean hasThrowable = hasException(md.getMethod().getExceptionTypes(),
Throwable.class);
/*
if (hasThrowable) {
println("try {");
pushDepth();
}
*/
Class returnCl = md.getReturnType();
if (! returnCl.getName().equals("void")) {
print("return ");
startJavaToES(returnCl);
}
Class []param = md.getParameterTypes();
int p;
if (isProp)
p = startProp(md);
else
p = startCall(md);
if (param.length == 2 &&
param[0].getName().equals("com.caucho.es.Call") &&
param[1].getName().equals("int")) {
if (p > 0)
print(", ");
print("call, length");
}
else {
for (int j = 0; j < param.length; j++) {
if (j + p > 0)
print(", ");
printArgToJava(param[j], j);
}
}
if (returnCl.getName().equals("void")) {
println(");");
println("return ESBase.esUndefined;");
}
else {
print(")");
endJavaToES(returnCl);
println(";");
}
/*
if (hasThrowable) {
popDepth();
println("} catch (Exception e) {");
println(" throw e;");
println("} catch (RuntimeException e) {");
println(" throw e;");
println("} catch (Error e) {");
println(" throw e;");
println("} catch (Throwable e) {");
println(" throw new com.caucho.es.ESException(String.valueOf(e));");
println("}");
}
*/
}
private boolean hasException(Class []exn, Class cl)
{
for (int i = 0; i < exn.length; i++)
if (exn[i].isAssignableFrom(cl))
return true;
return false;
}
/**
* Starts a method call that overloads a property.
*/
private int startProp(ESMethodDescriptor md)
throws IOException
{
Method method = md.getMethod();
int p = 0;
if (md.isStaticVirtual()) {
print(md.getMethodClassName());
print(".");
print(md.getMethod().getName());
if (isPublic)
print("(_value");
else
print("((" + toJavaClassName(md.getObjectClassName()) + ") value");
p = 1;
} else if (Modifier.isStatic(method.getModifiers())) {
print(md.getMethodClassName());
print(".");
print(md.getMethod().getName());
print("(");
} else {
if (isPublic)
print("_value.");
else
print("((" + toJavaClassName(md.getObjectClassName()) + ") value).");
print(md.getMethod().getName());
print("(");
}
return p;
}
private int startCall(ESMethodDescriptor md)
throws IOException
{
Method method = md.getMethod();
int p = 0;
if (md.isStaticVirtual()) {
print(toJavaClassName(md.getMethodClassName()));
print(".");
print(md.getMethod().getName());
print("((" + md.getObjectClassName() + ") call.getThisWrapper()");
p = 1;
} else if (Modifier.isStatic(method.getModifiers())) {
print(toJavaClassName(md.getMethodClassName()));
print(".");
print(md.getMethod().getName());
print("(");
} else {
print("((" + toJavaClassName(md.getObjectClassName()) + ") call.getThisWrapper()).");
print(md.getMethod().getName());
print("(");
}
return p;
}
private void startJavaToES(Class cl)
throws IOException
{
String name = cl.getName();
switch (classTypes.get(name)) {
case T_V:
//addJsUndefinedRef();
break;
case T_Z:
print("ESBoolean.create(");
break;
case T_C:
print("ESString.createFromCharCode(");
break;
case T_B: case T_S: case T_I: case T_L:
case T_F: case T_D:
print("ESNumber.create(");
break;
case T_STRING:
print("ESString.toStr(");
break;
default:
if (esBase.isAssignableFrom(cl))
print("((temp = ");
else
print("Global.wrap(");
break;
}
}
private void endJavaToES(Class cl)
throws IOException
{
String name = cl.getName();
switch (classTypes.get(name)) {
case T_V:
//addJsUndefinedRef();
break;
case T_Z:
case T_C:
case T_B: case T_S: case T_I: case T_L:
case T_F: case T_D:
case T_STRING:
print(")");
break;
default:
if (esBase.isAssignableFrom(cl))
print(") == null ? ESBase.esNull : temp)");
else
print(")");
break;
}
}
private void printValueToJava(Class cl, String value)
throws IOException
{
String name = cl.getName();
switch (classTypes.get(name)) {
case T_V:
throw new RuntimeException();
case T_Z:
print(value + ".toBoolean()");
break;
case T_C:
print("(char) " + value + ".toStr().carefulCharAt(0)");
break;
case T_B:
print("(byte) " + value + ".toInt32()");
break;
case T_S:
print("(short) " + value + ".toInt32()");
break;
case T_I:
print(value + ".toInt32()");
break;
case T_L:
print("(long)" + value + ".toNum()");
break;
case T_F:
print("(float)" + value + ".toNum()");
break;
case T_D:
print(value + ".toNum()");
break;
case T_STRING:
print("(" + value + ").toJavaString()");
break;
default:
if (cl.isAssignableFrom(esBase))
print(value);
else if (esBase.isAssignableFrom(cl)) {
print("(");
printClassType(cl);
print(") " + value);
}
else {
print("(");
printClassType(cl);
print(") " + value + ".toJavaObject()");
}
break;
}
}
private void printArgToJava(Class cl, int i)
throws IOException
{
String name = cl.getName();
switch (classTypes.get(name)) {
case T_V:
throw new RuntimeException();
case T_Z:
print("call.getArg(" + i + ", length).toBoolean()");
break;
case T_C:
print("(char) call.getArg(" + i + ", length).toStr().carefulCharAt(0)");
break;
case T_B:
print("(byte) call.getArgInt32(" + i + ", length)");
break;
case T_S:
print("(short) call.getArgInt32(" + i + ", length)");
break;
case T_I:
print("call.getArgInt32(" + i + ", length)");
break;
case T_L:
print("(long) call.getArgNum(" + i + ", length)");
break;
case T_F:
print("(float)call.getArgNum(" + i + ", length)");
break;
case T_D:
print("call.getArgNum(" + i + ", length)");
break;
case T_STRING:
print("call.getArgString(" + i + ", length)");
break;
default:
if (cl.isAssignableFrom(esBase) &&
! cl.getName().equals("java.lang.Object"))
print("call.getArg(" + i + ", length)");
else if (esBase.isAssignableFrom(cl)) {
print("(");
printClassType(cl);
print(") ");
print("call.getArg(" + i + ", length)");
}
else {
print("(");
printClassType(cl);
print(") call.getArgObject(" + i + ", length)");
}
break;
}
}
private void printClassType(Class cl)
throws IOException
{
if (cl.isArray()) {
printClassType(cl.getComponentType());
print("[]");
}
else {
print(cl.getName().replace('$', '.'));
}
}
private void printInit() throws IOException
{
println("private int delId = -1;");
println();
println("private static com.caucho.util.IntMap instanceHasDispatch;");
println("private static com.caucho.util.IntMap instanceSetDispatch;");
println("private static com.caucho.util.IntMap instanceMethodDispatch;");
println("private static com.caucho.util.IntMap staticMethodDispatch;");
println("private static com.caucho.util.IntMap staticHasDispatch;");
println("private static com.caucho.util.IntMap staticSetDispatch;");
Iterator iter = namedProperties.values().iterator();
while (iter.hasNext()) {
Named named = (Named) iter.next();
println(name + " name" + named.n + ";");
print("private static ConstIntMap has" + named.n);
println(" = new ConstIntMap(" + named.get + ");");
print("private static ConstIntMap set" + named.n);
println(" = new ConstIntMap(" + named.set + ");");
}
for (int i = 0; i < overloadDispatch.size(); i++) {
print("private static com.caucho.es.wrapper.MethodDispatcher dispatch" + i);
println(" = new com.caucho.es.wrapper.MethodDispatcher(new Class[][] {");
pushDepth();
ESMethodDescriptor []mds;
mds = (ESMethodDescriptor []) overloadDispatch.get(i);
for (int j = 0; j < mds.length; j++) {
print("new Class[] {");
Class []param = mds[j].getParameterTypes();
for (int k = 0; k < param.length; k++) {
printClass(param[k]);
print(", ");
}
println("},");
}
popDepth();
println("});");
}
println();
println("static { _init(); }");
println();
println("public boolean isModified()");
println("{");
pushDepth();
com.caucho.make.ClassDependency dep = new com.caucho.make.ClassDependency(cl);
println("try {");
println(" Class cl = Class.forName(\"" + cl.getName() + "\", false, Thread.currentThread().getContextClassLoader());");
println(" return new com.caucho.make.ClassDependency(\""
+ cl.getName() + "\", " + dep.getDigest() + "L).isModified();");
println("} catch (Throwable e) {");
println(" return true;");
println("}");
popDepth();
println("}");
println();
println("private static void _init()");
println("{");
pushDepth();
println("instanceHasDispatch = new com.caucho.util.IntMap();");
iter = hasDispatch.iterator();
while (iter.hasNext()) {
String key = (String) iter.next();
println("instanceHasDispatch.put(ESId.intern(\"" + key + "\"), " +
hasDispatch.get(key) + ");");
}
println();
println("staticHasDispatch = new com.caucho.util.IntMap();");
iter = staticHasDispatch.iterator();
while (iter.hasNext()) {
String key = (String) iter.next();
println("staticHasDispatch.put(ESId.intern(\"" + key + "\"), " +
staticHasDispatch.get(key) + ");");
}
println();
println("instanceSetDispatch = new com.caucho.util.IntMap();");
iter = setDispatch.iterator();
while (iter.hasNext()) {
String key = (String) iter.next();
println("instanceSetDispatch.put(ESId.intern(\"" + key + "\"), " +
setDispatch.get(key) + ");");
}
println();
println("staticSetDispatch = new com.caucho.util.IntMap();");
iter = staticSetDispatch.iterator();
while (iter.hasNext()) {
String key = (String) iter.next();
println("staticSetDispatch.put(ESId.intern(\"" + key + "\"), " +
staticSetDispatch.get(key) + ");");
}
println();
println("instanceMethodDispatch = new com.caucho.util.IntMap();");
iter = methodDispatch.iterator();
while (iter.hasNext()) {
String key = (String) iter.next();
println("instanceMethodDispatch.put(ESId.intern(\"" + key + "\"), " +
methodDispatch.get(key) + ");");
}
println();
println("staticMethodDispatch = new com.caucho.util.IntMap();");
iter = staticMethodDispatch.iterator();
while (iter.hasNext()) {
String key = (String) iter.next();
println("staticMethodDispatch.put(ESId.intern(\"" + key + "\"), " +
staticMethodDispatch.get(key) + ");");
}
popDepth();
println("}");
}
private void printClass(Class cl)
throws IOException
{
if (! cl.isArray()) {
print(cl.getName() + ".class");
return;
}
print("(new ");
printArrayClass(cl.getComponentType());
print("[0]).getClass()");
}
private void printArrayClass(Class cl)
throws IOException
{
if (cl.isArray()) {
printArrayClass(cl.getComponentType());
print("[]");
}
else
print(cl.getName());
}
private void printFooter() throws IOException
{
popDepth();
println("}");
}
private void pushDepth()
{
depth += 2;
}
private void popDepth()
{
depth -= 2;
}
private void print(String s) throws IOException
{
if (isNewline)
printDepth();
os.print(s);
}
private void println(String s) throws IOException
{
if (isNewline)
printDepth();
os.println(s);
isNewline = true;
}
private void println() throws IOException
{
if (isNewline)
printDepth();
os.println();
isNewline = true;
}
private void printDepth() throws IOException
{
for (int i = 0; i < depth; i++)
os.print(' ');
isNewline = false;
}
private String toJavaClassName(String name)
{
CharBuffer cb = CharBuffer.allocate();
for (int i = 0; i < name.length(); i++) {
char ch = name.charAt(i);
if (ch == '$' && i > 0 && name.charAt(i - 1) != '.')
cb.append(".");
else
cb.append(ch);
}
return cb.close();
}
static HashMap<String,String> classNames;
static {
classNames = new HashMap<String,String>();
classNames.put("void", "V");
classNames.put("boolean", "Z");
classNames.put("byte", "B");
classNames.put("short", "S");
classNames.put("char", "C");
classNames.put("int", "I");
classNames.put("long", "J");
classNames.put("float", "F");
classNames.put("double", "D");
}
static IntMap classTypes;
static final int T_V = 0;
static final int T_Z = T_V + 1;
static final int T_B = T_Z + 1;
static final int T_S = T_B + 1;
static final int T_C = T_S + 1;
static final int T_I = T_C + 1;
static final int T_L = T_I + 1;
static final int T_F = T_L + 1;
static final int T_D = T_F + 1;
static final int T_STRING = T_D + 1;
static {
classTypes = new IntMap();
classTypes.put("void", T_V);
classTypes.put("boolean", T_Z);
classTypes.put("byte", T_B);
classTypes.put("short", T_S);
classTypes.put("char", T_C);
classTypes.put("int", T_I);
classTypes.put("long", T_L);
classTypes.put("float", T_F);
classTypes.put("double", T_D);
classTypes.put("java.lang.String", T_STRING);
}
static class Named {
String name;
int n;
int get = -1;
int set = -1;
int keys = -1;
int remove = -1;
Named(String name, int n)
{
this.name = name;
this.n = n;
}
}
}