view libinterp/octave-value/ov-java.cc @ 21691:263d18409fdf

Eliminate unused variable warnings for conditionally compiled code. We had more or less decided not to bother trying to eliminate all these warnings for cases in which external dependencies are missing. But then we get people trying to fix these in various ways, so we might as well do it for all cases and use a consistent method. * oct-conf-post.in.h (octave_unused_parameter): New function for C++ code and new macro for C code. * mk-octave-config-h.sh: Emit octave_unused_parameter function and macro for octave-config.h. * CSparse.cc, __delaunayn__.cc, __eigs__.cc, __fltk_uigetfile__.cc, __glpk__.cc, __magick_read__.cc, __osmesa_print__.cc, __voronoi__.cc, amd.cc, audiodevinfo.cc, audioread.cc, ccolamd.cc, cdisplay.c, colamd.cc, convhulln.cc, dSparse.cc, dmperm.cc, fftw.cc, gl-render.cc, lo-error.c, load-save.cc, ls-hdf5.cc, ls-mat5.cc, oct-hdf5-types.cc, ov-base-int.cc, ov-bool-mat.cc, ov-bool-sparse.cc, ov-bool.cc, ov-cell.cc, ov-class.cc, ov-complex.cc, ov-cx-mat.cc, ov-cx-sparse.cc, ov-fcn-handle.cc, ov-fcn-inline.cc, ov-float.cc, ov-flt-complex.cc, ov-flt-cx-mat.cc, ov-flt-re-mat.cc, ov-java.cc, ov-range.cc, ov-re-mat.cc, ov-re-sparse.cc, ov-scalar.cc, ov-str-mat.cc, ov-struct.cc, sparse-chol.cc, sparse-dmsolve.cc, sparse-lu.cc, sparse-qr.cc, sparse-util.cc, symbfact.cc: Use octave_unused_parameter to eliminate warnings for conditionally compiled code.
author John W. Eaton <jwe@octave.org>
date Fri, 13 May 2016 09:36:14 -0400
parents 1b9a36a66b01
children aba2e6293dd8
line wrap: on
line source

/*

Copyright (C) 2007, 2013 Michael Goffioul

This file is part of Octave.

Octave is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version.

Octave is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with Octave; see the file COPYING.  If not, see
<http://www.gnu.org/licenses/>.

*/

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include "defun.h"
#include "error.h"
#include "errwarn.h"
#include "fpucw.h"

#if defined (HAVE_FPU_CONTROL_H)
#  include <fpu_control.h>
#endif

#if defined (HAVE_WINDOWS_H)
#  include <windows.h>
#endif

#include <algorithm>
#include <map>
#include <iostream>
#include <fstream>
#include <string>

#include <clocale>

#include "Cell.h"
#include "cmd-edit.h"
#include "defaults.h"
#include "file-ops.h"
#include "file-stat.h"
#include "load-path.h"
#include "oct-env.h"
#include "oct-shlib.h"
#include "ov-java.h"
#include "parse.h"
#include "variables.h"

#if defined (HAVE_JAVA)
#include <jni.h>
#endif

#if defined (HAVE_JAVA)

#define TO_JOBJECT(obj) reinterpret_cast<jobject> (obj)
#define TO_JCLASS(obj) reinterpret_cast<jclass> (obj)

#define TO_JNIENV(env) reinterpret_cast<JNIEnv *> (env)

typedef jint (JNICALL *JNI_CreateJavaVM_t) (JavaVM **pvm, JNIEnv **penv,
                                            void *args);

typedef jint (JNICALL *JNI_GetCreatedJavaVMs_t) (JavaVM **pvm, jsize bufLen,
                                                 jsize *nVMs);

template <typename T>
class java_local_ref
{
public:

  java_local_ref (JNIEnv *_env)
    : jobj (0), detached (false), env (_env) { }

  java_local_ref (JNIEnv *_env, T obj)
    : jobj (obj), detached (false), env (_env) { }

  ~java_local_ref (void) { release (); }

  T& operator = (T obj)
  {
    release ();

    jobj = obj;
    detached = false;

    return jobj;
  }

  operator bool () const { return (jobj != 0); }
  operator T () { return jobj; }

  void detach (void) { detached = true; }

private:

  void release (void)
  {
    if (env && jobj && ! detached)
      env->DeleteLocalRef (jobj);

    jobj = 0;
  }

  java_local_ref (void)
    : jobj (0), detached (false), env (0)
  { }

protected:

  T jobj;
  bool detached;
  JNIEnv *env;
};

typedef java_local_ref<jobject> jobject_ref;
typedef java_local_ref<jclass> jclass_ref;
typedef java_local_ref<jstring> jstring_ref;
typedef java_local_ref<jobjectArray> jobjectArray_ref;
typedef java_local_ref<jintArray> jintArray_ref;
typedef java_local_ref<jbyteArray> jbyteArray_ref;
typedef java_local_ref<jdoubleArray> jdoubleArray_ref;
typedef java_local_ref<jthrowable> jthrowable_ref;

static std::string
jstring_to_string (JNIEnv* jni_env, jstring s);

static std::string
jstring_to_string (JNIEnv* jni_env, jobject obj);

static octave_value
box (JNIEnv* jni_env, void *jobj, void *jcls_arg = 0);

static octave_value
box_more (JNIEnv* jni_env, void *jobj_arg, void *jcls_arg = 0);

static bool
unbox (JNIEnv* jni_env, const octave_value& val, jobject_ref& jobj,
       jclass_ref& jcls);

static bool
unbox (JNIEnv* jni_env, const octave_value_list& args,
       jobjectArray_ref& jobjs, jobjectArray_ref& jclss);

extern "C"
{
  JNIEXPORT jboolean JNICALL
  Java_org_octave_Octave_call (JNIEnv *, jclass, jstring, jobjectArray,
                               jobjectArray);

  JNIEXPORT void JNICALL
  Java_org_octave_OctaveReference_doFinalize (JNIEnv *, jclass, jint);

  JNIEXPORT void JNICALL
  Java_org_octave_Octave_doInvoke (JNIEnv *, jclass, jint, jobjectArray);

  JNIEXPORT void JNICALL
  Java_org_octave_Octave_doEvalString (JNIEnv *, jclass, jstring);

  JNIEXPORT jboolean JNICALL
  Java_org_octave_Octave_needThreadedInvokation (JNIEnv *, jclass);
}

static JavaVM *jvm = 0;
static bool jvm_attached = false;

// Need to keep hold of the shared library handle until exit.
static octave_shlib jvm_lib;

static std::map<int,octave_value> listener_map;
static std::map<int,octave_value> octave_ref_map;
static int octave_java_refcount = 0;
static long octave_thread_ID = -1;

bool Vjava_matrix_autoconversion = false;
bool Vjava_unsigned_autoconversion = true;
bool Vdebug_java = false;

class JVMArgs
{
public:

  JVMArgs (void)
  {
    vm_args.version = JNI_VERSION_1_2;
    vm_args.nOptions = 0;
    vm_args.options = 0;
    vm_args.ignoreUnrecognized = false;
  }

  ~JVMArgs (void)
  {
    clean ();
  }

  JavaVMInitArgs *to_args ()
  {
    update ();
    return &vm_args;
  }

  void add (const std::string& opt)
  {
    java_opts.push_back (opt);
  }

  void read_java_opts (const std::string& filename)
  {
    std::ifstream js (filename.c_str ());

    if (! js.bad () && ! js.fail ())
      {
        std::string line;

        while (! js.eof () && ! js.fail ())
          {
            std::getline (js, line);

            if (line.find ("-") == 0)
              java_opts.push_back (line);
            else if (line.length () > 0 && Vdebug_java)
              std::cerr << "invalid JVM option, skipping: " << line << std::endl;
          }
      }
  }

private:

  void clean (void)
  {
    if (vm_args.options != 0)
      {
        for (int i = 0; i < vm_args.nOptions; i++)
          delete [] vm_args.options[i].optionString;

        delete [] vm_args.options;

        vm_args.options = 0;
        vm_args.nOptions = 0;
      }
  }

  void update (void)
  {
    clean ();

    if (java_opts.size () > 0)
      {
        int index = 0;

        vm_args.nOptions = java_opts.size ();
        vm_args.options = new JavaVMOption [vm_args.nOptions];

        for (std::list<std::string>::const_iterator it = java_opts.begin ();
             it != java_opts.end (); ++it)
          {
            if (Vdebug_java)
              std::cout << *it << std::endl;
            vm_args.options[index++].optionString = strsave ((*it).c_str ());
          }

        java_opts.clear ();
      }
  }

private:

  JavaVMInitArgs vm_args;

  std::list<std::string> java_opts;
};

#ifdef __WIN32__

static std::string
read_registry_string (const std::string& key, const std::string& value)
{
  HKEY hkey;
  DWORD len;

  std::string retval;

  if (! RegOpenKeyEx (HKEY_LOCAL_MACHINE, key.c_str (), 0, KEY_READ, &hkey))
    {
      if (! RegQueryValueEx (hkey, value.c_str (), 0, 0, 0, &len))
        {
          retval.resize (len);
          if (RegQueryValueEx (hkey, value.c_str (), 0, 0,
                               (LPBYTE)&retval[0], &len))
            retval = "";
          else if (retval[len-1] == '\0')
            retval.resize (--len);
        }
      RegCloseKey (hkey);
    }

  return retval;
}

static std::string
get_module_filename (HMODULE hMod)
{
  int n = 1024;
  std::string retval (n, '\0');
  bool found = false;

  while (n < 65536)
    {
      int status = GetModuleFileName (hMod, &retval[0], n);

      if (status < n)
        {
          retval.resize (n);
          found = true;
          break;
        }
      else
        {
          n *= 2;
          retval.resize (n);
        }
    }

  return (found ? retval : "");
}

static void
set_dll_directory (const std::string& dir = "")
{
  typedef BOOL (WINAPI *dllfcn_t) (LPCTSTR path);

  static dllfcn_t dllfcn = 0;
  static bool first = true;

  if (! dllfcn && first)
    {
      HINSTANCE hKernel32 = GetModuleHandle ("kernel32");
      dllfcn = reinterpret_cast<dllfcn_t> (GetProcAddress (hKernel32,
                                           "SetDllDirectoryA"));
      first = false;
    }

  if (dllfcn)
    dllfcn (dir.empty () ? 0 : dir.c_str ());
}

#endif

static std::string
initial_java_dir (void)
{
  static std::string java_dir;

  if (java_dir.empty ())
    {
      java_dir = octave_env::getenv ("OCTAVE_JAVA_DIR");

      if (java_dir.empty ())
        java_dir = Vfcn_file_dir + file_ops::dir_sep_str () + "java";
    }

  return java_dir;
}

// Read the content of a file filename (usually "classpath.txt")
//
// Returns a string with all lines concatenated and separated
// by the path separator character.
// The return string also starts with a path separator so that
// it can be appended easily to a base classpath.
//
// The file "classpath.txt" must contain single lines, each
// with a classpath.
// Comment lines starting with a '#' or a '%' in column 1 are allowed.

static std::string
read_classpath_txt (const std::string& filepath)
{
  std::string classpath;

  std::ifstream fs (filepath.c_str ());

  if (! fs.bad () && ! fs.fail ())
    {
      std::string line;

      while (! fs.eof () && ! fs.fail ())
        {
          std::getline (fs, line);

          if (line.length () > 0)
            {
              if (line[0] == '#' || line[0] == '%')
                ; // skip comments
              else
                {
                  // prepend separator character
                  classpath.append (dir_path::path_sep_str ());

                  // append content of line without whitespace
                  int last = line.find_last_not_of (" \t\f\v\r\n");

                  classpath.append (file_ops::tilde_expand (line.substr (0, last+1)));
                }
            }
        }
    }

  return (classpath);
}

static std::string
initial_class_path (void)
{
  std::string java_dir = initial_java_dir ();

  std::string retval = java_dir;

  // find octave.jar file
  if (! retval.empty ())
    {
      std::string sep = file_ops::dir_sep_str ();

      std::string jar_file = java_dir + sep + "octave.jar";

      file_stat jar_exists (jar_file);

      if (jar_exists)
        {
          // initialize static classpath to octave.jar
          retval = jar_file;

          // The base classpath has been set.
          // Try to find an optional file specifying classpaths in 3 places.
          // 1) Current directory
          // 2) User's home directory
          // 3) Octave installation directory where octave.jar resides

          std::string cwd = octave_env::get_current_directory ();
          std::string home_dir = octave_env::get_home_directory ();

          // The filename is "javaclasspath.txt", but historically
          // has been "classpath.txt" so both are supported.
          std::string cp_list[] = {"javaclasspath.txt", "classpath.txt"};

          for (int i=0; i<2; i++)
            {
              std::string filename = cp_list[i];
              std::string cp_file = filename;
              file_stat   cp_exists;

              // Try to find classpath file in the current directory.

              cp_exists = file_stat (cp_file);
              if (cp_exists)
                {
                  // File found.  Add its contents to the static classpath.
                  std::string classpath = read_classpath_txt (cp_file);
                  retval.append (classpath);
                }

              // Try to find classpath file in the user's home directory.

              if (cwd != home_dir)
                {
                  cp_file = "~" + sep + filename;
                  cp_file = file_ops::tilde_expand (cp_file);
                  cp_exists = file_stat (cp_file);
                  if (cp_exists)
                    {
                      // File found.  Add its contents to the static classpath.
                      std::string classpath = read_classpath_txt (cp_file);
                      retval.append (classpath);
                    }
                }

              // Try to find classpath file in the Octave install directory.

              if (cwd != java_dir)
                {
                  cp_file = java_dir + sep + filename;
                  cp_exists = file_stat (cp_file);
                  if (cp_exists)
                    {
                      // File found.  Add its contents to the static classpath.
                      std::string classpath = read_classpath_txt (cp_file);
                      retval.append (classpath);
                    }
                }
            }
        }
      else
        error ("octave.jar does not exist: %s", jar_file.c_str ());
    }
  else
    error ("initial java dir is empty");

  return retval;
}

#ifndef _FPU_DEFAULT
#  if defined __i386__ || defined __x86_64__
#    define _FPU_DEFAULT 0x037f
#  else
#    define _FPU_DEFAULT 0
#  endif
#endif

static void
restore_fpu_state (void)
{
  fpucw_t cw = GET_FPUCW ();

  if (cw != _FPU_DEFAULT)
    SET_FPUCW (_FPU_DEFAULT);
}

static void
initialize_jvm (void)
{
  // Most of the time JVM already exists and has been initialized.
  if (jvm)
    return;

  JNIEnv *current_env;
  const char *static_locale = setlocale (LC_ALL, 0);
  const std::string locale (static_locale);

#if defined (__WIN32__)

  HMODULE hMod = GetModuleHandle ("jvm.dll");
  std::string jvm_lib_path;
  std::string old_cwd;

  if (hMod)
    {
      // JVM seems to be already loaded, better to use that DLL instead
      // of looking in the registry, to avoid opening a different JVM.
      jvm_lib_path = get_module_filename (hMod);

      if (jvm_lib_path.empty ())
        error ("unable to find Java Runtime Environment");
    }
  else
    {
      // In windows, find the location of the JRE from the registry
      // and load the symbol from the dll.
      std::string key, value;

      key = "software\\javasoft\\java runtime environment";

      value = octave_env::getenv ("JAVA_VERSION");
      if (value.empty ())
        {
          value = "Currentversion";
          std::string regval = read_registry_string (key,value);

          if (regval.empty ())
            error ("unable to find Java Runtime Environment: %s::%s",
                   key.c_str (), value.c_str ());

          value = regval;
        }

      key = key + "\\" + value;
      value = "RuntimeLib";
      jvm_lib_path = read_registry_string (key, value);

      if (jvm_lib_path.empty ())
        error ("unable to find Java Runtime Environment: %s::%s",
               key.c_str (), value.c_str ());

      std::string jvm_bin_path;

      value = "JavaHome";
      jvm_bin_path = read_registry_string (key, value);
      if (! jvm_bin_path.empty ())
        {
          jvm_bin_path = (jvm_bin_path + std::string ("\\bin"));

          old_cwd = octave_env::get_current_directory ();

          set_dll_directory (jvm_bin_path);
          octave_env::chdir (jvm_bin_path);
        }
    }

#else

  // JAVA_LDPATH determined by configure and set in config.h
#  if defined (__APPLE__)
  std::string jvm_lib_path = JAVA_LDPATH + std::string ("/libjvm.dylib");
#  else
  std::string jvm_lib_path = JAVA_LDPATH + std::string ("/libjvm.so");
#  endif

#endif

  jsize nVMs = 0;

#if ! defined (__APPLE__) && ! defined (__MACH__)

  octave_shlib lib (jvm_lib_path);

  if (! lib)
    error ("unable to load Java Runtime Environment from %s",
           jvm_lib_path.c_str ());

#if defined (__WIN32__)

  set_dll_directory ();

  if (! old_cwd.empty ())
    octave_env::chdir (old_cwd);

#endif

  JNI_CreateJavaVM_t create_vm =
    reinterpret_cast<JNI_CreateJavaVM_t> (lib.search ("JNI_CreateJavaVM"));
  JNI_GetCreatedJavaVMs_t get_vm =
    reinterpret_cast<JNI_GetCreatedJavaVMs_t> (lib.search ("JNI_GetCreatedJavaVMs"));

  if (! create_vm)
    error ("unable to find JNI_CreateJavaVM in %s", jvm_lib_path.c_str ());

  if (! get_vm)
    error ("unable to find JNI_GetCreatedJavaVMs in %s", jvm_lib_path.c_str ());

  if (get_vm (&jvm, 1, &nVMs) == 0 && nVMs > 0)

#else

  // FIXME: There exists a problem on the Mac platform that
  //   octave_shlib lib (jvm_lib_path)
  // doesn't work with 'not-bundled' *.oct files.

  if (JNI_GetCreatedJavaVMs (&jvm, 1, &nVMs) == 0 && nVMs > 0)

#endif

    {
      // At least one JVM exists, try to attach to it

      switch (jvm->GetEnv (reinterpret_cast<void **> (&current_env),
                           JNI_VERSION_1_2))
        {
        case JNI_EDETACHED:
          // Attach the current thread
          JavaVMAttachArgs vm_args;
          vm_args.version = JNI_VERSION_1_2;
          vm_args.name = const_cast<char *> ("octave");
          vm_args.group = 0;
          if (jvm->AttachCurrentThread (reinterpret_cast<void **> (&current_env),
                                        &vm_args) < 0)
            error ("JVM internal error, unable to attach octave to existing JVM");
          break;

        case JNI_EVERSION:
          error ("JVM internal error, the required JNI version is not supported");
          break;

        case JNI_OK:
          // Don't do anything, the current thread is already attached to JVM
          break;
        }

      jvm_attached = true;
      //printf ("JVM attached\n");
    }
  else
    {
      // No JVM exists, create one

      JVMArgs vm_args;

      vm_args.add ("-Djava.class.path=" + initial_class_path ());
      vm_args.add ("-Xrs");
      vm_args.add ("-Djava.system.class.loader=org.octave.OctClassLoader");
      vm_args.read_java_opts (initial_java_dir () + file_ops::dir_sep_str () +
                              "java.opts");

#if ! defined (__APPLE__) && ! defined (__MACH__)

      if (create_vm (&jvm, &current_env, vm_args.to_args ()) != JNI_OK)
        error ("unable to start Java VM in %s", jvm_lib_path.c_str ());
    }

  jvm_lib = lib;

#else

      if (JNI_CreateJavaVM (&jvm, reinterpret_cast<void **> (&current_env),
                            vm_args.to_args ()) != JNI_OK)
        error ("unable to start Java VM in %s", jvm_lib_path.c_str ());

    }

#endif

  setlocale (LC_ALL, locale.c_str ());
}

static void
terminate_jvm (void)
{
  if (jvm)
    {
      if (jvm_attached)
        jvm->DetachCurrentThread ();
      else
        jvm->DestroyJavaVM ();

      jvm = 0;
      jvm_attached = false;

      if (jvm_lib)
        jvm_lib.close ();

      restore_fpu_state ();
    }
}

static std::string
jstring_to_string (JNIEnv *jni_env, jstring s)
{
  std::string retval;

  if (jni_env)
    {
      const char *cstr = jni_env->GetStringUTFChars (s, 0);
      retval = cstr;
      jni_env->ReleaseStringUTFChars (s, cstr);
    }

  return retval;
}

static std::string
jstring_to_string (JNIEnv *jni_env, jobject obj)
{
  std::string retval;

  if (jni_env && obj)
    {
      jclass_ref cls (jni_env, jni_env->FindClass ("java/lang/String"));
      if (cls)
        {
          if (jni_env->IsInstanceOf (obj, cls))
            retval = jstring_to_string (jni_env,
                                        reinterpret_cast<jstring> (obj));
        }
    }

  return retval;
}

static inline JNIEnv *
thread_jni_env (void)
{
  JNIEnv *env = 0;

  if (jvm)
    jvm->GetEnv (reinterpret_cast<void **> (&env), JNI_VERSION_1_2);

  return env;
}

#endif

bool
octave_java::is_java_string (void) const
{
#if defined (HAVE_JAVA)

  JNIEnv *current_env = thread_jni_env ();

  if (current_env && java_object)
    {
      jclass_ref cls (current_env, current_env->FindClass ("java/lang/String"));
      return current_env->IsInstanceOf (TO_JOBJECT (java_object), cls);
    }

  return false;

#else

  // This shouldn't happen because construction of octave_java
  // objects is supposed to be impossible if Java is not available.

  panic_impossible ();

#endif
}

bool
octave_java::is_instance_of (const std::string& cls_name) const
{
#if defined (HAVE_JAVA)

  JNIEnv *current_env = thread_jni_env ();

  std::string cls_cpp = cls_name;
  std::replace (cls_cpp.begin (), cls_cpp.end (), '.', '/');

  if (current_env && java_object)
    {
      jclass_ref cls (current_env, current_env->FindClass (cls_cpp.c_str ()));
      if (current_env->ExceptionCheck ())
        current_env->ExceptionClear ();
      else
        return current_env->IsInstanceOf (TO_JOBJECT (java_object), cls);
    }
  return false;

#else

  octave_unused_parameter (cls_name);

  // This shouldn't happen because construction of octave_java
  // objects is supposed to be impossible if Java is not available.

  panic_impossible ();

#endif
}

#if defined (HAVE_JAVA)

static octave_value
check_exception (JNIEnv *jni_env)
{
  octave_value retval;

  jthrowable_ref ex (jni_env, jni_env->ExceptionOccurred ());

  if (ex)
    {
      if (Vdebug_java)
        jni_env->ExceptionDescribe ();

      jni_env->ExceptionClear ();

      jclass_ref jcls (jni_env, jni_env->GetObjectClass (ex));
      jmethodID mID = jni_env->GetMethodID (jcls, "toString",
                                            "()Ljava/lang/String;");
      jstring_ref js (jni_env,
                      reinterpret_cast<jstring> (jni_env->CallObjectMethod (ex, mID)));
      std::string msg = jstring_to_string (jni_env, js);

      error ("[java] %s", msg.c_str ());
    }
  else
    retval = Matrix ();

  return retval;
}

static jclass
find_octave_class (JNIEnv *jni_env, const char *name)
{
  static std::string class_loader;
  static jclass uiClass = 0;

  jclass jcls = jni_env->FindClass (name);

  if (jcls == 0)
    {
      jni_env->ExceptionClear ();

      if (! uiClass)
        {
          if (class_loader.empty ())
            {
              jclass_ref syscls (jni_env,
                                 jni_env->FindClass ("java/lang/System"));
              jmethodID mID = jni_env->GetStaticMethodID (syscls, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;");
              jstring_ref js (jni_env, jni_env->NewStringUTF ("octave.class.loader"));
              js = reinterpret_cast<jstring> (jni_env->CallStaticObjectMethod (syscls, mID, jstring (js)));
              class_loader = jstring_to_string (jni_env, jstring (js));
              std::replace (class_loader.begin (), class_loader.end (),
                            '.', '/');
            }

          jclass_ref uicls (jni_env, jni_env->FindClass (class_loader.c_str ()));

          if (! uicls)
            {
              jni_env->ExceptionClear ();

              // Try the netbeans way
              std::replace (class_loader.begin (), class_loader.end (),
                            '/', '.');
              jclass_ref jcls2 (jni_env, jni_env->FindClass ("org/openide/util/Lookup"));
              jmethodID mID = jni_env->GetStaticMethodID (jcls2, "getDefault", "()Lorg/openide/util/Lookup;");
              jobject_ref lObj (jni_env, jni_env->CallStaticObjectMethod (jcls2, mID));
              mID = jni_env->GetMethodID (jcls2, "lookup",
                                          "(Ljava/lang/Class;)Ljava/lang/Object;");
              jclass_ref cLoaderCls (jni_env, jni_env->FindClass ("java/lang/ClassLoader"));
              jobject_ref cLoader (jni_env, jni_env->CallObjectMethod (lObj, mID, jclass (cLoaderCls)));
              mID = jni_env->GetMethodID (cLoaderCls, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
              jstring_ref js (jni_env, jni_env->NewStringUTF (class_loader.c_str ()));
              uicls = reinterpret_cast<jclass> (jni_env->CallObjectMethod (cLoader, mID, jstring (js)));
            }

          if (uicls)
            uiClass = reinterpret_cast<jclass> (jni_env->NewGlobalRef (jclass (uicls)));
        }

      if (uiClass)
        {
          jmethodID mID = jni_env->GetStaticMethodID (uiClass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
          jstring_ref js (jni_env, jni_env->NewStringUTF (name));
          jcls = reinterpret_cast<jclass> (jni_env->CallStaticObjectMethod (uiClass, mID, jstring (js)));
        }
    }

  return jcls;
}

static dim_vector
compute_array_dimensions (JNIEnv *jni_env, jobject obj)
{
  jobjectArray_ref jobj (jni_env, reinterpret_cast<jobjectArray> (obj));
  jclass_ref jcls (jni_env, jni_env->GetObjectClass (obj));
  jclass_ref ccls (jni_env, jni_env->GetObjectClass (jcls));
  jmethodID isArray_ID = jni_env->GetMethodID (ccls, "isArray", "()Z");
  jmethodID getComponentType_ID = jni_env->GetMethodID (ccls, "getComponentType", "()Ljava/lang/Class;");

  dim_vector dv (1, 1);
  int idx = 0;

  jobj.detach ();
  while (jcls && jni_env->CallBooleanMethod (jcls, isArray_ID))
    {
      int len = (jobj ? jni_env->GetArrayLength (jobj) : 0);
      if (idx >= dv.ndims ())
        dv.resize (idx+1);
      dv(idx) = len;
      jcls = reinterpret_cast<jclass> (jni_env->CallObjectMethod (jcls, getComponentType_ID));
      jobj = (len > 0 ? reinterpret_cast<jobjectArray> (jni_env->GetObjectArrayElement (jobj, 0)) : 0);
      idx++;
    }

  restore_fpu_state ();

  return dv;
}

static jobject
make_java_index (JNIEnv *jni_env, const octave_value_list& idx)
{
  jclass_ref ocls (jni_env, jni_env->FindClass ("[I"));
  jobjectArray retval = jni_env->NewObjectArray (idx.length (), ocls, 0);

  for (int i = 0; i < idx.length (); i++)
    try
      {
        idx_vector v = idx(i).index_vector ();

        jintArray_ref i_array (jni_env, jni_env->NewIntArray (v.length ()));
        jint *buf = jni_env->GetIntArrayElements (i_array, 0);

        for (int k = 0; k < v.length (); k++)
          buf[k] = v(k);

        jni_env->ReleaseIntArrayElements (i_array, buf, 0);
        jni_env->SetObjectArrayElement (retval, i, i_array);

        check_exception (jni_env);
      }
    catch (index_exception& e)
      {
        // Rethrow to allow more info to be reported later.
        e.set_pos_if_unset (idx.length (), i+1);
        throw;
      }

  return retval;
}

static octave_value
get_array_elements (JNIEnv *jni_env, jobject jobj,
                    const octave_value_list& idx)
{
  octave_value retval;
  jobject_ref resObj (jni_env);
  jobject_ref java_idx (jni_env, make_java_index (jni_env, idx));

  jclass_ref helperClass (jni_env, find_octave_class (jni_env, "org/octave/ClassHelper"));
  jmethodID mID = jni_env->GetStaticMethodID (helperClass, "arraySubsref", "(Ljava/lang/Object;[[I)Ljava/lang/Object;");
  resObj = jni_env->CallStaticObjectMethod (helperClass, mID, jobj, jobject (java_idx));

  if (resObj)
    retval = box (jni_env, resObj);
  else
    retval = check_exception (jni_env);

  restore_fpu_state ();

  return retval;
}

static octave_value
set_array_elements (JNIEnv *jni_env, jobject jobj,
                    const octave_value_list& idx, const octave_value& rhs)
{
  octave_value retval;

  jclass_ref rhsCls (jni_env);
  jobject_ref resObj (jni_env);
  jobject_ref rhsObj (jni_env);
  jobject_ref java_idx (jni_env, make_java_index (jni_env, idx));

  if (unbox (jni_env, rhs, rhsObj, rhsCls))
    {
      jclass_ref helperClass (jni_env, find_octave_class (jni_env, "org/octave/ClassHelper"));
      jmethodID mID = jni_env->GetStaticMethodID (helperClass, "arraySubsasgn",
          "(Ljava/lang/Object;[[ILjava/lang/Object;)Ljava/lang/Object;");
      resObj = jni_env->CallStaticObjectMethod (helperClass, mID,
          jobj, jobject (java_idx), jobject (rhsObj));
    }

  if (resObj)
    retval = box (jni_env, resObj);
  else
    retval = check_exception (jni_env);

  restore_fpu_state ();

  return retval;
}

static string_vector
get_invoke_list (JNIEnv *jni_env, void *jobj_arg)
{
  jobject jobj = TO_JOBJECT (jobj_arg);

  std::list<std::string> name_list;

  if (jni_env)
    {
      jclass_ref cls (jni_env, jni_env->GetObjectClass (jobj));
      jclass_ref ccls (jni_env, jni_env->GetObjectClass (cls));
      jmethodID getMethods_ID = jni_env->GetMethodID (ccls, "getMethods", "()[Ljava/lang/reflect/Method;");
      jmethodID getFields_ID = jni_env->GetMethodID (ccls, "getFields", "()[Ljava/lang/reflect/Field;");
      jobjectArray_ref mList (jni_env, reinterpret_cast<jobjectArray> (jni_env->CallObjectMethod (cls, getMethods_ID)));
      jobjectArray_ref fList (jni_env, reinterpret_cast<jobjectArray> (jni_env->CallObjectMethod (cls, getFields_ID)));
      int mLen = jni_env->GetArrayLength (mList);
      int fLen = jni_env->GetArrayLength (fList);
      jclass_ref mCls (jni_env, jni_env->FindClass ("java/lang/reflect/Method"));
      jclass_ref fCls (jni_env, jni_env->FindClass ("java/lang/reflect/Field"));
      jmethodID m_getName_ID = jni_env->GetMethodID (mCls, "getName", "()Ljava/lang/String;");
      jmethodID f_getName_ID = jni_env->GetMethodID (fCls, "getName", "()Ljava/lang/String;");

      for (int i = 0; i < mLen; i++)
        {
          jobject_ref meth (jni_env, jni_env->GetObjectArrayElement (mList, i));
          jstring_ref methName (jni_env, reinterpret_cast<jstring> (jni_env->CallObjectMethod (meth, m_getName_ID)));
          name_list.push_back (jstring_to_string (jni_env, methName));
        }

      for (int i = 0; i < fLen; i++)
        {
          jobject_ref field (jni_env, jni_env->GetObjectArrayElement (fList, i));
          jstring_ref fieldName (jni_env, reinterpret_cast<jstring> (jni_env->CallObjectMethod (field, f_getName_ID)));
          name_list.push_back (jstring_to_string (jni_env, fieldName));
        }

      restore_fpu_state ();
    }

  string_vector v (name_list);

  return v.sort (true);
}

static octave_value
convert_to_string (JNIEnv *jni_env, jobject java_object, bool force, char type)
{
  octave_value retval;

  if (jni_env && java_object)
    {
      jclass_ref cls (jni_env, jni_env->FindClass ("java/lang/String"));

      if (jni_env->IsInstanceOf (java_object, cls))
        retval = octave_value (jstring_to_string (jni_env, java_object), type);
      else if (force)
        {
          cls = jni_env->FindClass ("[Ljava/lang/String;");

          if (jni_env->IsInstanceOf (java_object, cls))
            {
              jobjectArray array = reinterpret_cast<jobjectArray> (java_object);
              int len = jni_env->GetArrayLength (array);
              Cell c (len, 1);

              for (int i = 0; i < len; i++)
                {
                  jstring_ref js (jni_env, reinterpret_cast<jstring> (jni_env->GetObjectArrayElement (array, i)));

                  if (js)
                    c(i) = octave_value (jstring_to_string (jni_env, js), type);
                  else
                    c(i) = check_exception (jni_env);
                }

              retval = octave_value (c);
            }
          else
            {
              cls = jni_env->FindClass ("java/lang/Object");
              jmethodID mID = jni_env->GetMethodID (cls, "toString", "()Ljava/lang/String;");
              jstring_ref js (jni_env, reinterpret_cast<jstring> (jni_env->CallObjectMethod (java_object, mID)));

              if (js)
                retval = octave_value (jstring_to_string (jni_env, js), type);
              else
                retval = check_exception (jni_env);
            }
        }
      else
        error ("unable to convert Java object to string");

      restore_fpu_state ();
    }

  return retval;
}

#define TO_JAVA(obj) dynamic_cast<octave_java*> ((obj).internal_rep ())

static octave_value
box (JNIEnv *jni_env, void *jobj_arg, void *jcls_arg)
{
  octave_value retval;

  jobject jobj = TO_JOBJECT (jobj_arg);
  jclass jcls = TO_JCLASS (jcls_arg);

  jclass_ref cls (jni_env);

  if (! jobj)
    retval = Matrix ();

  while (retval.is_undefined ())
    {
      // Convert a scalar of any numeric class (byte, short, integer, long,
      // float, double) to a double value.  Matlab does the same thing.
      cls = jni_env->FindClass ("java/lang/Number");
      if (jni_env->IsInstanceOf (jobj, cls))
        {
          jmethodID m = jni_env->GetMethodID (cls, "doubleValue", "()D");
          retval = jni_env->CallDoubleMethod (jobj, m);
          break;
        }

      cls = jni_env->FindClass ("java/lang/Boolean");
      if (jni_env->IsInstanceOf (jobj, cls))
        {
          jmethodID m = jni_env->GetMethodID (cls, "booleanValue", "()Z");
          retval = (jni_env->CallBooleanMethod (jobj, m) ? true : false);
          break;
        }

      cls = jni_env->FindClass ("java/lang/String");
      if (jni_env->IsInstanceOf (jobj, cls))
        {
          retval = jstring_to_string (jni_env, jobj);
          break;
        }

      cls = jni_env->FindClass ("java/lang/Character");
      if (jni_env->IsInstanceOf (jobj, cls))
        {
          jmethodID m = jni_env->GetMethodID (cls, "charValue", "()C");
          retval = jni_env->CallCharMethod (jobj, m);
          retval = retval.convert_to_str (false, true);
          break;
        }

#define BOX_PRIMITIVE_ARRAY(JAVA_TYPE, JAVA_ID, JAVA_TYPE_CAP, OCTAVE_ID) \
      cls = jni_env->FindClass (JAVA_ID); \
      if (jni_env->IsInstanceOf (jobj, cls)) \
        { \
          const JAVA_TYPE ## Array jarr = reinterpret_cast<JAVA_TYPE ## Array> (jobj); \
          const jsize len = jni_env->GetArrayLength (jarr); \
          OCTAVE_ID ## NDArray d (dim_vector (len, 1)); \
          JAVA_TYPE *buffer = reinterpret_cast<JAVA_TYPE *> (d.fortran_vec ()); \
          jni_env->Get ## JAVA_TYPE_CAP ## ArrayRegion (jarr, 0, len, buffer); \
          retval = d; \
          break; \
        }

      BOX_PRIMITIVE_ARRAY (jdouble,  "[D", Double,  )
      BOX_PRIMITIVE_ARRAY (jboolean, "[Z", Boolean, bool)
      BOX_PRIMITIVE_ARRAY (jfloat,   "[F", Float,   Float)
      BOX_PRIMITIVE_ARRAY (jchar,    "[C", Char,    char)
      BOX_PRIMITIVE_ARRAY (jbyte,    "[B", Byte,    int8)
      BOX_PRIMITIVE_ARRAY (jshort,   "[S", Short,   int16)
      BOX_PRIMITIVE_ARRAY (jint,     "[I", Int,     int32)
      BOX_PRIMITIVE_ARRAY (jlong,    "[J", Long,    int64)

#undef BOX_PRIMITIVE_ARRAY

      if (Vjava_matrix_autoconversion)
        {
          cls = find_octave_class (jni_env, "org/octave/Matrix");

          if (jni_env->IsInstanceOf (jobj, cls))
            {
              jmethodID mID = jni_env->GetMethodID (cls, "getDims", "()[I");
              jintArray_ref iv (jni_env, reinterpret_cast<jintArray> (jni_env->CallObjectMethod (jobj, mID)));
              jint *iv_data = jni_env->GetIntArrayElements (jintArray (iv), 0);
              dim_vector dims;
              dims.resize (jni_env->GetArrayLength (jintArray (iv)));

              for (int i = 0; i < dims.ndims (); i++)
                dims(i) = iv_data[i];

              jni_env->ReleaseIntArrayElements (jintArray (iv), iv_data, 0);
              mID = jni_env->GetMethodID (cls, "getClassName", "()Ljava/lang/String;");
              jstring_ref js (jni_env, reinterpret_cast<jstring> (jni_env->CallObjectMethod (jobj, mID)));

              std::string s = jstring_to_string (jni_env, js);

              if (s == "double")
                {
                  NDArray m (dims);
                  mID = jni_env->GetMethodID (cls, "toDouble", "()[D");
                  jdoubleArray_ref dv (jni_env, reinterpret_cast<jdoubleArray> (jni_env->CallObjectMethod (jobj, mID)));
                  jni_env->GetDoubleArrayRegion (dv, 0, m.numel (), m.fortran_vec ());
                  retval = m;
                  break;
                }
              else if (s == "byte")
                {
                  if (Vjava_unsigned_autoconversion)
                    {
                      uint8NDArray m (dims);
                      mID = jni_env->GetMethodID (cls, "toByte", "()[B");
                      jbyteArray_ref dv (jni_env, reinterpret_cast<jbyteArray> (jni_env->CallObjectMethod (jobj, mID)));
                      jni_env->GetByteArrayRegion (dv, 0, m.numel (), reinterpret_cast<jbyte *> (m.fortran_vec ()));
                      retval = m;
                      break;
                    }
                  else
                    {
                      int8NDArray m (dims);
                      mID = jni_env->GetMethodID (cls, "toByte", "()[B");
                      jbyteArray_ref dv (jni_env, reinterpret_cast<jbyteArray> (jni_env->CallObjectMethod (jobj, mID)));
                      jni_env->GetByteArrayRegion (dv, 0, m.numel (), reinterpret_cast<jbyte *> (m.fortran_vec ()));
                      retval = m;
                      break;
                    }
                }
              else if (s == "integer")
                {
                  if (Vjava_unsigned_autoconversion)
                    {
                      uint32NDArray m (dims);
                      mID = jni_env->GetMethodID (cls, "toInt", "()[I");
                      jintArray_ref dv (jni_env, reinterpret_cast<jintArray> (jni_env->CallObjectMethod (jobj, mID)));
                      jni_env->GetIntArrayRegion (dv, 0, m.numel (), reinterpret_cast<jint *> (m.fortran_vec ()));
                      retval = m;
                      break;
                    }
                  else
                    {
                      int32NDArray m (dims);
                      mID = jni_env->GetMethodID (cls, "toInt", "()[I");
                      jintArray_ref dv (jni_env, reinterpret_cast<jintArray> (jni_env->CallObjectMethod (jobj, mID)));
                      jni_env->GetIntArrayRegion (dv, 0, m.numel (), reinterpret_cast<jint *> (m.fortran_vec ()));
                      retval = m;
                      break;
                    }
                }
            }
        }

      cls = find_octave_class (jni_env, "org/octave/OctaveReference");
      if (jni_env->IsInstanceOf (jobj, cls))
        {
          jmethodID mID = jni_env->GetMethodID (cls, "getID", "()I");
          int ID = jni_env->CallIntMethod (jobj, mID);
          std::map<int,octave_value>::iterator it = octave_ref_map.find (ID);

          if (it != octave_ref_map.end ())
            retval = it->second;

          break;
        }

      // No suitable class found.  Return a generic octave_java object
      retval = octave_value (new octave_java (jobj, jcls));
      break;
    }

  return retval;
}

static octave_value
box_more (JNIEnv *jni_env, void *jobj_arg, void *jcls_arg)
{
  jobject jobj = TO_JOBJECT (jobj_arg);
  jclass jcls = TO_JCLASS (jcls_arg);

  octave_value retval = box (jni_env, jobj, jcls);

  if (retval.is_java ())
    {
      retval = octave_value ();

      jclass_ref cls (jni_env);

      if (retval.is_undefined ())
        {
          cls = jni_env->FindClass ("[D");

          if (jni_env->IsInstanceOf (jobj, cls))
            {
              jdoubleArray jarr = reinterpret_cast<jdoubleArray> (jobj);
              int len = jni_env->GetArrayLength (jarr);

              if (len > 0)
                {
                  Matrix m (1, len);
                  jni_env->GetDoubleArrayRegion (jarr, 0, len, m.fortran_vec ());
                  retval = m;
                }
              else
                retval = Matrix ();
            }
        }

      if (retval.is_undefined ())
        {
          cls = jni_env->FindClass ("[[D");

          if (jni_env->IsInstanceOf (jobj, cls))
            {
              jobjectArray jarr = reinterpret_cast<jobjectArray> (jobj);
              int rows = jni_env->GetArrayLength (jarr);
              int cols = 0;

              if (rows > 0)
                {
                  Matrix m;

                  for (int r = 0; r < rows; r++)
                    {
                      jdoubleArray_ref row (jni_env,
                                            reinterpret_cast<jdoubleArray> (jni_env->GetObjectArrayElement (jarr, r)));

                      if (m.is_empty ())
                        {
                          cols = jni_env->GetArrayLength (row);
                          m.resize (cols, rows);
                        }
                      jni_env->GetDoubleArrayRegion (row, 0, cols, m.fortran_vec () + r * cols);
                    }
                  retval = m.transpose ();
                }
              else
                retval = Matrix ();
            }
        }

      if (retval.is_undefined ())
        {
          cls = jni_env->FindClass ("[Ljava/lang/String;");

          if (jni_env->IsInstanceOf (jobj, cls))
            {
              jobjectArray jarr = reinterpret_cast<jobjectArray> (jobj);
              int len = jni_env->GetArrayLength (jarr);
              Cell m (len, 1);

              for (int i = 0; i < len; i++)
                {
                  jstring_ref js (jni_env,
                                  reinterpret_cast<jstring> (jni_env->GetObjectArrayElement (jarr, i)));
                  m(i) = jstring_to_string (jni_env, js);
                }

              retval = m;
            }
        }
    }

  if (retval.is_undefined ())
    retval = octave_value (new octave_java (jobj, jcls));

  restore_fpu_state ();

  return retval;
}

static bool
unbox (JNIEnv *jni_env, const octave_value& val, jobject_ref& jobj,
       jclass_ref& jcls)
{
  bool found = true;

  if (val.is_java ())
    {
      octave_java *ovj = TO_JAVA (val);
      jobj = TO_JOBJECT (ovj->to_java ());
      jobj.detach ();
      jcls = jni_env->GetObjectClass (jobj);
    }
  else if (val.is_string ())
    {
      std::string s = val.string_value ();

      jobj = jni_env->NewStringUTF (s.c_str ());
      jcls = jni_env->GetObjectClass (jobj);
    }
  else if (val.is_cellstr ())
    {
      const Array<std::string> str_arr = val.cellstr_value ();
      const octave_idx_type n = str_arr.numel ();

      jclass_ref scls (jni_env, jni_env->FindClass ("java/lang/String"));
      jobjectArray array = jni_env->NewObjectArray (n, scls, NULL);

      for (octave_idx_type i = 0; i < n; i++)
        {
          jstring_ref jstr (jni_env, jni_env->NewStringUTF (str_arr(i).c_str ()));
          jni_env->SetObjectArrayElement (array, i, jstr);
        }

      jobj = array;
      jcls = jni_env->GetObjectClass (jobj);
    }
  else if (val.numel () > 1 && val.dims ().is_vector ())
    {
      // FIXME: Is there any way to avoid code duplication here without
      // using a macro?

#define UNBOX_PRIMITIVE_ARRAY(METHOD_T, OCTAVE_T, JAVA_T, JAVA_T_CAP) \
  do \
    { \
      const OCTAVE_T ## NDArray v = val.METHOD_T ## array_value (); \
      JAVA_T ## Array jarr = jni_env->New ## JAVA_T_CAP ## Array (v.numel ()); \
      const JAVA_T *jv = reinterpret_cast<const JAVA_T*> (v.data ()); \
      jni_env->Set ## JAVA_T_CAP ## ArrayRegion (jarr, 0, v.numel (), jv); \
      jobj = reinterpret_cast<jobject> (jarr); \
      jcls = jni_env->GetObjectClass (jobj); \
    } \
  while (0)

      // Note that we do NOT handle char here because they are unboxed
      // into a String[], not into a char array

      if (val.is_double_type ())
        UNBOX_PRIMITIVE_ARRAY ( , , jdouble,  Double);
      else if (val.is_bool_type ())
        UNBOX_PRIMITIVE_ARRAY (bool_, bool, jboolean, Boolean);
      else if (val.is_float_type ())
        UNBOX_PRIMITIVE_ARRAY (float_, Float, jfloat, Float);
      else if (val.is_int8_type ())
        UNBOX_PRIMITIVE_ARRAY (int8_, int8, jbyte, Byte);
      else if (val.is_uint8_type ())
        UNBOX_PRIMITIVE_ARRAY (uint8_, uint8, jbyte, Byte);
      else if (val.is_int16_type ())
        UNBOX_PRIMITIVE_ARRAY (int16_, int16, jshort, Short);
      else if (val.is_uint16_type ())
        UNBOX_PRIMITIVE_ARRAY (uint16_, uint16, jshort, Short);
      else if (val.is_int32_type ())
        UNBOX_PRIMITIVE_ARRAY (int32_, int32, jint, Int);
      else if (val.is_uint32_type ())
        UNBOX_PRIMITIVE_ARRAY (uint32_, uint32, jint, Int);
      else if (val.is_int64_type ())
        UNBOX_PRIMITIVE_ARRAY (int64_, int64, jlong, Long);
      else if (val.is_uint64_type ())
        UNBOX_PRIMITIVE_ARRAY (uint64_, uint64, jlong, Long);

#undef UNBOX_PRIMITIVE_ARRAY
    }
  else if (val.is_real_scalar () || val.is_bool_scalar ())
    {
      // FIXME: Is there any way to avoid code duplication here without
      // using a macro?

#define UNBOX_PRIMITIVE_SCALAR(OCTAVE_T, METHOD_T, JAVA_T, JAVA_CON)    \
  do \
    { \
      const OCTAVE_T ov = val.METHOD_T ## _value (); \
      jclass_ref dcls (jni_env, jni_env->FindClass (JAVA_T)); \
      const jfieldID fid = jni_env->GetStaticFieldID (dcls, "TYPE", "Ljava/lang/Class;"); \
      const jmethodID mid = jni_env->GetMethodID (dcls, "<init>", JAVA_CON); \
      jcls = reinterpret_cast<jclass> (jni_env->GetStaticObjectField (dcls, fid)); \
      jobj = jni_env->NewObject (dcls, mid, ov); \
     } \
   while (0)

      if (val.is_double_type ())
        UNBOX_PRIMITIVE_SCALAR (double, double, "java/lang/Double", "(D)V");
      else if (val.is_bool_type ())
        UNBOX_PRIMITIVE_SCALAR (bool, bool, "java/lang/Boolean", "(Z)V");
      else if (val.is_float_type ())
        UNBOX_PRIMITIVE_SCALAR (float, float, "java/lang/Float", "(F)V");
      else if (val.is_int8_type ())
        UNBOX_PRIMITIVE_SCALAR (int8_t, int8_scalar, "java/lang/Byte", "(B)V");
      else if (val.is_uint8_type ())
        UNBOX_PRIMITIVE_SCALAR (uint8_t, uint8_scalar, "java/lang/Byte", "(B)V");
      else if (val.is_int16_type ())
        UNBOX_PRIMITIVE_SCALAR (int16_t, int16_scalar, "java/lang/Short", "(S)V");
      else if (val.is_uint16_type ())
        UNBOX_PRIMITIVE_SCALAR (uint16_t, uint16_scalar, "java/lang/Short", "(S)V");
      else if (val.is_int32_type ())
        UNBOX_PRIMITIVE_SCALAR (int32_t, int32_scalar, "java/lang/Int", "(I)V");
      else if (val.is_uint32_type ())
        UNBOX_PRIMITIVE_SCALAR (uint32_t, uint32_scalar, "java/lang/Int", "(I)V");
      else if (val.is_int64_type ())
        UNBOX_PRIMITIVE_SCALAR (int64_t, int64_scalar, "java/lang/Long", "(L)V");
      else if (val.is_uint64_type ())
        UNBOX_PRIMITIVE_SCALAR (uint64_t, uint64_scalar, "java/lang/Long", "(L)V");

#undef UNBOX_PRIMITIVE_SCALAR
      }
  else if (val.is_empty ())
    {
      jobj = 0;
      jcls = 0;
      //jcls = jni_env->FindClass ("java/lang/Object");
    }
  else if (! Vjava_matrix_autoconversion
           && ((val.is_real_matrix ()
                && (val.rows () == 1 || val.columns () == 1))
               || val.is_range ()))
    {
      Matrix m = val.matrix_value ();
      jdoubleArray dv = jni_env->NewDoubleArray (m.numel ());
      jni_env->SetDoubleArrayRegion (dv, 0, m.numel (), m.fortran_vec ());
      jobj = dv;
      jcls = jni_env->GetObjectClass (jobj);
    }
  else if (Vjava_matrix_autoconversion
           && (val.is_matrix_type () || val.is_range ()) && val.is_real_type ())
    {
      jclass_ref mcls (jni_env, find_octave_class (jni_env, "org/octave/Matrix"));
      dim_vector dims = val.dims ();
      jintArray_ref iv (jni_env, jni_env->NewIntArray (dims.ndims ()));
      jint *iv_data = jni_env->GetIntArrayElements (jintArray (iv), 0);

      for (int i = 0; i < dims.ndims (); i++)
        iv_data[i] = dims(i);

      jni_env->ReleaseIntArrayElements (jintArray (iv), iv_data, 0);

      if (val.is_double_type ())
        {
          NDArray m = val.array_value ();
          jdoubleArray_ref dv (jni_env, jni_env->NewDoubleArray (m.numel ()));
          jni_env->SetDoubleArrayRegion (jdoubleArray (dv), 0, m.numel (),
                                         m.fortran_vec ());
          jmethodID mID = jni_env->GetMethodID (mcls, "<init>", "([D[I)V");
          jobj = jni_env->NewObject (jclass (mcls), mID, jdoubleArray (dv),
                                     jintArray (iv));
          jcls = jni_env->GetObjectClass (jobj);
        }
      else if (val.is_int8_type ())
        {
          int8NDArray m = val.int8_array_value ();
          jbyteArray_ref bv (jni_env, jni_env->NewByteArray (m.numel ()));
          jni_env->SetByteArrayRegion (jbyteArray (bv), 0, m.numel (),
                                       reinterpret_cast<jbyte *> (m.fortran_vec ()));
          jmethodID mID = jni_env->GetMethodID (mcls, "<init>", "([B[I)V");
          jobj = jni_env->NewObject (jclass (mcls), mID, jbyteArray (bv), jintArray (iv));
          jcls = jni_env->GetObjectClass (jobj);
        }
      else if (val.is_uint8_type ())
        {
          uint8NDArray m = val.uint8_array_value ();
          jbyteArray_ref bv (jni_env, jni_env->NewByteArray (m.numel ()));
          jni_env->SetByteArrayRegion (jbyteArray (bv), 0, m.numel (),
                                       reinterpret_cast<jbyte *> (m.fortran_vec ()));
          jmethodID mID = jni_env->GetMethodID (mcls, "<init>", "([B[I)V");
          jobj = jni_env->NewObject (jclass (mcls), mID, jbyteArray (bv), jintArray (iv));
          jcls = jni_env->GetObjectClass (jobj);
        }
      else if (val.is_int32_type ())
        {
          int32NDArray m = val.int32_array_value ();
          jintArray_ref v (jni_env, jni_env->NewIntArray (m.numel ()));
          jni_env->SetIntArrayRegion (jintArray (v), 0, m.numel (),
                                      reinterpret_cast<jint *> (m.fortran_vec ()));
          jmethodID mID = jni_env->GetMethodID (mcls, "<init>", "([I[I)V");
          jobj = jni_env->NewObject (jclass (mcls), mID, jintArray (v), jintArray (iv));
          jcls = jni_env->GetObjectClass (jobj);
        }
      else
        {
          found = false;
          error ("cannot convert matrix of type '%s'", val.class_name ().c_str ());
        }
    }
  else
    {
      jclass rcls = find_octave_class (jni_env, "org/octave/OctaveReference");
      jmethodID mID = jni_env->GetMethodID (rcls, "<init>", "(I)V");
      int ID = octave_java_refcount++;

      jobj = jni_env->NewObject (rcls, mID, ID);
      jcls = rcls;
      octave_ref_map[ID] = val;
    }

  return found;
}

static bool
unbox (JNIEnv *jni_env, const octave_value_list& args,
       jobjectArray_ref& jobjs, jobjectArray_ref& jclss)
{
  bool found = true;

  jclass_ref ocls (jni_env, jni_env->FindClass ("java/lang/Object"));
  jclass_ref ccls (jni_env, jni_env->FindClass ("java/lang/Class"));

  if (! jobjs)
    jobjs = jni_env->NewObjectArray (args.length (), ocls, 0);

  if (! jclss)
    jclss = jni_env->NewObjectArray (args.length (), ccls, 0);

  for (int i = 0; i < args.length (); i++)
    {
      jobject_ref jobj (jni_env);
      jclass_ref jcls (jni_env);

      found = unbox (jni_env, args(i), jobj, jcls);
      if (! found)
        break;

      jni_env->SetObjectArrayElement (jobjs, i, jobj);
      jni_env->SetObjectArrayElement (jclss, i, jcls);
    }

  return found;
}

static long
get_current_thread_ID (JNIEnv *jni_env)
{
  if (jni_env)
    {
      jclass_ref cls (jni_env, jni_env->FindClass ("java/lang/Thread"));
      jmethodID mID = jni_env->GetStaticMethodID (cls, "currentThread", "()Ljava/lang/Thread;");
      jobject_ref jthread (jni_env, jni_env->CallStaticObjectMethod (cls, mID));

      if (jthread)
        {
          jclass_ref jth_cls (jni_env, jni_env->GetObjectClass (jthread));
          mID = jni_env->GetMethodID (jth_cls, "getId", "()J");
          long result = jni_env->CallLongMethod (jthread, mID);
          //printf ("current java thread ID = %ld\n", result);
          return result;
        }
    }

  return -1;
}

static int
java_event_hook (void)
{
  JNIEnv *current_env = thread_jni_env ();

  if (current_env)
    {
      jclass_ref cls (current_env, find_octave_class (current_env, "org/octave/Octave"));
      jmethodID mID = current_env->GetStaticMethodID (cls, "checkPendingAction", "()V");
      current_env->CallStaticVoidMethod (cls, mID);

      restore_fpu_state ();
    }

  return 0;
}

static void
initialize_java (void)
{
  if (! jvm)
    {
      try
        {
          initialize_jvm ();

          JNIEnv *current_env = thread_jni_env ();

          command_editor::add_event_hook (java_event_hook);

          octave_thread_ID = get_current_thread_ID (current_env);
          //printf ("octave thread ID=%ld\n", octave_thread_ID);
        }
      catch (std::string msg)
        {
          error (msg.c_str ());
        }

      restore_fpu_state ();
    }
}

JNIEXPORT jboolean JNICALL
Java_org_octave_Octave_call (JNIEnv *env, jclass, jstring funcName,
                             jobjectArray argin, jobjectArray argout)
{
  std::string fname = jstring_to_string (env, funcName);

  int nargout = env->GetArrayLength (argout);
  int nargin = env->GetArrayLength (argin);

  octave_value_list varargin, varargout;

  for (int i = 0; i < nargin; i++)
    varargin(i) = box (env, env->GetObjectArrayElement (argin, i), 0);

  varargout = feval (fname, varargin, nargout);

  jobjectArray_ref out_objs (env, argout), out_clss (env);
  out_objs.detach ();
  return unbox (env, varargout, out_objs, out_clss);
}

JNIEXPORT void JNICALL
Java_org_octave_OctaveReference_doFinalize (JNIEnv *, jclass, jint ID)
{
  octave_ref_map.erase (ID);
}

JNIEXPORT void JNICALL
Java_org_octave_Octave_doInvoke (JNIEnv *env, jclass, jint ID,
                                 jobjectArray args)
{
  std::map<int,octave_value>::iterator it = octave_ref_map.find (ID);

  if (it != octave_ref_map.end ())
    {
      octave_value val = it->second;
      int len = env->GetArrayLength (args);
      octave_value_list oct_args;

      for (int i = 0; i < len; i++)
        {
          jobject_ref jobj (env, env->GetObjectArrayElement (args, i));
          oct_args(i) = box (env, jobj, 0);
        }

      BEGIN_INTERRUPT_WITH_EXCEPTIONS;

      if (val.is_function_handle ())
        {
          octave_function *fcn = val.function_value ();
          feval (fcn, oct_args);
        }
      else if (val.is_cell () && val.length () > 0
               && (val.rows () == 1 || val.columns () == 1)
               && val.cell_value()(0).is_function_handle ())
        {
          Cell c = val.cell_value ();
          octave_function *fcn = c(0).function_value ();

          for (int i=1; i<c.numel (); i++)
            oct_args(len+i-1) = c(i);

          feval (fcn, oct_args);
        }
      else
        error ("trying to invoke non-invocable object");

      END_INTERRUPT_WITH_EXCEPTIONS;
    }
}

JNIEXPORT void JNICALL
Java_org_octave_Octave_doEvalString (JNIEnv *env, jclass, jstring cmd)
{
  std::string s = jstring_to_string (env, cmd);
  int pstatus;
  eval_string (s, false, pstatus, 0);
}

JNIEXPORT jboolean JNICALL
Java_org_octave_Octave_needThreadedInvokation (JNIEnv *env, jclass)
{
  return (get_current_thread_ID (env) != octave_thread_ID);
}

#endif

// octave_java class definition

octave_java::octave_java (void)
  : octave_base_value (), java_object (0), java_class (0)
{
#if ! defined (HAVE_JAVA)

  err_disabled_feature ("Java Objects", "Java");

#endif
}

octave_java::octave_java (const voidptr& jobj, void *jcls)
  : octave_base_value (), java_object (0), java_class (0)
{
#if defined (HAVE_JAVA)

  init (jobj, jcls);

#else

  octave_unused_parameter (jobj);
  octave_unused_parameter (jcls);

  err_disabled_feature ("Java Objects", "Java");

#endif
}

int octave_java::t_id (-1);

const std::string octave_java::t_name ("octave_java");

void
octave_java::register_type (void)
{
#if defined (HAVE_JAVA)

  t_id = octave_value_typeinfo::register_type
         (octave_java::t_name, "<unknown>", octave_value (new octave_java ()));

#endif
}

dim_vector
octave_java::dims (void) const
{
#if defined (HAVE_JAVA)

  JNIEnv *current_env = thread_jni_env ();

  if (current_env && java_object)
    return compute_array_dimensions (current_env, TO_JOBJECT (java_object));
  else
    return dim_vector (1, 1);

#else

  // This shouldn't happen because construction of octave_java
  // objects is supposed to be impossible if Java is not available.

  panic_impossible ();

#endif
}

octave_value_list
octave_java::subsref (const std::string& type,
                      const std::list<octave_value_list>& idx, int nargout)
{
#if defined (HAVE_JAVA)

  octave_value_list retval;
  int skip = 1;

  JNIEnv *current_env = thread_jni_env ();

  switch (type[0])
    {
    case '.':
      if (type.length () > 1 && type[1] == '(')
        {
          octave_value_list ovl;
          count++;
          ovl(1) = octave_value (this);
          ovl(0) = (idx.front ())(0);
          std::list<octave_value_list>::const_iterator it = idx.begin ();
          ovl.append (*++it);
          retval = feval (std::string ("javaMethod"), ovl, 1);
          skip++;
        }
      else
        {
          octave_value_list ovl;
          count++;
          ovl(0) = octave_value (this);
          ovl(1) = (idx.front ())(0);
          retval = feval (std::string ("__java_get__"), ovl, 1);
        }
      break;

    case '(':
      if (current_env)
        retval = get_array_elements (current_env, TO_JOBJECT (to_java ()), idx.front ());
      break;

    default:
      error ("subsref: Java object cannot be indexed with %c", type[0]);
      break;
    }

  if (idx.size () > 1 && type.length () > 1)
    retval = retval(0).next_subsref (nargout, type, idx, skip);

  return retval;

#else

  octave_unused_parameter (type);
  octave_unused_parameter (idx);
  octave_unused_parameter (nargout);

  // This shouldn't happen because construction of octave_java
  // objects is supposed to be impossible if Java is not available.

  panic_impossible ();

#endif
}

octave_value
octave_java::subsasgn (const std::string& type,
                       const std::list<octave_value_list>& idx,
                       const octave_value& rhs)
{
#if defined (HAVE_JAVA)

  octave_value retval;

  JNIEnv *current_env = thread_jni_env ();

  switch (type[0])
    {
    case '.':
      if (type.length () == 1)
        {
          // field assignment
          octave_value_list ovl;
          count++;
          ovl(0) = octave_value (this);
          ovl(1) = (idx.front ())(0);
          ovl(2) = rhs;
          feval ("__java_set__", ovl, 0);

          count++;
          retval = octave_value (this);
        }
      else if (type.length () > 2 && type[1] == '(')
        {
          std::list<octave_value_list> new_idx;
          std::list<octave_value_list>::const_iterator it = idx.begin ();
          new_idx.push_back (*it++);
          new_idx.push_back (*it++);
          octave_value_list u = subsref (type.substr (0, 2), new_idx, 1);

          std::list<octave_value_list> next_idx (idx);
          next_idx.erase (next_idx.begin ());
          next_idx.erase (next_idx.begin ());
          u(0).subsasgn (type.substr (2), next_idx, rhs);

          count++;
          retval = octave_value (this);
        }
      else if (type[1] == '.')
        {
          octave_value_list u = subsref (type.substr (0, 1), idx, 1);

          std::list<octave_value_list> next_idx (idx);
          next_idx.erase (next_idx.begin ());
          u(0).subsasgn (type.substr (1), next_idx, rhs);

          count++;
          retval = octave_value (this);
        }
      else
        error ("invalid indexing/assignment on Java object");
      break;

    case '(':
      if (current_env)
        {
          set_array_elements (current_env, TO_JOBJECT (to_java ()), idx.front (), rhs);

          count++;
          retval = octave_value (this);
        }
      break;

    default:
      error ("Java object cannot be indexed with %c", type[0]);
      break;
    }

  return retval;

#else

  octave_unused_parameter (type);
  octave_unused_parameter (idx);
  octave_unused_parameter (rhs);

  // This shouldn't happen because construction of octave_java
  // objects is supposed to be impossible if Java is not available.

  panic_impossible ();

#endif
}

string_vector
octave_java::map_keys (void) const
{
#if defined (HAVE_JAVA)

  JNIEnv *current_env = thread_jni_env ();

  if (current_env)
    return get_invoke_list (current_env, to_java ());
  else
    return string_vector ();

#else

  // This shouldn't happen because construction of octave_java
  // objects is supposed to be impossible if Java is not available.

  panic_impossible ();

#endif
}

octave_value
octave_java::convert_to_str_internal (bool, bool force, char type) const
{
#if defined (HAVE_JAVA)

  JNIEnv *current_env = thread_jni_env ();

  if (current_env)
    return convert_to_string (current_env, TO_JOBJECT (to_java ()), force, type);
  else
    return octave_value ("");

#else

  octave_unused_parameter (force);
  octave_unused_parameter (type);

  // This shouldn't happen because construction of octave_java
  // objects is supposed to be impossible if Java is not available.

  panic_impossible ();

#endif
}

void
octave_java::print (std::ostream& os, bool)
{
  print_raw (os);
  newline (os);
}

void
octave_java::print_raw (std::ostream& os, bool) const
{
  os << "<Java object: " << java_classname << ">";
}

// FIXME: Need routines to actually save/load java objects through Serialize.
//        See bug #42112.

bool
octave_java::save_ascii (std::ostream& /* os */)
{
  warning ("save: unable to save java objects, skipping");

  return true;
}

bool
octave_java::load_ascii (std::istream& /* is */)
{
  // Silently skip over java object that was not saved
  return true;
}

bool
octave_java::save_binary (std::ostream& /* os */, bool& /* save_as_floats */)
{
  warning ("save: unable to save java objects, skipping");

  return true;
}

bool
octave_java::load_binary (std::istream& /* is */, bool /* swap*/,
                          oct_mach_info::float_format /* fmt */)
{
  // Silently skip over java object that was not saved
  return true;
}

bool
octave_java::save_hdf5 (octave_hdf5_id /* loc_id */, const char * /* name */,
                        bool /* save_as_floats */)
{
  warning ("save: unable to save java objects, skipping");

  return true;
}

bool
octave_java::load_hdf5 (octave_hdf5_id /* loc_id */, const char * /* name */)
{
  // Silently skip object that was not saved
  return true;
}

octave_value
octave_java::do_javaMethod (void *jni_env_arg, const std::string& name,
                            const octave_value_list& args)
{
#if defined (HAVE_JAVA)

  octave_value retval;

  JNIEnv *jni_env = TO_JNIENV (jni_env_arg);

  if (jni_env)
    {
      jobjectArray_ref arg_objs (jni_env), arg_types (jni_env);
      if (unbox (jni_env, args, arg_objs, arg_types))
        {
          jclass_ref helperClass (jni_env, find_octave_class (jni_env, "org/octave/ClassHelper"));
          jmethodID mID = jni_env->GetStaticMethodID (helperClass, "invokeMethod",
                                                      "(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;[Ljava/lang/Class;)Ljava/lang/Object;");
          jstring_ref methName (jni_env, jni_env->NewStringUTF (name.c_str ()));
          jobjectArray_ref resObj (jni_env, reinterpret_cast<jobjectArray> (jni_env->CallStaticObjectMethod (helperClass, mID,
                                                                                                             to_java (), jstring (methName), jobjectArray (arg_objs), jobjectArray (arg_types))));
          if (resObj)
            retval = box (jni_env, resObj);
          else
            retval = check_exception (jni_env);
        }

      restore_fpu_state ();
    }

  return retval;

#else

  octave_unused_parameter (jni_env_arg);
  octave_unused_parameter (name);
  octave_unused_parameter (args);

  // This shouldn't happen because construction of octave_java
  // objects is supposed to be impossible if Java is not available.

  panic_impossible ();

#endif
}

octave_value
octave_java::do_javaMethod (const std::string& name, const octave_value_list& args)
{
#if defined (HAVE_JAVA)

  return do_javaMethod (thread_jni_env (), name, args);

#else

  octave_unused_parameter (name);
  octave_unused_parameter (args);

  // This shouldn't happen because construction of octave_java
  // objects is supposed to be impossible if Java is not available.

  panic_impossible ();

#endif
}

octave_value
octave_java:: do_javaMethod (void *jni_env_arg,
                             const std::string& class_name,
                             const std::string& name,
                             const octave_value_list& args)
{
#if defined (HAVE_JAVA)

  octave_value retval;

  JNIEnv *jni_env = TO_JNIENV (jni_env_arg);

  if (jni_env)
    {
      jobjectArray_ref arg_objs (jni_env), arg_types (jni_env);
      if (unbox (jni_env, args, arg_objs, arg_types))
        {
          jclass_ref helperClass (jni_env, find_octave_class (jni_env, "org/octave/ClassHelper"));
          jmethodID mID = jni_env->GetStaticMethodID (helperClass, "invokeStaticMethod",
                                                      "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;[Ljava/lang/Class;)Ljava/lang/Object;");
          jstring_ref methName (jni_env, jni_env->NewStringUTF (name.c_str ()));
          jstring_ref clsName (jni_env, jni_env->NewStringUTF (class_name.c_str ()));
          jobject_ref resObj (jni_env, jni_env->CallStaticObjectMethod (helperClass, mID,
                                                                        jstring (clsName), jstring (methName), jobjectArray (arg_objs), jobjectArray (arg_types)));
          if (resObj)
            retval = box (jni_env, resObj);
          else
            retval = check_exception (jni_env);
        }

      restore_fpu_state ();
    }

  return retval;

#else

  octave_unused_parameter (jni_env_arg);
  octave_unused_parameter (class_name);
  octave_unused_parameter (name);
  octave_unused_parameter (args);

  // This shouldn't happen because construction of octave_java
  // objects is supposed to be impossible if Java is not available.

  panic_impossible ();

#endif
}

octave_value
octave_java::do_javaMethod (const std::string& class_name,
                            const std::string& name,
                            const octave_value_list& args)
{
#if defined (HAVE_JAVA)

  return do_javaMethod (thread_jni_env (), class_name, name, args);

#else

  octave_unused_parameter (class_name);
  octave_unused_parameter (name);
  octave_unused_parameter (args);

  // This shouldn't happen because construction of octave_java
  // objects is supposed to be impossible if Java is not available.

  panic_impossible ();

#endif
}

octave_value
octave_java::do_javaObject (void *jni_env_arg, const std::string& name,
                            const octave_value_list& args)
{
#if defined (HAVE_JAVA)

  octave_value retval;

  JNIEnv *jni_env = TO_JNIENV (jni_env_arg);

  if (jni_env)
    {
      jobjectArray_ref arg_objs (jni_env), arg_types (jni_env);

      if (unbox (jni_env, args, arg_objs, arg_types))
        {
          jclass_ref helperClass (jni_env, find_octave_class (jni_env, "org/octave/ClassHelper"));
          jmethodID mID = jni_env->GetStaticMethodID (helperClass, "invokeConstructor",
                                                      "(Ljava/lang/String;[Ljava/lang/Object;[Ljava/lang/Class;)Ljava/lang/Object;");
          jstring_ref clsName (jni_env, jni_env->NewStringUTF (name.c_str ()));
          jobject_ref resObj (jni_env, jni_env->CallStaticObjectMethod (helperClass, mID,
                                                                        jstring (clsName), jobjectArray (arg_objs), jobjectArray (arg_types)));

          if (resObj)
            retval = octave_value (new octave_java (resObj, 0));
          else
            check_exception (jni_env);
        }

      restore_fpu_state ();
    }

  return retval;

#else

  octave_unused_parameter (jni_env_arg);
  octave_unused_parameter (name);
  octave_unused_parameter (args);

  // This shouldn't happen because construction of octave_java
  // objects is supposed to be impossible if Java is not available.

  panic_impossible ();

#endif
}

octave_value
octave_java::do_javaObject (const std::string& name, const octave_value_list& args)
{
#if defined (HAVE_JAVA)

  return do_javaObject (thread_jni_env (), name, args);

#else

  octave_unused_parameter (name);
  octave_unused_parameter (args);

  // This shouldn't happen because construction of octave_java
  // objects is supposed to be impossible if Java is not available.

  panic_impossible ();

#endif
}

octave_value
octave_java::do_java_get (void *jni_env_arg, const std::string& name)
{
#if defined (HAVE_JAVA)

  octave_value retval;

  JNIEnv *jni_env = TO_JNIENV (jni_env_arg);

  if (jni_env)
    {
      jclass_ref helperClass (jni_env, find_octave_class (jni_env, "org/octave/ClassHelper"));
      jmethodID mID = jni_env->GetStaticMethodID (helperClass, "getField",
          "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;");
      jstring_ref fName (jni_env, jni_env->NewStringUTF (name.c_str ()));
      jobject_ref resObj (jni_env, jni_env->CallStaticObjectMethod (helperClass, mID,
          to_java (), jstring (fName)));

      if (resObj)
        retval = box (jni_env, resObj);
      else
        retval = check_exception (jni_env);

      restore_fpu_state ();
    }

  return retval;

#else

  octave_unused_parameter (jni_env_arg);
  octave_unused_parameter (name);

  // This shouldn't happen because construction of octave_java
  // objects is supposed to be impossible if Java is not available.

  panic_impossible ();

#endif
}

octave_value
octave_java::do_java_get (const std::string& name)
{
#if defined (HAVE_JAVA)

  return do_java_get (thread_jni_env (), name);

#else

  octave_unused_parameter (name);

  // This shouldn't happen because construction of octave_java
  // objects is supposed to be impossible if Java is not available.

  panic_impossible ();

#endif
}

octave_value
octave_java::do_java_get (void *jni_env_arg, const std::string& class_name,
                          const std::string& name)
{
#if defined (HAVE_JAVA)

  octave_value retval;

  JNIEnv *jni_env = TO_JNIENV (jni_env_arg);

  if (jni_env)
    {
      jclass_ref helperClass (jni_env, find_octave_class (jni_env, "org/octave/ClassHelper"));
      jmethodID mID = jni_env->GetStaticMethodID (helperClass, "getStaticField",
          "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;");
      jstring_ref cName (jni_env, jni_env->NewStringUTF (class_name.c_str ()));
      jstring_ref fName (jni_env, jni_env->NewStringUTF (name.c_str ()));
      jobject_ref resObj (jni_env, jni_env->CallStaticObjectMethod (helperClass, mID,
          jstring (cName), jstring (fName)));
      if (resObj)
        retval = box (jni_env, resObj);
      else
        retval = check_exception (jni_env);

      restore_fpu_state ();
    }

  return retval;

#else

  octave_unused_parameter (jni_env_arg);
  octave_unused_parameter (class_name);
  octave_unused_parameter (name);

  // This shouldn't happen because construction of octave_java
  // objects is supposed to be impossible if Java is not available.

  panic_impossible ();

#endif
}

octave_value
octave_java::do_java_get (const std::string& class_name, const std::string& name)
{
#if defined (HAVE_JAVA)

  return do_java_get (thread_jni_env (), class_name, name);

#else

  octave_unused_parameter (class_name);
  octave_unused_parameter (name);

  // This shouldn't happen because construction of octave_java
  // objects is supposed to be impossible if Java is not available.

  panic_impossible ();

#endif
}

octave_value
octave_java::do_java_set (void *jni_env_arg, const std::string& name,
                          const octave_value& val)
{
#if defined (HAVE_JAVA)

  octave_value retval;

  JNIEnv *jni_env = TO_JNIENV (jni_env_arg);

  if (jni_env)
    {
      jobject_ref jobj (jni_env);
      jclass_ref jcls (jni_env);

      if (unbox (jni_env, val, jobj, jcls))
        {
          jclass_ref helperClass (jni_env, find_octave_class (jni_env, "org/octave/ClassHelper"));
          jmethodID mID = jni_env->GetStaticMethodID (helperClass, "setField",
                                                      "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)V");
          jstring_ref fName (jni_env, jni_env->NewStringUTF (name.c_str ()));
          jni_env->CallStaticObjectMethod (helperClass, mID, to_java (), jstring (fName), jobject (jobj));
          check_exception (jni_env);
        }

      restore_fpu_state ();
    }

  return retval;

#else

  octave_unused_parameter (jni_env_arg);
  octave_unused_parameter (name);
  octave_unused_parameter (val);

  // This shouldn't happen because construction of octave_java
  // objects is supposed to be impossible if Java is not available.

  panic_impossible ();

#endif
}

octave_value
octave_java::do_java_set (const std::string& name, const octave_value& val)
{
#if defined (HAVE_JAVA)

  return do_java_set (thread_jni_env (), name, val);

#else

  octave_unused_parameter (name);
  octave_unused_parameter (val);

  // This shouldn't happen because construction of octave_java
  // objects is supposed to be impossible if Java is not available.

  panic_impossible ();

#endif
}

octave_value
octave_java::do_java_set (void *jni_env_arg, const std::string& class_name,
                          const std::string& name, const octave_value& val)
{
#if defined (HAVE_JAVA)

  octave_value retval;

  JNIEnv *jni_env = TO_JNIENV (jni_env_arg);

  if (jni_env)
    {
      jobject_ref jobj (jni_env);
      jclass_ref jcls (jni_env);

      if (unbox (jni_env, val, jobj, jcls))
        {
          jclass_ref helperClass (jni_env, find_octave_class (jni_env, "org/octave/ClassHelper"));
          jmethodID mID = jni_env->GetStaticMethodID (helperClass, "setStaticField",
                                                      "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V");
          jstring_ref cName (jni_env, jni_env->NewStringUTF (class_name.c_str ()));
          jstring_ref fName (jni_env, jni_env->NewStringUTF (name.c_str ()));
          jni_env->CallStaticObjectMethod (helperClass, mID, jstring (cName), jstring (fName), jobject (jobj));
          check_exception (jni_env);
        }

      restore_fpu_state ();
    }

  return retval;

#else

  octave_unused_parameter (jni_env_arg);
  octave_unused_parameter (class_name);
  octave_unused_parameter (name);
  octave_unused_parameter (val);

  // This shouldn't happen because construction of octave_java
  // objects is supposed to be impossible if Java is not available.

  panic_impossible ();

#endif
}

octave_value
octave_java::do_java_set (const std::string& class_name, const std::string& name,
                          const octave_value& val)
{
#if defined (HAVE_JAVA)

  return do_java_set (thread_jni_env (), class_name, name, val);

#else

  octave_unused_parameter (class_name);
  octave_unused_parameter (name);
  octave_unused_parameter (val);

  // This shouldn't happen because construction of octave_java
  // objects is supposed to be impossible if Java is not available.

  panic_impossible ();

#endif
}

void
octave_java::init (void *jobj_arg, void *jcls_arg)
{
#if defined (HAVE_JAVA)

  jobject jobj = TO_JOBJECT (jobj_arg);
  jclass jcls = TO_JCLASS (jcls_arg);

  JNIEnv *current_env = thread_jni_env ();

  if (current_env)
    {
      if (jobj)
        java_object = current_env->NewGlobalRef (jobj);

      if (jcls)
        java_class = current_env->NewGlobalRef (jcls);
      else if (java_object)
        {
          jclass_ref ocls (current_env, current_env->GetObjectClass (TO_JOBJECT (java_object)));
          java_class = current_env->NewGlobalRef (jclass (ocls));
        }

      if (java_class)
        {
          jclass_ref clsCls (current_env, current_env->GetObjectClass (TO_JCLASS (java_class)));
          jmethodID mID = current_env->GetMethodID (clsCls, "getCanonicalName", "()Ljava/lang/String;");
          jobject_ref resObj (current_env, current_env->CallObjectMethod (TO_JCLASS (java_class), mID));
          java_classname = jstring_to_string (current_env, resObj);
        }
    }

#else

  octave_unused_parameter (jobj_arg);
  octave_unused_parameter (jcls_arg);

  // This shouldn't happen because construction of octave_java
  // objects is supposed to be impossible if Java is not available.

  panic_impossible ();

#endif
}

void
octave_java::release (void)
{
#if defined (HAVE_JAVA)

  JNIEnv *current_env = thread_jni_env ();

  if (current_env)
    {
      if (java_object)
        current_env->DeleteGlobalRef (TO_JOBJECT (java_object));

      if (java_class)
        current_env->DeleteGlobalRef (TO_JCLASS (java_class));

      java_object = 0;
      java_class = 0;
    }

#else

  // This shouldn't happen because construction of octave_java
  // objects is supposed to be impossible if Java is not available.

  panic_impossible ();

#endif
}

// DEFUN blocks below must be outside of HAVE_JAVA block so that
// documentation strings are always available, even when functions are not.

DEFUN (__java_init__, , ,
       "-*- texinfo -*-\n\
@deftypefn {} {} __java_init__ ()\n\
Internal function used @strong{only} when debugging Java interface.\n\
\n\
Function will directly call initialize_java to create an instance of a JVM.\n\
@end deftypefn")
{
#if defined (HAVE_JAVA)

  octave_value retval;

  retval = 0;

  initialize_java ();

  retval = 1;

  return retval;

#else

  err_disabled_feature ("__java_init__", "Java");

#endif
}

DEFUN (__java_exit__, , ,
       "-*- texinfo -*-\n\
@deftypefn {} {} __java_exit__ ()\n\
Internal function used @strong{only} when debugging Java interface.\n\
\n\
Function will directly call terminate_jvm to destroy the current JVM\n\
instance.\n\
@end deftypefn")
{
#if defined (HAVE_JAVA)

  terminate_jvm ();

  return ovl ();

#else

  err_disabled_feature ("__java_exit__", "Java");

#endif
}

DEFUN (javaObject, args, ,
       "-*- texinfo -*-\n\
@deftypefn  {} {@var{jobj} =} javaObject (@var{classname})\n\
@deftypefnx {} {@var{jobj} =} javaObject (@var{classname}, @var{arg1}, @dots{})\n\
Create a Java object of class @var{classsname}, by calling the class\n\
constructor with the arguments @var{arg1}, @dots{}\n\
\n\
The first example below creates an uninitialized object, while the second\n\
example supplies an initial argument to the constructor.\n\
\n\
@example\n\
@group\n\
x = javaObject (\"java.lang.StringBuffer\")\n\
x = javaObject (\"java.lang.StringBuffer\", \"Initial string\")\n\
@end group\n\
@end example\n\
\n\
@seealso{javaMethod, javaArray}\n\
@end deftypefn")
{
#if defined (HAVE_JAVA)

  if (args.length () == 0)
    print_usage ();

  std::string classname = args(0).xstring_value ("javaObject: CLASSNAME must be a string");

  initialize_java ();

  JNIEnv *current_env = thread_jni_env ();

  octave_value_list tmp;
  for (int i=1; i<args.length (); i++)
    tmp(i-1) = args(i);

  return ovl (octave_java::do_javaObject (current_env, classname, tmp));

#else

  octave_unused_parameter (args);

  err_disabled_feature ("javaObject", "Java");

#endif
}

/*
## The tests below merely check if javaObject works at all.  Whether
## it works properly, i.e., creates the right values, is a matter of
## Java itself.  Create a Short and check if it really is a short, i.e.,
## whether it overflows.
%!testif HAVE_JAVA
%! assert (javaObject ("java.lang.Short", 40000).doubleValue < 0);
*/

DEFUN (javaMethod, args, ,
       "-*- texinfo -*-\n\
@deftypefn  {} {@var{ret} =} javaMethod (@var{methodname}, @var{obj})\n\
@deftypefnx {} {@var{ret} =} javaMethod (@var{methodname}, @var{obj}, @var{arg1}, @dots{})\n\
Invoke the method @var{methodname} on the Java object @var{obj} with the\n\
arguments @var{arg1}, @dots{}.\n\
\n\
For static methods, @var{obj} can be a string representing the fully\n\
qualified name of the corresponding class.\n\
\n\
When @var{obj} is a regular Java object, structure-like indexing can be\n\
used as a shortcut syntax.  For instance, the two following statements are\n\
equivalent\n\
\n\
@example\n\
@group\n\
  ret = javaMethod (\"method1\", x, 1.0, \"a string\")\n\
  ret = x.method1 (1.0, \"a string\")\n\
@end group\n\
@end example\n\
\n\
@code{javaMethod} returns the result of the method invocation.\n\
\n\
@seealso{methods, javaObject}\n\
@end deftypefn")
{
#if defined (HAVE_JAVA)

  if (args.length () < 2)
    print_usage ();

  std::string methodname = args(0).xstring_value ("javaMethod: METHODNAME must be a string");

  initialize_java ();

  JNIEnv *current_env = thread_jni_env ();

  octave_value retval;

  octave_value_list tmp;
  for (int i=2; i<args.length (); i++)
    tmp(i-2) = args(i);

  if (args(1).is_java ())
    {
      octave_java *jobj = TO_JAVA (args(1));
      retval = jobj->do_javaMethod (current_env, methodname, tmp);
    }
  else if (args(1).is_string ())
    {
      std::string cls = args(1).string_value ();
      retval = octave_java::do_javaMethod (current_env, cls, methodname, tmp);
    }
  else
    error ("javaMethod: OBJ must be a Java object or a string");

  return retval;

#else

  octave_unused_parameter (args);

  err_disabled_feature ("javaMethod", "Java");

#endif
}

/*
%!testif HAVE_JAVA
%! ## Check for valid first two Java version numbers
%! jver = strsplit (javaMethod ("getProperty", "java.lang.System", "java.version"), ".");
%! assert (isfinite (str2double (jver{1})) && isfinite (str2double (jver{2})));
*/

DEFUN (__java_get__, args, ,
       "-*- texinfo -*-\n\
@deftypefn {} {@var{val} =} __java_get__ (@var{obj}, @var{name})\n\
Get the value of the field @var{name} of the Java object @var{obj}.\n\
\n\
For static fields, @var{obj} can be a string representing the fully\n\
qualified name of the corresponding class.\n\
\n\
When @var{obj} is a regular Java object, structure-like indexing can be used\n\
as a shortcut syntax.  For instance, the two following statements are\n\
equivalent\n\
\n\
@example\n\
@group\n\
  __java_get__ (x, \"field1\")\n\
  x.field1\n\
@end group\n\
@end example\n\
\n\
@seealso{__java_set__, javaMethod, javaObject}\n\
@end deftypefn")
{
#if defined (HAVE_JAVA)

  if (args.length () != 2)
    print_usage ();

  std::string name = args(1).string_value ("__java_get__: NAME must be a string");

  initialize_java ();

  JNIEnv *current_env = thread_jni_env ();

  octave_value retval;

  if (args(0).is_java ())
    {
      octave_java *jobj = TO_JAVA (args(0));
      retval = jobj->do_java_get (current_env, name);
    }
  else if (args(0).is_string ())
    {
      std::string cls = args(0).string_value ();
      retval = octave_java::do_java_get (current_env, cls, name);
    }
  else
    error ("__java_get__: OBJ must be a Java object or a string");

  return retval;

#else

  octave_unused_parameter (args);

  err_disabled_feature ("__java_get__", "Java");

#endif
}

DEFUN (__java_set__, args, ,
       "-*- texinfo -*-\n\
@deftypefn {} {@var{obj} =} __java_set__ (@var{obj}, @var{name}, @var{val})\n\
Set the value of the field @var{name} of the Java object @var{obj} to\n\
@var{val}.\n\
\n\
For static fields, @var{obj} can be a string representing the fully\n\
qualified named of the corresponding Java class.\n\
\n\
When @var{obj} is a regular Java object, structure-like indexing can be\n\
used as a shortcut syntax.  For instance, the two following statements are\n\
equivalent\n\
\n\
@example\n\
@group\n\
  __java_set__ (x, \"field1\", val)\n\
  x.field1 = val\n\
@end group\n\
@end example\n\
\n\
@seealso{__java_get__, javaMethod, javaObject}\n\
@end deftypefn")
{
#if defined (HAVE_JAVA)

  if (args.length () != 3)
    print_usage ();

  std::string name = args(1).xstring_value ("__java_set__: NAME must be a string");

  initialize_java ();

  JNIEnv *current_env = thread_jni_env ();

  octave_value retval;

  if (args(0).is_java ())
    {
      octave_java *jobj = TO_JAVA (args(0));
      retval = jobj->do_java_set (current_env, name, args(2));
    }
  else if (args(0).is_string ())
    {
      std::string cls = args(0).string_value ();
      retval = octave_java::do_java_set (current_env, cls, name, args(2));
    }
  else
    error ("__java_set__: OBJ must be a Java object or a string");

  return retval;

#else

  octave_unused_parameter (args);

  err_disabled_feature ("__java_set__", "Java");

#endif
}

DEFUN (java2mat, args, ,
       "-*- texinfo -*-\n\
@deftypefn {} {} java2mat (@var{javaobj})\n\
Undocumented internal function.\n\
@end deftypefn")
{
#if defined (HAVE_JAVA)

  if (args.length () != 1)
    print_usage ();

  initialize_java ();

  JNIEnv *current_env = thread_jni_env ();

  octave_value_list retval;

  if (args(0).is_java ())
    {
      octave_java *jobj = TO_JAVA (args(0));
      retval = ovl (box_more (current_env, jobj->to_java (), 0));
    }
  else
    retval = ovl (args(0));

  return retval;

#else

  octave_unused_parameter (args);

  err_disabled_feature ("java2mat", "Java");

#endif
}

DEFUN (java_matrix_autoconversion, args, nargout,
       "-*- texinfo -*-\n\
@deftypefn  {} {@var{val} =} java_matrix_autoconversion ()\n\
@deftypefnx {} {@var{old_val} =} java_matrix_autoconversion (@var{new_val})\n\
@deftypefnx {} {} java_matrix_autoconversion (@var{new_val}, \"local\")\n\
Query or set the internal variable that controls whether Java arrays are\n\
automatically converted to Octave matrices.\n\
\n\
The default value is false.\n\
\n\
When called from inside a function with the @qcode{\"local\"} option, the\n\
variable is changed locally for the function and any subroutines it calls.\n\
The original variable value is restored when exiting the function.\n\
@seealso{java_unsigned_autoconversion, debug_java}\n\
@end deftypefn")
{
#if defined (HAVE_JAVA)

  return SET_INTERNAL_VARIABLE (java_matrix_autoconversion);

#else

  octave_unused_parameter (args);
  octave_unused_parameter (nargout);

  err_disabled_feature ("java_matrix_autoconversion", "Java");

#endif
}

DEFUN (java_unsigned_autoconversion, args, nargout,
       "-*- texinfo -*-\n\
@deftypefn  {} {@var{val} =} java_unsigned_autoconversion ()\n\
@deftypefnx {} {@var{old_val} =} java_unsigned_autoconversion (@var{new_val})\n\
@deftypefnx {} {} java_unsigned_autoconversion (@var{new_val}, \"local\")\n\
Query or set the internal variable that controls how integer classes are\n\
converted when @code{java_matrix_autoconversion} is enabled.\n\
\n\
When enabled, Java arrays of class Byte or Integer are converted to matrices\n\
of class uint8 or uint32 respectively.  The default value is true.\n\
\n\
When called from inside a function with the @qcode{\"local\"} option, the\n\
variable is changed locally for the function and any subroutines it calls.\n\
The original variable value is restored when exiting the function.\n\
@seealso{java_matrix_autoconversion, debug_java}\n\
@end deftypefn")
{
#if defined (HAVE_JAVA)

  return SET_INTERNAL_VARIABLE (java_unsigned_autoconversion);

#else

  octave_unused_parameter (args);
  octave_unused_parameter (nargout);

  err_disabled_feature ("java_unsigned_autoconversion", "Java");

#endif
}

DEFUN (debug_java, args, nargout,
       "-*- texinfo -*-\n\
@deftypefn  {} {@var{val} =} debug_java ()\n\
@deftypefnx {} {@var{old_val} =} debug_java (@var{new_val})\n\
@deftypefnx {} {} debug_java (@var{new_val}, \"local\")\n\
Query or set the internal variable that determines whether extra debugging\n\
information regarding the initialization of the JVM and any Java exceptions\n\
is printed.\n\
\n\
When called from inside a function with the @qcode{\"local\"} option, the\n\
variable is changed locally for the function and any subroutines it calls.\n\
The original variable value is restored when exiting the function.\n\
@seealso{java_matrix_autoconversion, java_unsigned_autoconversion}\n\
@end deftypefn")
{
#if defined (HAVE_JAVA)

  return SET_INTERNAL_VARIABLE (debug_java);

#else

  octave_unused_parameter (args);
  octave_unused_parameter (nargout);

  err_disabled_feature ("debug_java", "Java");

#endif
}

// Outside of #if defined (HAVE_JAVA) because it is desirable to be able
// to test for the presence of a Java object without having Java
// installed.

DEFUN (isjava, args, ,
       "-*- texinfo -*-\n\
@deftypefn {} {} isjava (@var{x})\n\
Return true if @var{x} is a Java object.\n\
@seealso{class, typeinfo, isa, javaObject}\n\
@end deftypefn")
{
  if (args.length () != 1)
    print_usage ();

  return ovl (args(0).is_java ());
}

/*
## Check automatic conversion of java primitive arrays into octave types.
%!testif HAVE_JAVA
%! assert (javaObject ("java.lang.String", "hello").getBytes (),
%!         int8 ([104 101 108 108 111]'));

## Check automatic conversion of octave types into java primitive arrays.
## Note that uint8 is casted to int8.
%!testif HAVE_JAVA
%! assert (javaMethod ("binarySearch", "java.util.Arrays", [90 100 255], 255), 2);
%! assert (javaMethod ("binarySearch", "java.util.Arrays", uint8 ([90 100 255]), uint8 (255)) < 0);
%! assert (javaMethod ("binarySearch", "java.util.Arrays", uint8 ([90 100 128]), uint8 (128)) < 0);
%! assert (javaMethod ("binarySearch", "java.util.Arrays", uint8 ([90 100 127]), uint8 (127)), 2);
%! assert (javaMethod ("binarySearch", "java.util.Arrays", uint16 ([90 100 128]), uint16 (128)), 2);

## Check we can create objects that wrap java literals (bug #38821).
%!testif HAVE_JAVA
%! assert (class (javaObject ("java.lang.Byte", uint8 (1))), "java.lang.Byte");
%! assert (class (javaObject ("java.lang.Byte", int8 (1))), "java.lang.Byte");
%! assert (class (javaObject ("java.lang.Short", uint16 (1))), "java.lang.Short");
%! assert (class (javaObject ("java.lang.Short", int16 (1))), "java.lang.Short");

## Automatic conversion from string cell array into String[] (bug #45290)
%!testif HAVE_JAVA
%! assert (javaMethod ("binarySearch", "java.util.Arrays", {"aaa", "bbb", "ccc", "zzz"}, "aaa"), 0);
%! assert (javaMethod ("binarySearch", "java.util.Arrays", {"aaa", "bbb", "ccc", "zzz"}, "zzz"), 3);
%! assert (javaMethod ("binarySearch", "java.util.Arrays", {"aaa", "bbb", "ccc", "zzz"}, "hhh") < 0);
*/