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 gatewayObj = NULL;
214 }
216 JavaBinderyImpl::~JavaBinderyImpl()
217 {
218 }
221 //########################################################################
222 //# MESSAGES
223 //########################################################################
225 void err(const char *fmt, ...)
226 {
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
242 }
244 void msg(const char *fmt, ...)
245 {
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
261 }
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)
278 {
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;
291 }
294 static String cleanPath(const String &s)
295 {
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;
304 }
307 /**
308 * Common places to find jvm.dll under JAVA_HOME
309 */
310 static const char *commonJavaPaths[] =
311 {
312 "\\jre\\bin\\client\\jvm.dll",
313 "\\bin\\client\\jvm.dll",
314 "\\jvm.dll",
315 NULL
316 };
318 static CreateVMFunc getCreateVMFunc()
319 {
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;
406 }
408 static void getJavaRoot(String &javaroot)
409 {
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");
419 }
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)
439 {
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;
473 }
476 static const char *commonJavaPaths[] =
477 {
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)
491 {
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;
526 }
530 static CreateVMFunc getCreateVMFunc()
531 {
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;
552 }
555 static void getJavaRoot(String &javaroot)
556 {
557 javaroot = INKSCAPE_BINDDIR;
558 javaroot.append("/java");
559 }
561 #endif /* !__WIN32__ */
564 //########################################################################
565 //# COMMON
566 //########################################################################
569 bool JavaBinderyImpl::isLoaded()
570 {
571 return (jvm != (void *)0);
572 }
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)
590 {
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);
630 result = cp;
631 }
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)
644 {
645 JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
646 bind->log(ch);
647 }
650 static JNINativeMethod gatewayMethods[] =
651 {
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()
665 {
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;
695 }
697 bool JavaBinderyImpl::scriptRun(const String &lang, const String &script)
698 {
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();
711 }
713 bool JavaBinderyImpl::scriptRunFile(const String &lang, const String &fname)
714 {
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();
727 }
729 bool JavaBinderyImpl::showConsole()
730 {
731 if (!loadJVM())
732 return false;
734 std::vector<Value> params;
735 Value retval;
736 callInstance(Value::BIND_VOID, gatewayObj, "showConsole",
737 "()Z", params, retval);
738 return retval.getBoolean();
739 }
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)
751 {
752 g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, args);
753 }
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()
764 {
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;
819 }
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> ¶ms,
842 Value &retval)
843 {
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;
947 }
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> ¶ms,
970 Value &retval)
971 {
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;
1068 }
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()
1080 {
1081 return getExceptionString(env);
1082 }
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)
1096 {
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);
1107 }
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)
1119 {
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());
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;
1159 }
1164 } // namespace Bind
1165 } // namespace Inkscape
1167 //########################################################################
1168 //# E N D O F F I L E
1169 //########################################################################