6bffe0cd71fce728752a207c2286026468da2df2
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>
56 //For repr and document
57 #include <document.h>
58 #include <inkscape.h>
59 #include <xml/repr.h>
61 /**
62 * Note: We must limit Java or JVM-specific code to this file
63 * and to dobinding.cpp. It should be hidden from javabind.h
64 *
65 * This file is mostly about getting things up and running, and
66 * providing the basic C-to-Java hooks.
67 *
68 * dobinding.cpp will have the rote and repetitious
69 * class-by-class binding
70 */
73 namespace Inkscape
74 {
76 namespace Bind
77 {
80 //########################################################################
81 //# DEFINITIONS
82 //########################################################################
84 typedef jint (*CreateVMFunc)(JavaVM **, JNIEnv **, void *);
88 //########################################################################
89 //# UTILITY
90 //########################################################################
92 /**
93 * Normalize path. Java wants '/', even on Windows
94 */
95 String normalizePath(const String &str)
96 {
97 String buf;
98 for (unsigned int i=0 ; i<str.size() ; i++)
99 {
100 char ch = str[i];
101 if (ch == '\\')
102 buf.push_back('/');
103 else
104 buf.push_back(ch);
105 }
106 return buf;
107 }
109 String getExceptionString(JNIEnv *env)
110 {
111 String buf;
112 jthrowable exc = env->ExceptionOccurred();
113 if (!exc)
114 return buf;
115 jclass cls = env->GetObjectClass(exc);
116 jmethodID mid = env->GetMethodID(cls, "toString", "()Ljava/lang/String;");
117 jstring jstr = (jstring) env->CallObjectMethod(exc, mid);
118 const char *str = env->GetStringUTFChars(jstr, JNI_FALSE);
119 buf.append(str);
120 env->ReleaseStringUTFChars(jstr, str);
121 env->ExceptionClear();
122 return buf;
123 }
125 jint getObjInt(JNIEnv *env, jobject obj, const char *name)
126 {
127 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "I");
128 return env->GetIntField(obj, fid);
129 }
131 void setObjInt(JNIEnv *env, jobject obj, const char *name, jint val)
132 {
133 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "I");
134 env->SetIntField(obj, fid, val);
135 }
137 jlong getObjLong(JNIEnv *env, jobject obj, const char *name)
138 {
139 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "J");
140 return env->GetLongField(obj, fid);
141 }
143 void setObjLong(JNIEnv *env, jobject obj, const char *name, jlong val)
144 {
145 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "J");
146 env->SetLongField(obj, fid, val);
147 }
149 jfloat getObjFloat(JNIEnv *env, jobject obj, const char *name)
150 {
151 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "F");
152 return env->GetFloatField(obj, fid);
153 }
155 void setObjFloat(JNIEnv *env, jobject obj, const char *name, jfloat val)
156 {
157 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "F");
158 env->SetFloatField(obj, fid, val);
159 }
161 jdouble getObjDouble(JNIEnv *env, jobject obj, const char *name)
162 {
163 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "D");
164 return env->GetDoubleField(obj, fid);
165 }
167 void setObjDouble(JNIEnv *env, jobject obj, const char *name, jdouble val)
168 {
169 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "D");
170 env->SetDoubleField(obj, fid, val);
171 }
173 String getString(JNIEnv *env, jstring jstr)
174 {
175 const char *chars = env->GetStringUTFChars(jstr, JNI_FALSE);
176 String str = chars;
177 env->ReleaseStringUTFChars(jstr, chars);
178 return str;
179 }
181 String getObjString(JNIEnv *env, jobject obj, const char *name)
182 {
183 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "Ljava/lang/String;");
184 jstring jstr = (jstring)env->GetObjectField(obj, fid);
185 return getString(env, jstr);
186 }
188 void setObjString(JNIEnv *env, jobject obj, const char *name, const String &val)
189 {
190 jstring jstr = env->NewStringUTF(val.c_str());
191 jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "Ljava/lang/String;");
192 env->SetObjectField(obj, fid, jstr);
193 }
198 //########################################################################
199 //# CONSTRUCTOR/DESTRUCTOR
200 //########################################################################
202 static JavaBinderyImpl *_instance = NULL;
204 JavaBindery *JavaBindery::getInstance()
205 {
206 return JavaBinderyImpl::getInstance();
207 }
209 JavaBinderyImpl *JavaBinderyImpl::getInstance()
210 {
211 if (!_instance)
212 {
213 _instance = new JavaBinderyImpl();
214 }
215 return _instance;
216 }
218 JavaBinderyImpl::JavaBinderyImpl()
219 {
220 jvm = NULL;
221 env = NULL;
222 gatewayObj = NULL;
223 }
225 JavaBinderyImpl::~JavaBinderyImpl()
226 {
227 }
230 //########################################################################
231 //# MESSAGES
232 //########################################################################
234 void err(const char *fmt, ...)
235 {
236 #if 0
237 va_list args;
238 fprintf(stderr, "JavaBinderyImpl err:");
239 va_start(args, fmt);
240 vfprintf(stderr, fmt, args);
241 va_end(args);
242 fprintf(stderr, "\n");
243 #else
244 va_list args;
245 g_warning("JavaBinderyImpl err:");
246 va_start(args, fmt);
247 g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, fmt, args);
248 va_end(args);
249 g_warning("\n");
250 #endif
251 }
253 void msg(const char *fmt, ...)
254 {
255 #if 0
256 va_list args;
257 fprintf(stdout, "JavaBinderyImpl:");
258 va_start(args, fmt);
259 vfprintf(stdout, fmt, args);
260 va_end(args);
261 fprintf(stdout, "\n");
262 #else
263 va_list args;
264 g_message("JavaBinderyImpl:");
265 va_start(args, fmt);
266 g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, args);
267 va_end(args);
268 g_message("\n");
269 #endif
270 }
274 //########################################################################
275 //# W I N 3 2 S T Y L E
276 //########################################################################
277 #ifdef __WIN32__
280 #define DIR_SEPARATOR "\\"
281 #define PATH_SEPARATOR ";"
285 static bool getRegistryString(HKEY /*root*/, const char *keyName,
286 const char *valName, char *buf, int buflen)
287 {
288 HKEY key;
289 DWORD bufsiz = buflen;
290 RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_READ, &key);
291 int ret = RegQueryValueEx(key, TEXT(valName),
292 NULL, NULL, (BYTE *)buf, &bufsiz);
293 if (ret != ERROR_SUCCESS)
294 {
295 err("Key '%s\\%s not found\n", keyName, valName);
296 return false;
297 }
298 RegCloseKey(key);
299 return true;
300 }
303 static String cleanPath(const String &s)
304 {
305 String buf;
306 for (unsigned int i=0 ; i<s.size() ; i++)
307 {
308 char ch = s[i];
309 if (ch != '"')
310 buf.push_back(ch);
311 }
312 return buf;
313 }
316 /**
317 * Common places to find jvm.dll under JAVA_HOME
318 */
319 static const char *commonJavaPaths[] =
320 {
321 "\\jre\\bin\\client\\jvm.dll",
322 "\\bin\\client\\jvm.dll",
323 "\\jvm.dll",
324 NULL
325 };
327 static CreateVMFunc getCreateVMFunc()
328 {
329 bool found = false;
330 String libname;
332 /**
333 * First, look for JAVA_HOME. This will allow the user
334 * to override what's in the registry
335 */
336 const char *envStr = getenv("JAVA_HOME");
337 if (envStr)
338 {
339 String javaHome = cleanPath(envStr);
340 msg("JAVA_HOME='%s'", javaHome.c_str());
341 for (const char **path = commonJavaPaths ; *path ; path++)
342 {
343 String jpath = javaHome;
344 jpath.append(*path);
345 //msg("trying '%s'", jpath.c_str());
346 struct stat finfo;
347 if (stat(jpath.c_str(), &finfo)>=0)
348 {
349 //msg("found");
350 libname = jpath;
351 found = true;
352 break;
353 }
354 }
355 }
357 //not at JAVA_HOME. check the registry
358 if (!found)
359 {
360 char verbuf[16];
361 char regpath[80];
362 strcpy(regpath, "SOFTWARE\\JavaSoft\\Java Runtime Environment");
363 bool ret = getRegistryString(HKEY_LOCAL_MACHINE,
364 regpath, "CurrentVersion", verbuf, 15);
365 if (!ret)
366 {
367 msg("JVM CurrentVersion not found in registry at '%s'", regpath);
368 }
369 else
370 {
371 strcat(regpath, "\\");
372 strcat(regpath, verbuf);
373 //msg("reg path: %s\n", regpath);
374 char valbuf[80];
375 ret = getRegistryString(HKEY_LOCAL_MACHINE,
376 regpath, "RuntimeLib", valbuf, 79);
377 if (ret)
378 {
379 found = true;
380 libname = valbuf;
381 }
382 else
383 {
384 msg("JVM RuntimeLib not found in registry at '%s'",
385 regpath);
386 }
387 }
388 }
390 if (!found)
391 {
392 err("JVM not found at JAVA_HOME or in registry");
393 return NULL;
394 }
396 /**
397 * If we are here, then we seem to have a valid path for jvm.dll
398 * Give it a try
399 */
400 msg("getCreateVMFunc: Loading JVM: %s", libname.c_str());
401 HMODULE lib = LoadLibrary(libname.c_str());
402 if (!lib)
403 {
404 err("Java VM not found at '%s'", libname.c_str());
405 return NULL;
406 }
407 CreateVMFunc createVM = (CreateVMFunc)GetProcAddress(lib, "JNI_CreateJavaVM");
408 if (!createVM)
409 {
410 err("Could not find 'JNI_CreateJavaVM' in shared library '%s'",
411 libname.c_str());
412 return NULL;
413 }
414 return createVM;
415 }
417 static void getJavaRoot(String &javaroot)
418 {
419 char exeName[80];
420 GetModuleFileName(NULL, exeName, 80);
421 char *slashPos = strrchr(exeName, '\\');
422 if (slashPos)
423 *slashPos = '\0';
424 javaroot = exeName;
425 javaroot.append("\\");
426 javaroot.append(INKSCAPE_BINDDIR);
427 javaroot.append("\\java");
428 }
433 //########################################################################
434 //# U N I X S T Y L E
435 //########################################################################
436 #else /* !__WIN32__ */
439 #define DIR_SEPARATOR "/"
440 #define PATH_SEPARATOR ":"
443 /**
444 * Recursively descend into a directory looking for libjvm.so
445 */
446 static bool findJVMRecursive(const String &dirpath,
447 std::vector<String> &results)
448 {
449 DIR *dir = opendir(dirpath.c_str());
450 if (!dir)
451 return false;
452 bool ret = false;
453 while (true)
454 {
455 struct dirent *de = readdir(dir);
456 if (!de)
457 break;
458 String fname = de->d_name;
459 if (fname == "." || fname == "..")
460 continue;
461 String path = dirpath;
462 path.push_back('/');
463 path.append(fname);
464 if (fname == "libjvm.so")
465 {
466 ret = true;
467 results.push_back(path);
468 continue;
469 }
470 struct stat finfo;
471 if (lstat(path.c_str(), &finfo)<0)
472 {
473 break;
474 }
475 if (finfo.st_mode & S_IFDIR)
476 {
477 ret |= findJVMRecursive(path, results);
478 }
479 }
480 closedir(dir);
481 return ret;
482 }
485 static const char *commonJavaPaths[] =
486 {
487 "/usr/lib/jvm/jre",
488 "/usr/lib/jvm",
489 "/usr/local/lib/jvm/jre",
490 "/usr/local/lib/jvm",
491 "/usr/java",
492 "/usr/local/java",
493 NULL
494 };
496 /**
497 * Look for a Java VM (libjvm.so) in several Unix places
498 */
499 static bool findJVM(String &result)
500 {
501 std::vector<String> results;
502 int found = false;
504 /* Is there one specified by the user? */
505 const char *javaHome = getenv("JAVA_HOME");
506 if (javaHome && findJVMRecursive(javaHome, results))
507 found = true;
508 else for (const char **path = commonJavaPaths ; *path ; path++)
509 {
510 if (findJVMRecursive(*path, results))
511 {
512 found = true;
513 break;
514 }
515 }
516 if (!found)
517 {
518 return false;
519 }
520 if (results.size() == 0)
521 return false;
522 //Look first for a Client VM
523 for (unsigned int i=0 ; i<results.size() ; i++)
524 {
525 String s = results[i];
526 if (s.find("client") != s.npos)
527 {
528 result = s;
529 return true;
530 }
531 }
532 //else default to the first
533 result = results[0];
534 return true;
535 }
539 static CreateVMFunc getCreateVMFunc()
540 {
541 String libname;
542 if (!findJVM(libname))
543 {
544 err("No Java VM found. Is JAVA_HOME defined? Need to find 'libjvm.so'");
545 return NULL;
546 }
547 msg("getCreateVMFunc: Loading JVM: %s", libname.c_str());
548 void *lib = dlopen(libname.c_str(), RTLD_NOW);
549 if (!lib)
550 {
551 err("Java VM not found at '%s' : %s", libname.c_str(), strerror(errno));
552 return NULL;
553 }
554 CreateVMFunc createVM = (CreateVMFunc)dlsym(lib, "JNI_CreateJavaVM");
555 if (!createVM)
556 {
557 err("Could not find 'JNI_CreateJavaVM' in shared library");
558 return NULL;
559 }
560 return createVM;
561 }
564 static void getJavaRoot(String &javaroot)
565 {
566 javaroot = INKSCAPE_BINDDIR;
567 javaroot.append("/java");
568 }
570 #endif /* !__WIN32__ */
573 //########################################################################
574 //# COMMON
575 //########################################################################
578 bool JavaBinderyImpl::isLoaded()
579 {
580 return (jvm != (void *)0);
581 }
585 /**
586 * This will set up the classpath for the launched VM.
587 * We will add two things:
588 * 1. INKSCAPE_JAVADIR/classes -- path to loose classes
589 * 2. A concatenation of all jar files in INKSCAPE_JAVADIR/lib
590 *
591 * This will allow people to add classes and jars to the JVM without
592 * needing to state them explicitly.
593 *
594 * @param javaroot. Should be INKSCAPE_JAVADIR
595 * @param result a string buffer to hold the result of this method
596 */
597 static void populateClassPath(const String &javaroot,
598 String &result)
599 {
600 String classdir = javaroot;
601 classdir.append(DIR_SEPARATOR);
602 classdir.append("classes");
604 String cp = classdir;
606 String libdir = javaroot;
607 libdir.append(DIR_SEPARATOR);
608 libdir.append("lib");
610 DIR *dir = opendir(libdir.c_str());
611 if (!dir)
612 {
613 result = cp;
614 return;
615 }
617 while (true)
618 {
619 struct dirent *de = readdir(dir);
620 if (!de)
621 break;
622 String fname = de->d_name;
623 if (fname == "." || fname == "..")
624 continue;
625 if (fname.size()<5) //x.jar
626 continue;
627 if (fname.compare(fname.size()-4, 4, ".jar") != 0)
628 continue;
630 String path = libdir;
631 path.append(DIR_SEPARATOR);
632 path.append(fname);
634 cp.append(PATH_SEPARATOR);
635 cp.append(path);
636 }
637 closedir(dir);
639 result = cp;
640 }
644 //========================================================================
645 // Gateway
646 //========================================================================
647 /**
648 * This is provided to scripts can grab the current copy or the
649 * repr tree. If anyone has a smarter way of doing this, please implement.
650 */
651 jstring JNICALL documentGet(JNIEnv *env, jobject /*obj*/, jlong ptr)
652 {
653 JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
654 String buf = sp_repr_save_buf((SP_ACTIVE_DOCUMENT)->rdoc);
655 jstring jstr = env->NewStringUTF(buf.c_str());
656 return jstr;
657 }
659 /**
660 * This is provided to scripts can load an XML tree into Inkscape.
661 * If anyone has a smarter way of doing this, please implement.
662 */
663 jboolean JNICALL documentSet(JNIEnv *env, jobject /*obj*/, jlong ptr, jstring jstr)
664 {
665 JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
666 String s = getString(env, jstr);
667 SPDocument *doc = sp_document_new_from_mem(s.c_str(), s.size(), true);
668 }
670 /**
671 * This method is used to allow the gateway class to
672 * redirect its logging stream here.
673 * For the main C++/Java bindings, see dobinding.cpp
674 */
675 void JNICALL logWrite(JNIEnv */*env*/, jobject /*obj*/, jlong ptr, jint ch)
676 {
677 JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
678 bind->log(ch);
679 }
682 static JNINativeMethod gatewayMethods[] =
683 {
684 { (char *)"documentGet", (char *)"(J)Ljava/lang/String;", (void *)documentGet },
685 { (char *)"documentSet", (char *)"(JLjava/lang/String;)Z", (void *)documentSet },
686 { (char *)"logWrite", (char *)"(JI)V", (void *)logWrite },
687 { NULL, NULL, NULL }
688 };
691 /**
692 * This sets up the 'Gateway' java class for execution of
693 * scripts. The class's constructor takes a jlong. This java long
694 * is used to store the pointer to 'this'. When ScriptRunner makes
695 * native calls, it passes that jlong back, so that it can call the
696 * methods of this C++ class.
697 */
698 bool JavaBinderyImpl::setupGateway()
699 {
700 String className = "org/inkscape/cmn/Gateway";
701 if (!registerNatives(className, gatewayMethods))
702 {
703 return false;
704 }
705 jclass cls = env->FindClass(className.c_str());
706 if (!cls)
707 {
708 err("setupGateway: cannot find class '%s' : %s",
709 className.c_str(), getException().c_str());
710 return false;
711 }
712 jmethodID mid = env->GetMethodID(cls, "<init>", "(J)V");
713 if (!mid)
714 {
715 err("setupGateway: cannot find constructor for '%s' : %s",
716 className.c_str(), getException().c_str());
717 return false;
718 }
719 gatewayObj = env->NewObject(cls, mid, ((jlong)this));
720 if (!gatewayObj)
721 {
722 err("setupGateway: cannot construct '%s' : %s",
723 className.c_str(), getException().c_str());
724 return false;
725 }
727 msg("Gateway ready");
728 return true;
729 }
731 bool JavaBinderyImpl::scriptRun(const String &lang, const String &script)
732 {
733 if (!loadJVM())
734 return false;
736 std::vector<Value> params;
737 Value langParm(lang);
738 params.push_back(langParm);
739 Value scriptParm(script);
740 params.push_back(scriptParm);
741 Value retval;
742 callInstance(Value::BIND_VOID, gatewayObj, "scriptRun",
743 "(Ljava/lang/String;Ljava/lang/String;)Z", params, retval);
744 return retval.getBoolean();
745 }
747 bool JavaBinderyImpl::scriptRunFile(const String &lang, const String &fname)
748 {
749 if (!loadJVM())
750 return false;
752 std::vector<Value> params;
753 Value langParm(lang);
754 params.push_back(langParm);
755 Value fnameParm(fname);
756 params.push_back(fnameParm);
757 Value retval;
758 callInstance(Value::BIND_VOID, gatewayObj, "scriptRunFile",
759 "(Ljava/lang/String;Ljava/lang/String;)Z", params, retval);
760 return retval.getBoolean();
761 }
763 bool JavaBinderyImpl::showConsole()
764 {
765 if (!loadJVM())
766 return false;
768 std::vector<Value> params;
769 Value retval;
770 callInstance(Value::BIND_VOID, gatewayObj, "showConsole",
771 "()Z", params, retval);
772 return retval.getBoolean();
773 }
776 //========================================================================
777 // End Gateway
778 //========================================================================
781 /**
782 * This is used to grab output from the VM itself. See 'options' below.
783 */
784 static int JNICALL vfprintfHook(FILE* /*f*/, const char *fmt, va_list args)
785 {
786 g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, args);
787 }
790 /**
791 * This is the most important part of this class. Here we
792 * attempt to find, load, and initialize a java (or mlvm?) virtual
793 * machine.
794 *
795 * @return true if successful, else false
796 */
797 bool JavaBinderyImpl::loadJVM()
798 {
799 if (jvm)
800 return true;
802 CreateVMFunc createVM = getCreateVMFunc();
803 if (!createVM)
804 {
805 err("Could not find 'JNI_CreateJavaVM' in shared library");
806 return false;
807 }
809 String javaroot;
810 getJavaRoot(javaroot);
811 String cp;
812 populateClassPath(javaroot, cp);
813 String classpath = "-Djava.class.path=";
814 classpath.append(normalizePath(cp));
815 msg("Class path is: '%s'", classpath.c_str());
817 String libpath = "-Djava.library.path=";
818 libpath.append(javaroot);
819 libpath.append(DIR_SEPARATOR);
820 libpath.append("libm");
821 libpath = normalizePath(libpath);
822 msg("Lib path is: '%s'", libpath.c_str());
824 JavaVMInitArgs vm_args;
825 JavaVMOption options[10];//should be enough
826 int nOptions = 0;
827 options[nOptions++].optionString = (char *)classpath.c_str();
828 options[nOptions++].optionString = (char *)libpath.c_str();
829 //options[nOptions++].optionString = (char *)"-verbose:jni";
830 options[nOptions ].optionString = (char *)"vfprintf";
831 options[nOptions++].extraInfo = (void *)vfprintfHook;
832 vm_args.version = JNI_VERSION_1_4;
833 vm_args.options = options;
834 vm_args.nOptions = nOptions;
835 vm_args.ignoreUnrecognized = true;
837 if (createVM(&jvm, &env, &vm_args) < 0)
838 {
839 err("JNI_CreateJavaVM() failed");
840 return false;
841 }
843 //get jvm version
844 jint vers = env->GetVersion();
845 int versionMajor = (vers>>16) & 0xffff;
846 int versionMinor = (vers ) & 0xffff;
847 msg("Loaded JVM version %d.%d", versionMajor, versionMinor);
849 if (!setupGateway())
850 return false;
852 return true;
853 }
856 /**
857 * This is a difficult method. What we are doing is trying to
858 * call a static method with a list of arguments. Similar to
859 * a varargs call, we need to marshal the Values into their
860 * Java equivalents and make the proper call.
861 *
862 * @param type the return type of the method
863 * @param className the full (package / name) name of the java class
864 * @param methodName the name of the method being invoked
865 * @param signature the method signature (ex: "(Ljava/lang/String;I)V" )
866 * that describes the param and return types of the method.
867 * @param retval the return value of the java method
868 * @return true if the call was successful, else false. This is not
869 * the return value of the method.
870 */
871 bool JavaBinderyImpl::callStatic(int type,
872 const String &className,
873 const String &methodName,
874 const String &signature,
875 const std::vector<Value> ¶ms,
876 Value &retval)
877 {
878 jclass cls = env->FindClass(className.c_str());
879 if (!cls)
880 {
881 err("Could not find class '%s' : %s",
882 className.c_str(), getException().c_str());
883 return false;
884 }
885 jmethodID mid = env->GetStaticMethodID(cls,
886 methodName.c_str(), signature.c_str());
887 if (!mid)
888 {
889 err("Could not find method '%s:%s/%s' : %s",
890 className.c_str(), methodName.c_str(),
891 signature.c_str(), getException().c_str());
892 return false;
893 }
894 /**
895 * Assemble your parameters into a form usable by JNI
896 */
897 jvalue *jvals = new jvalue[params.size()];
898 for (unsigned int i=0 ; i<params.size() ; i++)
899 {
900 Value v = params[i];
901 switch (v.getType())
902 {
903 case Value::BIND_BOOLEAN:
904 {
905 jvals[i].z = (jboolean)v.getBoolean();
906 break;
907 }
908 case Value::BIND_INT:
909 {
910 jvals[i].i = (jint)v.getInt();
911 break;
912 }
913 case Value::BIND_DOUBLE:
914 {
915 jvals[i].d = (jdouble)v.getDouble();
916 break;
917 }
918 case Value::BIND_STRING:
919 {
920 jvals[i].l = (jobject) env->NewStringUTF(v.getString().c_str());
921 break;
922 }
923 default:
924 {
925 err("Unknown value type: %d", v.getType());
926 return false;
927 }
928 }
929 }
930 switch (type)
931 {
932 case Value::BIND_VOID:
933 {
934 env->CallStaticVoidMethodA(cls, mid, jvals);
935 break;
936 }
937 case Value::BIND_BOOLEAN:
938 {
939 jboolean ret = env->CallStaticBooleanMethodA(cls, mid, jvals);
940 if (ret == JNI_TRUE) //remember, don't truncate
941 retval.setBoolean(true);
942 else
943 retval.setBoolean(false);
944 break;
945 }
946 case Value::BIND_INT:
947 {
948 jint ret = env->CallStaticIntMethodA(cls, mid, jvals);
949 retval.setInt(ret);
950 break;
951 }
952 case Value::BIND_DOUBLE:
953 {
954 jdouble ret = env->CallStaticDoubleMethodA(cls, mid, jvals);
955 retval.setDouble(ret);
956 break;
957 }
958 case Value::BIND_STRING:
959 {
960 jobject ret = env->CallStaticObjectMethodA(cls, mid, jvals);
961 jstring jstr = (jstring) ret;
962 const char *str = env->GetStringUTFChars(jstr, JNI_FALSE);
963 retval.setString(str);
964 env->ReleaseStringUTFChars(jstr, str);
965 break;
966 }
967 default:
968 {
969 err("Unknown return type: %d", type);
970 return false;
971 }
972 }
973 delete jvals;
974 String errStr = getException();
975 if (errStr.size()>0)
976 {
977 err("callStatic: %s", errStr.c_str());
978 return false;
979 }
980 return true;
981 }
985 /**
986 * Another difficult method. However, this time we are operating
987 * on an existing instance jobject.
988 *
989 * @param type the return type of the method
990 * @param obj the instance upon which to make the call
991 * @param methodName the name of the method being invoked
992 * @param signature the method signature (ex: "(Ljava/lang/String;I)V" )
993 * that describes the param and return types of the method.
994 * @param retval the return value of the java method
995 * @return true if the call was successful, else false. This is not
996 * the return value of the method.
997 */
998 bool JavaBinderyImpl::callInstance(
999 int type,
1000 const jobject obj,
1001 const String &methodName,
1002 const String &signature,
1003 const std::vector<Value> ¶ms,
1004 Value &retval)
1005 {
1006 jmethodID mid = env->GetMethodID(env->GetObjectClass(obj),
1007 methodName.c_str(), signature.c_str());
1008 if (!mid)
1009 {
1010 err("Could not find method '%s/%s' : %s",
1011 methodName.c_str(),
1012 signature.c_str(), getException().c_str());
1013 return false;
1014 }
1015 /**
1016 * Assemble your parameters into a form usable by JNI
1017 */
1018 jvalue *jvals = new jvalue[params.size()];
1019 for (unsigned int i=0 ; i<params.size() ; i++)
1020 {
1021 Value v = params[i];
1022 switch (v.getType())
1023 {
1024 case Value::BIND_BOOLEAN:
1025 {
1026 jvals[i].z = (jboolean)v.getBoolean();
1027 break;
1028 }
1029 case Value::BIND_INT:
1030 {
1031 jvals[i].i = (jint)v.getInt();
1032 break;
1033 }
1034 case Value::BIND_DOUBLE:
1035 {
1036 jvals[i].d = (jdouble)v.getDouble();
1037 break;
1038 }
1039 case Value::BIND_STRING:
1040 {
1041 jvals[i].l = (jobject) env->NewStringUTF(v.getString().c_str());
1042 break;
1043 }
1044 default:
1045 {
1046 err("Unknown value type: %d", v.getType());
1047 return false;
1048 }
1049 }
1050 }
1051 switch (type)
1052 {
1053 case Value::BIND_VOID:
1054 {
1055 env->CallVoidMethodA(obj, mid, jvals);
1056 break;
1057 }
1058 case Value::BIND_BOOLEAN:
1059 {
1060 jboolean ret = env->CallBooleanMethodA(obj, mid, jvals);
1061 if (ret == JNI_TRUE) //remember, don't truncate
1062 retval.setBoolean(true);
1063 else
1064 retval.setBoolean(false);
1065 break;
1066 }
1067 case Value::BIND_INT:
1068 {
1069 jint ret = env->CallIntMethodA(obj, mid, jvals);
1070 retval.setInt(ret);
1071 break;
1072 }
1073 case Value::BIND_DOUBLE:
1074 {
1075 jdouble ret = env->CallDoubleMethodA(obj, mid, jvals);
1076 retval.setDouble(ret);
1077 break;
1078 }
1079 case Value::BIND_STRING:
1080 {
1081 jobject ret = env->CallObjectMethodA(obj, mid, jvals);
1082 jstring jstr = (jstring) ret;
1083 const char *str = env->GetStringUTFChars(jstr, JNI_FALSE);
1084 retval.setString(str);
1085 env->ReleaseStringUTFChars(jstr, str);
1086 break;
1087 }
1088 default:
1089 {
1090 err("Unknown return type: %d", type);
1091 return false;
1092 }
1093 }
1094 delete jvals;
1095 String errStr = getException();
1096 if (errStr.size()>0)
1097 {
1098 err("callStatic: %s", errStr.c_str());
1099 return false;
1100 }
1101 return true;
1102 }
1107 /**
1108 * Fetch the last exception from the JVM, if any. Clear it to
1109 * continue processing
1110 *
1111 * @return the exception's descriptio,if any. Else ""
1112 */
1113 String JavaBinderyImpl::getException()
1114 {
1115 return getExceptionString(env);
1116 }
1120 /**
1121 * Convenience method to call the static void main(String argv[])
1122 * method of a given class
1123 *
1124 * @param className full name of the java class
1125 * @args the argument strings to the method
1126 * @return true if successful, else false
1127 */
1128 bool JavaBinderyImpl::callMain(const String &className,
1129 const std::vector<String> &args)
1130 {
1131 std::vector<Value> parms;
1132 for (unsigned int i=0 ; i<args.size() ; i++)
1133 {
1134 Value v;
1135 v.setString(args[i]);
1136 parms.push_back(v);
1137 }
1138 Value retval;
1139 return callStatic(Value::BIND_VOID, className, "main",
1140 "([Ljava/lang/String;)V", parms, retval);
1141 }
1144 /**
1145 * Used to register an array of native methods for a named class
1146 *
1147 * @param className the full name of the java class
1148 * @param the method array
1149 * @return true if successful, else false
1150 */
1151 bool JavaBinderyImpl::registerNatives(const String &className,
1152 const JNINativeMethod *methods)
1153 {
1154 jclass cls = env->FindClass(className.c_str());
1155 if (!cls)
1156 {
1157 err("Could not find class '%s'", className.c_str());
1158 return false;
1159 }
1160 //msg("registerNatives: class '%s' found", className.c_str());
1162 /**
1163 * hack for JDK bug http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6493522
1164 */
1165 jmethodID mid = env->GetMethodID(env->GetObjectClass(cls), "getConstructors",
1166 "()[Ljava/lang/reflect/Constructor;");
1167 if (!mid)
1168 {
1169 err("Could not get reflect mid for 'getConstructors' : %s",
1170 getException().c_str());
1171 return false;
1172 }
1173 jobject res = env->CallObjectMethod(cls, mid);
1174 if (!res)
1175 {
1176 err("Could not get constructors : %s", getException().c_str());
1177 return false;
1178 }
1179 /**
1180 * end hack
1181 */
1182 jint nrMethods = 0;
1183 for (const JNINativeMethod *m = methods ; m->name ; m++)
1184 nrMethods++;
1185 jint ret = env->RegisterNatives(cls, methods, nrMethods);
1186 if (ret < 0)
1187 {
1188 err("Could not register %d native methods for '%s' : %s",
1189 nrMethods, className.c_str(), getException().c_str());
1190 return false;
1191 }
1192 return true;
1193 }
1198 } // namespace Bind
1199 } // namespace Inkscape
1201 //########################################################################
1202 //# E N D O F F I L E
1203 //########################################################################