Code

Delegate MI methods to impl classes
[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;
215 JavaBinderyImpl::~JavaBinderyImpl()
220 //########################################################################
221 //# MESSAGES
222 //########################################################################
224 void err(const char *fmt, ...)
226 #if 0
227     va_list args;
228     fprintf(stderr, "JavaBinderyImpl err:");
229     va_start(args, fmt);
230     vfprintf(stderr, fmt, args);
231     va_end(args);
232     fprintf(stderr, "\n");
233 #else
234     va_list args;
235     g_warning("JavaBinderyImpl err:");
236     va_start(args, fmt);
237     g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, fmt, args);
238     va_end(args);
239     g_warning("\n");
240 #endif
243 void msg(const char *fmt, ...)
245 #if 0
246     va_list args;
247     fprintf(stdout, "JavaBinderyImpl:");
248     va_start(args, fmt);
249     vfprintf(stdout, fmt, args);
250     va_end(args);
251     fprintf(stdout, "\n");
252 #else
253     va_list args;
254     g_message("JavaBinderyImpl:");
255     va_start(args, fmt);
256     g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, args);
257     va_end(args);
258     g_message("\n");
259 #endif
264 //########################################################################
265 //# W I N 3 2      S T Y L E
266 //########################################################################
267 #ifdef __WIN32__
270 #define DIR_SEPARATOR "\\"
271 #define PATH_SEPARATOR ";"
275 static bool getRegistryString(HKEY root, const char *keyName,
276                const char *valName, char *buf, int buflen)
278     HKEY key;
279     DWORD bufsiz  = buflen;
280     RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_READ, &key);
281     int ret = RegQueryValueEx(key, TEXT(valName),
282             NULL, NULL, (BYTE *)buf, &bufsiz);
283     if (ret != ERROR_SUCCESS)
284         {
285         err("Key '%s\\%s not found\n", keyName, valName);
286         return false;
287         }
288     RegCloseKey(key);
289     return true;
293 static String cleanPath(const String &s)
295     String buf;
296     for (unsigned int i=0 ; i<s.size() ; i++)
297         {
298         char ch = s[i];
299         if (ch != '"')
300             buf.push_back(ch);
301                 }
302         return buf;
306 /**
307  * Common places to find jvm.dll under JAVA_HOME
308  */ 
309 static const char *commonJavaPaths[] =
311     "\\jre\\bin\\client\\jvm.dll",
312     "\\bin\\client\\jvm.dll",
313     "\\jvm.dll",
314     NULL
315 };
317 static CreateVMFunc getCreateVMFunc()
319     bool found = false;
320     String libname;
322     /**
323      * First, look for JAVA_HOME.  This will allow the user
324      * to override what's in the registry
325      */              
326     const char *envStr = getenv("JAVA_HOME");
327     if (envStr)
328         {
329         String javaHome = cleanPath(envStr);
330         msg("JAVA_HOME='%s'", javaHome.c_str());
331         for (const char **path = commonJavaPaths ; *path ; path++)
332             {
333             String jpath = javaHome;
334             jpath.append(*path);
335             //msg("trying '%s'", jpath.c_str());
336             struct stat finfo;
337             if (stat(jpath.c_str(), &finfo)>=0)
338                 {
339                 //msg("found");
340                 libname = jpath;
341                 found = true;
342                 break;
343                 }
344             }
345         }
347     //not at JAVA_HOME.  check the registry
348     if (!found)
349         {
350         char verbuf[16];
351         char regpath[80];
352         strcpy(regpath, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
353         bool ret = getRegistryString(HKEY_LOCAL_MACHINE,
354                      regpath, "CurrentVersion", verbuf, 15);
355         if (!ret)
356             {
357             msg("JVM CurrentVersion not found in registry at '%s'", regpath);
358             }
359         else
360             {
361             strcat(regpath, "\\");
362             strcat(regpath, verbuf);
363             //msg("reg path: %s\n", regpath);
364             char valbuf[80];
365             ret = getRegistryString(HKEY_LOCAL_MACHINE,
366                      regpath, "RuntimeLib", valbuf, 79);
367             if (ret)
368                 {
369                 found = true;
370                 libname = valbuf;
371                 }
372             else
373                 {
374                 msg("JVM RuntimeLib not found in registry at '%s'",
375                                           regpath);
376                                 }
377                         }
378         }
380     if (!found)
381         {
382         err("JVM not found at JAVA_HOME or in registry");
383         return NULL;
384         }
386     /**
387      * If we are here, then we seem to have a valid path for jvm.dll
388      * Give it a try
389      */              
390     msg("getCreateVMFunc: Loading JVM: %s", libname.c_str());
391     HMODULE lib = LoadLibrary(libname.c_str());
392     if (!lib)
393         {
394         err("Java VM not found at '%s'", libname.c_str());
395         return NULL;
396         }
397     CreateVMFunc createVM = (CreateVMFunc)GetProcAddress(lib, "JNI_CreateJavaVM");
398     if (!createVM)
399         {
400         err("Could not find 'JNI_CreateJavaVM' in shared library '%s'",
401                                    libname.c_str());
402         return NULL;
403         }
404     return createVM;
407 static void getJavaRoot(String &javaroot)
409     char exeName[80];
410     GetModuleFileName(NULL, exeName, 80);
411     char *slashPos = strrchr(exeName, '\\');
412     if (slashPos)
413         *slashPos = '\0';
414     javaroot = exeName;
415     javaroot.append("\\");
416     javaroot.append(INKSCAPE_JAVADIR);
422 //########################################################################
423 //# U N I X    S T Y L E
424 //########################################################################
425 #else /* !__WIN32__ */
428 #define DIR_SEPARATOR "/"
429 #define PATH_SEPARATOR ":"
432 /**
433  * Recursively descend into a directory looking for libjvm.so
434  */
435 static bool findJVMRecursive(const String &dirpath,
436                              std::vector<String> &results)
438     DIR *dir = opendir(dirpath.c_str());
439     if (!dir)
440         return false;
441     bool ret = false;
442     while (true)
443         {
444         struct dirent *de = readdir(dir);
445         if (!de)
446             break;
447         String fname = de->d_name;
448         if (fname == "." || fname == "..")
449             continue;
450         String path = dirpath;
451         path.push_back('/');
452         path.append(fname);
453         if (fname == "libjvm.so")
454             {
455             ret = true;
456             results.push_back(path);
457             continue;
458             }
459         struct stat finfo;
460         if (lstat(path.c_str(), &finfo)<0)
461             {
462             break;
463             }
464         if (finfo.st_mode & S_IFDIR)
465             {
466             ret |= findJVMRecursive(path, results);
467             }
468         }
469     closedir(dir);
470     return ret;
474 static const char *commonJavaPaths[] =
476     "/usr/lib/jvm/jre",
477     "/usr/lib/jvm",
478     "/usr/local/lib/jvm/jre",
479     "/usr/local/lib/jvm",
480     "/usr/java",
481     "/usr/local/java",
482     NULL
483 };
485 /**
486  * Look for a Java VM (libjvm.so) in several Unix places
487  */
488 static bool findJVM(String &result)
490     std::vector<String> results;
491     int found = false;
493     /* Is there one specified by the user? */
494     const char *javaHome = getenv("JAVA_HOME");
495     if (javaHome && findJVMRecursive(javaHome, results))
496         found = true;
497     else for (const char **path = commonJavaPaths ; *path ; path++)
498         {
499         if (findJVMRecursive(*path, results))
500             {
501             found = true;
502             break;
503             }
504         }
505     if (!found)
506         {
507         return false;
508         }
509     if (results.size() == 0)
510         return false;
511     //Look first for a Client VM
512     for (unsigned int i=0 ; i<results.size() ; i++)
513         {
514         String s = results[i];
515         if (s.find("client") != s.npos)
516             {
517             result = s;
518             return true;
519             }
520         }
521     //else default to the first
522     result = results[0];
523     return true;
528 static CreateVMFunc getCreateVMFunc()
530     String libname;
531     if (!findJVM(libname))
532         {
533         err("No Java VM found. Is JAVA_HOME defined?  Need to find 'libjvm.so'");
534         return NULL;
535         }
536     msg("getCreateVMFunc: Loading JVM: %s", libname.c_str());
537     void *lib = dlopen(libname.c_str(), RTLD_NOW);
538     if (!lib)
539         {
540         err("Java VM not found at '%s' : %s", libname.c_str(), strerror(errno));
541         return NULL;
542         }
543     CreateVMFunc createVM = (CreateVMFunc)dlsym(lib, "JNI_CreateJavaVM");
544     if (!createVM)
545         {
546         err("Could not find 'JNI_CreateJavaVM' in shared library");
547             return NULL;
548         }
549     return createVM;
553 static void getJavaRoot(String &javaroot)
555     javaroot = INKSCAPE_JAVADIR;
558 #endif /* !__WIN32__ */
561 //########################################################################
562 //# COMMON
563 //########################################################################
566 bool JavaBinderyImpl::isLoaded()
568     return (jvm != (void *)0);
573 /**
574  * This will set up the classpath for the launched VM.
575  * We will add two things:
576  *   1.  INKSCAPE_JAVADIR/classes -- path to loose classes
577  *   2.  A concatenation of all jar files in INKSCAPE_JAVADIR/lib
578  *
579  * This will allow people to add classes and jars to the JVM without
580  * needing to state them explicitly.
581  * 
582  * @param javaroot.  Should be INKSCAPE_JAVADIR
583  * @param result a string buffer to hold the result of this method   
584  */        
585 static void populateClassPath(const String &javaroot,
586                               String &result)
588     String classdir = javaroot;
589     classdir.append(DIR_SEPARATOR);
590     classdir.append("classes");
592     String cp = classdir;
594     String libdir = javaroot;
595     libdir.append(DIR_SEPARATOR);
596     libdir.append("lib");
598     DIR *dir = opendir(libdir.c_str());
599     if (!dir)
600         {
601         result = cp;
602         return;
603         }
605     while (true)
606         {
607         struct dirent *de = readdir(dir);
608         if (!de)
609             break;
610         String fname = de->d_name;
611         if (fname == "." || fname == "..")
612             continue;
613         if (fname.size()<5) //x.jar
614             continue;
615         if (fname.compare(fname.size()-4, 4, ".jar") != 0)
616             continue;
618         String path = libdir;
619         path.append(DIR_SEPARATOR);
620         path.append(fname);
622         cp.append(PATH_SEPARATOR);
623         cp.append(path);
624         }
625     closedir(dir);
626     
627     result = cp;
632 //========================================================================
633 // SCRIPT RUNNER
634 //========================================================================
635 /**
636  * These methods are used to allow the ScriptRunner class to
637  * redirect its stderr and stdout streams to here, to be caught
638  * by two string buffers.  We can then use those buffers how we
639  * want.  These native methods are only those needed for running
640  * a script.  For the main C++/Java bindings, see dobinding.cpp 
641  */    
642 void JNICALL stdOutWrite(JNIEnv */*env*/, jobject /*obj*/, jlong ptr, jint ch)
644     JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
645     bind->stdOut(ch);
648 void JNICALL stdErrWrite(JNIEnv */*env*/, jobject /*obj*/, jlong ptr, jint ch)
650     JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
651     bind->stdErr(ch);
655 void JNICALL logWrite(JNIEnv */*env*/, jobject /*obj*/, jlong ptr, jint ch)
657     JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
658     bind->log(ch);
662 static JNINativeMethod scriptRunnerMethods[] =
664 { (char *)"stdOutWrite", (char *)"(JI)V", (void *)stdOutWrite },
665 { (char *)"stdErrWrite", (char *)"(JI)V", (void *)stdErrWrite },
666 { (char *)"logWrite",    (char *)"(JI)V", (void *)logWrite    },
667 { NULL,  NULL, NULL }
668 };
671 /**
672  * This sets up the 'ScriptRunner' java class for execution of
673  * scripts.   The class's constructor takes a jlong.  This java long
674  * is used to store the pointer to 'this'.  When ScriptRunner makes
675  * native calls, it passes that jlong back, so that it can call the
676  * methods of this C++ class.  
677  */  
678 bool JavaBinderyImpl::setupScriptRunner()
680     String className = "org/inkscape/cmn/ScriptRunner";
681     if (!registerNatives(className, scriptRunnerMethods))
682         {
683         return false;
684         }
685     jclass cls = env->FindClass(className.c_str());
686     if (!cls)
687         {
688         err("setupScriptRunner: cannot find class '%s' : %s",
689                          className.c_str(), getException().c_str());
690         return false;
691                 }
692         jmethodID mid = env->GetMethodID(cls, "<init>", "(J)V");
693         if (!mid)
694         {
695         err("setupScriptRunner: cannot find constructor for '%s' : %s",
696                           className.c_str(), getException().c_str());
697         return false;
698                 }
699     jobject obj = env->NewObject(cls, mid, ((jlong)this));
700     if (!obj)
701         {
702         err("setupScriptRunner: cannot construct '%s' : %s",
703                          className.c_str(), getException().c_str());
704         return false;
705                 }
707         msg("ScriptRunner ready");
708     return true;
712 //========================================================================
713 // End SCRIPT RUNNER
714 //========================================================================
717 /**
718  * This is used to grab output from the VM itself. See 'options' below.
719  */ 
720 static int JNICALL vfprintfHook(FILE* /*f*/, const char *fmt, va_list args)
722     g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, args);
726 /**
727  * This is the most important part of this class.  Here we
728  * attempt to find, load, and initialize a java (or mlvm?) virtual
729  * machine.
730  * 
731  * @return true if successful, else false    
732  */ 
733 bool JavaBinderyImpl::loadJVM()
735     if (jvm)
736         return true;
738     CreateVMFunc createVM = getCreateVMFunc();
739     if (!createVM)
740         {
741         err("Could not find 'JNI_CreateJavaVM' in shared library");
742         return false;
743         }
745     String javaroot;
746     getJavaRoot(javaroot);
747     String cp;
748     populateClassPath(javaroot, cp);
749     String classpath = "-Djava.class.path=";
750     classpath.append(normalizePath(cp));
751     msg("Class path is: '%s'", classpath.c_str());
753     String libpath = "-Djava.library.path=";
754     libpath.append(javaroot);
755     libpath.append(DIR_SEPARATOR);
756     libpath.append("libm");
757     libpath = normalizePath(libpath);
758     msg("Lib path is: '%s'", libpath.c_str());
760     JavaVMInitArgs vm_args;
761     JavaVMOption options[10];//should be enough
762     int nOptions = 0;
763     options[nOptions++].optionString = (char *)classpath.c_str();
764     options[nOptions++].optionString = (char *)libpath.c_str();
765     //options[nOptions++].optionString = (char *)"-verbose:jni";
766     options[nOptions  ].optionString = (char *)"vfprintf";
767     options[nOptions++].extraInfo    = (void *)vfprintfHook;
768     vm_args.version                  = JNI_VERSION_1_4;
769     vm_args.options                  = options;
770     vm_args.nOptions                 = nOptions;
771     vm_args.ignoreUnrecognized       = true;
773     if (createVM(&jvm, &env, &vm_args) < 0)
774         {
775         err("JNI_CreateJavaVM() failed");
776         return false;
777         }
779     //get jvm version
780     jint vers = env->GetVersion();
781     int versionMajor = (vers>>16) & 0xffff;
782     int versionMinor = (vers    ) & 0xffff;
783     msg("Loaded JVM version %d.%d", versionMajor, versionMinor);
785     if (!setupScriptRunner())
786         return false;
788     return true;
792 /**
793  *  This is a difficult method.  What we are doing is trying to
794  *  call a static method with a list of arguments.  Similar to 
795  *  a varargs call, we need to marshal the Values into their
796  *  Java equivalents and make the proper call.
797  *  
798  * @param type the return type of the method
799  * @param className the full (package / name) name of the java class
800  * @param methodName the name of the method being invoked
801  * @param signature the method signature (ex: "(Ljava/lang/String;I)V" )
802  *    that describes the param and return types of the method.
803  * @param retval the return value of the java method
804  * @return true if the call was successful, else false.  This is not
805  *    the return value of the method.    
806  */    
807 bool JavaBinderyImpl::callStatic(int type,
808                         const String &className,
809                         const String &methodName,
810                         const String &signature,
811                         const std::vector<Value> &params,
812                         Value &retval)
814     jclass cls = env->FindClass(className.c_str());
815     if (!cls)
816         {
817         err("Could not find class '%s' : %s",
818                        className.c_str(), getException().c_str());
819         return false;
820         }
821     jmethodID mid = env->GetStaticMethodID(cls,
822                 methodName.c_str(), signature.c_str());
823     if (!mid)
824         {
825         err("Could not find method '%s:%s/%s' : %s",
826                         className.c_str(), methodName.c_str(),
827                             signature.c_str(), getException().c_str());
828         return false;
829         }
830     /**
831      * Assemble your parameters into a form usable by JNI
832      */
833     jvalue *jvals = new jvalue[params.size()];
834     for (unsigned int i=0 ; i<params.size() ; i++)
835         {
836         Value v = params[i];
837         switch (v.getType())
838             {
839             case Value::BIND_BOOLEAN:
840                 {
841                 jvals[i].z = (jboolean)v.getBoolean();
842                 break;
843                 }
844             case Value::BIND_INT:
845                 {
846                 jvals[i].i = (jint)v.getInt();
847                 break;
848                 }
849             case Value::BIND_DOUBLE:
850                 {
851                 jvals[i].d = (jdouble)v.getDouble();
852                 break;
853                 }
854             case Value::BIND_STRING:
855                 {
856                 jvals[i].l = (jobject) env->NewStringUTF(v.getString().c_str());
857                 break;
858                 }
859             default:
860                 {
861                 err("Unknown value type: %d", v.getType());
862                 return false;
863                 }
864             }
865         }
866     switch (type)
867         {
868         case Value::BIND_VOID:
869             {
870             env->CallStaticVoidMethodA(cls, mid, jvals);
871             break;
872             }
873         case Value::BIND_BOOLEAN:
874             {
875             jboolean ret = env->CallStaticBooleanMethodA(cls, mid, jvals);
876             if (ret == JNI_TRUE) //remember, don't truncate
877                 retval.setBoolean(true);
878             else
879                 retval.setBoolean(false);
880             break;
881             }
882         case Value::BIND_INT:
883             {
884             jint ret = env->CallStaticIntMethodA(cls, mid, jvals);
885             retval.setInt(ret);
886             break;
887             }
888         case Value::BIND_DOUBLE:
889             {
890             jdouble ret = env->CallStaticDoubleMethodA(cls, mid, jvals);
891             retval.setDouble(ret);
892             break;
893             }
894         case Value::BIND_STRING:
895             {
896             jobject ret = env->CallStaticObjectMethodA(cls, mid, jvals);
897             jstring jstr = (jstring) ret;
898             const char *str = env->GetStringUTFChars(jstr, JNI_FALSE);
899             retval.setString(str);
900             env->ReleaseStringUTFChars(jstr, str);
901             break;
902             }
903         default:
904             {
905             err("Unknown return type: %d", type);
906             return false;
907             }
908         }
909     delete jvals;
910     String errStr = getException();
911     if (errStr.size()>0)
912         {
913         err("callStatic: %s", errStr.c_str());
914         return false;
915                 }
916     return true;
922 /**
923  * Fetch the last exception from the JVM, if any.  Clear it to
924  * continue processing
925  * 
926  * @return the exception's descriptio,if any.  Else ""  
927  */  
928 String JavaBinderyImpl::getException()
930     return getExceptionString(env);
935 /**
936  * Convenience method to call the static void main(String argv[])
937  * method of a given class
938  * 
939  * @param className full name of the java class
940  * @args the argument strings to the method 
941  * @return true if successful, else false   
942  */ 
943 bool JavaBinderyImpl::callMain(const String &className,
944                                const std::vector<String> &args)
946     std::vector<Value> parms;
947     for (unsigned int i=0 ; i<args.size() ; i++)
948         {
949         Value v;
950         v.setString(args[i]);
951         parms.push_back(v);
952                 }
953     Value retval;
954     return callStatic(Value::BIND_VOID, className, "main",
955              "([Ljava/lang/String;)V", parms, retval);
959 /**
960  * Used to register an array of native methods for a named class
961  * 
962  * @param className the full name of the java class
963  * @param the method array
964  * @return true if successful, else false     
965  */ 
966 bool JavaBinderyImpl::registerNatives(const String &className,
967                            const JNINativeMethod *methods)
969     jclass cls = env->FindClass(className.c_str());
970     if (!cls)
971         {
972         err("Could not find class '%s'", className.c_str());
973         return false;
974         }
975     //msg("registerNatives: class '%s' found", className.c_str());
976     
977     /**
978      * hack for JDK bug http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6493522
979      */
980         jmethodID mid = env->GetMethodID(env->GetObjectClass(cls), "getConstructors",
981                   "()[Ljava/lang/reflect/Constructor;");
982         if (!mid)
983             {
984             err("Could not get reflect mid for 'getConstructors' : %s",
985              getException().c_str());
986                 return false;
987                 }
988         jobject res = env->CallObjectMethod(cls, mid);
989         if (!res)
990             {
991             err("Could not get constructors : %s", getException().c_str());
992                 return false;
993                 }
994         /**
995          * end hack
996          */             
997     jint nrMethods = 0;
998     for (const JNINativeMethod *m = methods ; m->name ; m++)
999         nrMethods++;
1000     jint ret = env->RegisterNatives(cls, methods, nrMethods);
1001     if (ret < 0)
1002         {
1003         err("Could not register %d native methods for '%s' : %s",
1004                     nrMethods, className.c_str(), getException().c_str());
1005         return false;
1006         }
1007     return true;
1013 } // namespace Bind
1014 } // namespace Inkscape
1016 //########################################################################
1017 //# E N D    O F    F I L E
1018 //########################################################################