// Copyright 2002-2005, FreeHEP.
package org.freehep.aid;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedSet;
import org.freehep.rtti.IClass;
import org.freehep.rtti.IConstructor;
import org.freehep.rtti.IMethod;
import org.freehep.rtti.INamedType;
import org.freehep.rtti.IType;
import org.freehep.util.UserProperties;
import org.freehep.util.io.IndentPrintWriter;
/**
* @author Mark Donszelmann
* @version $Id: JNICodeGenerator.java 8584 2006-08-10 23:06:37Z duns $
*/
public class JNICodeGenerator extends AbstractCPPGenerator {
public final static String indent = " ";
public final static String cr = "\n";
protected UserProperties jniProperties = new UserProperties();
protected UserProperties importProperties = new UserProperties();
protected JNITypeConverter converter;
public JNICodeGenerator(String propDir) {
super(propDir);
properties.setProperty("jni", true);
properties.setProperty("jni.code", true);
AidUtil.loadProperties(jniProperties, getClass(), propDir, "aid.jni.cpp.properties");
AidUtil.loadProperties(importProperties, getClass(), propDir, "aid.imports.java.properties");
converter = new JNITypeConverter(propDir, indent, cr);
}
protected String prefix() {
return "J";
}
public String filename(IClass clazz) {
return prefix() + clazz.getName() + ".cpp";
}
public boolean print(File file, IClass clazz) throws IOException {
IndentPrintWriter out = new IndentPrintWriter(new PrintWriter(new BufferedWriter(new FileWriter(file))));
out.setIndentString(" ");
warning(out);
printIncludeStatements(out, clazz);
if (!namespace(clazz).equals("")) {
out.println();
out.println("using namespace " + namespace(clazz) + ";");
out.println("using namespace " + prefix()+namespace(clazz) + ";");
}
out.println();
printJNIConstructor(out, clazz);
printDestructor(out, clazz);
IMethod methods[] = clazz.getMethods();
for (int i = 0; i < methods.length; i++) {
printMethod(out, clazz, methods[i]);
}
out.close();
return false;
}
protected void includeStatements(IndentPrintWriter out, IClass clazz, SortedSet sysIncludes, SortedSet includes, String namespace, SortedSet types) {
sysIncludes.add("cstdlib");
sysIncludes.add("iostream");
includes.add(prefix() + clazz.getName()+".h");
String[] interfaces = clazz.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
includes.add(prefix()+interfaces[i]+".h");
}
IConstructor constructors[] = clazz.getConstructors();
for (int c = 0; c < constructors.length; c++) {
INamedType parameterTypes[] = constructors[c].getParameterTypes();
for (int p = 0; p < parameterTypes.length; p++) {
includeFrom(parameterTypes[p].getType(), null, sysIncludes, includes, prefix()+namespace, includes);
}
}
IMethod methods[] = clazz.getMethods();
for (int m = 0; m < methods.length; m++) {
includeFrom(methods[m].getReturnType(), null, sysIncludes, includes, prefix()+namespace, includes);
INamedType parameterTypes[] = methods[m].getParameterTypes();
for (int p = 0; p < parameterTypes.length; p++) {
includeFrom(parameterTypes[p].getType(), null, sysIncludes, includes, prefix()+namespace, includes);
}
}
}
protected void printJNIConstructor(IndentPrintWriter out, IClass clazz) {
out.println();
out.print(prefix() + clazz.getName());
out.print("::");
out.print(prefix() + clazz.getName());
out.print("(JNIEnv *env, jobject object)");
String interfaces[] = clazz.getInterfaces();
int k = 0;
out.println();
out.print ((interfaces.length == 0) ? " : JAID::JAIDRef(env, object)" : " : ");
for (int i = 0; i < interfaces.length; i++) {
if (k > 0) {
out.print(", ");
}
k++;
out.print(prefix() + interfaces[i]);
out.print("(env, object)");
}
out.println(" {");
out.print(indent);
out.print("jclass cls = env->GetObjectClass(getRef());");
out.print(cr);
out.print(cr);
String packageName = clazz.getPackageName();
Map methods = new HashMap();
for (int i=0; i<clazz.getMethods().length; i++) {
IMethod m = clazz.getMethods()[i];
String name = m.getName()+converter.getSignature(m, clazz.getPackageName());
methods.put(name, m);
}
for (Iterator i = methods.entrySet().iterator(); i.hasNext(); ) {
Map.Entry entry = (Map.Entry)i.next();
IMethod method = (IMethod)entry.getValue();
String methodID = (String)entry.getKey()+"Method";
String methodName = "\""+method.getName()+"\"";
String signature = "\""+method.getSignature(packageName, importProperties)+"\"";
out.print(indent);
out.print(methodID);
out.print(" = env->GetMethodID(cls, ");
out.print(methodName);
out.print(", ");
out.print(signature);
out.print(");");
out.print(cr);
out.print(indent);
out.print("if (");
out.print(methodID);
out.print(" == NULL) {");
out.print(cr);
out.print(indent+indent);
out.print("std::cerr << ");
out.print("\""+clazz.getName()+"\"");
out.print(" << ");
out.print("\": Could not find method: \"");
out.print(" << ");
out.print(methodName);
out.print(" << ");
out.print(signature);
out.print(" << std::endl;");
out.print(cr);
out.print(indent);
out.print("}");
out.print(cr);
out.print(cr);
}
out.println("}");
}
protected void printDestructor(IndentPrintWriter out, IClass clazz) {
out.println();
out.print(prefix() + clazz.getName());
out.print("::");
out.println("~" + prefix() + clazz.getName() + "() {");
out.println("}");
}
protected void printMethod(IndentPrintWriter out, IClass clazz, IMethod method) {
out.println();
if ((method.getExceptionTypes().length > 0) && (method.getReturnType().isVoid())) {
out.print("bool");
} else {
out.print(converter.type(method.getReturnType(), namespace(clazz)));
}
out.print(" ");
out.print(prefix() + clazz.getName());
out.print("::");
out.print(method.getName());
out.print("(");
INamedType parameterTypes[] = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
if (i > 0) {
out.print(", ");
}
out.print(namedType(parameterTypes[i], namespace(clazz)));
}
out.print(")");
if (method.isConst()) {
out.print(" const");
}
out.println(" {");
// FIXME, code inserted to generate dummy stubs
// printMethodBody(out, clazz, method);
printDummyBody(out, clazz, method);
out.println("}");
}
// FIXME this is temporary until we can generate the code for these
// methods. However, the return values are being generated as
// well as we can do now...
protected void printDummyBody(IndentPrintWriter out, IClass clazz, IMethod method) {
out.println(" std::cerr << \"'"+prefix()+clazz.getName()+"."+method.getName()+"(..)' not implemented!\" << std::endl;");
IType returnType = method.getReturnType();
if (returnType.isVoid()) {
if (method.getExceptionTypes().length > 0) {
out.println(" return true;");
}
return;
}
if (returnType.getPointer() > 0) {
out.println(" return NULL;");
return;
}
// handle arrays...
if (returnType.getDimension() == 0) {
if (returnType.getName().equals("boolean")) {
out.println(" return false;");
return;
}
if (returnType.getName().equals("int") || returnType.getName().equals("long")) {
out.println(" return 0;");
return;
}
if (returnType.getName().equals("double") || returnType.getName().equals("float")) {
out.println(" return 0.0;");
return;
}
}
String btname = converter.basicType(returnType, "");
out.println(" "+btname+" *r;");
out.println(" return *r;");
}
protected void printMethodBody(IndentPrintWriter out, IClass clazz, IMethod method) {
StringBuffer pre = new StringBuffer();
StringBuffer call = new StringBuffer();
StringBuffer post = new StringBuffer();
IType returnType = method.getReturnType();
call.append(indent);
call.append("// Call to Java");
call.append(cr);
call.append(indent);
// analyze result
if (!returnType.isVoid()) {
pre.append(indent);
pre.append(converter.basicType(returnType, namespace(clazz)));
pre.append(" result;");
pre.append(cr);
call.append(converter.jniType(returnType, returnType.getDimension()));
call.append(" jniResult = ");
call.append("(");
call.append(converter.jniType(returnType, returnType.getDimension()));
call.append(")");
post.append(converter.convertToCPP(1, returnType, returnType.getDimension(), namespace(clazz), "jniResult", "result"));
// FIXME should move
// s.append(freeStringToCharStar(1, src, "tmpResult"));
if ((returnType.getPointer() > 0) || returnType.isReference()) {
post.append(indent);
post.append("// copying into instance variable");
post.append(cr);
String resultName = method.getName()+converter.getSignature(method,"")+"Result";
post.append(indent);
post.append("const_cast<");
post.append(prefix()+clazz.getName());
post.append("*>(this) -> ");
post.append(resultName);
post.append(" = result;");
post.append(cr);
post.append(indent);
post.append("return ");
post.append((returnType.getPointer() > 0) ? "&"+resultName : resultName);
post.append(";");
post.append(cr);
} else {
post.append(indent);
post.append("return ");
post.append("result");
post.append(";");
post.append(cr);
}
} else {
// return is void, but exceptions could be thrown
if (method.getExceptionTypes().length > 0) {
post.append(indent);
post.append("jthrowable e = env->ExceptionOccurred();");
post.append(cr);
post.append(indent);
post.append("env->ExceptionClear();");
post.append(cr);
boolean returnInCaseOfException = valueProperties.isProperty("returnInCaseOfException", true);
post.append(indent);
post.append("return (e != NULL) ? ");
post.append(returnInCaseOfException ? "true" : "false");
post.append(" : ");
post.append(returnInCaseOfException ? "false" : "true");
post.append(";");
post.append(cr);
}
}
call.append("env->Call");
call.append(converter.jniCall(returnType, returnType.getDimension()));
call.append("Method");
// fixed parameters
call.append("(ref, ");
call.append(method.getName()+converter.getSignature(method,"")+"Method");
// parameters
INamedType[] parameters = method.getParameterTypes();
for (int i=0; i<parameters.length; i++) {
INamedType parameter = parameters[i];
IType type = parameter.getType();
String jniName = "jni"+parameter.getName();
pre.append(indent);
pre.append(converter.jniType(type));
pre.append(" ");
pre.append(jniName);
pre.append(";");
pre.append(cr);
pre.append(converter.convertToJava(1, type, type.getDimension(), namespace(clazz), parameter.getName(), jniName));
call.append(", ");
call.append(jniName);
}
// end call
call.append(");");
call.append(cr);
// write
if (pre.length() > 0) out.print(pre);
if (call.length() > 0) out.print(call);
if (post.length() > 0) out.print(post);
}
}