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;
103 }
105 String getExceptionString(JNIEnv *env)
106 {
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;
119 }
121 jint getInt(JNIEnv *env, jobject obj, const char *name)
122 {
123 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "I");
124 return env->GetIntField(obj, fid);
125 }
127 void setInt(JNIEnv *env, jobject obj, const char *name, jint val)
128 {
129 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "I");
130 env->SetIntField(obj, fid, val);
131 }
133 jlong getLong(JNIEnv *env, jobject obj, const char *name)
134 {
135 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "J");
136 return env->GetLongField(obj, fid);
137 }
139 void setLong(JNIEnv *env, jobject obj, const char *name, jlong val)
140 {
141 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "J");
142 env->SetLongField(obj, fid, val);
143 }
145 jfloat getFloat(JNIEnv *env, jobject obj, const char *name)
146 {
147 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "F");
148 return env->GetFloatField(obj, fid);
149 }
151 void setFloat(JNIEnv *env, jobject obj, const char *name, jfloat val)
152 {
153 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "F");
154 env->SetFloatField(obj, fid, val);
155 }
157 jdouble getDouble(JNIEnv *env, jobject obj, const char *name)
158 {
159 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "D");
160 return env->GetDoubleField(obj, fid);
161 }
163 void setDouble(JNIEnv *env, jobject obj, const char *name, jdouble val)
164 {
165 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "D");
166 env->SetDoubleField(obj, fid, val);
167 }
169 String getString(JNIEnv *env, jobject obj, const char *name)
170 {
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;
177 }
179 void setString(JNIEnv *env, jobject obj, const char *name, const String &val)
180 {
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);
184 }
189 //########################################################################
190 //# CONSTRUCTOR/DESTRUCTOR
191 //########################################################################
193 static JavaBinderyImpl *_instance = NULL;
195 JavaBindery *JavaBindery::getInstance()
196 {
197 return JavaBinderyImpl::getInstance();
198 }
200 JavaBinderyImpl *JavaBinderyImpl::getInstance()
201 {
202 if (!_instance)
203 {
204 _instance = new JavaBinderyImpl();
205 }
206 return _instance;
207 }
209 JavaBinderyImpl::JavaBinderyImpl()
210 {
211 jvm = NULL;
212 env = NULL;
213 }
215 JavaBinderyImpl::~JavaBinderyImpl()
216 {
217 }
220 //########################################################################
221 //# MESSAGES
222 //########################################################################
224 void err(const char *fmt, ...)
225 {
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
241 }
243 void msg(const char *fmt, ...)
244 {
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
260 }
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)
277 {
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;
290 }
293 static String cleanPath(const String &s)
294 {
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;
303 }
306 /**
307 * Common places to find jvm.dll under JAVA_HOME
308 */
309 static const char *commonJavaPaths[] =
310 {
311 "\\jre\\bin\\client\\jvm.dll",
312 "\\bin\\client\\jvm.dll",
313 "\\jvm.dll",
314 NULL
315 };
317 static CreateVMFunc getCreateVMFunc()
318 {
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;
405 }
407 static void getJavaRoot(String &javaroot)
408 {
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);
417 }
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)
437 {
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;
471 }
474 static const char *commonJavaPaths[] =
475 {
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)
489 {
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;
524 }
528 static CreateVMFunc getCreateVMFunc()
529 {
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;
550 }
553 static void getJavaRoot(String &javaroot)
554 {
555 javaroot = INKSCAPE_JAVADIR;
556 }
558 #endif /* !__WIN32__ */
561 //########################################################################
562 //# COMMON
563 //########################################################################
566 bool JavaBinderyImpl::isLoaded()
567 {
568 return (jvm != (void *)0);
569 }
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)
587 {
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);
627 result = cp;
628 }
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)
643 {
644 JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
645 bind->stdOut(ch);
646 }
648 void JNICALL stdErrWrite(JNIEnv */*env*/, jobject /*obj*/, jlong ptr, jint ch)
649 {
650 JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
651 bind->stdErr(ch);
652 }
655 void JNICALL logWrite(JNIEnv */*env*/, jobject /*obj*/, jlong ptr, jint ch)
656 {
657 JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
658 bind->log(ch);
659 }
662 static JNINativeMethod scriptRunnerMethods[] =
663 {
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()
679 {
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;
709 }
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)
721 {
722 g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, args);
723 }
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()
734 {
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;
789 }
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> ¶ms,
812 Value &retval)
813 {
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;
917 }
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()
929 {
930 return getExceptionString(env);
931 }
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)
945 {
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);
956 }
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)
968 {
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());
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;
1008 }
1013 } // namespace Bind
1014 } // namespace Inkscape
1016 //########################################################################
1017 //# E N D O F F I L E
1018 //########################################################################