Code

Improve script engine listing
[inkscape.git] / src / bind / javabind.cpp
1 /**
2  * This is a simple mechanism to bind Inkscape to Java, and thence
3  * to all of the nice things that can be layered upon that.
4  *
5  * Authors:
6  *   Bob Jamison
7  *
8  * Copyright (C) 2007-2008 Bob Jamison
9  *
10  *  This library is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU Lesser General Public
12  *  License as published by the Free Software Foundation; either
13  *  version 3 of the License, or (at your option) any later version.
14  *
15  *  This library is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  *  Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; if not, write to the Free Software
22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
23  */
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <stdarg.h>
32 #include <string.h>
33 #include <jni.h>
35 #include <sys/types.h>
36 #include <dirent.h>
39 #ifdef __WIN32__
40 #include <windows.h>
41 #else
42 #include <dlfcn.h>
43 #include <errno.h>
44 #endif
46 #if HAVE_SYS_STAT_H
47 #include <sys/stat.h>
48 #endif
50 #include "javabind.h"
51 #include "javabind-private.h"
52 #include <path-prefix.h>
53 #include <prefix.h>
54 #include <glib/gmessages.h>
57 /**
58  * Note: We must limit Java or JVM-specific code to this file
59  * and to dobinding.cpp.  It should be hidden from javabind.h
60  * 
61  * This file is mostly about getting things up and running, and
62  * providing the basic C-to-Java hooks.
63  *   
64  * dobinding.cpp will have the rote and repetitious
65  * class-by-class binding   
66  */  
69 namespace Inkscape
70 {
72 namespace Bind
73 {
76 //########################################################################
77 //# DEFINITIONS
78 //########################################################################
80 typedef jint (*CreateVMFunc)(JavaVM **, JNIEnv **, void *);
84 //########################################################################
85 //# UTILITY
86 //########################################################################
88 /**
89  * Normalize path.  Java wants '/', even on Windows
90  */ 
91 String normalizePath(const String &str)
92 {
93     String buf;
94     for (unsigned int i=0 ; i<str.size() ; i++)
95         {
96         char ch = str[i];
97         if (ch == '\\')
98             buf.push_back('/');
99         else
100             buf.push_back(ch);
101                 }
102         return buf;
105 String getExceptionString(JNIEnv *env)
107     String buf;
108     jthrowable exc = env->ExceptionOccurred();
109     if (!exc)
110         return buf;
111     jclass cls = env->GetObjectClass(exc);
112     jmethodID mid = env->GetMethodID(cls, "toString", "()Ljava/lang/String;");
113     jstring jstr = (jstring) env->CallObjectMethod(exc, mid);
114     const char *str = env->GetStringUTFChars(jstr, JNI_FALSE);
115     buf.append(str);
116     env->ReleaseStringUTFChars(jstr, str);
117     env->ExceptionClear();
118         return buf;
121 jint getInt(JNIEnv *env, jobject obj, const char *name)
123     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "I");
124     return env->GetIntField(obj, fid);
127 void setInt(JNIEnv *env, jobject obj, const char *name, jint val)
129     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "I");
130     env->SetIntField(obj, fid, val);
133 jlong getLong(JNIEnv *env, jobject obj, const char *name)
135     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "J");
136     return env->GetLongField(obj, fid);
139 void setLong(JNIEnv *env, jobject obj, const char *name, jlong val)
141     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "J");
142     env->SetLongField(obj, fid, val);
145 jfloat getFloat(JNIEnv *env, jobject obj, const char *name)
147     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "F");
148     return env->GetFloatField(obj, fid);
151 void setFloat(JNIEnv *env, jobject obj, const char *name, jfloat val)
153     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "F");
154     env->SetFloatField(obj, fid, val);
157 jdouble getDouble(JNIEnv *env, jobject obj, const char *name)
159     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "D");
160     return env->GetDoubleField(obj, fid);
163 void setDouble(JNIEnv *env, jobject obj, const char *name, jdouble val)
165     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "D");
166     env->SetDoubleField(obj, fid, val);
169 String getString(JNIEnv *env, jobject obj, const char *name)
171     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "Ljava/lang/String;");
172     jstring jstr = (jstring)env->GetObjectField(obj, fid);
173     const char *chars = env->GetStringUTFChars(jstr, JNI_FALSE);
174     String str = chars;
175     env->ReleaseStringUTFChars(jstr, chars);
176     return str;
179 void setString(JNIEnv *env, jobject obj, const char *name, const String &val)
181     jstring jstr = env->NewStringUTF(val.c_str());
182     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "Ljava/lang/String;");
183     env->SetObjectField(obj, fid, jstr);
189 //########################################################################
190 //# CONSTRUCTOR/DESTRUCTOR
191 //########################################################################
193 static JavaBinderyImpl *_instance = NULL;
195 JavaBindery *JavaBindery::getInstance()
197     return JavaBinderyImpl::getInstance();
200 JavaBinderyImpl *JavaBinderyImpl::getInstance()
202     if (!_instance)
203         {
204         _instance = new JavaBinderyImpl();
205         }
206     return _instance;
209 JavaBinderyImpl::JavaBinderyImpl()
211     jvm        = NULL;
212     env        = NULL;
213     gatewayObj = NULL;
216 JavaBinderyImpl::~JavaBinderyImpl()
221 //########################################################################
222 //# MESSAGES
223 //########################################################################
225 void err(const char *fmt, ...)
227 #if 0
228     va_list args;
229     fprintf(stderr, "JavaBinderyImpl err:");
230     va_start(args, fmt);
231     vfprintf(stderr, fmt, args);
232     va_end(args);
233     fprintf(stderr, "\n");
234 #else
235     va_list args;
236     g_warning("JavaBinderyImpl err:");
237     va_start(args, fmt);
238     g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, fmt, args);
239     va_end(args);
240     g_warning("\n");
241 #endif
244 void msg(const char *fmt, ...)
246 #if 0
247     va_list args;
248     fprintf(stdout, "JavaBinderyImpl:");
249     va_start(args, fmt);
250     vfprintf(stdout, fmt, args);
251     va_end(args);
252     fprintf(stdout, "\n");
253 #else
254     va_list args;
255     g_message("JavaBinderyImpl:");
256     va_start(args, fmt);
257     g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, args);
258     va_end(args);
259     g_message("\n");
260 #endif
265 //########################################################################
266 //# W I N 3 2      S T Y L E
267 //########################################################################
268 #ifdef __WIN32__
271 #define DIR_SEPARATOR "\\"
272 #define PATH_SEPARATOR ";"
276 static bool getRegistryString(HKEY /*root*/, const char *keyName,
277                const char *valName, char *buf, int buflen)
279     HKEY key;
280     DWORD bufsiz  = buflen;
281     RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_READ, &key);
282     int ret = RegQueryValueEx(key, TEXT(valName),
283             NULL, NULL, (BYTE *)buf, &bufsiz);
284     if (ret != ERROR_SUCCESS)
285         {
286         err("Key '%s\\%s not found\n", keyName, valName);
287         return false;
288         }
289     RegCloseKey(key);
290     return true;
294 static String cleanPath(const String &s)
296     String buf;
297     for (unsigned int i=0 ; i<s.size() ; i++)
298         {
299         char ch = s[i];
300         if (ch != '"')
301             buf.push_back(ch);
302                 }
303         return buf;
307 /**
308  * Common places to find jvm.dll under JAVA_HOME
309  */ 
310 static const char *commonJavaPaths[] =
312     "\\jre\\bin\\client\\jvm.dll",
313     "\\bin\\client\\jvm.dll",
314     "\\jvm.dll",
315     NULL
316 };
318 static CreateVMFunc getCreateVMFunc()
320     bool found = false;
321     String libname;
323     /**
324      * First, look for JAVA_HOME.  This will allow the user
325      * to override what's in the registry
326      */              
327     const char *envStr = getenv("JAVA_HOME");
328     if (envStr)
329         {
330         String javaHome = cleanPath(envStr);
331         msg("JAVA_HOME='%s'", javaHome.c_str());
332         for (const char **path = commonJavaPaths ; *path ; path++)
333             {
334             String jpath = javaHome;
335             jpath.append(*path);
336             //msg("trying '%s'", jpath.c_str());
337             struct stat finfo;
338             if (stat(jpath.c_str(), &finfo)>=0)
339                 {
340                 //msg("found");
341                 libname = jpath;
342                 found = true;
343                 break;
344                 }
345             }
346         }
348     //not at JAVA_HOME.  check the registry
349     if (!found)
350         {
351         char verbuf[16];
352         char regpath[80];
353         strcpy(regpath, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
354         bool ret = getRegistryString(HKEY_LOCAL_MACHINE,
355                      regpath, "CurrentVersion", verbuf, 15);
356         if (!ret)
357             {
358             msg("JVM CurrentVersion not found in registry at '%s'", regpath);
359             }
360         else
361             {
362             strcat(regpath, "\\");
363             strcat(regpath, verbuf);
364             //msg("reg path: %s\n", regpath);
365             char valbuf[80];
366             ret = getRegistryString(HKEY_LOCAL_MACHINE,
367                      regpath, "RuntimeLib", valbuf, 79);
368             if (ret)
369                 {
370                 found = true;
371                 libname = valbuf;
372                 }
373             else
374                 {
375                 msg("JVM RuntimeLib not found in registry at '%s'",
376                                           regpath);
377                                 }
378                         }
379         }
381     if (!found)
382         {
383         err("JVM not found at JAVA_HOME or in registry");
384         return NULL;
385         }
387     /**
388      * If we are here, then we seem to have a valid path for jvm.dll
389      * Give it a try
390      */              
391     msg("getCreateVMFunc: Loading JVM: %s", libname.c_str());
392     HMODULE lib = LoadLibrary(libname.c_str());
393     if (!lib)
394         {
395         err("Java VM not found at '%s'", libname.c_str());
396         return NULL;
397         }
398     CreateVMFunc createVM = (CreateVMFunc)GetProcAddress(lib, "JNI_CreateJavaVM");
399     if (!createVM)
400         {
401         err("Could not find 'JNI_CreateJavaVM' in shared library '%s'",
402                                    libname.c_str());
403         return NULL;
404         }
405     return createVM;
408 static void getJavaRoot(String &javaroot)
410     char exeName[80];
411     GetModuleFileName(NULL, exeName, 80);
412     char *slashPos = strrchr(exeName, '\\');
413     if (slashPos)
414         *slashPos = '\0';
415     javaroot = exeName;
416     javaroot.append("\\");
417     javaroot.append(INKSCAPE_BINDDIR);
418     javaroot.append("\\java");
424 //########################################################################
425 //# U N I X    S T Y L E
426 //########################################################################
427 #else /* !__WIN32__ */
430 #define DIR_SEPARATOR "/"
431 #define PATH_SEPARATOR ":"
434 /**
435  * Recursively descend into a directory looking for libjvm.so
436  */
437 static bool findJVMRecursive(const String &dirpath,
438                              std::vector<String> &results)
440     DIR *dir = opendir(dirpath.c_str());
441     if (!dir)
442         return false;
443     bool ret = false;
444     while (true)
445         {
446         struct dirent *de = readdir(dir);
447         if (!de)
448             break;
449         String fname = de->d_name;
450         if (fname == "." || fname == "..")
451             continue;
452         String path = dirpath;
453         path.push_back('/');
454         path.append(fname);
455         if (fname == "libjvm.so")
456             {
457             ret = true;
458             results.push_back(path);
459             continue;
460             }
461         struct stat finfo;
462         if (lstat(path.c_str(), &finfo)<0)
463             {
464             break;
465             }
466         if (finfo.st_mode & S_IFDIR)
467             {
468             ret |= findJVMRecursive(path, results);
469             }
470         }
471     closedir(dir);
472     return ret;
476 static const char *commonJavaPaths[] =
478     "/usr/lib/jvm/jre",
479     "/usr/lib/jvm",
480     "/usr/local/lib/jvm/jre",
481     "/usr/local/lib/jvm",
482     "/usr/java",
483     "/usr/local/java",
484     NULL
485 };
487 /**
488  * Look for a Java VM (libjvm.so) in several Unix places
489  */
490 static bool findJVM(String &result)
492     std::vector<String> results;
493     int found = false;
495     /* Is there one specified by the user? */
496     const char *javaHome = getenv("JAVA_HOME");
497     if (javaHome && findJVMRecursive(javaHome, results))
498         found = true;
499     else for (const char **path = commonJavaPaths ; *path ; path++)
500         {
501         if (findJVMRecursive(*path, results))
502             {
503             found = true;
504             break;
505             }
506         }
507     if (!found)
508         {
509         return false;
510         }
511     if (results.size() == 0)
512         return false;
513     //Look first for a Client VM
514     for (unsigned int i=0 ; i<results.size() ; i++)
515         {
516         String s = results[i];
517         if (s.find("client") != s.npos)
518             {
519             result = s;
520             return true;
521             }
522         }
523     //else default to the first
524     result = results[0];
525     return true;
530 static CreateVMFunc getCreateVMFunc()
532     String libname;
533     if (!findJVM(libname))
534         {
535         err("No Java VM found. Is JAVA_HOME defined?  Need to find 'libjvm.so'");
536         return NULL;
537         }
538     msg("getCreateVMFunc: Loading JVM: %s", libname.c_str());
539     void *lib = dlopen(libname.c_str(), RTLD_NOW);
540     if (!lib)
541         {
542         err("Java VM not found at '%s' : %s", libname.c_str(), strerror(errno));
543         return NULL;
544         }
545     CreateVMFunc createVM = (CreateVMFunc)dlsym(lib, "JNI_CreateJavaVM");
546     if (!createVM)
547         {
548         err("Could not find 'JNI_CreateJavaVM' in shared library");
549             return NULL;
550         }
551     return createVM;
555 static void getJavaRoot(String &javaroot)
557     javaroot = INKSCAPE_BINDDIR;
558     javaroot.append("/java");
561 #endif /* !__WIN32__ */
564 //########################################################################
565 //# COMMON
566 //########################################################################
569 bool JavaBinderyImpl::isLoaded()
571     return (jvm != (void *)0);
576 /**
577  * This will set up the classpath for the launched VM.
578  * We will add two things:
579  *   1.  INKSCAPE_JAVADIR/classes -- path to loose classes
580  *   2.  A concatenation of all jar files in INKSCAPE_JAVADIR/lib
581  *
582  * This will allow people to add classes and jars to the JVM without
583  * needing to state them explicitly.
584  * 
585  * @param javaroot.  Should be INKSCAPE_JAVADIR
586  * @param result a string buffer to hold the result of this method   
587  */        
588 static void populateClassPath(const String &javaroot,
589                               String &result)
591     String classdir = javaroot;
592     classdir.append(DIR_SEPARATOR);
593     classdir.append("classes");
595     String cp = classdir;
597     String libdir = javaroot;
598     libdir.append(DIR_SEPARATOR);
599     libdir.append("lib");
601     DIR *dir = opendir(libdir.c_str());
602     if (!dir)
603         {
604         result = cp;
605         return;
606         }
608     while (true)
609         {
610         struct dirent *de = readdir(dir);
611         if (!de)
612             break;
613         String fname = de->d_name;
614         if (fname == "." || fname == "..")
615             continue;
616         if (fname.size()<5) //x.jar
617             continue;
618         if (fname.compare(fname.size()-4, 4, ".jar") != 0)
619             continue;
621         String path = libdir;
622         path.append(DIR_SEPARATOR);
623         path.append(fname);
625         cp.append(PATH_SEPARATOR);
626         cp.append(path);
627         }
628     closedir(dir);
629     
630     result = cp;
635 //========================================================================
636 // Gateway
637 //========================================================================
638 /**
639  * This method is used to allow the gateway class to
640  * redirect its logging stream here.
641  * For the main C++/Java bindings, see dobinding.cpp 
642  */    
643 void JNICALL logWrite(JNIEnv */*env*/, jobject /*obj*/, jlong ptr, jint ch)
645     JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
646     bind->log(ch);
650 static JNINativeMethod gatewayMethods[] =
652 { (char *)"logWrite",    (char *)"(JI)V", (void *)logWrite    },
653 { NULL,  NULL, NULL }
654 };
657 /**
658  * This sets up the 'Gateway' java class for execution of
659  * scripts.   The class's constructor takes a jlong.  This java long
660  * is used to store the pointer to 'this'.  When ScriptRunner makes
661  * native calls, it passes that jlong back, so that it can call the
662  * methods of this C++ class.  
663  */  
664 bool JavaBinderyImpl::setupGateway()
666     String className = "org/inkscape/cmn/Gateway";
667     if (!registerNatives(className, gatewayMethods))
668         {
669         return false;
670         }
671     jclass cls = env->FindClass(className.c_str());
672     if (!cls)
673         {
674         err("setupGateway: cannot find class '%s' : %s",
675                          className.c_str(), getException().c_str());
676         return false;
677                 }
678         jmethodID mid = env->GetMethodID(cls, "<init>", "(J)V");
679         if (!mid)
680         {
681         err("setupGateway: cannot find constructor for '%s' : %s",
682                           className.c_str(), getException().c_str());
683         return false;
684                 }
685     gatewayObj = env->NewObject(cls, mid, ((jlong)this));
686     if (!gatewayObj)
687         {
688         err("setupGateway: cannot construct '%s' : %s",
689                          className.c_str(), getException().c_str());
690         return false;
691                 }
693         msg("Gateway ready");
694     return true;
697 bool JavaBinderyImpl::scriptRun(const String &lang, const String &script)
699     if (!loadJVM())
700         return false;
702     std::vector<Value> params;
703     Value langParm(lang);
704     params.push_back(langParm);
705     Value scriptParm(script);
706     params.push_back(scriptParm);
707     Value retval;
708     callInstance(Value::BIND_VOID, gatewayObj, "scriptRun",
709              "(Ljava/lang/String;Ljava/lang/String;)Z", params, retval);
710     return retval.getBoolean();
713 bool JavaBinderyImpl::scriptRunFile(const String &lang, const String &fname)
715     if (!loadJVM())
716         return false;
718     std::vector<Value> params;
719     Value langParm(lang);
720     params.push_back(langParm);
721     Value fnameParm(fname);
722     params.push_back(fnameParm);
723     Value retval;
724     callInstance(Value::BIND_VOID, gatewayObj, "scriptRunFile",
725              "(Ljava/lang/String;Ljava/lang/String;)Z", params, retval);
726     return retval.getBoolean();
729 bool JavaBinderyImpl::showConsole()
731     if (!loadJVM())
732         return false;
733         
734     std::vector<Value> params;
735     Value retval;
736     callInstance(Value::BIND_VOID, gatewayObj, "showConsole",
737              "()Z", params, retval);
738     return retval.getBoolean();
742 //========================================================================
743 // End Gateway
744 //========================================================================
747 /**
748  * This is used to grab output from the VM itself. See 'options' below.
749  */ 
750 static int JNICALL vfprintfHook(FILE* /*f*/, const char *fmt, va_list args)
752     g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, args);
756 /**
757  * This is the most important part of this class.  Here we
758  * attempt to find, load, and initialize a java (or mlvm?) virtual
759  * machine.
760  * 
761  * @return true if successful, else false    
762  */ 
763 bool JavaBinderyImpl::loadJVM()
765     if (jvm)
766         return true;
768     CreateVMFunc createVM = getCreateVMFunc();
769     if (!createVM)
770         {
771         err("Could not find 'JNI_CreateJavaVM' in shared library");
772         return false;
773         }
775     String javaroot;
776     getJavaRoot(javaroot);
777     String cp;
778     populateClassPath(javaroot, cp);
779     String classpath = "-Djava.class.path=";
780     classpath.append(normalizePath(cp));
781     msg("Class path is: '%s'", classpath.c_str());
783     String libpath = "-Djava.library.path=";
784     libpath.append(javaroot);
785     libpath.append(DIR_SEPARATOR);
786     libpath.append("libm");
787     libpath = normalizePath(libpath);
788     msg("Lib path is: '%s'", libpath.c_str());
790     JavaVMInitArgs vm_args;
791     JavaVMOption options[10];//should be enough
792     int nOptions = 0;
793     options[nOptions++].optionString = (char *)classpath.c_str();
794     options[nOptions++].optionString = (char *)libpath.c_str();
795     //options[nOptions++].optionString = (char *)"-verbose:jni";
796     options[nOptions  ].optionString = (char *)"vfprintf";
797     options[nOptions++].extraInfo    = (void *)vfprintfHook;
798     vm_args.version                  = JNI_VERSION_1_4;
799     vm_args.options                  = options;
800     vm_args.nOptions                 = nOptions;
801     vm_args.ignoreUnrecognized       = true;
803     if (createVM(&jvm, &env, &vm_args) < 0)
804         {
805         err("JNI_CreateJavaVM() failed");
806         return false;
807         }
809     //get jvm version
810     jint vers = env->GetVersion();
811     int versionMajor = (vers>>16) & 0xffff;
812     int versionMinor = (vers    ) & 0xffff;
813     msg("Loaded JVM version %d.%d", versionMajor, versionMinor);
815     if (!setupGateway())
816         return false;
818     return true;
822 /**
823  *  This is a difficult method.  What we are doing is trying to
824  *  call a static method with a list of arguments.  Similar to 
825  *  a varargs call, we need to marshal the Values into their
826  *  Java equivalents and make the proper call.
827  *  
828  * @param type the return type of the method
829  * @param className the full (package / name) name of the java class
830  * @param methodName the name of the method being invoked
831  * @param signature the method signature (ex: "(Ljava/lang/String;I)V" )
832  *    that describes the param and return types of the method.
833  * @param retval the return value of the java method
834  * @return true if the call was successful, else false.  This is not
835  *    the return value of the method.    
836  */    
837 bool JavaBinderyImpl::callStatic(int type,
838                         const String &className,
839                         const String &methodName,
840                         const String &signature,
841                         const std::vector<Value> &params,
842                         Value &retval)
844     jclass cls = env->FindClass(className.c_str());
845     if (!cls)
846         {
847         err("Could not find class '%s' : %s",
848                        className.c_str(), getException().c_str());
849         return false;
850         }
851     jmethodID mid = env->GetStaticMethodID(cls,
852                 methodName.c_str(), signature.c_str());
853     if (!mid)
854         {
855         err("Could not find method '%s:%s/%s' : %s",
856                         className.c_str(), methodName.c_str(),
857                             signature.c_str(), getException().c_str());
858         return false;
859         }
860     /**
861      * Assemble your parameters into a form usable by JNI
862      */
863     jvalue *jvals = new jvalue[params.size()];
864     for (unsigned int i=0 ; i<params.size() ; i++)
865         {
866         Value v = params[i];
867         switch (v.getType())
868             {
869             case Value::BIND_BOOLEAN:
870                 {
871                 jvals[i].z = (jboolean)v.getBoolean();
872                 break;
873                 }
874             case Value::BIND_INT:
875                 {
876                 jvals[i].i = (jint)v.getInt();
877                 break;
878                 }
879             case Value::BIND_DOUBLE:
880                 {
881                 jvals[i].d = (jdouble)v.getDouble();
882                 break;
883                 }
884             case Value::BIND_STRING:
885                 {
886                 jvals[i].l = (jobject) env->NewStringUTF(v.getString().c_str());
887                 break;
888                 }
889             default:
890                 {
891                 err("Unknown value type: %d", v.getType());
892                 return false;
893                 }
894             }
895         }
896     switch (type)
897         {
898         case Value::BIND_VOID:
899             {
900             env->CallStaticVoidMethodA(cls, mid, jvals);
901             break;
902             }
903         case Value::BIND_BOOLEAN:
904             {
905             jboolean ret = env->CallStaticBooleanMethodA(cls, mid, jvals);
906             if (ret == JNI_TRUE) //remember, don't truncate
907                 retval.setBoolean(true);
908             else
909                 retval.setBoolean(false);
910             break;
911             }
912         case Value::BIND_INT:
913             {
914             jint ret = env->CallStaticIntMethodA(cls, mid, jvals);
915             retval.setInt(ret);
916             break;
917             }
918         case Value::BIND_DOUBLE:
919             {
920             jdouble ret = env->CallStaticDoubleMethodA(cls, mid, jvals);
921             retval.setDouble(ret);
922             break;
923             }
924         case Value::BIND_STRING:
925             {
926             jobject ret = env->CallStaticObjectMethodA(cls, mid, jvals);
927             jstring jstr = (jstring) ret;
928             const char *str = env->GetStringUTFChars(jstr, JNI_FALSE);
929             retval.setString(str);
930             env->ReleaseStringUTFChars(jstr, str);
931             break;
932             }
933         default:
934             {
935             err("Unknown return type: %d", type);
936             return false;
937             }
938         }
939     delete jvals;
940     String errStr = getException();
941     if (errStr.size()>0)
942         {
943         err("callStatic: %s", errStr.c_str());
944         return false;
945                 }
946     return true;
951 /**
952  *  Another difficult method.  However, this time we are operating
953  *  on an existing instance jobject. 
954  *  
955  * @param type the return type of the method
956  * @param obj the instance upon which to make the call
957  * @param methodName the name of the method being invoked
958  * @param signature the method signature (ex: "(Ljava/lang/String;I)V" )
959  *    that describes the param and return types of the method.
960  * @param retval the return value of the java method
961  * @return true if the call was successful, else false.  This is not
962  *    the return value of the method.    
963  */    
964 bool JavaBinderyImpl::callInstance(
965                         int type,
966                         const jobject obj,
967                         const String &methodName,
968                         const String &signature,
969                         const std::vector<Value> &params,
970                         Value &retval)
972     jmethodID mid = env->GetMethodID(env->GetObjectClass(obj),
973                 methodName.c_str(), signature.c_str());
974     if (!mid)
975         {
976         err("Could not find method '%s/%s' : %s",
977                         methodName.c_str(),
978                             signature.c_str(), getException().c_str());
979         return false;
980         }
981     /**
982      * Assemble your parameters into a form usable by JNI
983      */
984     jvalue *jvals = new jvalue[params.size()];
985     for (unsigned int i=0 ; i<params.size() ; i++)
986         {
987         Value v = params[i];
988         switch (v.getType())
989             {
990             case Value::BIND_BOOLEAN:
991                 {
992                 jvals[i].z = (jboolean)v.getBoolean();
993                 break;
994                 }
995             case Value::BIND_INT:
996                 {
997                 jvals[i].i = (jint)v.getInt();
998                 break;
999                 }
1000             case Value::BIND_DOUBLE:
1001                 {
1002                 jvals[i].d = (jdouble)v.getDouble();
1003                 break;
1004                 }
1005             case Value::BIND_STRING:
1006                 {
1007                 jvals[i].l = (jobject) env->NewStringUTF(v.getString().c_str());
1008                 break;
1009                 }
1010             default:
1011                 {
1012                 err("Unknown value type: %d", v.getType());
1013                 return false;
1014                 }
1015             }
1016         }
1017     switch (type)
1018         {
1019         case Value::BIND_VOID:
1020             {
1021             env->CallVoidMethodA(obj, mid, jvals);
1022             break;
1023             }
1024         case Value::BIND_BOOLEAN:
1025             {
1026             jboolean ret = env->CallBooleanMethodA(obj, mid, jvals);
1027             if (ret == JNI_TRUE) //remember, don't truncate
1028                 retval.setBoolean(true);
1029             else
1030                 retval.setBoolean(false);
1031             break;
1032             }
1033         case Value::BIND_INT:
1034             {
1035             jint ret = env->CallIntMethodA(obj, mid, jvals);
1036             retval.setInt(ret);
1037             break;
1038             }
1039         case Value::BIND_DOUBLE:
1040             {
1041             jdouble ret = env->CallDoubleMethodA(obj, mid, jvals);
1042             retval.setDouble(ret);
1043             break;
1044             }
1045         case Value::BIND_STRING:
1046             {
1047             jobject ret = env->CallObjectMethodA(obj, mid, jvals);
1048             jstring jstr = (jstring) ret;
1049             const char *str = env->GetStringUTFChars(jstr, JNI_FALSE);
1050             retval.setString(str);
1051             env->ReleaseStringUTFChars(jstr, str);
1052             break;
1053             }
1054         default:
1055             {
1056             err("Unknown return type: %d", type);
1057             return false;
1058             }
1059         }
1060     delete jvals;
1061     String errStr = getException();
1062     if (errStr.size()>0)
1063         {
1064         err("callStatic: %s", errStr.c_str());
1065         return false;
1066                 }
1067     return true;
1073 /**
1074  * Fetch the last exception from the JVM, if any.  Clear it to
1075  * continue processing
1076  * 
1077  * @return the exception's descriptio,if any.  Else ""  
1078  */  
1079 String JavaBinderyImpl::getException()
1081     return getExceptionString(env);
1086 /**
1087  * Convenience method to call the static void main(String argv[])
1088  * method of a given class
1089  * 
1090  * @param className full name of the java class
1091  * @args the argument strings to the method 
1092  * @return true if successful, else false   
1093  */ 
1094 bool JavaBinderyImpl::callMain(const String &className,
1095                                const std::vector<String> &args)
1097     std::vector<Value> parms;
1098     for (unsigned int i=0 ; i<args.size() ; i++)
1099         {
1100         Value v;
1101         v.setString(args[i]);
1102         parms.push_back(v);
1103                 }
1104     Value retval;
1105     return callStatic(Value::BIND_VOID, className, "main",
1106              "([Ljava/lang/String;)V", parms, retval);
1110 /**
1111  * Used to register an array of native methods for a named class
1112  * 
1113  * @param className the full name of the java class
1114  * @param the method array
1115  * @return true if successful, else false     
1116  */ 
1117 bool JavaBinderyImpl::registerNatives(const String &className,
1118                            const JNINativeMethod *methods)
1120     jclass cls = env->FindClass(className.c_str());
1121     if (!cls)
1122         {
1123         err("Could not find class '%s'", className.c_str());
1124         return false;
1125         }
1126     //msg("registerNatives: class '%s' found", className.c_str());
1127     
1128     /**
1129      * hack for JDK bug http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6493522
1130      */
1131         jmethodID mid = env->GetMethodID(env->GetObjectClass(cls), "getConstructors",
1132                   "()[Ljava/lang/reflect/Constructor;");
1133         if (!mid)
1134             {
1135             err("Could not get reflect mid for 'getConstructors' : %s",
1136              getException().c_str());
1137                 return false;
1138                 }
1139         jobject res = env->CallObjectMethod(cls, mid);
1140         if (!res)
1141             {
1142             err("Could not get constructors : %s", getException().c_str());
1143                 return false;
1144                 }
1145         /**
1146          * end hack
1147          */             
1148     jint nrMethods = 0;
1149     for (const JNINativeMethod *m = methods ; m->name ; m++)
1150         nrMethods++;
1151     jint ret = env->RegisterNatives(cls, methods, nrMethods);
1152     if (ret < 0)
1153         {
1154         err("Could not register %d native methods for '%s' : %s",
1155                     nrMethods, className.c_str(), getException().c_str());
1156         return false;
1157         }
1158     return true;
1164 } // namespace Bind
1165 } // namespace Inkscape
1167 //########################################################################
1168 //# E N D    O F    F I L E
1169 //########################################################################