From b9bc5a4a6da6f8e5a9a9df641c172ff34c3cc034 Mon Sep 17 00:00:00 2001 From: Bob Jamison Date: Sat, 8 Mar 2008 22:50:44 +0000 Subject: First commit for java binding (bzr r5002) --- src/bind/javabind.cpp | 665 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 665 insertions(+) create mode 100644 src/bind/javabind.cpp (limited to 'src/bind/javabind.cpp') diff --git a/src/bind/javabind.cpp b/src/bind/javabind.cpp new file mode 100644 index 000000000..36facee88 --- /dev/null +++ b/src/bind/javabind.cpp @@ -0,0 +1,665 @@ +/** + * This is a simple mechanism to bind Inkscape to Java, and thence + * to all of the nice things that can be layered upon that. + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2007 Bob Jamison + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include +#include +#include +#include +#include + +#include +#include + + +#ifdef __WIN32__ +#include +#else +#include +#include +#endif + +#include "javabind.h" +#include "javabind-private.h" +#include +#include + + + + + +namespace Inkscape +{ + +namespace Bind +{ + + +//######################################################################## +//# DEFINITIONS +//######################################################################## + +typedef jint (*CreateVMFunc)(JavaVM **, JNIEnv **, void *); + + + +//######################################################################## +//# UTILITY +//######################################################################## + +jint getInt(JNIEnv *env, jobject obj, const char *name) +{ + jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "I"); + return env->GetIntField(obj, fid); +} + +void setInt(JNIEnv *env, jobject obj, const char *name, jint val) +{ + jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "I"); + env->SetIntField(obj, fid, val); +} + +jlong getLong(JNIEnv *env, jobject obj, const char *name) +{ + jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "J"); + return env->GetLongField(obj, fid); +} + +void setLong(JNIEnv *env, jobject obj, const char *name, jlong val) +{ + jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "J"); + env->SetLongField(obj, fid, val); +} + +jfloat getFloat(JNIEnv *env, jobject obj, const char *name) +{ + jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "F"); + return env->GetFloatField(obj, fid); +} + +void setFloat(JNIEnv *env, jobject obj, const char *name, jfloat val) +{ + jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "F"); + env->SetFloatField(obj, fid, val); +} + +jdouble getDouble(JNIEnv *env, jobject obj, const char *name) +{ + jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "D"); + return env->GetDoubleField(obj, fid); +} + +void setDouble(JNIEnv *env, jobject obj, const char *name, jdouble val) +{ + jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "D"); + env->SetDoubleField(obj, fid, val); +} + +String getString(JNIEnv *env, jobject obj, const char *name) +{ + jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "Ljava/lang/String;"); + jstring jstr = (jstring)env->GetObjectField(obj, fid); + const char *chars = env->GetStringUTFChars(jstr, JNI_FALSE); + String str = chars; + env->ReleaseStringUTFChars(jstr, chars); + return str; +} + +void setString(JNIEnv *env, jobject obj, const char *name, const String &val) +{ + jstring jstr = env->NewStringUTF(val.c_str()); + jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "Ljava/lang/String;"); + env->SetObjectField(obj, fid, jstr); +} + + + + +//######################################################################## +//# CONSTRUCTOR/DESTRUCTOR +//######################################################################## + +static JavaBinderyImpl *_instance = NULL; + +JavaBindery *JavaBindery::getInstance() +{ + return JavaBinderyImpl::getInstance(); +} + +JavaBinderyImpl *JavaBinderyImpl::getInstance() +{ + if (!_instance) + { + _instance = new JavaBinderyImpl(); + } + return _instance; +} + +JavaBinderyImpl::JavaBinderyImpl() +{ + jvm = NULL; + env = NULL; +} + +JavaBinderyImpl::~JavaBinderyImpl() +{ +} + +void err(const char *fmt, ...) +{ +#if 0 + va_list args; + fprintf(stderr, "JavaBinderyImpl err:"); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); +#else + va_list args; + g_warning("JavaBinderyImpl err:"); + va_start(args, fmt); + g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, fmt, args); + va_end(args); + g_warning("\n"); +#endif +} + +void msg(const char *fmt, ...) +{ +#if 0 + va_list args; + fprintf(stdout, "JavaBinderyImpl:"); + va_start(args, fmt); + vfprintf(stdout, fmt, args); + va_end(args); + fprintf(stdout, "\n"); +#else + va_list args; + g_message("JavaBinderyImpl:"); + va_start(args, fmt); + g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, args); + va_end(args); + g_message("\n"); +#endif +} + +bool JavaBinderyImpl::isLoaded() +{ + return (jvm != (void *)0); +} + + + +#ifdef __WIN32__ + + +//######################################################################## +//# W I N 3 2 S T Y L E +//######################################################################## + + +#define DIR_SEPARATOR "\\" +#define PATH_SEPARATOR ";" + + + +static bool getRegistryString(HKEY root, const char *keyName, + const char *valName, char *buf, int buflen) +{ + HKEY key; + DWORD bufsiz = buflen; + RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_READ, &key); + int ret = RegQueryValueEx(key, TEXT(valName), + NULL, NULL, (BYTE *)buf, &bufsiz); + if (ret != ERROR_SUCCESS) + { + err("Key '%s\\%s not found\n", keyName, valName); + return false; + } + RegCloseKey(key); + return true; +} + + +static CreateVMFunc getCreateVMFunc() +{ + char verbuf[16]; + char regpath[80]; + strcpy(regpath, "SOFTWARE\\JavaSoft\\Java Runtime Environment"); + bool ret = getRegistryString(HKEY_LOCAL_MACHINE, + regpath, "CurrentVersion", verbuf, 15); + if (!ret) + { + err("JVM CurrentVersion not found in registry\n"); + return NULL; + } + strcat(regpath, "\\"); + strcat(regpath, verbuf); + //msg("reg path: %s\n", regpath); + char libname[80]; + ret = getRegistryString(HKEY_LOCAL_MACHINE, + regpath, "RuntimeLib", libname, 79); + if (!ret) + { + err("Current JVM RuntimeLib not found in registry\n"); + return NULL; + } + //msg("jvm path: %s\n", libname); + HMODULE lib = LoadLibrary(libname); + if (!lib) + { + err("Java VM not found at '%s'", libname); + return NULL; + } + CreateVMFunc createVM = (CreateVMFunc)GetProcAddress(lib, "JNI_CreateJavaVM"); + if (!createVM) + { + err("Could not find 'JNI_CreateJavaVM' in shared library"); + return NULL; + } + return createVM; +} + +static void getJavaRoot(String &javaroot) +{ + char exeName[80]; + GetModuleFileName(NULL, exeName, 80); + char *slashPos = strrchr(exeName, '\\'); + if (slashPos) + *slashPos = '\0'; + javaroot = exeName; + javaroot.append("\\share\\java"); +} + + +#else + + +//######################################################################## +//# U N I X S T Y L E +//######################################################################## + + +#define DIR_SEPARATOR "/" +#define PATH_SEPARATOR ":" + + +/** + * Recursively descend into a directory looking for libjvm.so + */ +static bool findJVMRecursive(const String &dirpath, + std::vector &results) +{ + DIR *dir = opendir(dirpath.c_str()); + if (!dir) + return false; + bool ret = false; + while (true) + { + struct dirent *de = readdir(dir); + if (!de) + break; + String fname = de->d_name; + if (fname == "." || fname == "..") + continue; + String path = dirpath; + path.push_back('/'); + path.append(fname); + if (fname == "libjvm.so") + { + ret = true; + results.push_back(path); + continue; + } + struct stat finfo; + if (lstat(path.c_str(), &finfo)<0) + { + break; + } + if (finfo.st_mode & S_IFDIR) + { + ret |= findJVMRecursive(path, results); + } + } + closedir(dir); + return ret; +} + + +static const char *commonJavaPaths[] = +{ + "/usr/java", + "/usr/local/java", + "/usr/lib/jvm", + "/usr/local/lib/jvm", + NULL +}; + +/** + * Look for a Java VM (libjvm.so) in several Unix places + */ +static bool findJVM(String &result) +{ + std::vector results; + int found = false; + + /* Is there one specified by the user? */ + const char *javaHome = getenv("JAVA_HOME"); + if (javaHome && findJVMRecursive(javaHome, results)) + found = true; + else for (const char **path = commonJavaPaths ; *path ; path++) + { + if (findJVMRecursive(*path, results)) + { + found = true; + break; + } + } + if (!found) + { + return false; + } + if (results.size() == 0) + return false; + //Look first for a Client VM + for (unsigned int i=0 ; id_name; + if (fname == "." || fname == "..") + continue; + if (fname.size()<5) //x.jar + continue; + if (fname.compare(fname.size()-4, 4, ".jar") != 0) + continue; + + String path = libdir; + path.append(DIR_SEPARATOR); + path.append(fname); + + cp.append(PATH_SEPARATOR); + cp.append(path); + } + closedir(dir); + + result = cp; + + return; +} + + + +bool JavaBinderyImpl::loadJVM() +{ + if (jvm) + return true; + + CreateVMFunc createVM = getCreateVMFunc(); + if (!createVM) + { + err("Could not find 'JNI_CreateJavaVM' in shared library"); + return false; + } + + String javaroot; + getJavaRoot(javaroot); + String cp; + populateClassPath(javaroot, cp); + String classpath = "-Djava.class.path="; + classpath.append(cp); + msg("Class path is: '%s'", classpath.c_str()); + + String libpath = "-Djava.library.path="; + libpath.append(javaroot); + libpath.append(DIR_SEPARATOR); + libpath.append("libm"); + msg("Lib path is: '%s'", libpath.c_str()); + + JavaVMInitArgs vm_args; + JavaVMOption options[4]; + options[0].optionString = (char *)classpath.c_str(); + options[1].optionString = (char *)libpath.c_str(); + vm_args.version = JNI_VERSION_1_2; + vm_args.options = options; + vm_args.nOptions = 2; + vm_args.ignoreUnrecognized = true; + + if (createVM(&jvm, &env, &vm_args) < 0) + { + err("JNI_GetDefaultJavaVMInitArgs() failed"); + return false; + } + + return true; +} + + + + +bool JavaBinderyImpl::callStatic(int type, + const String &className, + const String &methodName, + const String &signature, + const std::vector ¶ms, + Value &retval) +{ + jclass cls = env->FindClass(className.c_str()); + if (!cls) + { + err("Could not find class '%s'", className.c_str()); + return false; + } + jmethodID mid = env->GetStaticMethodID(cls, + methodName.c_str(), signature.c_str()); + if (!mid) + { + err("Could not find method '%s:%s/%s'", className.c_str(), + methodName.c_str(), signature.c_str()); + return false; + } + jvalue *jvals = new jvalue[params.size()]; + for (unsigned int i=0 ; iNewStringUTF(v.getString().c_str()); + break; + } + default: + { + err("Unknown value type: %d", v.getType()); + return false; + } + } + } + switch (type) + { + case Value::BIND_VOID: + { + env->CallStaticVoidMethodA(cls, mid, jvals); + break; + } + case Value::BIND_BOOLEAN: + { + env->CallStaticBooleanMethodA(cls, mid, jvals); + break; + } + case Value::BIND_INT: + { + env->CallStaticIntMethodA(cls, mid, jvals); + break; + } + case Value::BIND_DOUBLE: + { + env->CallStaticDoubleMethodA(cls, mid, jvals); + break; + } + case Value::BIND_STRING: + { + env->CallStaticObjectMethodA(cls, mid, jvals); + break; + } + default: + { + err("Unknown return type: %d", type); + return false; + } + } + delete jvals; + return true; +} + + + + +bool JavaBinderyImpl::callMain(const String &className) +{ + std::vector parms; + Value retval; + return callStatic(Value::BIND_VOID, className, "main", + "([Ljava/lang/String;)V", parms, retval); +} + + + +bool JavaBinderyImpl::registerNatives(const String &className, + const JNINativeMethod *methods) +{ + jclass cls = env->FindClass(className.c_str()); + if (!cls) + { + err("Could not find class '%s'", className.c_str()); + return false; + } + int nrMethods = 0; + for (const JNINativeMethod *m = methods ; m->name ; m++) + nrMethods++; + if (env->RegisterNatives(cls, (const JNINativeMethod *)methods, nrMethods) < 0) + { + err("Could not register natives"); + return false; + } + return true; +} + + + + +} // namespace Bind +} // namespace Inkscape + +//######################################################################## +//# E N D O F F I L E +//######################################################################## -- cgit v1.2.3