/** * FUSE-J: Java bindings for FUSE (Filesystem in Userspace by Miklos Szeredi (mszeredi@inf.bme.hu)) * * Copyright (C) 2003 Peter Levart (peter@select-tech.si) * * This program can be distributed under the terms of the GNU LGPL. * See the file COPYING.LIB */ package java2c; import fuse.FuseContext; import fuse.FuseFS; import fuse.FuseFSDirEnt; import fuse.FuseFSDirFiller; import fuse.FuseFSFactory; import fuse.FuseGetattr; import fuse.FuseOpen; import fuse.FuseSize; import fuse.FuseStatfs; import fuse.PasswordEntry; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @SuppressWarnings("unchecked") public class CAPIGenerator { private Class clazz; public CAPIGenerator(Class clazz) { this.clazz = clazz; } public void generateClassAPI(Map<String, String> overload, PrintWriter hOut, PrintWriter cOut, boolean extraArgForInterfaces, boolean includeStaticFields) { String className = clazz.getName(); String jniClassName = className.replace('.', '/'); String structTypeName = "jclass_" + className.replace('.', '_'); String structVarName = className.replace('.', '_'); // split all public fields into static and instance fields Field[] fields = clazz.getFields(); Arrays.sort(fields, new FieldComparator()); List staticFieldsList = new ArrayList(); List instanceFieldsList = new ArrayList(); for(int i = 0; i < fields.length; i++) { Field field = fields[i]; int mod = field.getModifiers(); if (Modifier.isPublic(mod)) { if (Modifier.isStatic(mod)) { staticFieldsList.add(field); } else { instanceFieldsList.add(field); } } } Field[] staticFields = (Field[]) staticFieldsList.toArray(new Field[staticFieldsList.size()]); Field[] instanceFields = (Field[]) instanceFieldsList.toArray(new Field[instanceFieldsList.size()]); // obtain all public constructors and give them C names Constructor[] constructors = clazz.getConstructors(); Arrays.sort(constructors, new ConstructorComparator(this)); List constructorsList = new ArrayList(); Map constructor2name = new HashMap(); for(int i = 0; i < constructors.length; i++) { Constructor constructor = constructors[i]; if (Modifier.isPublic(constructor.getModifiers())) { constructorsList.add(constructor); constructor2name.put(constructor, getMethodName("new", constructor.getParameterTypes())); } } constructors = (Constructor[]) constructorsList.toArray(new Constructor[constructorsList.size()]); // split all public methods into static and instance methods and give them C names Method[] methods = clazz.getMethods(); Arrays.sort(methods, new MethodComparator(this)); List staticMethodsList = new ArrayList(); List instanceMethodsList = new ArrayList(); Map method2name = new HashMap(); for(int i = 0; i < methods.length; i++) { Method method = methods[i]; int mod = method.getModifiers(); if (Modifier.isPublic(mod)) { final String methodName = method.getName(); final String selectReturn = overload.get(methodName); if (selectReturn == null || selectReturn.equals(method.getReturnType().getName())) { if (Modifier.isStatic(mod)) { staticMethodsList.add(method); } else { instanceMethodsList.add(method); } method2name.put(method, getMethodName(methodName, method.getParameterTypes())); } } } Method[] staticMethods = (Method[]) staticMethodsList.toArray(new Method[staticMethodsList.size()]); Method[] instanceMethods = (Method[]) instanceMethodsList.toArray(new Method[instanceMethodsList.size()]); // before we begin, we output header hOut.print( "\n" + "/**\n" + " * structure with a reference to " + className + " java class and cached field & method IDs\n" + " */\n" + "typedef struct _" + structTypeName + "\n" + "{\n" + " // a pointer to globaly referenced Java class\n" + " jclass class;\n" + "\n" ); // 1st output static fields if (staticFields.length > 0 && includeStaticFields) { hOut.print( " // cached static field IDs\n" + " struct\n" + " {\n" ); for(int i = 0; i < staticFields.length; i++) { Field field = staticFields[i]; hOut.print(" jfieldID " + field.getName() + ";\n"); } hOut.print( "\n" + " } static_field;\n" + "\n" ); } // 2nd output instance fields if (instanceFields.length > 0) { hOut.print( " // cached instance field IDs\n" + " struct\n" + " {\n" ); for(int i = 0; i < instanceFields.length; i++) { Field field = instanceFields[i]; hOut.print(" jfieldID " + field.getName() + ";\n"); } hOut.print( "\n" + " } field;\n" + "\n" ); } // 3rd output constructors if (constructors.length > 0) { hOut.print( " // cached constructor IDs\n" + " struct\n" + " {\n" ); for(int i = 0; i < constructors.length; i++) { Constructor constructor = constructors[i]; hOut.print(" jmethodID " + constructor2name.get(constructor) + ";\n"); } hOut.print( "\n" + " } constructor;\n" + "\n" ); } // 4th output static methods if (staticMethods.length > 0) { hOut.print( " // cached static method IDs\n" + " struct\n" + " {\n" ); for(int i = 0; i < staticMethods.length; i++) { Method method = staticMethods[i]; hOut.print(" jmethodID " + method2name.get(method) + ";\n"); } hOut.print( "\n" + " } static_method;\n" + "\n" ); } // 5th output instance methods if (instanceMethods.length > 0) { hOut.print( " // cached instance method IDs\n" + " struct\n" + " {\n" ); for(int i = 0; i < instanceMethods.length; i++) { Method method = instanceMethods[i]; hOut.print(" jmethodID " + method2name.get(method) + ";\n"); } hOut.print( "\n" + " } method;\n" + "\n" ); } // and finaly the closing brace for structure... hOut.print( "} " + structTypeName + ";\n" + "\n" ); // now output to .c & .h file // free_* method hOut.print( "// free structure\n" + "void free_" + structTypeName + "(JNIEnv *env, " + structTypeName + " *" + structVarName + ");\n" + "\n" ); cOut.print( "/**\n" + " * free structure with a reference to " + className + " java class and cached field & method IDs\n" + " */\n" + "void free_" + structTypeName + "(JNIEnv *env, " + structTypeName + " *" + structVarName + ")\n" + "{\n" + " if (" + structVarName + "->class != NULL)\n" + " (*env)->DeleteGlobalRef(env, " + structVarName + "->class);\n" + "\n" + " free(" + structVarName + ");\n" + "}\n" + "\n" ); // alloc_* method boolean extraArg = extraArgForInterfaces && Modifier.isInterface(clazz.getModifiers()); String cnArg = extraArg ? ", const char *className" : ""; String cnVar = extraArg ? "className" : "\"" + jniClassName + "\""; hOut.print( "// alloc structure\n" + structTypeName + " *alloc_" + structTypeName + "(JNIEnv *env" + cnArg + ");\n" + "\n" ); cOut.print( "/**\n" + " * alloc structure with a reference to " + className + " java class and cached field & method IDs\n" + " */\n" + structTypeName + " *alloc_" + structTypeName + "(JNIEnv *env" + cnArg + ")\n" + "{\n" + " jclass class;\n" + "\n" + " " + structTypeName + " *" + structVarName + " = (" + structTypeName + "*)calloc(1, sizeof(" + structTypeName + "));\n" + " if (" + structVarName + " == NULL)\n" + " {\n" + " WARN(\"Can't allocate structure " + structTypeName + "\");\n" + " return NULL;\n" + " }\n" + "\n" + " while (1)\n" + " {\n" + " class = (*env)->FindClass(env, " + cnVar + ");\n" + " if ((*env)->ExceptionCheck(env)) break;\n" + "\n" + " " + structVarName + "->class = (*env)->NewGlobalRef(env, class);\n" + " if ((*env)->ExceptionCheck(env)) break;\n" + "\n" ); // static fields if (staticFields.length > 0 && includeStaticFields) { cOut.print(" // obtain static field IDs\n"); for(int i = 0; i < staticFields.length; i++) { Field field = staticFields[i]; cOut.print( " " + structVarName + "->static_field." + field.getName() + " = (*env)->GetStaticFieldID(env, " + structVarName + "->class, \"" + field.getName() + "\", \"" + getJVMTypeSignature(field.getType()) + "\");\n" + " if ((*env)->ExceptionCheck(env)) break;\n" ); } cOut.print("\n"); } // instance fields if (instanceFields.length > 0) { cOut.print(" // obtain instance field IDs\n"); for(int i = 0; i < instanceFields.length; i++) { Field field = instanceFields[i]; cOut.print( " " + structVarName + "->field." + field.getName() + " = (*env)->GetFieldID(env, " + structVarName + "->class, \"" + field.getName() + "\", \"" + getJVMTypeSignature(field.getType()) + "\");\n" + " if ((*env)->ExceptionCheck(env)) break;\n" ); } cOut.print("\n"); } // constructors if (constructors.length > 0) { cOut.print(" // obtain constructor method IDs\n"); for(int i = 0; i < constructors.length; i++) { Constructor constructor = constructors[i]; cOut.print( " " + structVarName + "->constructor." + constructor2name.get(constructor) + " = (*env)->GetMethodID(env, " + structVarName + "->class, \"" + "<init>" + "\", \"" + getJVMConstructorSignature(constructor) + "\");\n" + " if ((*env)->ExceptionCheck(env)) break;\n" ); } cOut.print("\n"); } // static methods if (staticMethods.length > 0) { cOut.print(" // obtain static method IDs\n"); for(int i = 0; i < staticMethods.length; i++) { Method method = staticMethods[i]; cOut.print( " " + structVarName + "->static_method." + method2name.get(method) + " = (*env)->GetStaticMethodID(env, " + structVarName + "->class, \"" + method.getName() + "\", \"" + getJVMMethodSignature(method) + "\");\n" + " if ((*env)->ExceptionCheck(env)) break;\n" ); } cOut.print("\n"); } // instance methods if (instanceMethods.length > 0) { cOut.print(" // obtain instance method IDs\n"); for(int i = 0; i < instanceMethods.length; i++) { Method method = instanceMethods[i]; cOut.print( " " + structVarName + "->method." + method2name.get(method) + " = (*env)->GetMethodID(env, " + structVarName + "->class, \"" + method.getName() + "\", \"" + getJVMMethodSignature(method) + "\");\n" + " if ((*env)->ExceptionCheck(env)) break;\n" ); } cOut.print("\n"); } // trailer and error handler cOut.print( " // we're done\n" + " return " + structVarName + ";\n" + " }\n" + "\n" + " // error handler\n" + " (*env)->ExceptionDescribe(env);\n" + " (*env)->ExceptionClear(env);\n" + " free_" + structTypeName + "(env, " + structVarName + ");\n" + " return NULL;\n" + "}\n" + "\n" ); } public static void main(String[] args) { if (args.length != 3) { System.err.println("Usage: " + CAPIGenerator.class.getName() + " file.h file.c common_include.h"); System.exit(-1); } Map<String, String> overload = new HashMap<String, String>(); overload.put("array", "[B"); File hFile = new File(args[0]); File cFile = new File(args[1]); File hIncluded = new File(args[2]); PrintWriter hOut = null; PrintWriter cOut = null; try { hOut = new PrintWriter(new OutputStreamWriter(new FileOutputStream(hFile))); cOut = new PrintWriter(new OutputStreamWriter(new FileOutputStream(cFile))); hOut.print( "/**\n" + " * " + hFile.getName() + " - autogenerated C <-> Java bindings\n" + " */\n" + "\n" + "#include <jni.h>\n" + "\n" ); cOut.print( "/**\n" + " * " + cFile.getName() + " - autogenerated C <-> Java bindings\n" + " */\n" + "\n" + "#include \"" + hIncluded.getPath() + "\"\n" + "\n" ); new CAPIGenerator(FuseGetattr.class).generateClassAPI(overload, hOut, cOut, false, false); new CAPIGenerator(FuseFSDirEnt.class).generateClassAPI(overload, hOut, cOut, false, false); new CAPIGenerator(FuseFSDirFiller.class).generateClassAPI(overload, hOut, cOut, false, false); new CAPIGenerator(FuseStatfs.class).generateClassAPI(overload, hOut, cOut, false, false); new CAPIGenerator(FuseSize.class).generateClassAPI(overload, hOut, cOut, false, false); new CAPIGenerator(FuseOpen.class).generateClassAPI(overload, hOut, cOut, false, false); new CAPIGenerator(FuseContext.class).generateClassAPI(overload, hOut, cOut, false, false); new CAPIGenerator(FuseFS.class).generateClassAPI(overload, hOut, cOut, false, false); new CAPIGenerator(ByteBuffer.class).generateClassAPI(overload, hOut, cOut, false, false); new CAPIGenerator(FuseFSFactory.class).generateClassAPI(overload, hOut, cOut, false, false); new CAPIGenerator(PasswordEntry.class).generateClassAPI(overload, hOut, cOut, false, false); } catch(IOException e) { e.printStackTrace(); } finally { if (hOut != null) { hOut.close(); } if (cOut != null) { cOut.close(); } } } /** * Java VM Type Signatures * <p/> * Type Signature Java Type * <p/> * Z boolean * B byte * C char * S short * I int * J long * F float * D double * L fully-qualified-class ; fully-qualified-class * [ type type[] * <p/> * ( arg-types ) ret-type method type */ private String getJVMTypeSignature(Class clazz) { return appendJVMTypeSignature(clazz, new StringBuffer()).toString(); } private String getJVMTypeSignatures(Class[] classes) { return appendJVMTypeSignatures(classes, new StringBuffer()).toString(); } private String getJVMMethodSignature(Method method) { return appendJVMMethodSignature(method, new StringBuffer()).toString(); } private String getJVMConstructorSignature(Constructor constructor) { return appendJVMConstructorSignature(constructor, new StringBuffer()).toString(); } private StringBuffer appendJVMTypeSignature(Class clazz, StringBuffer buff) { while(clazz.isArray()) { buff.append('['); clazz = clazz.getComponentType(); } if (clazz == Void.TYPE) { buff.append("V"); } else if (clazz == Boolean.TYPE) { buff.append("Z"); } else if (clazz == Byte.TYPE) { buff.append("B"); } else if (clazz == Character.TYPE) { buff.append("C"); } else if (clazz == Short.TYPE) { buff.append("S"); } else if (clazz == Integer.TYPE) { buff.append("I"); } else if (clazz == Long.TYPE) { buff.append("J"); } else if (clazz == Float.TYPE) { buff.append("F"); } else if (clazz == Double.TYPE) { buff.append("D"); } else { buff.append("L").append(clazz.getName().replace('.', '/')).append(";"); } return buff; } private StringBuffer appendJVMTypeSignatures(Class[] classes, StringBuffer buff) { for(int i = 0; i < classes.length; i++) { appendJVMTypeSignature(classes[i], buff); } return buff; } private StringBuffer appendJVMMethodSignature(Method method, StringBuffer buff) { buff.append("("); appendJVMTypeSignatures(method.getParameterTypes(), buff); buff.append(")"); appendJVMTypeSignature(method.getReturnType(), buff); return buff; } private StringBuffer appendJVMConstructorSignature(Constructor constructor, StringBuffer buff) { buff.append("("); appendJVMTypeSignatures(constructor.getParameterTypes(), buff); buff.append(")V"); return buff; } private String mangle(String str) { char[] chars = str.toCharArray(); for(int i = 0; i < chars.length; i++) { char c = chars[i]; if ((i == 0 && !Character.isJavaIdentifierStart(c)) || (i > 0 && !Character.isJavaIdentifierPart(c))) { chars[i] = '_'; } } return new String(chars); } String getMethodName(String methodName, Class[] argumentTypes) { if (argumentTypes == null || argumentTypes.length == 0) { return mangle(methodName); } else { return mangle(methodName + "__" + getJVMTypeSignatures(argumentTypes)); } } }