Code

* Merge from trunk
[inkscape.git] / src / bind / javabind.cpp
index 0262aee54c5cc570756d7d94a1ef663cadfef620..6dc8c9a9bb2b24d1615c0f3abfbd1a03d57f2a5d 100644 (file)
@@ -1,5 +1,6 @@
 /**
- * This is a simple mechanism to bind Inkscape to Java, and thence
+ * @file
+ * @brief This is a simple mechanism to bind Inkscape to Java, and thence
  * to all of the nice things that can be layered upon that.
  *
  * Authors:
 #include <prefix.h>
 #include <glib/gmessages.h>
 
+//For repr and document
+#include <document.h>
+#include <inkscape.h>
+#include <xml/repr.h>
 
 /**
  * Note: We must limit Java or JVM-specific code to this file
@@ -102,6 +107,23 @@ String normalizePath(const String &str)
        return buf;
 }
 
+
+/**
+ * Convert a java string to a C++ string
+ */
+String getString(JNIEnv *env, jstring jstr)
+{
+    const char *chars = env->GetStringUTFChars(jstr, JNI_FALSE);
+    String str = chars;
+    env->ReleaseStringUTFChars(jstr, chars);
+    return str;
+}
+
+
+/**
+ * Check if the VM has encountered an Exception.  If so, get the String for it
+ * and clear the exception
+ */
 String getExceptionString(JNIEnv *env)
 {
     String buf;
@@ -111,72 +133,67 @@ String getExceptionString(JNIEnv *env)
     jclass cls = env->GetObjectClass(exc);
     jmethodID mid = env->GetMethodID(cls, "toString", "()Ljava/lang/String;");
     jstring jstr = (jstring) env->CallObjectMethod(exc, mid);
-    const char *str = env->GetStringUTFChars(jstr, JNI_FALSE);
-    buf.append(str);
-    env->ReleaseStringUTFChars(jstr, str);
+    buf.append(getString(env, jstr));
     env->ExceptionClear();
        return buf;
 }
 
-jint getInt(JNIEnv *env, jobject obj, const char *name)
+jint getObjInt(JNIEnv *env, jobject obj, const char *name)
 {
     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "I");
     return env->GetIntField(obj, fid);
 }
 
-void setInt(JNIEnv *env, jobject obj, const char *name, jint val)
+void setObjInt(JNIEnv *env, jobject obj, const char *name, jint val)
 {
     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "I");
     env->SetIntField(obj, fid, val);
 }
 
-jlong getLong(JNIEnv *env, jobject obj, const char *name)
+jlong getObjLong(JNIEnv *env, jobject obj, const char *name)
 {
     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "J");
     return env->GetLongField(obj, fid);
 }
 
-void setLong(JNIEnv *env, jobject obj, const char *name, jlong val)
+void setObjLong(JNIEnv *env, jobject obj, const char *name, jlong val)
 {
     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "J");
     env->SetLongField(obj, fid, val);
 }
 
-jfloat getFloat(JNIEnv *env, jobject obj, const char *name)
+jfloat getObjFloat(JNIEnv *env, jobject obj, const char *name)
 {
     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "F");
     return env->GetFloatField(obj, fid);
 }
 
-void setFloat(JNIEnv *env, jobject obj, const char *name, jfloat val)
+void setObjFloat(JNIEnv *env, jobject obj, const char *name, jfloat val)
 {
     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "F");
     env->SetFloatField(obj, fid, val);
 }
 
-jdouble getDouble(JNIEnv *env, jobject obj, const char *name)
+jdouble getObjDouble(JNIEnv *env, jobject obj, const char *name)
 {
     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "D");
     return env->GetDoubleField(obj, fid);
 }
 
-void setDouble(JNIEnv *env, jobject obj, const char *name, jdouble val)
+void setObjDouble(JNIEnv *env, jobject obj, const char *name, jdouble val)
 {
     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "D");
     env->SetDoubleField(obj, fid, val);
 }
 
-String getString(JNIEnv *env, jobject obj, const char *name)
+String getObjString(JNIEnv *env, jobject obj, const char *name)
 {
     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "Ljava/lang/String;");
     jstring jstr = (jstring)env->GetObjectField(obj, fid);
-    const char *chars = env->GetStringUTFChars(jstr, JNI_FALSE);
-    String str = chars;
-    env->ReleaseStringUTFChars(jstr, chars);
-    return str;
+    return getString(env, jstr);
 }
 
-void setString(JNIEnv *env, jobject obj, const char *name, const String &val)
+void setObjString(JNIEnv *env, jobject obj, const char *name, const String &val)
 {
     jstring jstr = env->NewStringUTF(val.c_str());
     jfieldID fid = env->GetFieldID(env->GetObjectClass(obj), name, "Ljava/lang/String;");
@@ -315,32 +332,84 @@ static const char *commonJavaPaths[] =
     NULL
 };
 
+
+/**
+ * Return the directory of the .exe that is currently running
+ */
+static String getExePath()
+{
+    char exeName[MAX_PATH+1];
+    GetModuleFileName(NULL, exeName, MAX_PATH);
+    char *slashPos = strrchr(exeName, '\\');
+    if (slashPos)
+        *slashPos = '\0';
+    String s = exeName;
+    return s;
+}
+
+
+/**
+ * Check a directory for several possibilities of sub-locations
+ * under it, where a jvm might exist.
+ */
+static String checkPathUnderRoot(const String &root)
+{
+    for (const char **path = commonJavaPaths ; *path ; path++)
+        {
+        String jpath = root;
+        jpath.append(*path);
+        //msg("trying '%s'", jpath.c_str());
+        struct stat finfo;
+        if (stat(jpath.c_str(), &finfo)>=0)
+            {
+            //msg("found");
+            return jpath;
+            }
+        }
+    return "";
+}
+
+
+
+/**
+ * Attempt to find and load a jvm.dll file.  Find the createVM()
+ * function's address and return it
+ */
 static CreateVMFunc getCreateVMFunc()
 {
     bool found = false;
     String libname;
 
     /**
-     * First, look for JAVA_HOME.  This will allow the user
+     * First, look for an embedded jre in the .exe's dir.
+     * This allows us to package our own JRE if we want to.
+     */
+    String inkscapeHome = getExePath();
+    inkscapeHome.append("\\jre");
+    msg("INKSCAPE_HOME='%s'", inkscapeHome.c_str());
+    String path = checkPathUnderRoot(inkscapeHome);
+    if (path.size() > 0)
+        {
+        libname = path;
+        found = true;
+        }
+
+    /**
+     * Next, look for JAVA_HOME.  This will allow the user
      * to override what's in the registry
-     */                     
-    const char *envStr = getenv("JAVA_HOME");
-    if (envStr)
+     */
+    if (!found)
         {
-        String javaHome = cleanPath(envStr);
-        msg("JAVA_HOME='%s'", javaHome.c_str());
-        for (const char **path = commonJavaPaths ; *path ; path++)
+        const char *envStr = getenv("JAVA_HOME");
+        if (envStr)
             {
-            String jpath = javaHome;
-            jpath.append(*path);
-            //msg("trying '%s'", jpath.c_str());
-            struct stat finfo;
-            if (stat(jpath.c_str(), &finfo)>=0)
+            String javaHome = cleanPath(envStr);
+            msg("JAVA_HOME='%s'", javaHome.c_str());
+            path = checkPathUnderRoot(javaHome);
+            if (path.size() > 0)
                 {
-                //msg("found");
-                libname = jpath;
+                libname = path;
                 found = true;
-                break;
                 }
             }
         }
@@ -405,17 +474,20 @@ static CreateVMFunc getCreateVMFunc()
     return createVM;
 }
 
+/**
+ * Return the directory where the Java classes/libs/resources are
+ * located
+ */
 static void getJavaRoot(String &javaroot)
 {
-    char exeName[80];
-    GetModuleFileName(NULL, exeName, 80);
-    char *slashPos = strrchr(exeName, '\\');
-    if (slashPos)
-        *slashPos = '\0';
-    javaroot = exeName;
+    /*
+       javaroot = getExePath();
     javaroot.append("\\");
     javaroot.append(INKSCAPE_BINDDIR);
     javaroot.append("\\java");
+    */
+    javaroot = INKSCAPE_BINDDIR;
+    javaroot.append("\\java");
 }
 
 
@@ -473,6 +545,10 @@ static bool findJVMRecursive(const String &dirpath,
 }
 
 
+/**
+ * Some common places on a Unix filesystem where JVMs are
+ * often found.
+ */
 static const char *commonJavaPaths[] =
 {
     "/usr/lib/jvm/jre",
@@ -484,6 +560,8 @@ static const char *commonJavaPaths[] =
     NULL
 };
 
+
+
 /**
  * Look for a Java VM (libjvm.so) in several Unix places
  */
@@ -527,6 +605,10 @@ static bool findJVM(String &result)
 
 
 
+/**
+ * Attempt to find and load a jvm.dll file.  Find the createVM()
+ * function's address and return it
+ */
 static CreateVMFunc getCreateVMFunc()
 {
     String libname;
@@ -552,6 +634,10 @@ static CreateVMFunc getCreateVMFunc()
 }
 
 
+/**
+ * Return the directory where the Java classes/libs/resources are
+ * located
+ */
 static void getJavaRoot(String &javaroot)
 {
     javaroot = INKSCAPE_BINDDIR;
@@ -635,6 +721,32 @@ static void populateClassPath(const String &javaroot,
 //========================================================================
 // Gateway
 //========================================================================
+/**
+ * This is provided to scripts can grab the current copy or the
+ * repr tree.  If anyone has a smarter way of doing this, please implement. 
+ */    
+jstring JNICALL documentGet(JNIEnv *env, jobject /*obj*/, jlong /*ptr*/)
+{
+    //JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
+    String buf =  sp_repr_save_buf((SP_ACTIVE_DOCUMENT)->rdoc);
+    jstring jstr = env->NewStringUTF(buf.c_str());
+    return jstr;
+}
+
+/**
+ * This is provided to scripts can load an XML tree into Inkscape.
+ * If anyone has a smarter way of doing this, please implement. 
+ */    
+jboolean JNICALL documentSet(JNIEnv */*env*/, jobject /*obj*/, jlong /*ptr*/, jstring /*jstr*/)
+{
+    /*
+    JavaBinderyImpl *bind = (JavaBinderyImpl *)ptr;
+    String s = getString(env, jstr);
+    SPDocument *doc = sp_document_new_from_mem(s.c_str(), s.size(), true);
+    */
+    return JNI_TRUE;
+}
+
 /**
  * This method is used to allow the gateway class to
  * redirect its logging stream here.
@@ -649,7 +761,9 @@ void JNICALL logWrite(JNIEnv */*env*/, jobject /*obj*/, jlong ptr, jint ch)
 
 static JNINativeMethod gatewayMethods[] =
 {
-{ (char *)"logWrite",    (char *)"(JI)V", (void *)logWrite    },
+{ (char *)"documentGet", (char *)"(J)Ljava/lang/String;",  (void *)documentGet },
+{ (char *)"documentSet", (char *)"(JLjava/lang/String;)Z", (void *)documentSet },
+{ (char *)"logWrite",    (char *)"(JI)V",                  (void *)logWrite    },
 { NULL,  NULL, NULL }
 };
 
@@ -750,6 +864,7 @@ bool JavaBinderyImpl::showConsole()
 static int JNICALL vfprintfHook(FILE* /*f*/, const char *fmt, va_list args)
 {
     g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, args);
+    return JNI_TRUE;
 }
 
 
@@ -812,8 +927,14 @@ bool JavaBinderyImpl::loadJVM()
     int versionMinor = (vers    ) & 0xffff;
     msg("Loaded JVM version %d.%d", versionMajor, versionMinor);
 
-    if (!setupGateway())
+    if (!setupGateway()) {
+        // set jvm = NULL, otherwise, this method will return true when called for the second time while the gateway might not have been created!
+        jvm->DestroyJavaVM();
+        jvm = NULL;
+        env = NULL;
+        err("Java bindings: setupGateway() failed");
         return false;
+    }
 
     return true;
 }
@@ -889,6 +1010,7 @@ bool JavaBinderyImpl::callStatic(int type,
             default:
                 {
                 err("Unknown value type: %d", v.getType());
+                delete [] jvals;
                 return false;
                 }
             }
@@ -936,7 +1058,7 @@ bool JavaBinderyImpl::callStatic(int type,
             return false;
             }
         }
-    delete jvals;
+    delete [] jvals;
     String errStr = getException();
     if (errStr.size()>0)
         {
@@ -1010,6 +1132,7 @@ bool JavaBinderyImpl::callInstance(
             default:
                 {
                 err("Unknown value type: %d", v.getType());
+                delete [] jvals;
                 return false;
                 }
             }
@@ -1057,7 +1180,7 @@ bool JavaBinderyImpl::callInstance(
             return false;
             }
         }
-    delete jvals;
+    delete [] jvals;
     String errStr = getException();
     if (errStr.size()>0)
         {