GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
ov-java.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2007-2024 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 
27 //! @file ov-java.cc
28 //!
29 //! Provides Octave's Java interface.
30 
31 #if defined (HAVE_CONFIG_H)
32 # include "config.h"
33 #endif
34 
35 #if defined (HAVE_WINDOWS_H)
36 # include <windows.h>
37 #endif
38 
39 #include <algorithm>
40 #include <array>
41 #include <fstream>
42 #include <map>
43 #include <string>
44 #include <vector>
45 
46 #include <clocale>
47 
48 #include "Cell.h"
49 #include "builtin-defun-decls.h"
50 #include "cmd-edit.h"
51 #include "defaults.h"
52 #include "defun.h"
53 #include "error.h"
54 #include "errwarn.h"
55 #include "file-ops.h"
56 #include "file-stat.h"
57 #include "fpucw-wrappers.h"
58 #include "interpreter.h"
59 #include "interpreter-private.h"
60 #include "load-path.h"
61 #include "lo-sysdep.h"
62 #include "oct-env.h"
63 #include "oct-process.h"
64 #include "oct-shlib.h"
65 #include "ov-java.h"
66 #include "variables.h"
67 
68 #if defined (HAVE_JAVA)
69 #include <jni.h>
70 #endif
71 
72 #if defined (HAVE_JAVA)
73 
74 // FIXME: Should these values be configurable at run time?
75 
76 #if defined (OCTAVE_USE_WINDOWS_API)
77 # define LIBJVM_FILE_NAME "jvm.dll"
78 #elif defined (__APPLE__)
79 # define LIBJVM_FILE_NAME "libjvm.dylib"
80 # define JAVA_HOME_CMD "/usr/libexec/java_home"
81 #else
82 # define LIBJVM_FILE_NAME "libjvm.so"
83 #endif
84 
85 #define TO_JOBJECT(obj) reinterpret_cast<jobject> (obj)
86 #define TO_JCLASS(obj) reinterpret_cast<jclass> (obj)
87 
88 #define TO_JNIENV(env) reinterpret_cast<JNIEnv *> (env)
89 
90 typedef jint (JNICALL *JNI_CreateJavaVM_t) (JavaVM **pvm, JNIEnv **penv,
91  void *args);
92 
93 typedef jint (JNICALL *JNI_GetCreatedJavaVMs_t) (JavaVM **pvm, jsize bufLen,
94  jsize *nVMs);
95 
96 template <typename T>
97 class java_local_ref
98 {
99 public:
100 
101  java_local_ref (JNIEnv *env)
102  : m_jobj (nullptr), m_detached (false), m_env (env) { }
103 
104  java_local_ref (JNIEnv *env, T obj)
105  : m_jobj (obj), m_detached (false), m_env (env) { }
106 
107  ~java_local_ref (void) { release (); }
108 
109  T& operator = (T obj)
110  {
111  release ();
112 
113  m_jobj = obj;
114  m_detached = false;
115 
116  return m_jobj;
117  }
118 
119  operator bool () const { return (m_jobj != 0); }
120  operator T () { return m_jobj; }
121 
122  void detach (void) { m_detached = true; }
123 
124 protected:
125 
126  T m_jobj;
127  bool m_detached;
128  JNIEnv *m_env;
129 
130 private:
131 
132  java_local_ref (void)
133  : m_jobj (0), m_detached (false), m_env (0)
134  { }
135 
136  void release (void)
137  {
138  if (m_env && m_jobj && ! m_detached)
139  m_env->DeleteLocalRef (m_jobj);
140 
141  m_jobj = nullptr;
142  }
143 
144 };
145 
146 typedef java_local_ref<jobject> jobject_ref;
147 typedef java_local_ref<jclass> jclass_ref;
148 typedef java_local_ref<jstring> jstring_ref;
149 typedef java_local_ref<jobjectArray> jobjectArray_ref;
150 typedef java_local_ref<jintArray> jintArray_ref;
151 typedef java_local_ref<jbyteArray> jbyteArray_ref;
152 typedef java_local_ref<jdoubleArray> jdoubleArray_ref;
153 typedef java_local_ref<jthrowable> jthrowable_ref;
154 
155 static std::string
156 jstring_to_string (JNIEnv *jni_env, jstring s);
157 
158 static std::string
159 jstring_to_string (JNIEnv *jni_env, jobject obj);
160 
161 static octave_value
162 box (JNIEnv *jni_env, void *jobj, void *jcls_arg = nullptr);
163 
164 static octave_value
165 box_more (JNIEnv *jni_env, void *jobj_arg, void *jcls_arg = nullptr);
166 
167 static bool
168 unbox (JNIEnv *jni_env, const octave_value& val, jobject_ref& jobj,
169  jclass_ref& jcls);
170 
171 static bool
172 unbox (JNIEnv *jni_env, const octave_value_list& args,
173  jobjectArray_ref& jobjs, jobjectArray_ref& jclss);
174 
175 extern "C"
176 {
177  JNIEXPORT jboolean JNICALL
178  Java_org_octave_Octave_call (JNIEnv *, jclass, jstring, jobjectArray,
179  jobjectArray);
180 
181  JNIEXPORT void JNICALL
183 
184  JNIEXPORT void JNICALL
185  Java_org_octave_Octave_doInvoke (JNIEnv *, jclass, jint, jobjectArray);
186 
187  JNIEXPORT void JNICALL
188  Java_org_octave_Octave_doEvalString (JNIEnv *, jclass, jstring);
189 
190  JNIEXPORT jboolean JNICALL
192 }
193 
194 //! The pointer to a java virtual machine either created in the current thread
195 //! or attached this thread to it.
196 
197 static JavaVM *jvm = nullptr;
198 
199 //! Whether the current thread is attached to the jvm given by #jvm.
200 //! This is @c false also if no jvm exists, i.e. if #jvm is @c nullptr.
201 //! @see #initialize_jvm()
202 //! @see #terminate_jvm()
203 
204 static bool jvm_attached = false;
205 
206 //! Need to keep hold of the shared library handle until exit.
207 //! @see #initialize_jvm()
208 //! @see #terminate_jvm()
209 
210 static octave::dynamic_library jvm_lib;
211 
212 static std::map<int, octave_value> listener_map;
213 static std::map<int, octave_value> octave_ref_map;
214 static int octave_java_refcount = 0;
215 
216 //! The thread id of the currently executing thread or @c -1 if this is
217 //! unknown.
218 //! @see #initialize_java()
219 
220 static long octave_thread_ID = -1;
221 
224 bool Vdebug_java = false;
225 
227 
228 class JVMArgs
229 {
230 public:
231 
232  JVMArgs ()
233  {
234  m_vm_args.version = JNI_VERSION_1_6;
235  m_vm_args.nOptions = 0;
236  m_vm_args.options = nullptr;
237  m_vm_args.ignoreUnrecognized = false;
238  }
239 
240  ~JVMArgs ()
241  {
242  clean ();
243  }
244 
245  JavaVMInitArgs * to_args ()
246  {
247  update ();
248  return &m_vm_args;
249  }
250 
251  void add (const std::string& opt)
252  {
253  m_java_opts.push_back (opt);
254  }
255 
256  void read_java_opts (const std::string& filename)
257  {
258  std::ifstream js = sys::ifstream (filename.c_str ());
259 
260  if (! js.bad () && ! js.fail ())
261  {
262  std::string line;
263 
264  while (! js.eof () && ! js.fail ())
265  {
266  std::getline (js, line);
267 
268  if (line.find ('-') == 0)
269  m_java_opts.push_back (line);
270  else if (line.length () > 0 && Vdebug_java)
271  warning ("invalid JVM option, skipping: %s", line.c_str ());
272  }
273  }
274  }
275 
276 private:
277 
278  void clean ()
279  {
280  if (m_vm_args.options != nullptr)
281  {
282  for (int i = 0; i < m_vm_args.nOptions; i++)
283  delete [] m_vm_args.options[i].optionString;
284 
285  delete [] m_vm_args.options;
286 
287  m_vm_args.options = nullptr;
288  m_vm_args.nOptions = 0;
289  }
290  }
291 
292  void update ()
293  {
294  clean ();
295 
296  if (m_java_opts.size () > 0)
297  {
298  int index = 0;
299 
300  m_vm_args.nOptions = m_java_opts.size ();
301  m_vm_args.options = new JavaVMOption [m_vm_args.nOptions];
302 
303  for (const auto& opt : m_java_opts)
304  {
305  if (Vdebug_java)
306  octave_stdout << opt << std::endl;
307  m_vm_args.options[index++].optionString = strsave (opt.c_str ());
308  }
309 
310  m_java_opts.clear ();
311  }
312  }
313 
314 private:
315 
316  JavaVMInitArgs m_vm_args;
317 
318  std::list<std::string> m_java_opts;
319 };
320 
321 OCTAVE_END_NAMESPACE(octave)
322 
323 //! The java initialization directory is given by the environment variable
324 //! @c OCTAVE_JAVA_DIR if defined; otherwise it is the directory of Octave's
325 //! m-files defining Java functions.
326 //!
327 //! The Java initialization directory is the directory where resides:
328 //!
329 //! - @c octave.jar, defining the java classes implementing octave's java
330 //! interface,
331 //! - @c javaclasspath.txt, defining the installation defined portion of the
332 //! (static) classpath,
333 //! - @c java.opts, defining the configurable options of the java virtual
334 //! machine.
335 //!
336 //! Note that the (static) java classpath of the java virtual machine starts
337 //! with @c octave.jar, and that the static java classpath ends with what
338 //! is read from @c javaclasspath.txt located in the initial java directory.
339 //! Moreover, the java virtual machine is created essentially with
340 //! the options given by @c java.opts.
341 
342 static std::string
343 initial_java_dir ()
344 {
345  static std::string java_dir;
346 
347  if (java_dir.empty ())
348  {
349  java_dir = octave::sys::env::getenv ("OCTAVE_JAVA_DIR");
350 
351  if (java_dir.empty ())
352  java_dir = (octave::config::fcn_file_dir ()
353  + octave::sys::file_ops::dir_sep_str () + "java");
354  }
355 
356  return java_dir;
357 }
358 
359 //! Return the classpath in the given file @c filepath as a string.
360 //!
361 //! In the classpath file, each line which is neither empty nor a comment, is
362 //! interpreted as a segment of a path. Comment lines are those starting with
363 //! a @c # or with a @c % in the very first column.
364 //!
365 //! @param filepath The path to the file (usually @c classpath.txt) containing
366 //! a portion of the classpath.
367 //!
368 //! @returns A string consisting of the lines of @c filepath which are neither
369 //! comments nor empty without trailing whitespace separated by
370 //! 'octave::directory_path::path_sep_str()'. The returned string also
371 //! starts with that path separator.
372 
373 static std::string
374 read_classpath_txt (const std::string& filepath)
375 {
376  std::string classpath;
377 
378  std::ifstream fs = octave::sys::ifstream (filepath.c_str ());
379 
380  if (! fs.bad () && ! fs.fail ())
381  {
382  std::string line;
383 
384  while (! fs.eof () && ! fs.fail ())
385  {
386  std::getline (fs, line);
387  if (line.length () > 0 && line[0] != '#' && line[0] != '%')
388  {
389  // prepend separator character
390  classpath.append (octave::directory_path::path_sep_str ());
391 
392  // append content of line without whitespace
393  int last = line.find_last_not_of (" \t\f\v\r\n");
394 
395  classpath.append (octave::sys::file_ops::tilde_expand (line.substr (0, last+1)));
396  }
397  }
398  }
399 
400  return (classpath);
401 }
402 
403 //! Return the initial classpath.
404 //!
405 //! The initial classpath starts with a pointer to @c octave.jar which is
406 //! located in the initial java directory given by #initial_java_dir().
407 //!
408 //! @attention This is nowhere documented and also the script
409 //! @c javaclasspath.m drops this. On the other hand, this is vital because
410 //! @c octave.jar contains the java core classes of octave's java interface.
411 //!
412 //! The rest of the classpath is read sequentially from files
413 //! @c javaclasspath.txt located in either:
414 //!
415 //! - the current directory,
416 //! - the user's home directory,
417 //! - the initial java directory returned by #initial_java_dir()
418 //!
419 //! @returns The initial classpath.
420 
421 static std::string
422 initial_class_path ()
423 {
424  std::string java_dir = initial_java_dir ();
425 
426  std::string retval = java_dir;
427 
428  // Find octave.jar file.
429  if (! retval.empty ())
430  {
431  std::string sep = octave::sys::file_ops::dir_sep_str ();
432 
433  std::string jar_file = java_dir + sep + "octave.jar";
434 
435  octave::sys::file_stat jar_exists (jar_file);
436 
437  if (jar_exists)
438  {
439  // Initialize static classpath to octave.jar.
440  retval = jar_file;
441 
442  // The base classpath has been set.
443  // Try to find an optional file specifying classpaths in 3 places.
444  // 1) Current directory
445  // 2) User's home directory
446  // 3) Octave installation directory where octave.jar resides
447 
448  std::string cwd = octave::sys::env::get_current_directory ();
449  std::string home_dir = octave::sys::env::get_home_directory ();
450 
451  // The filename is "javaclasspath.txt", but historically has been
452  // "classpath.txt" so both are supported.
453  std::vector<std::string> cp_list = {"javaclasspath.txt",
454  "classpath.txt"
455  };
456 
457  for (std::string filename : cp_list)
458  {
459  std::string cp_file = filename;
460  octave::sys::file_stat cp_exists;
461 
462  // Try to find classpath file in the current directory.
463 
464  cp_exists = octave::sys::file_stat (cp_file);
465  if (cp_exists)
466  {
467  // File found. Add its contents to the static classpath.
468  std::string classpath = read_classpath_txt (cp_file);
469  retval.append (classpath);
470  }
471 
472  // Try to find classpath file in the user's home directory.
473 
474  if (cwd != home_dir)
475  {
476  cp_file = '~' + sep + filename;
477  cp_file = octave::sys::file_ops::tilde_expand (cp_file);
478  cp_exists = octave::sys::file_stat (cp_file);
479  if (cp_exists)
480  {
481  // File found. Add its contents to the static classpath.
482  std::string classpath = read_classpath_txt (cp_file);
483  retval.append (classpath);
484  }
485  }
486 
487  // Try to find classpath file in the Octave install directory.
488 
489  if (cwd != java_dir)
490  {
491  cp_file = java_dir + sep + filename;
492  cp_exists = octave::sys::file_stat (cp_file);
493  if (cp_exists)
494  {
495  // File found. Add its contents to the static classpath.
496  std::string classpath = read_classpath_txt (cp_file);
497  retval.append (classpath);
498  }
499  }
500  }
501  }
502  else
503  error ("octave.jar does not exist: %s", jar_file.c_str ());
504  }
505  else
506  error ("initial java dir is empty");
507 
508  return retval;
509 }
510 
511 static std::string
512 get_jvm_lib_path_in_subdir (std::string java_home_path)
513 {
514  // This assumes that whatever architectures are installed are appropriate for
515  // this machine
516 #if defined (OCTAVE_USE_WINDOWS_API)
517  const std::array<const std::string, 2> subdirs = {"bin/client", "bin/server"};
518 #else
519  const std::array<const std::string, 8> subdirs =
520  {
521  "jre/lib/server", "jre/lib", "lib/client", "lib/server",
522  "jre/lib/amd64/client", "jre/lib/amd64/server",
523  "jre/lib/i386/client", "jre/lib/i386/server"
524  };
525 #endif
526 
527  for (std::size_t i = 0; i < subdirs.size (); i++)
528  {
529  std::string candidate = java_home_path + "/" + subdirs[i]
530  + "/" LIBJVM_FILE_NAME;
531  if (octave::sys::file_stat (candidate))
532  return candidate;
533  }
534  return "";
535 }
536 
537 #if defined (OCTAVE_USE_WINDOWS_API)
538 
540 // Declare function defined in sysdep.cc
541 extern LONG
542 get_regkey_value (HKEY h_rootkey, const std::string subkey,
543  const std::string name, octave_value& value);
544 OCTAVE_END_NAMESPACE(octave)
545 
546 static std::string
547 get_jvm_lib_path_from_registry ()
548 {
549  // In Windows, find the location of the JRE from the registry
550  std::string key, jversion, value;
551 
552  // First search for JRE >= 15
553  key = R"(software\javasoft\jdk)";
554 
555  jversion = octave::sys::env::getenv ("JAVA_VERSION");
556  bool maybe_version_15_or_newer = true;
557  octave_value regval;
558  LONG retval;
559  if (jversion.empty ())
560  {
561  value = "CurrentVersion";
562  retval = octave::get_regkey_value (HKEY_LOCAL_MACHINE, key, value,
563  regval);
564 
565  if (retval != ERROR_SUCCESS)
566  {
567  // Search for JRE < 15
568  maybe_version_15_or_newer = false;
569  key = R"(software\javasoft\jre)";
570  retval = octave::get_regkey_value (HKEY_LOCAL_MACHINE, key, value,
571  regval);
572 
573  if (retval != ERROR_SUCCESS)
574  {
575  // Search for JRE < 9
576  key = R"(software\javasoft\java runtime environment)";
577  retval = octave::get_regkey_value (HKEY_LOCAL_MACHINE, key,
578  value, regval);
579  }
580  }
581 
582  if (retval != ERROR_SUCCESS)
583  error ("unable to find Java Runtime Environment: %s::%s",
584  key.c_str (), value.c_str ());
585 
586  jversion = regval.xstring_value ("initialize_jvm: registry value "
587  R"("%s" at "%s" must be a string)",
588  value.c_str (), key.c_str ());
589  }
590 
591  std::string jvm_lib_path;
592  if (maybe_version_15_or_newer)
593  {
594  // Look for value used by JRE >= 15
595  key = key + '\\' + jversion;
596  value = "JavaHome";
597  retval = octave::get_regkey_value (HKEY_LOCAL_MACHINE, key, value,
598  regval);
599 
600  if (retval != ERROR_SUCCESS)
601  error ("unable to find Java Runtime Environment: %s::%s",
602  key.c_str (), value.c_str ());
603 
604  jvm_lib_path
605  = regval.xstring_value (R"(initialize_jvm: registry value "%s" at )"
606  R"("%s" must be a string)",
607  value.c_str (), key.c_str ())
608  + R"(\bin\server\jvm.dll)";
609 
610  if (! jvm_lib_path.empty ())
611  return jvm_lib_path;
612 
613  }
614 
615  // Search for JRE < 15
616  key = R"(software\javasoft\jre\)" + jversion;
617  value = "RuntimeLib";
618  retval = octave::get_regkey_value (HKEY_LOCAL_MACHINE, key, value,
619  regval);
620 
621  if (retval != ERROR_SUCCESS)
622  {
623  // Search for JRE < 9
624  key = R"(software\javasoft\java runtime environment\)" + jversion;
625  retval = octave::get_regkey_value (HKEY_LOCAL_MACHINE, key, value,
626  regval);
627  }
628 
629  if (retval != ERROR_SUCCESS)
630  error ("unable to find Java Runtime Environment: %s::%s",
631  key.c_str (), value.c_str ());
632 
633  jvm_lib_path
634  = regval.xstring_value (R"(initialize_jvm: registry value "%s" at )"
635  R"("%s" must be a string)",
636  value.c_str (), key.c_str ());
637 
638  if (jvm_lib_path.empty ())
639  error ("unable to find Java Runtime Environment: %s::%s",
640  key.c_str (), value.c_str ());
641 
642  return jvm_lib_path;
643 }
644 #endif
645 
646 //! Initialize the java virtual machine (jvm) and field #jvm if necessary.
647 //!
648 //! If the jvm exists and is initialized, #jvm points to it, i.e. is not 0
649 //! and there is nothing to do.
650 //!
651 //! If #jvm is 0 and if at least one jvm exists, attach the current thread to
652 //! it by setting #jvm_attached. Otherwise, create a #jvm with some hard-
653 //! coded options:
654 //!
655 //! - '-Djava.class.path=classpath', where @c classpath is given by
656 //! #initial_class_path().
657 //! - '-Xrs'
658 //!
659 //! Further options are read from the file @c java.opts in the directory given
660 //! by #initial_java_dir().
661 //!
662 //! Note that #initial_class_path() determines the initial classpath. This
663 //! is the static classpath which cannot be changed. Elements of the dynamic
664 //! classpath can be added and removed using the m-file scripts
665 //! @c javaaddpath.m and @c javarmpath.m.
666 //!
667 //! @see #terminate_jvm()
668 
669 static void
670 initialize_jvm ()
671 {
672  // Most of the time JVM already exists and has been initialized.
673  // Also it seems, as if jvm is set, the jvm is already attached.
674  // This does not fit terminate_jvm.
675  if (jvm)
676  return;
677 
678  JNIEnv *current_env;
679  const char *static_locale = setlocale (LC_ALL, nullptr);
680  std::string locale;
681  if (static_locale)
682  locale = std::string (static_locale);
683 
684  octave::dynamic_library lib ("");
685  std::string jvm_lib_path;
686 
687  // Check whether the Java VM library is already loaded or linked in.
688  JNI_CreateJavaVM_t create_vm = reinterpret_cast<JNI_CreateJavaVM_t>
689  (lib.search ("JNI_CreateJavaVM"));
690  JNI_GetCreatedJavaVMs_t get_vm = reinterpret_cast<JNI_GetCreatedJavaVMs_t>
691  (lib.search ("JNI_GetCreatedJavaVMs"));
692 
693  if (create_vm && get_vm)
694  jvm_lib_path = "linked in or loaded libraries";
695  else
696  {
697  // JAVA_HOME environment variable takes precedence
698  std::string java_home_env = octave::sys::env::getenv ("JAVA_HOME");
699  if (! java_home_env.empty ())
700  {
701  jvm_lib_path = get_jvm_lib_path_in_subdir (java_home_env);
702 
703  // If JAVA_HOME does not look like a Java directory, use it anyway
704  // to fail with a useful error message indicating the directory
705  if (jvm_lib_path.empty ())
706  jvm_lib_path = java_home_env + "/" LIBJVM_FILE_NAME;
707  }
708 
709 # if defined (__APPLE__)
710  // Use standard /usr/libexec/java_home if available.
711  if (jvm_lib_path.empty ())
712  {
713  octave::sys::file_stat libexec_java_home_exists (JAVA_HOME_CMD);
714  if (libexec_java_home_exists)
715  {
716  // FIXME: Should this command be fully configurable at run
717  // time? Or is it OK for the options to be fixed here?
718 
719  std::string java_home_cmd = std::string (JAVA_HOME_CMD)
720  + " --failfast --version 1.6+ 2>/dev/null";
721 
722  octave::process_execution_result rslt
723  = octave::run_command_and_return_output (java_home_cmd);
724 
725  if (rslt.exit_status () == 0)
726  {
727  std::string output = rslt.stdout_output ();
728  std::string found_path = output.substr (0, output.length() - 1);
729  std::string jvm_lib_found = get_jvm_lib_path_in_subdir (found_path);
730  if (!jvm_lib_found.empty ())
731  jvm_lib_path = jvm_lib_found;
732  }
733  }
734  }
735 # endif
736 
737  if (jvm_lib_path.empty ())
738  {
739 #if defined (OCTAVE_USE_WINDOWS_API)
740  jvm_lib_path = get_jvm_lib_path_from_registry ();
741 #else
742  // Fall back to JAVA_LDPATH, determined by the build system
743  jvm_lib_path = std::string (JAVA_LDPATH) + "/" LIBJVM_FILE_NAME;
744 #endif
745  }
746 
747  lib = octave::dynamic_library (jvm_lib_path);
748 
749  if (! lib)
750  error ("unable to load Java Runtime Environment from %s",
751  jvm_lib_path.c_str ());
752 
753  create_vm = reinterpret_cast<JNI_CreateJavaVM_t>
754  (lib.search ("JNI_CreateJavaVM"));
755  get_vm = reinterpret_cast<JNI_GetCreatedJavaVMs_t>
756  (lib.search ("JNI_GetCreatedJavaVMs"));
757 
758  if (! create_vm)
759  error ("unable to find JNI_CreateJavaVM in %s", jvm_lib_path.c_str ());
760 
761  if (! get_vm)
762  error ("unable to find JNI_GetCreatedJavaVMs in %s",
763  jvm_lib_path.c_str ());
764  }
765 
766  //! The number of created jvm's.
767  jsize nVMs = 0;
768 
769  if (get_vm (&jvm, 1, &nVMs) == 0 && nVMs > 0)
770  {
771  // At least one JVM exists, try to attach the current thread to it.
772 
773  switch (jvm->GetEnv (reinterpret_cast<void **> (&current_env),
774  JNI_VERSION_1_6))
775  {
776  case JNI_EDETACHED:
777  // Attach the current thread
778  JavaVMAttachArgs m_vm_args;
779  m_vm_args.version = JNI_VERSION_1_6;
780  m_vm_args.name = const_cast<char *> ("octave");
781  m_vm_args.group = nullptr;
782  if (jvm->AttachCurrentThread (reinterpret_cast<void **> (&current_env),
783  &m_vm_args) < 0)
784  error ("JVM internal error, unable to attach octave to existing JVM");
785  break;
786 
787  case JNI_EVERSION:
788  error ("JVM internal error, the required JNI version is not supported");
789 
790  case JNI_OK:
791  // Don't do anything, the current thread is already attached to JVM
792  break;
793  }
794 
795  jvm_attached = true;
796  }
797  else
798  {
799  // No JVM exists, create one
800 
801  octave::JVMArgs m_vm_args;
802 
803  // Hard-coded options for the jvm.
804  m_vm_args.add ("-Djava.class.path=" + initial_class_path ());
805 #if defined (HAVE_BROKEN_PTHREAD_STACKSIZE)
806  m_vm_args.add ("-Djdk.lang.processReaperUseDefaultStackSize=true");
807 #endif
808  m_vm_args.add ("-Xrs");
809 
810  // Additional options given by file java.opts.
811  m_vm_args.read_java_opts (initial_java_dir () +
813  "java.opts");
814 
815  if (create_vm (&jvm, &current_env, m_vm_args.to_args ()) != JNI_OK)
816  error ("unable to start Java VM in %s", jvm_lib_path.c_str ());
817  }
818 
819  jvm_lib = lib;
820 
821  setlocale (LC_ALL, locale.c_str ());
822 }
823 
824 //! Terminate the current jvm, if there is any.
825 //!
826 //! Otherwise, detach the jvm if this thread is attached to it and unload it
827 //! if this thread created it itself.
828 //!
829 //! @see #initialize_jvm()
830 
831 static void
832 terminate_jvm ()
833 {
834  // There is nothing to do if jvm is not set (= nullptr).
835  if (jvm)
836  {
837  // FIXME: Seems that if jvm_attached is always true if jvm is not null.
838  if (jvm_attached)
839  jvm->DetachCurrentThread ();
840  else
841  jvm->DestroyJavaVM ();
842 
843  jvm = nullptr;
844  jvm_attached = false;
845 
846  if (jvm_lib)
847  jvm_lib.close ();
848 
850  }
851 }
852 
853 //! Converts a Java string object to std::string.
854 //!{
855 static std::string
856 jstring_to_string (JNIEnv *jni_env, jstring s)
857 {
858  std::string retval;
859 
860  if (jni_env)
861  {
862  const char *cstr = jni_env->GetStringUTFChars (s, nullptr);
863  retval = cstr;
864  jni_env->ReleaseStringUTFChars (s, cstr);
865  }
866 
867  return retval;
868 }
869 
870 static std::string
871 jstring_to_string (JNIEnv *jni_env, jobject obj)
872 {
873  std::string retval;
874 
875  if (jni_env && obj)
876  {
877  jclass_ref cls (jni_env, jni_env->FindClass ("java/lang/String"));
878  if (cls)
879  {
880  if (jni_env->IsInstanceOf (obj, cls))
881  retval = jstring_to_string (jni_env,
882  reinterpret_cast<jstring> (obj));
883  }
884  }
885 
886  return retval;
887 }
888 //!}
889 
890 //! Returns a reference to the jni (java native interface) environment of the
891 //! Java virtual machine #jvm.
892 //!
893 //! @returns A reference to jni, if #jvm is present, otherwise @c nullptr.
894 
895 static inline JNIEnv *
896 thread_jni_env ()
897 {
898  JNIEnv *env = nullptr;
899 
900  if (jvm)
901  jvm->GetEnv (reinterpret_cast<void **> (&env), JNI_VERSION_1_6);
902 
903  return env;
904 }
905 
906 #endif
907 
908 bool
910 {
911 #if defined (HAVE_JAVA)
912 
913  JNIEnv *current_env = thread_jni_env ();
914 
915  if (current_env && m_java_object)
916  {
917  jclass_ref cls (current_env, current_env->FindClass ("java/lang/String"));
918  return current_env->IsInstanceOf (TO_JOBJECT (m_java_object), cls);
919  }
920 
921  return false;
922 
923 #else
924 
925  // This shouldn't happen because construction of octave_java objects is
926  // supposed to be impossible if Java is not available.
927 
928  panic_impossible ();
929 
930 #endif
931 }
932 
933 bool
934 octave_java::is_instance_of (const std::string& cls_name) const
935 {
936 #if defined (HAVE_JAVA)
937 
938  JNIEnv *current_env = thread_jni_env ();
939 
940  std::string cls_cpp = cls_name;
941  std::replace (cls_cpp.begin (), cls_cpp.end (), '.', '/');
942 
943  if (current_env && m_java_object)
944  {
945  jclass_ref cls (current_env, current_env->FindClass (cls_cpp.c_str ()));
946  if (current_env->ExceptionCheck ())
947  current_env->ExceptionClear ();
948  else
949  return current_env->IsInstanceOf (TO_JOBJECT (m_java_object), cls);
950  }
951  return false;
952 
953 #else
954 
955  octave_unused_parameter (cls_name);
956 
957  // This shouldn't happen because construction of octave_java objects is
958  // supposed to be impossible if Java is not available.
959 
960  panic_impossible ();
961 
962 #endif
963 }
964 
965 #if defined (HAVE_JAVA)
966 
967 static octave_value
968 check_exception (JNIEnv *jni_env)
969 {
970  octave_value retval;
971 
972  jthrowable_ref ex (jni_env, jni_env->ExceptionOccurred ());
973 
974  if (ex)
975  {
976  if (Vdebug_java)
977  jni_env->ExceptionDescribe ();
978 
979  jni_env->ExceptionClear ();
980 
981  jclass_ref jcls (jni_env, jni_env->GetObjectClass (ex));
982  jmethodID mID = jni_env->GetMethodID (jcls, "toString",
983  "()Ljava/lang/String;");
984  jstring_ref js (jni_env,
985  reinterpret_cast<jstring> (jni_env->CallObjectMethod (ex,
986  mID)));
987  std::string msg = jstring_to_string (jni_env, js);
988 
989  error ("[java] %s", msg.c_str ());
990  }
991  else
992  retval = Matrix ();
993 
994  return retval;
995 }
996 
997 static jclass
998 find_octave_class (JNIEnv *jni_env, const char *name)
999 {
1000  static std::string class_loader;
1001  static jclass uiClass = nullptr;
1002 
1003  jclass jcls = jni_env->FindClass (name);
1004 
1005  if (jcls == nullptr)
1006  {
1007  jni_env->ExceptionClear ();
1008 
1009  if (! uiClass)
1010  {
1011  if (class_loader.empty ())
1012  {
1013  jclass_ref syscls (jni_env,
1014  jni_env->FindClass ("java/lang/System"));
1015  jmethodID mID = jni_env->GetStaticMethodID
1016  (syscls,
1017  "getProperty",
1018  "(Ljava/lang/String;)Ljava/lang/String;");
1019  jstring_ref js (jni_env,
1020  jni_env->NewStringUTF ("octave.class.loader"));
1021  js = reinterpret_cast<jstring> (jni_env->CallStaticObjectMethod
1022  (syscls, mID, jstring (js)));
1023  class_loader = jstring_to_string (jni_env, jstring (js));
1024  std::replace (class_loader.begin (), class_loader.end (),
1025  '.', '/');
1026  }
1027 
1028  jclass_ref uicls (jni_env,
1029  jni_env->FindClass (class_loader.c_str ()));
1030 
1031  if (! uicls)
1032  {
1033  jni_env->ExceptionClear ();
1034 
1035  // Try the netbeans way
1036  std::replace (class_loader.begin (), class_loader.end (),
1037  '/', '.');
1038  jclass_ref jcls2 (jni_env,
1039  jni_env->FindClass ("org/openide/util/Lookup"));
1040  jmethodID mID = jni_env->GetStaticMethodID
1041  (jcls2, "getDefault", "()Lorg/openide/util/Lookup;");
1042  jobject_ref lObj (jni_env,
1043  jni_env->CallStaticObjectMethod (jcls2, mID));
1044  mID = jni_env->GetMethodID (jcls2, "lookup",
1045  "(Ljava/lang/Class;)Ljava/lang/Object;");
1046  jclass_ref cLoaderCls (jni_env,
1047  jni_env->FindClass ("java/lang/ClassLoader"));
1048  jobject_ref cLoader (jni_env,
1049  jni_env->CallObjectMethod
1050  (lObj, mID, jclass (cLoaderCls)));
1051  mID = jni_env->GetMethodID (cLoaderCls, "loadClass",
1052  "(Ljava/lang/String;)Ljava/lang/Class;");
1053  jstring_ref js (jni_env,
1054  jni_env->NewStringUTF (class_loader.c_str ()));
1055  uicls = reinterpret_cast<jclass>
1056  (jni_env->CallObjectMethod (cLoader, mID, jstring (js)));
1057  }
1058 
1059  if (uicls)
1060  uiClass = reinterpret_cast<jclass>
1061  (jni_env->NewGlobalRef (jclass (uicls)));
1062  }
1063 
1064  if (uiClass)
1065  {
1066  jmethodID mID = jni_env->GetStaticMethodID
1067  (uiClass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
1068  jstring_ref js (jni_env, jni_env->NewStringUTF (name));
1069  jcls = reinterpret_cast<jclass>
1070  (jni_env->CallStaticObjectMethod (uiClass, mID, jstring (js)));
1071  }
1072  }
1073 
1074  return jcls;
1075 }
1076 
1077 static dim_vector
1078 compute_array_dimensions (JNIEnv *jni_env, jobject obj)
1079 {
1080  jobjectArray_ref jobj (jni_env, reinterpret_cast<jobjectArray> (obj));
1081  jclass_ref jcls (jni_env, jni_env->GetObjectClass (obj));
1082  jclass_ref ccls (jni_env, jni_env->GetObjectClass (jcls));
1083  jmethodID isArray_ID = jni_env->GetMethodID (ccls, "isArray", "()Z");
1084  jmethodID getComponentType_ID = jni_env->GetMethodID (ccls,
1085  "getComponentType",
1086  "()Ljava/lang/Class;");
1087 
1088  dim_vector dv (1, 1);
1089  int idx = 0;
1090 
1091  jobj.detach ();
1092  while (jcls && jni_env->CallBooleanMethod (jcls, isArray_ID))
1093  {
1094  int len = (jobj ? jni_env->GetArrayLength (jobj) : 0);
1095  if (idx >= dv.ndims ())
1096  dv.resize (idx+1);
1097  dv(idx) = len;
1098  jcls = reinterpret_cast<jclass>
1099  (jni_env->CallObjectMethod (jcls, getComponentType_ID));
1100  jobj = len > 0
1101  ? reinterpret_cast<jobjectArray> (jni_env->GetObjectArrayElement (jobj,
1102  0))
1103  : nullptr;
1104  idx++;
1105  }
1106 
1108 
1109  return dv;
1110 }
1111 
1112 static jobject
1113 make_java_index (JNIEnv *jni_env, const octave_value_list& idx)
1114 {
1115  jclass_ref ocls (jni_env, jni_env->FindClass ("[I"));
1116  jobjectArray retval = jni_env->NewObjectArray (idx.length (), ocls, nullptr);
1117  // Here retval has the same length as idx
1118 
1119  // Fill in entries of idx into retval
1120  for (int i = 0; i < idx.length (); i++)
1121  try
1122  {
1123  octave::idx_vector v = idx(i).index_vector ();
1124 
1125  jintArray_ref i_array (jni_env, jni_env->NewIntArray (v.length ()));
1126  jint *buf = jni_env->GetIntArrayElements (i_array, nullptr);
1127  // Here, buf points to the beginning of i_array
1128 
1129  // Copy v to buf.
1130  for (int k = 0; k < v.length (); k++)
1131  buf[k] = v(k);
1132 
1133  // Set retval[i] = i_array
1134  jni_env->ReleaseIntArrayElements (i_array, buf, 0);
1135  jni_env->SetObjectArrayElement (retval, i, i_array);
1136 
1137  check_exception (jni_env);
1138  }
1139  catch (octave::index_exception& ie)
1140  {
1141  // Rethrow to allow more info to be reported later.
1142  ie.set_pos_if_unset (idx.length (), i + 1);
1143  throw;
1144  }
1145 
1146  return retval;
1147 }
1148 
1149 static octave_value
1150 get_array_elements (JNIEnv *jni_env, jobject jobj,
1151  const octave_value_list& idx)
1152 {
1153  octave_value retval;
1154  jobject_ref resObj (jni_env);
1155  jobject_ref java_idx (jni_env, make_java_index (jni_env, idx));
1156 
1157  jclass_ref helperClass (jni_env,
1158  find_octave_class (jni_env,
1159  "org/octave/ClassHelper"));
1160  jmethodID mID = jni_env
1161  ->GetStaticMethodID (helperClass, "arraySubsref",
1162  "(Ljava/lang/Object;[[I)Ljava/lang/Object;");
1163  resObj = jni_env->CallStaticObjectMethod
1164  (helperClass, mID, jobj, jobject (java_idx));
1165 
1166  if (resObj)
1167  retval = box (jni_env, resObj);
1168  else
1169  retval = check_exception (jni_env);
1170 
1172 
1173  return retval;
1174 }
1175 
1176 static octave_value
1177 set_array_elements (JNIEnv *jni_env, jobject jobj,
1178  const octave_value_list& idx, const octave_value& rhs)
1179 {
1180  octave_value retval;
1181 
1182  jclass_ref rhsCls (jni_env);
1183  jobject_ref resObj (jni_env);
1184  jobject_ref rhsObj (jni_env);
1185  jobject_ref java_idx (jni_env, make_java_index (jni_env, idx));
1186 
1187  if (unbox (jni_env, rhs, rhsObj, rhsCls))
1188  {
1189  jclass_ref helperClass (jni_env,
1190  find_octave_class (jni_env,
1191  "org/octave/ClassHelper"));
1192  jmethodID mID = jni_env->GetStaticMethodID (helperClass, "arraySubsasgn",
1193  "(Ljava/lang/Object;[[ILjava/lang/Object;)" "Ljava/lang/Object;");
1194  resObj = jni_env->CallStaticObjectMethod
1195  (helperClass, mID, jobj, jobject (java_idx), jobject (rhsObj));
1196  }
1197 
1198  if (resObj)
1199  retval = box (jni_env, resObj);
1200  else
1201  retval = check_exception (jni_env);
1202 
1204 
1205  return retval;
1206 }
1207 
1208 static string_vector
1209 get_invoke_list (JNIEnv *jni_env, void *jobj_arg)
1210 {
1211  jobject jobj = TO_JOBJECT (jobj_arg);
1212 
1213  std::list<std::string> name_list;
1214 
1215  if (jni_env)
1216  {
1217  jclass_ref cls (jni_env, jni_env->GetObjectClass (jobj));
1218  jclass_ref ccls (jni_env, jni_env->GetObjectClass (cls));
1219  jmethodID getMethods_ID = jni_env->GetMethodID
1220  (ccls, "getMethods", "()[Ljava/lang/reflect/Method;");
1221  jmethodID getFields_ID = jni_env->GetMethodID
1222  (ccls, "getFields", "()[Ljava/lang/reflect/Field;");
1223  jobjectArray_ref mList (jni_env,
1224  reinterpret_cast<jobjectArray>
1225  (jni_env->CallObjectMethod (cls, getMethods_ID)));
1226  jobjectArray_ref fList (jni_env,
1227  reinterpret_cast<jobjectArray>
1228  (jni_env->CallObjectMethod (cls, getFields_ID)));
1229  int mLen = jni_env->GetArrayLength (mList);
1230  int fLen = jni_env->GetArrayLength (fList);
1231  jclass_ref mCls (jni_env,
1232  jni_env->FindClass ("java/lang/reflect/Method"));
1233  jclass_ref fCls (jni_env,
1234  jni_env->FindClass ("java/lang/reflect/Field"));
1235  jmethodID m_getName_ID = jni_env->GetMethodID (mCls, "getName",
1236  "()Ljava/lang/String;");
1237  jmethodID f_getName_ID = jni_env->GetMethodID (fCls, "getName",
1238  "()Ljava/lang/String;");
1239 
1240  for (int i = 0; i < mLen; i++)
1241  {
1242  jobject_ref meth (jni_env, jni_env->GetObjectArrayElement (mList, i));
1243  jstring_ref methName (jni_env, reinterpret_cast<jstring>
1244  (jni_env->CallObjectMethod (meth,
1245  m_getName_ID)));
1246  name_list.push_back (jstring_to_string (jni_env, methName));
1247  }
1248 
1249  for (int i = 0; i < fLen; i++)
1250  {
1251  jobject_ref field (jni_env,
1252  jni_env->GetObjectArrayElement (fList, i));
1253  jstring_ref fieldName (jni_env,
1254  reinterpret_cast<jstring>
1255  (jni_env->CallObjectMethod
1256  (field, f_getName_ID)));
1257  name_list.push_back (jstring_to_string (jni_env, fieldName));
1258  }
1259 
1261  }
1262 
1263  string_vector v (name_list);
1264 
1265  return v.sort (true);
1266 }
1267 
1268 static octave_value
1269 convert_to_string (JNIEnv *jni_env, jobject m_java_object, bool force,
1270  char type)
1271 {
1272  octave_value retval;
1273 
1274  if (jni_env && m_java_object)
1275  {
1276  jclass_ref cls (jni_env, jni_env->FindClass ("java/lang/String"));
1277 
1278  if (jni_env->IsInstanceOf (m_java_object, cls))
1279  retval = octave_value (jstring_to_string (jni_env, m_java_object),
1280  type);
1281  else if (force)
1282  {
1283  cls = jni_env->FindClass ("[Ljava/lang/String;");
1284 
1285  if (jni_env->IsInstanceOf (m_java_object, cls))
1286  {
1287  jobjectArray array = reinterpret_cast<jobjectArray> (m_java_object);
1288  int len = jni_env->GetArrayLength (array);
1289  Cell c (len, 1);
1290 
1291  for (int i = 0; i < len; i++)
1292  {
1293  jstring_ref js (jni_env,
1294  reinterpret_cast<jstring>
1295  (jni_env->GetObjectArrayElement (array, i)));
1296 
1297  if (js)
1298  c(i) = octave_value (jstring_to_string (jni_env, js), type);
1299  else
1300  c(i) = check_exception (jni_env);
1301  }
1302 
1303  retval = octave_value (c);
1304  }
1305  else
1306  {
1307  cls = jni_env->FindClass ("java/lang/Object");
1308  jmethodID mID = jni_env->GetMethodID (cls, "toString",
1309  "()Ljava/lang/String;");
1310  jstring_ref js (jni_env,
1311  reinterpret_cast<jstring>
1312  (jni_env->CallObjectMethod (m_java_object,
1313  mID)));
1314 
1315  if (js)
1316  retval = octave_value (jstring_to_string (jni_env, js), type);
1317  else
1318  retval = check_exception (jni_env);
1319  }
1320  }
1321  else
1322  error ("unable to convert Java object to string");
1323 
1325  }
1326 
1327  return retval;
1328 }
1329 
1330 #define TO_JAVA(obj) dynamic_cast<octave_java *> ((obj).internal_rep ())
1331 
1332 //! Return whether @c jobj shall be automatically converted to an Octave
1333 //! numeric value.
1334 //!
1335 //! If @c jobj is an instance of any of the numeric wrapper classes @c Byte,
1336 //! @c Integer, @c Long, @c Short, @c Float, or @c Double, then it will be
1337 //! converted using the @c java.lang.Number.doubleValue() method.
1338 //!
1339 //! @param jni_env JNI environment pointer.
1340 //! @param jobj Java object being returned to Octave
1341 //! @return @c true if @c jobj shall be converted into a numeric value
1342 //! automatically, @c false otherwise
1343 static bool
1344 is_auto_convertible_number (JNIEnv *jni_env, jobject jobj)
1345 {
1346  jclass_ref cls (jni_env);
1347  cls = jni_env->FindClass ("java/lang/Double");
1348  if (jni_env->IsInstanceOf (jobj, cls))
1349  return true;
1350  cls = jni_env->FindClass ("java/lang/Float");
1351  if (jni_env->IsInstanceOf (jobj, cls))
1352  return true;
1353  cls = jni_env->FindClass ("java/lang/Byte");
1354  if (jni_env->IsInstanceOf (jobj, cls))
1355  return true;
1356  cls = jni_env->FindClass ("java/lang/Short");
1357  if (jni_env->IsInstanceOf (jobj, cls))
1358  return true;
1359  cls = jni_env->FindClass ("java/lang/Integer");
1360  if (jni_env->IsInstanceOf (jobj, cls))
1361  return true;
1362  cls = jni_env->FindClass ("java/lang/Long");
1363  if (jni_env->IsInstanceOf (jobj, cls))
1364  return true;
1365 
1366  return false;
1367 }
1368 
1369 //! Convert the Java object pointed to by @c jobj_arg with class @c jcls_arg
1370 //! to an Octave value.
1371 //!
1372 //! @param jni_env JNI environment pointer.
1373 //! @param jobj_arg Pointer to a Java object.
1374 //! @param jcls_arg Optional pointer to the Java class of @c jobj_arg.
1375 //!
1376 //! @return
1377 //! @arg numeric value as a @c double if @c jobj_arg is of type @c Byte,
1378 //! @c Short, @c Integer, @c Long, @c Float or @c Double
1379 //! @arg logical value if @c jobj_arg is of type @c Boolean
1380 //! @arg string value if @c jobj_arg is of type @c Character or @c String
1381 //! @arg Octave array of numeric, logical, or char type if @c jobj_arg is
1382 //! a Java array of primitive types
1383 //! @arg Octave matrix if @c jobj_arg is of type @c org.octave.Matrix and
1384 //! #Vjava_matrix_autoconversion is enabled
1385 //! @arg Octave object if @c jobj_arg is of type
1386 //! @c org.octave.OctaveReference
1387 //! @arg @c octave_java object wrapping the Java object otherwise.
1388 
1389 static octave_value
1390 box (JNIEnv *jni_env, void *jobj_arg, void *jcls_arg)
1391 {
1392  octave_value retval;
1393 
1394  jobject jobj = TO_JOBJECT (jobj_arg);
1395  jclass jcls = TO_JCLASS (jcls_arg);
1396 
1397  jclass_ref cls (jni_env);
1398 
1399  if (! jobj)
1400  retval = Matrix ();
1401 
1402  while (retval.is_undefined ())
1403  {
1404  // Convert a scalar of any numeric class wrapping a primitive class
1405  // (byte, short, integer, long, float, double) to a double value.
1406  // Test whether java.lang.Number before testing for each type.
1407  cls = jni_env->FindClass ("java/lang/Number");
1408  if (jni_env->IsInstanceOf (jobj, cls)
1409  && is_auto_convertible_number (jni_env, jobj))
1410  {
1411  jmethodID m = jni_env->GetMethodID (cls, "doubleValue", "()D");
1412  retval = jni_env->CallDoubleMethod (jobj, m);
1413  break;
1414  }
1415 
1416  cls = jni_env->FindClass ("java/lang/Boolean");
1417  if (jni_env->IsInstanceOf (jobj, cls))
1418  {
1419  jmethodID m = jni_env->GetMethodID (cls, "booleanValue", "()Z");
1420  retval = (jni_env->CallBooleanMethod (jobj, m) ? true : false);
1421  break;
1422  }
1423 
1424  cls = jni_env->FindClass ("java/lang/String");
1425  if (jni_env->IsInstanceOf (jobj, cls))
1426  {
1427  retval = jstring_to_string (jni_env, jobj);
1428  break;
1429  }
1430 
1431  cls = jni_env->FindClass ("java/lang/Character");
1432  if (jni_env->IsInstanceOf (jobj, cls))
1433  {
1434  jmethodID m = jni_env->GetMethodID (cls, "charValue", "()C");
1435  retval = jni_env->CallCharMethod (jobj, m);
1436  retval = retval.convert_to_str (false, true);
1437  break;
1438  }
1439 
1440 #define BOX_PRIMITIVE_ARRAY(JAVA_TYPE, JAVA_ID, JAVA_TYPE_CAP, OCTAVE_ID) \
1441  cls = jni_env->FindClass (JAVA_ID); \
1442  if (jni_env->IsInstanceOf (jobj, cls)) \
1443  { \
1444  const JAVA_TYPE ## Array jarr = reinterpret_cast<JAVA_TYPE ## Array> (jobj); \
1445  const jsize len = jni_env->GetArrayLength (jarr); \
1446  OCTAVE_ID ## NDArray d (dim_vector (len, 1)); \
1447  JAVA_TYPE *buffer = reinterpret_cast<JAVA_TYPE *> (d.fortran_vec ()); \
1448  jni_env->Get ## JAVA_TYPE_CAP ## ArrayRegion (jarr, 0, len, buffer); \
1449  retval = d; \
1450  break; \
1451  }
1452 
1453  BOX_PRIMITIVE_ARRAY (jdouble, "[D", Double, )
1454  BOX_PRIMITIVE_ARRAY (jboolean, "[Z", Boolean, bool)
1455  BOX_PRIMITIVE_ARRAY (jfloat, "[F", Float, Float)
1456  BOX_PRIMITIVE_ARRAY (jchar, "[C", Char, char)
1457  BOX_PRIMITIVE_ARRAY (jbyte, "[B", Byte, int8)
1458  BOX_PRIMITIVE_ARRAY (jshort, "[S", Short, int16)
1459  BOX_PRIMITIVE_ARRAY (jint, "[I", Int, int32)
1460  BOX_PRIMITIVE_ARRAY (jlong, "[J", Long, int64)
1461 
1462 #undef BOX_PRIMITIVE_ARRAY
1463 
1465  {
1466  cls = find_octave_class (jni_env, "org/octave/Matrix");
1467 
1468  if (jni_env->IsInstanceOf (jobj, cls))
1469  {
1470  jmethodID mID = jni_env->GetMethodID (cls, "getDims", "()[I");
1471  jintArray_ref iv (jni_env,
1472  reinterpret_cast<jintArray>
1473  (jni_env->CallObjectMethod (jobj, mID)));
1474  jint *iv_data = jni_env->GetIntArrayElements (jintArray (iv),
1475  nullptr);
1476  dim_vector dims;
1477  dims.resize (jni_env->GetArrayLength (jintArray (iv)));
1478 
1479  for (int i = 0; i < dims.ndims (); i++)
1480  dims(i) = iv_data[i];
1481 
1482  jni_env->ReleaseIntArrayElements (jintArray (iv), iv_data, 0);
1483  mID = jni_env->GetMethodID (cls, "getClassName",
1484  "()Ljava/lang/String;");
1485  jstring_ref js (jni_env,
1486  reinterpret_cast<jstring>
1487  (jni_env->CallObjectMethod (jobj, mID)));
1488 
1489  std::string s = jstring_to_string (jni_env, js);
1490 
1491  if (s == "double")
1492  {
1493  NDArray m (dims);
1494  mID = jni_env->GetMethodID (cls, "toDouble", "()[D");
1495  jdoubleArray_ref dv (jni_env,
1496  reinterpret_cast<jdoubleArray>
1497  (jni_env->CallObjectMethod (jobj,
1498  mID)));
1499  jni_env->GetDoubleArrayRegion (dv, 0, m.numel (),
1500  m.fortran_vec ());
1501  retval = m;
1502  break;
1503  }
1504  else if (s == "byte")
1505  {
1507  {
1508  uint8NDArray m (dims);
1509  mID = jni_env->GetMethodID (cls, "toByte", "()[B");
1510  jbyteArray_ref dv (jni_env,
1511  reinterpret_cast<jbyteArray>
1512  (jni_env->CallObjectMethod (jobj,
1513  mID)));
1514  jni_env->GetByteArrayRegion (dv, 0, m.numel (),
1515  reinterpret_cast<jbyte *>
1516  (m.fortran_vec ()));
1517  retval = m;
1518  break;
1519  }
1520  else
1521  {
1522  int8NDArray m (dims);
1523  mID = jni_env->GetMethodID (cls, "toByte", "()[B");
1524  jbyteArray_ref dv (jni_env,
1525  reinterpret_cast<jbyteArray>
1526  (jni_env->CallObjectMethod (jobj,
1527  mID)));
1528  jni_env->GetByteArrayRegion (dv, 0, m.numel (),
1529  reinterpret_cast<jbyte *>
1530  (m.fortran_vec ()));
1531  retval = m;
1532  break;
1533  }
1534  }
1535  else if (s == "integer")
1536  {
1538  {
1539  uint32NDArray m (dims);
1540  mID = jni_env->GetMethodID (cls, "toInt", "()[I");
1541  jintArray_ref dv (jni_env,
1542  reinterpret_cast<jintArray>
1543  (jni_env->CallObjectMethod (jobj,
1544  mID)));
1545  jni_env->GetIntArrayRegion (dv, 0, m.numel (),
1546  reinterpret_cast<jint *>
1547  (m.fortran_vec ()));
1548  retval = m;
1549  break;
1550  }
1551  else
1552  {
1553  int32NDArray m (dims);
1554  mID = jni_env->GetMethodID (cls, "toInt", "()[I");
1555  jintArray_ref dv (jni_env,
1556  reinterpret_cast<jintArray>
1557  (jni_env->CallObjectMethod (jobj,
1558  mID)));
1559  jni_env->GetIntArrayRegion (dv, 0, m.numel (),
1560  reinterpret_cast<jint *>
1561  (m.fortran_vec ()));
1562  retval = m;
1563  break;
1564  }
1565  }
1566  }
1567  }
1568 
1569  cls = find_octave_class (jni_env, "org/octave/OctaveReference");
1570  if (jni_env->IsInstanceOf (jobj, cls))
1571  {
1572  jmethodID mID = jni_env->GetMethodID (cls, "getID", "()I");
1573  int ID = jni_env->CallIntMethod (jobj, mID);
1574  auto it = octave_ref_map.find (ID);
1575 
1576  if (it != octave_ref_map.end ())
1577  retval = it->second;
1578  break;
1579  }
1580 
1581  // No suitable class found. Return a generic octave_java object.
1582  retval = octave_value (new octave_java (jobj, jcls));
1583  break;
1584  }
1585 
1586  return retval;
1587 }
1588 
1589 static octave_value
1590 box_more (JNIEnv *jni_env, void *jobj_arg, void *jcls_arg)
1591 {
1592  jobject jobj = TO_JOBJECT (jobj_arg);
1593  jclass jcls = TO_JCLASS (jcls_arg);
1594 
1595  octave_value retval = box (jni_env, jobj, jcls);
1596 
1597  if (retval.isjava ())
1598  {
1599  retval = octave_value ();
1600 
1601  jclass_ref cls (jni_env);
1602 
1603  if (retval.is_undefined ())
1604  {
1605  cls = jni_env->FindClass ("[D");
1606 
1607  if (jni_env->IsInstanceOf (jobj, cls))
1608  {
1609  jdoubleArray jarr = reinterpret_cast<jdoubleArray> (jobj);
1610  int len = jni_env->GetArrayLength (jarr);
1611 
1612  if (len > 0)
1613  {
1614  Matrix m (1, len);
1615  jni_env->GetDoubleArrayRegion (jarr, 0, len,
1616  m.fortran_vec ());
1617  retval = m;
1618  }
1619  else
1620  retval = Matrix ();
1621  }
1622  }
1623 
1624  if (retval.is_undefined ())
1625  {
1626  cls = jni_env->FindClass ("[[D");
1627 
1628  if (jni_env->IsInstanceOf (jobj, cls))
1629  {
1630  jobjectArray jarr = reinterpret_cast<jobjectArray> (jobj);
1631  int rows = jni_env->GetArrayLength (jarr);
1632  int cols = 0;
1633 
1634  if (rows > 0)
1635  {
1636  Matrix m;
1637 
1638  for (int r = 0; r < rows; r++)
1639  {
1640  jdoubleArray_ref row (jni_env,
1641  reinterpret_cast<jdoubleArray>
1642  (jni_env->GetObjectArrayElement
1643  (jarr, r)));
1644 
1645  if (m.isempty ())
1646  {
1647  cols = jni_env->GetArrayLength (row);
1648  m.resize (cols, rows);
1649  }
1650  jni_env->GetDoubleArrayRegion
1651  (row, 0, cols, m.fortran_vec () + r * cols);
1652  }
1653  retval = m.transpose ();
1654  }
1655  else
1656  retval = Matrix ();
1657  }
1658  }
1659 
1660  if (retval.is_undefined ())
1661  {
1662  cls = jni_env->FindClass ("[Ljava/lang/String;");
1663 
1664  if (jni_env->IsInstanceOf (jobj, cls))
1665  {
1666  jobjectArray jarr = reinterpret_cast<jobjectArray> (jobj);
1667  int len = jni_env->GetArrayLength (jarr);
1668  Cell m (len, 1);
1669 
1670  for (int i = 0; i < len; i++)
1671  {
1672  jstring_ref js (jni_env,
1673  reinterpret_cast<jstring>
1674  (jni_env->GetObjectArrayElement (jarr, i)));
1675  m(i) = jstring_to_string (jni_env, js);
1676  }
1677 
1678  retval = m;
1679  }
1680  }
1681  }
1682 
1683  if (retval.is_undefined ())
1684  retval = octave_value (new octave_java (jobj, jcls));
1685 
1687 
1688  return retval;
1689 }
1690 
1691 static bool
1692 unbox (JNIEnv *jni_env, const octave_value& val, jobject_ref& jobj,
1693  jclass_ref& jcls)
1694 {
1695  bool found = true;
1696 
1697  if (val.isjava ())
1698  {
1699  octave_java *ovj = TO_JAVA (val);
1700  jobj = TO_JOBJECT (ovj->to_java ());
1701  jobj.detach ();
1702  jcls = jni_env->GetObjectClass (jobj);
1703  }
1704  else if (val.is_string ())
1705  {
1706  std::string s = val.string_value ();
1707 
1708  jobj = jni_env->NewStringUTF (s.c_str ());
1709  jcls = jni_env->GetObjectClass (jobj);
1710  }
1711  else if (val.iscellstr ())
1712  {
1713  const Array<std::string> str_arr = val.cellstr_value ();
1714  const octave_idx_type n = str_arr.numel ();
1715 
1716  jclass_ref scls (jni_env, jni_env->FindClass ("java/lang/String"));
1717  jobjectArray array = jni_env->NewObjectArray (n, scls, nullptr);
1718 
1719  for (octave_idx_type i = 0; i < n; i++)
1720  {
1721  jstring_ref jstr (jni_env, jni_env->NewStringUTF
1722  (str_arr(i).c_str ()));
1723  jni_env->SetObjectArrayElement (array, i, jstr);
1724  }
1725 
1726  jobj = array;
1727  jcls = jni_env->GetObjectClass (jobj);
1728  }
1729  else if (val.numel () > 1 && val.dims ().isvector ())
1730  {
1731  // FIXME: Is there any way to avoid code duplication here without
1732  // using a macro?
1733 
1734 #define UNBOX_PRIMITIVE_ARRAY(METHOD_T, OCTAVE_T, JAVA_T, JAVA_T_CAP) \
1735  do \
1736  { \
1737  const OCTAVE_T ## NDArray v = val.METHOD_T ## array_value (); \
1738  JAVA_T ## Array jarr = jni_env->New ## JAVA_T_CAP ## Array (v.numel ()); \
1739  const JAVA_T *jv = reinterpret_cast<const JAVA_T *> (v.data ()); \
1740  jni_env->Set ## JAVA_T_CAP ## ArrayRegion (jarr, 0, v.numel (), jv); \
1741  jobj = reinterpret_cast<jobject> (jarr); \
1742  jcls = jni_env->GetObjectClass (jobj); \
1743  } \
1744  while (0)
1745 
1746  // Note that we do NOT handle char here because they are unboxed
1747  // into a String[], not into a char array
1748 
1749  if (val.is_double_type ())
1750  UNBOX_PRIMITIVE_ARRAY (,, jdouble, Double);
1751  else if (val.islogical ())
1752  UNBOX_PRIMITIVE_ARRAY (bool_, bool, jboolean, Boolean);
1753  else if (val.isfloat ())
1754  UNBOX_PRIMITIVE_ARRAY (float_, Float, jfloat, Float);
1755  else if (val.is_int8_type ())
1756  UNBOX_PRIMITIVE_ARRAY (int8_, int8, jbyte, Byte);
1757  else if (val.is_uint8_type ())
1758  UNBOX_PRIMITIVE_ARRAY (uint8_, uint8, jbyte, Byte);
1759  else if (val.is_int16_type ())
1760  UNBOX_PRIMITIVE_ARRAY (int16_, int16, jshort, Short);
1761  else if (val.is_uint16_type ())
1762  UNBOX_PRIMITIVE_ARRAY (uint16_, uint16, jshort, Short);
1763  else if (val.is_int32_type ())
1764  UNBOX_PRIMITIVE_ARRAY (int32_, int32, jint, Int);
1765  else if (val.is_uint32_type ())
1766  UNBOX_PRIMITIVE_ARRAY (uint32_, uint32, jint, Int);
1767  else if (val.is_int64_type ())
1768  UNBOX_PRIMITIVE_ARRAY (int64_, int64, jlong, Long);
1769  else if (val.is_uint64_type ())
1770  UNBOX_PRIMITIVE_ARRAY (uint64_, uint64, jlong, Long);
1771 
1772 #undef UNBOX_PRIMITIVE_ARRAY
1773  }
1774  else if (val.is_real_scalar () || val.is_bool_scalar ())
1775  {
1776  // FIXME: Is there any way to avoid code duplication here without
1777  // using a macro?
1778 
1779 #define UNBOX_PRIMITIVE_SCALAR(OCTAVE_T, METHOD_T, JAVA_T, JAVA_CON) \
1780  do \
1781  { \
1782  const OCTAVE_T ov = val.METHOD_T ## _value (); \
1783  jclass_ref dcls (jni_env, jni_env->FindClass (JAVA_T)); \
1784  const jfieldID fid = jni_env->GetStaticFieldID (dcls, "TYPE", "Ljava/lang/Class;"); \
1785  const jmethodID mid = jni_env->GetMethodID (dcls, "<init>", JAVA_CON); \
1786  jcls = reinterpret_cast<jclass> (jni_env->GetStaticObjectField (dcls, fid)); \
1787  jobj = jni_env->NewObject (dcls, mid, ov); \
1788  } \
1789  while (0)
1790 
1791  if (val.is_double_type ())
1792  UNBOX_PRIMITIVE_SCALAR (double, double, "java/lang/Double", "(D)V");
1793  else if (val.islogical ())
1794  UNBOX_PRIMITIVE_SCALAR (bool, bool, "java/lang/Boolean", "(Z)V");
1795  else if (val.isfloat ())
1796  UNBOX_PRIMITIVE_SCALAR (float, float, "java/lang/Float", "(F)V");
1797  else if (val.is_int8_type ())
1798  UNBOX_PRIMITIVE_SCALAR (int8_t, int8_scalar, "java/lang/Byte", "(B)V");
1799  else if (val.is_uint8_type ())
1800  UNBOX_PRIMITIVE_SCALAR (uint8_t, uint8_scalar, "java/lang/Byte", "(B)V");
1801  else if (val.is_int16_type ())
1802  UNBOX_PRIMITIVE_SCALAR (int16_t, int16_scalar, "java/lang/Short", "(S)V");
1803  else if (val.is_uint16_type ())
1804  UNBOX_PRIMITIVE_SCALAR (uint16_t, uint16_scalar, "java/lang/Short", "(S)V");
1805  else if (val.is_int32_type ())
1806  UNBOX_PRIMITIVE_SCALAR (int32_t, int32_scalar, "java/lang/Integer", "(I)V");
1807  else if (val.is_uint32_type ())
1808  UNBOX_PRIMITIVE_SCALAR (uint32_t, uint32_scalar, "java/lang/Integer", "(I)V");
1809  else if (val.is_int64_type ())
1810  UNBOX_PRIMITIVE_SCALAR (int64_t, int64_scalar, "java/lang/Long", "(J)V");
1811  else if (val.is_uint64_type ())
1812  UNBOX_PRIMITIVE_SCALAR (uint64_t, uint64_scalar, "java/lang/Long", "(J)V");
1813 
1814 #undef UNBOX_PRIMITIVE_SCALAR
1815  }
1816  else if (val.isempty ())
1817  {
1818  jobj = nullptr;
1819  jcls = nullptr;
1820  //jcls = jni_env->FindClass ("java/lang/Object");
1821  }
1822  else if (! Vjava_matrix_autoconversion
1823  && ((val.is_real_matrix ()
1824  && (val.rows () == 1 || val.columns () == 1))
1825  || val.is_range ()))
1826  {
1827  Matrix m = val.matrix_value ();
1828  jdoubleArray dv = jni_env->NewDoubleArray (m.numel ());
1829  jni_env->SetDoubleArrayRegion (dv, 0, m.numel (), m.fortran_vec ());
1830  jobj = dv;
1831  jcls = jni_env->GetObjectClass (jobj);
1832  }
1834  && (val.is_matrix_type () || val.is_range ())
1835  && val.isreal ())
1836  {
1837  jclass_ref mcls (jni_env, find_octave_class (jni_env,
1838  "org/octave/Matrix"));
1839  dim_vector dims = val.dims ();
1840  jintArray_ref iv (jni_env, jni_env->NewIntArray (dims.ndims ()));
1841  jint *iv_data = jni_env->GetIntArrayElements (jintArray (iv), nullptr);
1842 
1843  for (int i = 0; i < dims.ndims (); i++)
1844  iv_data[i] = dims(i);
1845 
1846  jni_env->ReleaseIntArrayElements (jintArray (iv), iv_data, 0);
1847 
1848  if (val.is_double_type ())
1849  {
1850  NDArray m = val.array_value ();
1851  jdoubleArray_ref dv (jni_env, jni_env->NewDoubleArray (m.numel ()));
1852  jni_env->SetDoubleArrayRegion (jdoubleArray (dv), 0, m.numel (),
1853  m.fortran_vec ());
1854  jmethodID mID = jni_env->GetMethodID (mcls, "<init>", "([D[I)V");
1855  jobj = jni_env->NewObject (jclass (mcls), mID, jdoubleArray (dv),
1856  jintArray (iv));
1857  jcls = jni_env->GetObjectClass (jobj);
1858  }
1859  else if (val.is_int8_type ())
1860  {
1861  int8NDArray m = val.int8_array_value ();
1862  jbyteArray_ref bv (jni_env, jni_env->NewByteArray (m.numel ()));
1863  jni_env->SetByteArrayRegion (jbyteArray (bv), 0, m.numel (),
1864  reinterpret_cast<jbyte *>
1865  (m.fortran_vec ()));
1866  jmethodID mID = jni_env->GetMethodID (mcls, "<init>", "([B[I)V");
1867  jobj = jni_env->NewObject
1868  (jclass (mcls), mID, jbyteArray (bv), jintArray (iv));
1869  jcls = jni_env->GetObjectClass (jobj);
1870  }
1871  else if (val.is_uint8_type ())
1872  {
1873  uint8NDArray m = val.uint8_array_value ();
1874  jbyteArray_ref bv (jni_env, jni_env->NewByteArray (m.numel ()));
1875  jni_env->SetByteArrayRegion (jbyteArray (bv), 0, m.numel (),
1876  reinterpret_cast<jbyte *>
1877  (m.fortran_vec ()));
1878  jmethodID mID = jni_env->GetMethodID (mcls, "<init>", "([B[I)V");
1879  jobj = jni_env->NewObject
1880  (jclass (mcls), mID, jbyteArray (bv), jintArray (iv));
1881  jcls = jni_env->GetObjectClass (jobj);
1882  }
1883  else if (val.is_int32_type ())
1884  {
1885  int32NDArray m = val.int32_array_value ();
1886  jintArray_ref v (jni_env, jni_env->NewIntArray (m.numel ()));
1887  jni_env->SetIntArrayRegion (jintArray (v), 0, m.numel (),
1888  reinterpret_cast<jint *>
1889  (m.fortran_vec ()));
1890  jmethodID mID = jni_env->GetMethodID (mcls, "<init>", "([I[I)V");
1891  jobj = jni_env->NewObject
1892  (jclass (mcls), mID, jintArray (v), jintArray (iv));
1893  jcls = jni_env->GetObjectClass (jobj);
1894  }
1895  else
1896  {
1897  error ("cannot convert matrix of type '%s'",
1898  val.class_name ().c_str ());
1899  }
1900  }
1901  else
1902  {
1903  jclass rcls = find_octave_class (jni_env, "org/octave/OctaveReference");
1904  jmethodID mID = jni_env->GetMethodID (rcls, "<init>", "(I)V");
1905  int ID = octave_java_refcount++;
1906 
1907  jobj = jni_env->NewObject (rcls, mID, ID);
1908  jcls = rcls;
1909  octave_ref_map[ID] = val;
1910  }
1911 
1912  return found;
1913 }
1914 
1915 static bool
1916 unbox (JNIEnv *jni_env, const octave_value_list& args,
1917  jobjectArray_ref& jobjs, jobjectArray_ref& jclss)
1918 {
1919  bool found = true;
1920 
1921  jclass_ref ocls (jni_env, jni_env->FindClass ("java/lang/Object"));
1922  jclass_ref ccls (jni_env, jni_env->FindClass ("java/lang/Class"));
1923 
1924  if (! jobjs)
1925  jobjs = jni_env->NewObjectArray (args.length (), ocls, nullptr);
1926 
1927  if (! jclss)
1928  jclss = jni_env->NewObjectArray (args.length (), ccls, nullptr);
1929 
1930  for (int i = 0; i < args.length (); i++)
1931  {
1932  jobject_ref jobj (jni_env);
1933  jclass_ref jcls (jni_env);
1934 
1935  found = unbox (jni_env, args(i), jobj, jcls);
1936  if (! found)
1937  break;
1938 
1939  jni_env->SetObjectArrayElement (jobjs, i, jobj);
1940  jni_env->SetObjectArrayElement (jclss, i, jcls);
1941  }
1942 
1943  return found;
1944 }
1945 
1946 //! Returns the id of the current thread.
1947 //!
1948 //! @param jni_env The current environment or @c nullptr.
1949 //!
1950 //! @returns The id of the current thread or -1 otherwise. The latter happens
1951 //! if @c jni_env is @c nullptr, for example.
1952 
1953 static long
1954 get_current_thread_ID (JNIEnv *jni_env)
1955 {
1956  if (jni_env)
1957  {
1958  // Call Java method static Thread java.lang.Thread.currentThread().
1959  jclass_ref cls (jni_env, jni_env->FindClass ("java/lang/Thread"));
1960  jmethodID mID = jni_env->GetStaticMethodID (cls, "currentThread",
1961  "()Ljava/lang/Thread;");
1962  jobject_ref jthread (jni_env, jni_env->CallStaticObjectMethod (cls, mID));
1963 
1964  if (jthread)
1965  {
1966  // Call Java method long java.lang.Thread.getId().
1967  jclass_ref jth_cls (jni_env, jni_env->GetObjectClass (jthread));
1968  mID = jni_env->GetMethodID (jth_cls, "getId", "()J");
1969  long result = jni_env->CallLongMethod (jthread, mID);
1970  return result;
1971  }
1972  }
1973 
1974  return -1;
1975 }
1976 
1977 //! Run the java method @c org.octave.Octave.checkPendingAction().
1978 //!
1979 //! @returns 0 in any case for good reason.
1980 
1981 static int
1982 java_event_hook ()
1983 {
1984  JNIEnv *current_env = thread_jni_env ();
1985 
1986  if (current_env)
1987  {
1988  // Invoke static void org.octave.Octave.checkPendingAction().
1989  jclass_ref cls (current_env, find_octave_class (current_env,
1990  "org/octave/Octave"));
1991  jmethodID mID = current_env->GetStaticMethodID
1992  (cls, "checkPendingAction", "()V");
1993  current_env->CallStaticVoidMethod (cls, mID);
1994 
1996  }
1997 
1998  return 0;
1999 }
2000 
2001 //! Initialize java including the virtual machine (jvm) if necessary.
2002 //!
2003 //! Initializes the fields #jvm, #jvm_attached, #jvm_lib, and
2004 //! #octave_thread_ID. To ensure that java is initialized, this method is
2005 //! used as part of octave functions @c javaObject, @c javaMethod,
2006 //! @c __java_get__, @c __java_set__, and @c __java2mat__.
2007 
2008 static void
2009 initialize_java ()
2010 {
2011  if (! jvm)
2012  {
2013  try
2014  {
2015  initialize_jvm ();
2016 
2017  JNIEnv *current_env = thread_jni_env ();
2018 
2019  octave::command_editor::add_event_hook (java_event_hook);
2020 
2021  octave_thread_ID = get_current_thread_ID (current_env);
2022  }
2023  catch (const std::string msg)
2024  {
2025  error ("%s", msg.c_str ());
2026  }
2027 
2029  }
2030 }
2031 
2032 JNIEXPORT jboolean JNICALL
2033 Java_org_octave_Octave_call (JNIEnv *env, jclass, jstring fcnName,
2034  jobjectArray argin, jobjectArray argout)
2035 {
2036  std::string fname = jstring_to_string (env, fcnName);
2037 
2038  int nargout = env->GetArrayLength (argout);
2039  int nargin = env->GetArrayLength (argin);
2040 
2041  octave_value_list varargin;
2042 
2043  for (int i = 0; i < nargin; i++)
2044  varargin(i) = box (env, env->GetObjectArrayElement (argin, i), nullptr);
2045 
2046  octave::interpreter& interp = octave::__get_interpreter__ ();
2047 
2048  octave_value_list varargout = interp.feval (fname, varargin, nargout);
2049 
2050  jobjectArray_ref out_objs (env, argout), out_clss (env);
2051  out_objs.detach ();
2052  return unbox (env, varargout, out_objs, out_clss);
2053 }
2054 
2055 JNIEXPORT void JNICALL
2056 Java_org_octave_OctaveReference_doFinalize (JNIEnv *, jclass, jint ID)
2057 {
2058  octave_ref_map.erase (ID);
2059 }
2060 
2061 JNIEXPORT void JNICALL
2062 Java_org_octave_Octave_doInvoke (JNIEnv *env, jclass, jint ID,
2063  jobjectArray args)
2064 {
2065  auto it = octave_ref_map.find (ID);
2066 
2067  if (it != octave_ref_map.end ())
2068  {
2069  octave_value val = it->second;
2070  int len = env->GetArrayLength (args);
2071  octave_value_list oct_args;
2072 
2073  for (int i = 0; i < len; i++)
2074  {
2075  jobject_ref jobj (env, env->GetObjectArrayElement (args, i));
2076  oct_args(i) = box (env, jobj, nullptr);
2077  }
2078 
2079  octave::interpreter& interp = octave::__get_interpreter__ ();
2080 
2081  if (val.is_function_handle ())
2082  {
2083  octave_function *fcn = val.function_value ();
2084  interp.feval (fcn, oct_args);
2085  }
2086  else if (val.iscell () && val.length () > 0
2087  && (val.rows () == 1 || val.columns () == 1)
2088  && val.cell_value()(0).is_function_handle ())
2089  {
2090  Cell c = val.cell_value ();
2091  octave_function *fcn = c(0).function_value ();
2092 
2093  for (int i=1; i<c.numel (); i++)
2094  oct_args(len+i-1) = c(i);
2095 
2096  interp.feval (fcn, oct_args);
2097  }
2098  else
2099  error ("trying to invoke non-invocable object");
2100  }
2101 }
2102 
2103 JNIEXPORT void JNICALL
2104 Java_org_octave_Octave_doEvalString (JNIEnv *env, jclass, jstring cmd)
2105 {
2106  octave::interpreter& interp = octave::__get_interpreter__ ();
2107 
2108  std::string s = jstring_to_string (env, cmd);
2109  int pstatus;
2110  interp.eval_string (s, false, pstatus, 0);
2111 }
2112 
2113 JNIEXPORT jboolean JNICALL
2115 {
2116  return (get_current_thread_ID (env) != octave_thread_ID);
2117 }
2118 
2119 #endif
2120 
2121 //! Ctor.
2122 
2124  : octave_base_value (), m_java_object (nullptr), m_java_class (nullptr)
2125 {
2126 #if ! defined (HAVE_JAVA)
2127 
2128  err_disabled_feature ("Java Objects", "Java");
2129 
2130 #endif
2131 }
2132 
2133 octave_java::octave_java (const voidptr& jobj, void *jcls)
2134  : octave_base_value (), m_java_object (nullptr), m_java_class (nullptr)
2135 {
2136 #if defined (HAVE_JAVA)
2137 
2138  init (jobj, jcls);
2139 
2140 #else
2141 
2142  octave_unused_parameter (jobj);
2143  octave_unused_parameter (jcls);
2144 
2145  err_disabled_feature ("Java Objects", "Java");
2146 
2147 #endif
2148 }
2149 
2150 int octave_java::s_t_id (-1);
2151 
2152 const std::string octave_java::s_t_name ("octave_java");
2153 
2154 void
2155 octave_java::register_type (octave::type_info& ti)
2156 {
2157 #if defined (HAVE_JAVA)
2158 
2159  s_t_id = ti.register_type (octave_java::s_t_name, "<unknown>",
2160  octave_value (new octave_java ()));
2161 
2162 #else
2163 
2164  octave_unused_parameter (ti);
2165 
2166 #endif
2167 }
2168 
2169 dim_vector
2170 octave_java::dims () const
2171 {
2172 #if defined (HAVE_JAVA)
2173 
2174  JNIEnv *current_env = thread_jni_env ();
2175 
2176  if (current_env && m_java_object)
2177  return compute_array_dimensions (current_env, TO_JOBJECT (m_java_object));
2178  else
2179  return dim_vector (1, 1);
2180 
2181 #else
2182 
2183  // This shouldn't happen because construction of octave_java objects is
2184  // supposed to be impossible if Java is not available.
2185 
2186  panic_impossible ();
2187 
2188 #endif
2189 }
2190 
2192 octave_java::subsref (const std::string& type,
2193  const std::list<octave_value_list>& idx, int nargout)
2194 {
2195 #if defined (HAVE_JAVA)
2196 
2197  octave_value_list retval;
2198  int skip = 1;
2199 
2200  JNIEnv *current_env = thread_jni_env ();
2201 
2202  switch (type[0])
2203  {
2204  case '.':
2205  if (type.length () > 1 && type[1] == '(')
2206  {
2207  octave_value_list ovl (2);
2208  m_count++;
2209  ovl(0) = (idx.front ())(0);
2210  ovl(1) = octave_value (this);
2211  auto it = idx.begin ();
2212  ovl.append (*++it);
2213  retval = octave::FjavaMethod (ovl, 1);
2214  skip++;
2215  }
2216  else
2217  {
2218  octave_value_list ovl (2);
2219  m_count++;
2220  ovl(0) = octave_value (this);
2221  ovl(1) = (idx.front ())(0);
2222  retval = octave::F__java_get__ (ovl, 1);
2223  }
2224  break;
2225 
2226  case '(':
2227  if (current_env)
2228  retval = get_array_elements
2229  (current_env, TO_JOBJECT (to_java ()), idx.front ());
2230  break;
2231 
2232  default:
2233  error ("subsref: Java object cannot be indexed with %c", type[0]);
2234  break;
2235  }
2236 
2237  if (idx.size () > 1 && type.length () > 1)
2238  retval = retval(0).next_subsref (nargout, type, idx, skip);
2239 
2240  return retval;
2241 
2242 #else
2243 
2244  octave_unused_parameter (type);
2245  octave_unused_parameter (idx);
2246  octave_unused_parameter (nargout);
2247 
2248  // This shouldn't happen because construction of octave_java objects is
2249  // supposed to be impossible if Java is not available.
2250 
2251  panic_impossible ();
2252 
2253 #endif
2254 }
2255 
2257 octave_java::subsasgn (const std::string& type,
2258  const std::list<octave_value_list>& idx,
2259  const octave_value& rhs)
2260 {
2261 #if defined (HAVE_JAVA)
2262 
2263  octave_value retval;
2264 
2265  JNIEnv *current_env = thread_jni_env ();
2266 
2267  switch (type[0])
2268  {
2269  case '.':
2270  if (type.length () == 1)
2271  {
2272  // field assignment
2273  octave_value_list ovl (3);
2274  m_count++;
2275  ovl(0) = octave_value (this);
2276  ovl(1) = (idx.front ())(0);
2277  ovl(2) = rhs;
2279 
2280  m_count++;
2281  retval = octave_value (this);
2282  }
2283  else if (type.length () > 2 && type[1] == '(')
2284  {
2285  std::list<octave_value_list> new_idx;
2286  auto it = idx.begin ();
2287  new_idx.push_back (*it++);
2288  new_idx.push_back (*it++);
2289  octave_value_list u = subsref (type.substr (0, 2), new_idx, 1);
2290 
2291  std::list<octave_value_list> next_idx (idx);
2292  next_idx.erase (next_idx.begin ());
2293  next_idx.erase (next_idx.begin ());
2294  u(0).subsasgn (type.substr (2), next_idx, rhs);
2295 
2296  m_count++;
2297  retval = octave_value (this);
2298  }
2299  else if (type[1] == '.')
2300  {
2301  octave_value_list u = subsref (type.substr (0, 1), idx, 1);
2302 
2303  std::list<octave_value_list> next_idx (idx);
2304  next_idx.erase (next_idx.begin ());
2305  u(0).subsasgn (type.substr (1), next_idx, rhs);
2306 
2307  m_count++;
2308  retval = octave_value (this);
2309  }
2310  else
2311  error ("invalid indexing/assignment on Java object");
2312  break;
2313 
2314  case '(':
2315  if (current_env)
2316  {
2317  set_array_elements (current_env, TO_JOBJECT (to_java ()),
2318  idx.front (), rhs);
2319 
2320  m_count++;
2321  retval = octave_value (this);
2322  }
2323  break;
2324 
2325  default:
2326  error ("Java object cannot be indexed with %c", type[0]);
2327  break;
2328  }
2329 
2330  return retval;
2331 
2332 #else
2333 
2334  octave_unused_parameter (type);
2335  octave_unused_parameter (idx);
2336  octave_unused_parameter (rhs);
2337 
2338  // This shouldn't happen because construction of octave_java objects is
2339  // supposed to be impossible if Java is not available.
2340 
2341  panic_impossible ();
2342 
2343 #endif
2344 }
2345 
2347 octave_java::map_keys () const
2348 {
2349 #if defined (HAVE_JAVA)
2350 
2351  JNIEnv *current_env = thread_jni_env ();
2352 
2353  if (current_env)
2354  return get_invoke_list (current_env, to_java ());
2355  else
2356  return string_vector ();
2357 
2358 #else
2359 
2360  // This shouldn't happen because construction of octave_java objects is
2361  // supposed to be impossible if Java is not available.
2362 
2363  panic_impossible ();
2364 
2365 #endif
2366 }
2367 
2369 octave_java::convert_to_str_internal (bool, bool force, char type) const
2370 {
2371 #if defined (HAVE_JAVA)
2372 
2373  JNIEnv *current_env = thread_jni_env ();
2374 
2375  if (current_env)
2376  return convert_to_string (current_env, TO_JOBJECT (to_java ()), force,
2377  type);
2378  else
2379  return octave_value ("");
2380 
2381 #else
2382 
2383  octave_unused_parameter (force);
2384  octave_unused_parameter (type);
2385 
2386  // This shouldn't happen because construction of octave_java objects is
2387  // supposed to be impossible if Java is not available.
2388 
2389  panic_impossible ();
2390 
2391 #endif
2392 }
2393 
2394 void
2395 octave_java::print (std::ostream& os, bool)
2396 {
2397  print_raw (os);
2398  newline (os);
2399 }
2400 
2401 void
2402 octave_java::print_raw (std::ostream& os, bool) const
2403 {
2404  os << "<Java object: " << m_java_classname << '>';
2405 }
2406 
2407 // FIXME: Need routines to actually save/load java objects through Serialize.
2408 // See bug #42112.
2409 
2410 bool
2411 octave_java::save_ascii (std::ostream& /* os */)
2412 {
2413  warning ("save: unable to save java objects, skipping");
2414 
2415  return true;
2416 }
2417 
2418 bool
2419 octave_java::load_ascii (std::istream& /* is */)
2420 {
2421  // Silently skip over java object that was not saved
2422  return true;
2423 }
2424 
2425 bool
2426 octave_java::save_binary (std::ostream& /* os */, bool /* save_as_floats */)
2427 {
2428  warning ("save: unable to save java objects, skipping");
2429 
2430  return true;
2431 }
2432 
2433 bool
2434 octave_java::load_binary (std::istream& /* is */, bool /* swap*/,
2435  octave::mach_info::float_format /* fmt */)
2436 {
2437  // Silently skip over java object that was not saved
2438  return true;
2439 }
2440 
2441 bool
2442 octave_java::save_hdf5 (octave_hdf5_id /* loc_id */, const char * /* name */,
2443  bool /* save_as_floats */)
2444 {
2445  warning ("save: unable to save java objects, skipping");
2446 
2447  return true;
2448 }
2449 
2450 bool
2451 octave_java::load_hdf5 (octave_hdf5_id /* loc_id */, const char * /* name */)
2452 {
2453  // Silently skip object that was not saved
2454  return true;
2455 }
2456 
2457 octave_value
2458 octave_java::do_javaMethod (void *jni_env_arg, const std::string& name,
2459  const octave_value_list& args)
2460 {
2461 #if defined (HAVE_JAVA)
2462 
2463  octave_value retval;
2464 
2465  JNIEnv *jni_env = TO_JNIENV (jni_env_arg);
2466 
2467  if (jni_env)
2468  {
2469  jobjectArray_ref arg_objs (jni_env), arg_types (jni_env);
2470  if (unbox (jni_env, args, arg_objs, arg_types))
2471  {
2472  jclass_ref helperClass (jni_env, find_octave_class (jni_env, "org/octave/ClassHelper"));
2473  jmethodID mID = jni_env->GetStaticMethodID (helperClass, "invokeMethod",
2474  "(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;[Ljava/lang/Class;)Ljava/lang/Object;");
2475  jstring_ref methName (jni_env, jni_env->NewStringUTF (name.c_str ()));
2476  jobjectArray_ref resObj (jni_env,
2477  reinterpret_cast<jobjectArray> (jni_env->CallStaticObjectMethod (helperClass, mID,
2478  to_java (), jstring (methName), jobjectArray (arg_objs), jobjectArray (arg_types))));
2479  if (resObj)
2480  retval = box (jni_env, resObj);
2481  else
2482  retval = check_exception (jni_env);
2483  }
2484 
2485  octave_set_default_fpucw ();
2486  }
2487 
2488  return retval;
2489 
2490 #else
2491 
2492  octave_unused_parameter (jni_env_arg);
2493  octave_unused_parameter (name);
2494  octave_unused_parameter (args);
2495 
2496  // This shouldn't happen because construction of octave_java objects is
2497  // supposed to be impossible if Java is not available.
2498 
2499  panic_impossible ();
2500 
2501 #endif
2502 }
2503 
2505 octave_java::do_javaMethod (const std::string& name,
2506  const octave_value_list& args)
2507 {
2508 #if defined (HAVE_JAVA)
2509 
2510  return do_javaMethod (thread_jni_env (), name, args);
2511 
2512 #else
2513 
2514  octave_unused_parameter (name);
2515  octave_unused_parameter (args);
2516 
2517  // This shouldn't happen because construction of octave_java
2518  // objects is supposed to be impossible if Java is not available.
2519 
2520  panic_impossible ();
2521 
2522 #endif
2523 }
2524 
2526 octave_java::do_javaMethod (void *jni_env_arg,
2527  const std::string& class_name,
2528  const std::string& name,
2529  const octave_value_list& args)
2530 {
2531 #if defined (HAVE_JAVA)
2532 
2533  octave_value retval;
2534 
2535  JNIEnv *jni_env = TO_JNIENV (jni_env_arg);
2536 
2537  if (jni_env)
2538  {
2539  jobjectArray_ref arg_objs (jni_env), arg_types (jni_env);
2540  if (unbox (jni_env, args, arg_objs, arg_types))
2541  {
2542  jclass_ref helperClass (jni_env,
2543  find_octave_class (jni_env,
2544  "org/octave/ClassHelper"));
2545  jmethodID mID = jni_env->GetStaticMethodID (helperClass,
2546  "invokeStaticMethod",
2547  "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;[Ljava/lang/Class;)Ljava/lang/Object;");
2548  jstring_ref methName (jni_env,
2549  jni_env->NewStringUTF (name.c_str ()));
2550  jstring_ref clsName (jni_env,
2551  jni_env->NewStringUTF (class_name.c_str ()));
2552  jobject_ref resObj (jni_env,
2553  jni_env->CallStaticObjectMethod (helperClass,
2554  mID,
2555  jstring (clsName),
2556  jstring (methName),
2557  jobjectArray (arg_objs),
2558  jobjectArray (arg_types)));
2559  if (resObj)
2560  retval = box (jni_env, resObj);
2561  else
2562  retval = check_exception (jni_env);
2563  }
2564 
2566  }
2567 
2568  return retval;
2569 
2570 #else
2571 
2572  octave_unused_parameter (jni_env_arg);
2573  octave_unused_parameter (class_name);
2574  octave_unused_parameter (name);
2575  octave_unused_parameter (args);
2576 
2577  // This shouldn't happen because construction of octave_java
2578  // objects is supposed to be impossible if Java is not available.
2579 
2580  panic_impossible ();
2581 
2582 #endif
2583 }
2584 
2586 octave_java::do_javaMethod (const std::string& class_name,
2587  const std::string& name,
2588  const octave_value_list& args)
2589 {
2590 #if defined (HAVE_JAVA)
2591 
2592  return do_javaMethod (thread_jni_env (), class_name, name, args);
2593 
2594 #else
2595 
2596  octave_unused_parameter (class_name);
2597  octave_unused_parameter (name);
2598  octave_unused_parameter (args);
2599 
2600  // This shouldn't happen because construction of octave_java
2601  // objects is supposed to be impossible if Java is not available.
2602 
2603  panic_impossible ();
2604 
2605 #endif
2606 }
2607 
2609 octave_java::do_javaObject (void *jni_env_arg, const std::string& name,
2610  const octave_value_list& args)
2611 {
2612 #if defined (HAVE_JAVA)
2613 
2614  octave_value retval;
2615 
2616  JNIEnv *jni_env = TO_JNIENV (jni_env_arg);
2617 
2618  if (jni_env)
2619  {
2620  jobjectArray_ref arg_objs (jni_env), arg_types (jni_env);
2621 
2622  if (unbox (jni_env, args, arg_objs, arg_types))
2623  {
2624  jclass_ref helperClass (jni_env,
2625  find_octave_class (jni_env,
2626  "org/octave/ClassHelper"));
2627  jmethodID mID = jni_env->GetStaticMethodID (helperClass,
2628  "invokeConstructor",
2629  "(Ljava/lang/String;[Ljava/lang/Object;[Ljava/lang/Class;)Ljava/lang/Object;");
2630  jstring_ref clsName (jni_env,
2631  jni_env->NewStringUTF (name.c_str ()));
2632  jobject_ref resObj (jni_env,
2633  jni_env->CallStaticObjectMethod (helperClass,
2634  mID,
2635  jstring (clsName),
2636  jobjectArray (arg_objs),
2637  jobjectArray (arg_types)));
2638 
2639  if (resObj)
2640  retval = octave_value (new octave_java (resObj, nullptr));
2641  else
2642  check_exception (jni_env);
2643  }
2644 
2646  }
2647 
2648  return retval;
2649 
2650 #else
2651 
2652  octave_unused_parameter (jni_env_arg);
2653  octave_unused_parameter (name);
2654  octave_unused_parameter (args);
2655 
2656  // This shouldn't happen because construction of octave_java
2657  // objects is supposed to be impossible if Java is not available.
2658 
2659  panic_impossible ();
2660 
2661 #endif
2662 }
2663 
2665 octave_java::do_javaObject (const std::string& name,
2666  const octave_value_list& args)
2667 {
2668 #if defined (HAVE_JAVA)
2669 
2670  return do_javaObject (thread_jni_env (), name, args);
2671 
2672 #else
2673 
2674  octave_unused_parameter (name);
2675  octave_unused_parameter (args);
2676 
2677  // This shouldn't happen because construction of octave_java
2678  // objects is supposed to be impossible if Java is not available.
2679 
2680  panic_impossible ();
2681 
2682 #endif
2683 }
2684 
2686 octave_java::do_java_get (void *jni_env_arg, const std::string& name)
2687 {
2688 #if defined (HAVE_JAVA)
2689 
2690  octave_value retval;
2691 
2692  JNIEnv *jni_env = TO_JNIENV (jni_env_arg);
2693 
2694  if (jni_env)
2695  {
2696  jclass_ref helperClass (jni_env,
2697  find_octave_class (jni_env,
2698  "org/octave/ClassHelper"));
2699  jmethodID mID = jni_env->GetStaticMethodID (helperClass, "getField",
2700  "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;");
2701  jstring_ref fName (jni_env, jni_env->NewStringUTF (name.c_str ()));
2702  jobject_ref resObj (jni_env,
2703  jni_env->CallStaticObjectMethod (helperClass,
2704  mID,
2705  to_java (),
2706  jstring (fName)));
2707 
2708  if (resObj)
2709  retval = box (jni_env, resObj);
2710  else
2711  retval = check_exception (jni_env);
2712 
2714  }
2715 
2716  return retval;
2717 
2718 #else
2719 
2720  octave_unused_parameter (jni_env_arg);
2721  octave_unused_parameter (name);
2722 
2723  // This shouldn't happen because construction of octave_java
2724  // objects is supposed to be impossible if Java is not available.
2725 
2726  panic_impossible ();
2727 
2728 #endif
2729 }
2730 
2732 octave_java::do_java_get (const std::string& name)
2733 {
2734 #if defined (HAVE_JAVA)
2735 
2736  return do_java_get (thread_jni_env (), name);
2737 
2738 #else
2739 
2740  octave_unused_parameter (name);
2741 
2742  // This shouldn't happen because construction of octave_java
2743  // objects is supposed to be impossible if Java is not available.
2744 
2745  panic_impossible ();
2746 
2747 #endif
2748 }
2749 
2751 octave_java::do_java_get (void *jni_env_arg, const std::string& class_name,
2752  const std::string& name)
2753 {
2754 #if defined (HAVE_JAVA)
2755 
2756  octave_value retval;
2757 
2758  JNIEnv *jni_env = TO_JNIENV (jni_env_arg);
2759 
2760  if (jni_env)
2761  {
2762  jclass_ref helperClass (jni_env,
2763  find_octave_class (jni_env,
2764  "org/octave/ClassHelper"));
2765  jmethodID mID = jni_env->GetStaticMethodID (helperClass,
2766  "getStaticField",
2767  "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;");
2768  jstring_ref cName (jni_env, jni_env->NewStringUTF (class_name.c_str ()));
2769  jstring_ref fName (jni_env, jni_env->NewStringUTF (name.c_str ()));
2770  jobject_ref resObj (jni_env,
2771  jni_env->CallStaticObjectMethod (helperClass, mID,
2772  jstring (cName),
2773  jstring (fName)));
2774  if (resObj)
2775  retval = box (jni_env, resObj);
2776  else
2777  retval = check_exception (jni_env);
2778 
2780  }
2781 
2782  return retval;
2783 
2784 #else
2785 
2786  octave_unused_parameter (jni_env_arg);
2787  octave_unused_parameter (class_name);
2788  octave_unused_parameter (name);
2789 
2790  // This shouldn't happen because construction of octave_java
2791  // objects is supposed to be impossible if Java is not available.
2792 
2793  panic_impossible ();
2794 
2795 #endif
2796 }
2797 
2799 octave_java::do_java_get (const std::string& class_name,
2800  const std::string& name)
2801 {
2802 #if defined (HAVE_JAVA)
2803 
2804  return do_java_get (thread_jni_env (), class_name, name);
2805 
2806 #else
2807 
2808  octave_unused_parameter (class_name);
2809  octave_unused_parameter (name);
2810 
2811  // This shouldn't happen because construction of octave_java
2812  // objects is supposed to be impossible if Java is not available.
2813 
2814  panic_impossible ();
2815 
2816 #endif
2817 }
2818 
2820 octave_java::do_java_set (void *jni_env_arg, const std::string& name,
2821  const octave_value& val)
2822 {
2823 #if defined (HAVE_JAVA)
2824 
2825  octave_value retval;
2826 
2827  JNIEnv *jni_env = TO_JNIENV (jni_env_arg);
2828 
2829  if (jni_env)
2830  {
2831  jobject_ref jobj (jni_env);
2832  jclass_ref jcls (jni_env);
2833 
2834  if (unbox (jni_env, val, jobj, jcls))
2835  {
2836  jclass_ref helperClass (jni_env,
2837  find_octave_class (jni_env,
2838  "org/octave/ClassHelper"));
2839  jmethodID mID = jni_env->GetStaticMethodID (helperClass, "setField",
2840  "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)V");
2841  jstring_ref fName (jni_env, jni_env->NewStringUTF (name.c_str ()));
2842  jni_env->CallStaticObjectMethod (helperClass, mID, to_java (),
2843  jstring (fName), jobject (jobj));
2844  check_exception (jni_env);
2845  }
2846 
2848  }
2849 
2850  return retval;
2851 
2852 #else
2853 
2854  octave_unused_parameter (jni_env_arg);
2855  octave_unused_parameter (name);
2856  octave_unused_parameter (val);
2857 
2858  // This shouldn't happen because construction of octave_java
2859  // objects is supposed to be impossible if Java is not available.
2860 
2861  panic_impossible ();
2862 
2863 #endif
2864 }
2865 
2867 octave_java::do_java_set (const std::string& name, const octave_value& val)
2868 {
2869 #if defined (HAVE_JAVA)
2870 
2871  return do_java_set (thread_jni_env (), name, val);
2872 
2873 #else
2874 
2875  octave_unused_parameter (name);
2876  octave_unused_parameter (val);
2877 
2878  // This shouldn't happen because construction of octave_java
2879  // objects is supposed to be impossible if Java is not available.
2880 
2881  panic_impossible ();
2882 
2883 #endif
2884 }
2885 
2887 octave_java::do_java_set (void *jni_env_arg, const std::string& class_name,
2888  const std::string& name, const octave_value& val)
2889 {
2890 #if defined (HAVE_JAVA)
2891 
2892  octave_value retval;
2893 
2894  JNIEnv *jni_env = TO_JNIENV (jni_env_arg);
2895 
2896  if (jni_env)
2897  {
2898  jobject_ref jobj (jni_env);
2899  jclass_ref jcls (jni_env);
2900 
2901  if (unbox (jni_env, val, jobj, jcls))
2902  {
2903  jclass_ref helperClass (jni_env,
2904  find_octave_class (jni_env,
2905  "org/octave/ClassHelper"));
2906  jmethodID mID = jni_env->GetStaticMethodID (helperClass,
2907  "setStaticField",
2908  "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V");
2909  jstring_ref cName (jni_env,
2910  jni_env->NewStringUTF (class_name.c_str ()));
2911  jstring_ref fName (jni_env, jni_env->NewStringUTF (name.c_str ()));
2912  jni_env->CallStaticObjectMethod (helperClass, mID, jstring (cName),
2913  jstring (fName), jobject (jobj));
2914  check_exception (jni_env);
2915  }
2916 
2918  }
2919 
2920  return retval;
2921 
2922 #else
2923 
2924  octave_unused_parameter (jni_env_arg);
2925  octave_unused_parameter (class_name);
2926  octave_unused_parameter (name);
2927  octave_unused_parameter (val);
2928 
2929  // This shouldn't happen because construction of octave_java
2930  // objects is supposed to be impossible if Java is not available.
2931 
2932  panic_impossible ();
2933 
2934 #endif
2935 }
2936 
2938 octave_java::do_java_set (const std::string& class_name,
2939  const std::string& name,
2940  const octave_value& val)
2941 {
2942 #if defined (HAVE_JAVA)
2943 
2944  return do_java_set (thread_jni_env (), class_name, name, val);
2945 
2946 #else
2947 
2948  octave_unused_parameter (class_name);
2949  octave_unused_parameter (name);
2950  octave_unused_parameter (val);
2951 
2952  // This shouldn't happen because construction of octave_java
2953  // objects is supposed to be impossible if Java is not available.
2954 
2955  panic_impossible ();
2956 
2957 #endif
2958 }
2959 
2960 void
2961 octave_java::init (void *jobj_arg, void *jcls_arg)
2962 {
2963 #if defined (HAVE_JAVA)
2964 
2965  jobject jobj = TO_JOBJECT (jobj_arg);
2966  jclass jcls = TO_JCLASS (jcls_arg);
2967 
2968  JNIEnv *current_env = thread_jni_env ();
2969 
2970  if (current_env)
2971  {
2972  if (jobj)
2973  m_java_object = current_env->NewGlobalRef (jobj);
2974 
2975  if (jcls)
2976  m_java_class = current_env->NewGlobalRef (jcls);
2977  else if (m_java_object)
2978  {
2979  jclass_ref ocls (current_env,
2980  current_env->GetObjectClass(TO_JOBJECT (m_java_object)));
2981  m_java_class = current_env->NewGlobalRef (jclass (ocls));
2982  }
2983 
2984  if (m_java_class)
2985  {
2986  jclass_ref clsCls (current_env,
2987  current_env->GetObjectClass (TO_JCLASS (m_java_class)));
2988  jmethodID mID = current_env->GetMethodID (clsCls,
2989  "getCanonicalName",
2990  "()Ljava/lang/String;");
2991  jobject_ref resObj (current_env,
2992  current_env->CallObjectMethod (TO_JCLASS (m_java_class), mID));
2993  m_java_classname = jstring_to_string (current_env, resObj);
2994  }
2995  }
2996 
2997 #else
2998 
2999  octave_unused_parameter (jobj_arg);
3000  octave_unused_parameter (jcls_arg);
3001 
3002  // This shouldn't happen because construction of octave_java
3003  // objects is supposed to be impossible if Java is not available.
3004 
3005  panic_impossible ();
3006 
3007 #endif
3008 }
3009 
3010 void
3011 octave_java::release ()
3012 {
3013 #if defined (HAVE_JAVA)
3014 
3015  JNIEnv *current_env = thread_jni_env ();
3016 
3017  if (current_env)
3018  {
3019  if (m_java_object)
3020  current_env->DeleteGlobalRef (TO_JOBJECT (m_java_object));
3021 
3022  if (m_java_class)
3023  current_env->DeleteGlobalRef (TO_JCLASS (m_java_class));
3024 
3025  m_java_object = nullptr;
3026  m_java_class = nullptr;
3027  }
3028 
3029 #else
3030 
3031  // This shouldn't happen because construction of octave_java objects is
3032  // supposed to be impossible if Java is not available.
3033 
3034  panic_impossible ();
3035 
3036 #endif
3037 }
3038 
3040 
3041 // DEFUN blocks below must be outside of HAVE_JAVA block so that documentation
3042 // strings are always available, even when functions are not.
3043 
3044 DEFUN (__java_init__, , ,
3045  doc: /* -*- texinfo -*-
3046 @deftypefn {} {@var{status} =} __java_init__ ()
3047 Internal function used @strong{only} when debugging the Java interface.
3048 
3049 This function will directly call @code{initialize_java} to create an instance
3050 of a JVM.
3051 
3052 The return variable @var{status} is 1 if the Java interface has been created,
3053 and 0 otherwise.
3054 @end deftypefn */)
3055 {
3056 #if defined (HAVE_JAVA)
3057 
3058  octave_value retval = 0;
3059 
3060  initialize_java ();
3061 
3062  retval = 1;
3063 
3064  return retval;
3065 
3066 #else
3067 
3068  err_disabled_feature ("__java_init__", "Java");
3069 
3070 #endif
3071 }
3072 
3073 DEFUN (__java_exit__, , ,
3074  doc: /* -*- texinfo -*-
3075 @deftypefn {} {} __java_exit__ ()
3076 Internal function used @strong{only} when debugging Java interface.
3077 
3078 Function will directly call terminate_jvm to destroy the current JVM instance.
3079 @end deftypefn */)
3080 {
3081 #if defined (HAVE_JAVA)
3082 
3083  terminate_jvm ();
3084 
3085  return ovl ();
3086 
3087 #else
3088 
3089  err_disabled_feature ("__java_exit__", "Java");
3090 
3091 #endif
3092 }
3093 
3094 DEFUN (javaObject, args, ,
3095  doc: /* -*- texinfo -*-
3096 @deftypefn {} {@var{jobj} =} javaObject (@var{classname})
3097 @deftypefnx {} {@var{jobj} =} javaObject (@var{classname}, @var{arg1}, @dots{})
3098 Create a Java object of class @var{classsname}, by calling the class
3099 constructor with the arguments @var{arg1}, @dots{}
3100 
3101 The first example below creates an uninitialized object, while the second
3102 example supplies an initial argument to the constructor.
3103 
3104 @example
3105 @group
3106 x = javaObject ("java.lang.StringBuffer")
3107 x = javaObject ("java.lang.StringBuffer", "Initial string")
3108 @end group
3109 @end example
3110 
3111 @seealso{javaMethod, javaArray}
3112 @end deftypefn */)
3113 {
3114 #if defined (HAVE_JAVA)
3115 
3116  if (args.length () == 0)
3117  print_usage ();
3118 
3119  std::string classname = args(0).xstring_value ("javaObject: CLASSNAME must be a string");
3120 
3121  initialize_java ();
3122 
3123  JNIEnv *current_env = thread_jni_env ();
3124 
3125  octave_value_list tmp;
3126  for (int i=1; i<args.length (); i++)
3127  tmp(i-1) = args(i);
3128 
3129  return ovl (octave_java::do_javaObject (current_env, classname, tmp));
3130 
3131 #else
3132 
3133  octave_unused_parameter (args);
3134 
3135  err_disabled_feature ("javaObject", "Java");
3136 
3137 #endif
3138 }
3139 
3140 /*
3141 ## The tests below merely check if javaObject works at all. Whether
3142 ## it works properly, i.e., creates the right values, is a matter of
3143 ## Java itself. Create a Short and check if it really is a short, i.e.,
3144 ## whether it overflows.
3145 %!testif HAVE_JAVA; usejava ("jvm")
3146 %! assert (javaObject ("java.lang.Short", 40000).doubleValue < 0);
3147 */
3148 
3149 DEFUN (javaMethod, args, ,
3150  doc: /* -*- texinfo -*-
3151 @deftypefn {} {@var{ret} =} javaMethod (@var{methodname}, @var{obj})
3152 @deftypefnx {} {@var{ret} =} javaMethod (@var{methodname}, @var{obj}, @var{arg1}, @dots{})
3153 Invoke the method @var{methodname} on the Java object @var{obj} with the
3154 arguments @var{arg1}, @dots{}.
3155 
3156 For static methods, @var{obj} can be a string representing the fully
3157 qualified name of the corresponding class.
3158 
3159 When @var{obj} is a regular Java object, structure-like indexing can be
3160 used as a shortcut syntax. For instance, the two following statements are
3161 equivalent
3162 
3163 @example
3164 @group
3165  ret = javaMethod ("method1", x, 1.0, "a string")
3166  ret = x.method1 (1.0, "a string")
3167 @end group
3168 @end example
3169 
3170 @code{javaMethod} returns the result of the method invocation.
3171 
3172 @seealso{methods, javaObject}
3173 @end deftypefn */)
3174 {
3175 #if defined (HAVE_JAVA)
3176 
3177  if (args.length () < 2)
3178  print_usage ();
3179 
3180  std::string methodname = args(0).xstring_value ("javaMethod: METHODNAME must be a string");
3181 
3182  initialize_java ();
3183 
3184  JNIEnv *current_env = thread_jni_env ();
3185 
3186  octave_value retval;
3187 
3188  octave_value_list tmp;
3189  for (int i=2; i<args.length (); i++)
3190  tmp(i-2) = args(i);
3191 
3192  if (args(1).isjava ())
3193  {
3194  octave_java *jobj = TO_JAVA (args(1));
3195  retval = jobj->do_javaMethod (current_env, methodname, tmp);
3196  }
3197  else if (args(1).is_string ())
3198  {
3199  std::string cls = args(1).string_value ();
3200  retval = octave_java::do_javaMethod (current_env, cls, methodname, tmp);
3201  }
3202  else
3203  error ("javaMethod: OBJ must be a Java object or a string");
3204 
3205  return retval;
3206 
3207 #else
3208 
3209  octave_unused_parameter (args);
3210 
3211  err_disabled_feature ("javaMethod", "Java");
3212 
3213 #endif
3214 }
3215 
3216 /*
3217 %!testif HAVE_JAVA; usejava ("jvm")
3218 %! jver = javaMethod ("getProperty", "java.lang.System", "java.version");
3219 %! jver = strsplit (jver, ".");
3220 %! if (numel (jver) > 1)
3221 %! assert (isfinite (str2double (jver{1})));
3222 %! assert (isfinite (str2double (jver{2})));
3223 %! else
3224 %! assert (isfinite (str2double (jver{1})));
3225 %! endif
3226 */
3227 
3228 DEFUN (__java_get__, args, ,
3229  doc: /* -*- texinfo -*-
3230 @deftypefn {} {@var{val} =} __java_get__ (@var{obj}, @var{name})
3231 Get the value of the field @var{name} of the Java object @var{obj}.
3232 
3233 For static fields, @var{obj} can be a string representing the fully
3234 qualified name of the corresponding class.
3235 
3236 When @var{obj} is a regular Java object, structure-like indexing can be used
3237 as a shortcut syntax. For instance, the two following statements are
3238 equivalent
3239 
3240 @example
3241 @group
3242  __java_get__ (x, "field1")
3243  x.field1
3244 @end group
3245 @end example
3246 
3247 @seealso{__java_set__, javaMethod, javaObject}
3248 @end deftypefn */)
3249 {
3250 #if defined (HAVE_JAVA)
3251 
3252  if (args.length () != 2)
3253  print_usage ();
3254 
3255  std::string name = args(1).xstring_value ("__java_get__: NAME must be a string");
3256 
3257  initialize_java ();
3258 
3259  JNIEnv *current_env = thread_jni_env ();
3260 
3261  octave_value retval;
3262 
3263  if (args(0).isjava ())
3264  {
3265  octave_java *jobj = TO_JAVA (args(0));
3266  retval = jobj->do_java_get (current_env, name);
3267  }
3268  else if (args(0).is_string ())
3269  {
3270  std::string cls = args(0).string_value ();
3271  retval = octave_java::do_java_get (current_env, cls, name);
3272  }
3273  else
3274  error ("__java_get__: OBJ must be a Java object or a string");
3275 
3276  return retval;
3277 
3278 #else
3279 
3280  octave_unused_parameter (args);
3281 
3282  err_disabled_feature ("__java_get__", "Java");
3283 
3284 #endif
3285 }
3286 
3287 DEFUN (__java_set__, args, ,
3288  doc: /* -*- texinfo -*-
3289 @deftypefn {} {@var{obj} =} __java_set__ (@var{obj}, @var{name}, @var{val})
3290 Set the value of the field @var{name} of the Java object @var{obj} to
3291 @var{val}.
3292 
3293 For static fields, @var{obj} can be a string representing the fully
3294 qualified named of the corresponding Java class.
3295 
3296 When @var{obj} is a regular Java object, structure-like indexing can be
3297 used as a shortcut syntax. For instance, the two following statements are
3298 equivalent
3299 
3300 @example
3301 @group
3302  __java_set__ (x, "field1", val)
3303  x.field1 = val
3304 @end group
3305 @end example
3306 
3307 @seealso{__java_get__, javaMethod, javaObject}
3308 @end deftypefn */)
3309 {
3310 #if defined (HAVE_JAVA)
3311 
3312  if (args.length () != 3)
3313  print_usage ();
3314 
3315  std::string name = args(1).xstring_value ("__java_set__: NAME must be a string");
3316 
3317  initialize_java ();
3318 
3319  JNIEnv *current_env = thread_jni_env ();
3320 
3321  octave_value retval;
3322 
3323  if (args(0).isjava ())
3324  {
3325  octave_java *jobj = TO_JAVA (args(0));
3326  retval = jobj->do_java_set (current_env, name, args(2));
3327  }
3328  else if (args(0).is_string ())
3329  {
3330  std::string cls = args(0).string_value ();
3331  retval = octave_java::do_java_set (current_env, cls, name, args(2));
3332  }
3333  else
3334  error ("__java_set__: OBJ must be a Java object or a string");
3335 
3336  return retval;
3337 
3338 #else
3339 
3340  octave_unused_parameter (args);
3341 
3342  err_disabled_feature ("__java_set__", "Java");
3343 
3344 #endif
3345 }
3346 
3347 DEFUN (__java2mat__, args, ,
3348  doc: /* -*- texinfo -*-
3349 @deftypefn {} {@var{mat} =} __java2mat__ (@var{javaobj})
3350 Undocumented internal function.
3351 @end deftypefn */)
3352 {
3353 #if defined (HAVE_JAVA)
3354 
3355  if (args.length () != 1)
3356  print_usage ();
3357 
3358  initialize_java ();
3359 
3360  JNIEnv *current_env = thread_jni_env ();
3361 
3362  octave_value_list retval;
3363 
3364  if (args(0).isjava ())
3365  {
3366  octave_java *jobj = TO_JAVA (args(0));
3367  retval = ovl (box_more (current_env, jobj->to_java (), nullptr));
3368  }
3369  else
3370  retval = ovl (args(0));
3371 
3372  return retval;
3373 
3374 #else
3375 
3376  octave_unused_parameter (args);
3377 
3378  err_disabled_feature ("__java2mat__", "Java");
3379 
3380 #endif
3381 }
3382 
3383 DEFUN (java_matrix_autoconversion, args, nargout,
3384  doc: /* -*- texinfo -*-
3385 @deftypefn {} {@var{val} =} java_matrix_autoconversion ()
3386 @deftypefnx {} {@var{old_val} =} java_matrix_autoconversion (@var{new_val})
3387 @deftypefnx {} {@var{old_val} =} java_matrix_autoconversion (@var{new_val}, "local")
3388 Query or set the internal variable that controls whether Java arrays are
3389 automatically converted to Octave matrices.
3390 
3391 The default value is false.
3392 
3393 When called from inside a function with the @qcode{"local"} option, the
3394 variable is changed locally for the function and any subroutines it calls.
3395 The original variable value is restored when exiting the function.
3396 @seealso{java_unsigned_autoconversion, debug_java}
3397 @end deftypefn */)
3398 {
3399 #if defined (HAVE_JAVA)
3400 
3401  return set_internal_variable (Vjava_matrix_autoconversion, args, nargout,
3402  "java_matrix_autoconversion");
3403 
3404 #else
3405 
3406  octave_unused_parameter (args);
3407  octave_unused_parameter (nargout);
3408 
3409  err_disabled_feature ("java_matrix_autoconversion", "Java");
3410 
3411 #endif
3412 }
3413 
3414 DEFUN (java_unsigned_autoconversion, args, nargout,
3415  doc: /* -*- texinfo -*-
3416 @deftypefn {} {@var{val} =} java_unsigned_autoconversion ()
3417 @deftypefnx {} {@var{old_val} =} java_unsigned_autoconversion (@var{new_val})
3418 @deftypefnx {} {@var{old_val} =} java_unsigned_autoconversion (@var{new_val}, "local")
3419 Query or set the internal variable that controls how integer classes are
3420 converted when @code{java_matrix_autoconversion} is enabled.
3421 
3422 When enabled, Java arrays of class Byte or Integer are converted to matrices
3423 of class uint8 or uint32 respectively. The default value is true.
3424 
3425 When called from inside a function with the @qcode{"local"} option, the
3426 variable is changed locally for the function and any subroutines it calls.
3427 The original variable value is restored when exiting the function.
3428 @seealso{java_matrix_autoconversion, debug_java}
3429 @end deftypefn */)
3430 {
3431 #if defined (HAVE_JAVA)
3432 
3434  "java_unsigned_autoconversion");
3435 
3436 #else
3437 
3438  octave_unused_parameter (args);
3439  octave_unused_parameter (nargout);
3440 
3441  err_disabled_feature ("java_unsigned_autoconversion", "Java");
3442 
3443 #endif
3444 }
3445 
3446 DEFUN (debug_java, args, nargout,
3447  doc: /* -*- texinfo -*-
3448 @deftypefn {} {@var{val} =} debug_java ()
3449 @deftypefnx {} {@var{old_val} =} debug_java (@var{new_val})
3450 @deftypefnx {} {@var{old_val} =} debug_java (@var{new_val}, "local")
3451 Query or set the internal variable that determines whether extra debugging
3452 information regarding the initialization of the JVM and any Java exceptions
3453 is printed.
3454 
3455 When called from inside a function with the @qcode{"local"} option, the
3456 variable is changed locally for the function and any subroutines it calls.
3457 The original variable value is restored when exiting the function.
3458 @seealso{java_matrix_autoconversion, java_unsigned_autoconversion}
3459 @end deftypefn */)
3460 {
3461 #if defined (HAVE_JAVA)
3462 
3463  return set_internal_variable (Vdebug_java, args, nargout, "debug_java");
3464 
3465 #else
3466 
3467  octave_unused_parameter (args);
3468  octave_unused_parameter (nargout);
3469 
3470  err_disabled_feature ("debug_java", "Java");
3471 
3472 #endif
3473 }
3474 
3475 // Outside of #if defined (HAVE_JAVA) because it is desirable to be able
3476 // to test for the presence of a Java object without having Java
3477 // installed.
3478 
3479 DEFUN (isjava, args, ,
3480  doc: /* -*- texinfo -*-
3481 @deftypefn {} {@var{tf} =} isjava (@var{x})
3482 Return true if @var{x} is a Java object.
3483 @seealso{class, typeinfo, isa, javaObject}
3484 @end deftypefn */)
3485 {
3486  if (args.length () != 1)
3487  print_usage ();
3488 
3489  return ovl (args(0).isjava ());
3490 }
3491 
3492 /*
3493 ## Check automatic conversion of java primitive arrays into octave types.
3494 %!testif HAVE_JAVA; usejava ("jvm")
3495 %! assert (javaObject ("java.lang.String", "hello").getBytes (),
3496 %! int8 ([104 101 108 108 111]'));
3497 
3498 ## Check automatic conversion of octave types into java primitive arrays.
3499 ## Note that uint8 is casted to int8.
3500 %!testif HAVE_JAVA; usejava ("jvm")
3501 %! assert (javaMethod ("binarySearch", "java.util.Arrays", [90 100 255], 255), 2);
3502 %! assert (javaMethod ("binarySearch", "java.util.Arrays", uint8 ([90 100 255]), uint8 (255)) < 0);
3503 %! assert (javaMethod ("binarySearch", "java.util.Arrays", uint8 ([90 100 128]), uint8 (128)) < 0);
3504 %! assert (javaMethod ("binarySearch", "java.util.Arrays", uint8 ([90 100 127]), uint8 (127)), 2);
3505 %! assert (javaMethod ("binarySearch", "java.util.Arrays", uint16 ([90 100 128]), uint16 (128)), 2);
3506 
3507 ## Check we can create objects that wrap java literals
3508 %!testif HAVE_JAVA; usejava ("jvm") <*38821>
3509 %! assert (class (javaObject ("java.lang.Byte", uint8 (1))),
3510 %! "java.lang.Byte");
3511 %! assert (class (javaObject ("java.lang.Byte", int8 (1))),
3512 %! "java.lang.Byte");
3513 %! assert (class (javaObject ("java.lang.Short", uint16 (1))),
3514 %! "java.lang.Short");
3515 %! assert (class (javaObject ("java.lang.Short", int16 (1))),
3516 %! "java.lang.Short");
3517 %! assert (class (javaObject ("java.lang.Integer", uint32 (1))),
3518 %! "java.lang.Integer");
3519 %! assert (class (javaObject ("java.lang.Integer", int32 (1))),
3520 %! "java.lang.Integer");
3521 %! assert (class (javaObject ("java.lang.Long", uint64 (1))),
3522 %! "java.lang.Long");
3523 %! assert (class (javaObject ("java.lang.Long", int64 (1))),
3524 %! "java.lang.Long");
3525 
3526 ## More checks of java numeric and boolean class instances
3527 %!testif HAVE_JAVA; usejava ("jvm")
3528 %! n = javaObject ("java.lang.Double", 1.35);
3529 %! assert (n.compareTo (1.0), 1);
3530 %! assert (n.compareTo (1.35), 0);
3531 %! assert (n.compareTo (10), -1);
3532 %! assert (n.isInfinite (), false);
3533 
3534 %!testif HAVE_JAVA; usejava ("jvm") <51804>
3535 %! n = javaObject ("java.lang.Float", 1.35);
3536 %! assert (n.compareTo (1.0), 1);
3537 %! assert (n.compareTo (1.35), 0);
3538 %! assert (n.compareTo (10), -1);
3539 %! assert (n.doubleValue (), 1.35, 1e7);
3540 
3541 %!testif HAVE_JAVA; usejava ("jvm")
3542 %! n = javaObject ("java.lang.Long", (int64 (1)));
3543 %! assert (n.compareTo (int64 (0)), 1);
3544 %! assert (n.compareTo (int64 (1)), 0);
3545 %! assert (n.compareTo (int64 (2)), -1);
3546 %! assert (n.toString (), "1");
3547 
3548 %!testif HAVE_JAVA; usejava ("jvm") <51804>
3549 %! n = javaObject ("java.lang.Integer", 1.35);
3550 %! assert (n.compareTo (0), 1);
3551 %! assert (n.compareTo (1), 0);
3552 %! assert (n.compareTo (2), -1);
3553 
3554 %!testif HAVE_JAVA; usejava ("jvm") <51804>
3555 %! n = javaObject ("java.lang.Short", 1.35);
3556 %! assert (n.compareTo (0), 1);
3557 %! assert (n.compareTo (1), 0);
3558 %! assert (n.compareTo (2), -1);
3559 
3560 %!testif HAVE_JAVA; usejava ("jvm")
3561 %! n = javaObject ("java.lang.Byte", int8 (17));
3562 %! assert (n.compareTo (int8 (20)), -3);
3563 %! assert (n.compareTo (int8 (10)), 7);
3564 %! assert (n.compareTo (int8 (17)), 0);
3565 
3566 %!testif HAVE_JAVA; usejava ("jvm")
3567 %! b = javaObject ("java.lang.Boolean", true);
3568 %! assert (b.compareTo (true), 0);
3569 %! assert (b.compareTo (false), 1);
3570 %! b = javaObject ("java.lang.Boolean", false);
3571 %! assert (b.compareTo (true), -1);
3572 %! assert (b.compareTo (false), 0);
3573 
3574 ## Test for automatic conversion of specific numeric classes
3575 %!testif HAVE_JAVA; usejava ("jvm") <*48013>
3576 %! assert (javaMethod ("valueOf", "java.lang.Byte", int8 (1)), 1)
3577 %! assert (javaMethod ("valueOf", "java.lang.Short", int16 (1)), 1)
3578 %! assert (javaMethod ("valueOf", "java.lang.Integer", int32 (1)), 1)
3579 %! assert (javaMethod ("valueOf", "java.lang.Long", int64 (1)), 1)
3580 %! assert (javaMethod ("valueOf", "java.lang.Float", single (1)), 1)
3581 %! assert (javaMethod ("valueOf", "java.lang.Double", double (1)), 1)
3582 %! assert (class (javaMethod ("valueOf", "java.math.BigDecimal", double (1))),
3583 %! "java.math.BigDecimal")
3584 %! assert (class (javaMethod ("valueOf", "java.math.BigInteger", int64 (1))),
3585 %! "java.math.BigInteger")
3586 
3587 ## Automatic conversion from string cell array into String[]
3588 %!testif HAVE_JAVA; usejava ("jvm") <*45290>
3589 %! assert (javaMethod ("binarySearch", "java.util.Arrays", {"aaa", "bbb", "ccc", "zzz"}, "aaa"), 0);
3590 %! assert (javaMethod ("binarySearch", "java.util.Arrays", {"aaa", "bbb", "ccc", "zzz"}, "zzz"), 3);
3591 %! assert (javaMethod ("binarySearch", "java.util.Arrays", {"aaa", "bbb", "ccc", "zzz"}, "hhh") < 0);
3592 
3593 ## Test that Octave index syntax allows Java object method calls with args
3594 %!testif HAVE_JAVA; usejava ("jvm") <*51152>
3595 %! s = javaObject ("java.lang.String", "Octave");
3596 %! assert (s.length (), 6)
3597 %! assert (s.charAt (0), "O")
3598 %! assert (s.charAt (5), "e")
3599 %! assert (s.matches ("^Octave$"))
3600 %! assert (s.startsWith ("Oct"))
3601 %! ## same tests with Java object as part of another indexing expression
3602 %! a(1).s = s;
3603 %! assert (! a(1).s.isEmpty ())
3604 %! assert (a(1).s.length (), 6)
3605 %! assert (a(1).s.charAt (0), "O")
3606 %! assert (a(1).s.charAt (5), "e")
3607 %! assert (a(1).s.matches ("^Octave$"))
3608 %! assert (a(1).s.startsWith ("Oct"))
3609 
3610 ## Check for basic usability of the java awt library
3611 ## Skip the test on OS X where we currently have Java 9 and attempting
3612 ## to use awt causes Octave to exit with a message about Java not being
3613 ## installed (it is) instead of returning false.
3614 %!testif HAVE_JAVA; ! ismac () && usejava ("jvm") && usejava ("awt") && have_window_system ()
3615 %! frame = javaObject ("java.awt.Frame");
3616 %! frame.setResizable (true);
3617 %! assert (frame.isResizable ());
3618 */
3619 
3620 OCTAVE_END_NAMESPACE(octave)
#define C(a, b)
Definition: Faddeeva.cc:259
octave_value_list F__java_set__(const octave_value_list &=octave_value_list(), int=0)
octave_value_list FjavaMethod(const octave_value_list &=octave_value_list(), int=0)
octave_value_list F__java_get__(const octave_value_list &=octave_value_list(), int=0)
octave_idx_type numel() const
Number of elements in the array.
Definition: Array.h:414
Definition: Cell.h:43
Definition: dMatrix.h:42
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
void resize(int n, int fill_value=0)
Definition: dim-vector.h:272
octave_idx_type ndims() const
Number of dimensions.
Definition: dim-vector.h:257
bool isvector() const
Definition: dim-vector.h:395
Definition: oct-env.h:40
void newline(std::ostream &os) const
Definition: ov-base.cc:1388
static void register_type()
Definition: ov-base.cc:101
octave::refcount< octave_idx_type > m_count
Definition: ov-base.h:933
friend class octave_value
Definition: ov-base.h:269
virtual octave_value subsref(const std::string &type, const std::list< octave_value_list > &idx)
Definition: ov-base.cc:248
std::string class_name() const
Definition: ov-java.h:185
octave_value do_java_set(void *jni_env, const std::string &name, const octave_value &val)
void print(std::ostream &os, bool pr_as_read_syntax=false)
string_vector map_keys() const
dim_vector dims() const
bool is_java_string() const
Definition: ov-java.cc:909
octave_value do_javaMethod(void *jni_env, const std::string &name, const octave_value_list &args)
octave_value convert_to_str_internal(bool pad, bool force, char type) const
octave_value do_java_get(void *jni_env, const std::string &name)
void print_raw(std::ostream &os, bool pr_as_read_syntax=false) const
bool is_instance_of(const std::string &) const
Definition: ov-java.cc:934
void * to_java() const
Definition: ov-java.h:58
octave_value subsasgn(const std::string &type, const std::list< octave_value_list > &idx, const octave_value &rhs)
static octave_value do_javaObject(void *jni_env, const std::string &name, const octave_value_list &args)
octave_value_list & append(const octave_value &val)
Definition: ovl.cc:98
octave_idx_type length() const
Definition: ovl.h:113
bool is_function_handle() const
Definition: ov.h:768
bool isfloat() const
Definition: ov.h:701
bool is_undefined() const
Definition: ov.h:595
bool is_uint32_type() const
Definition: ov.h:724
std::string class_name() const
Definition: ov.h:1347
bool is_real_scalar() const
Definition: ov.h:610
int32NDArray int32_array_value() const
Definition: ov.h:956
Cell cell_value() const
int8NDArray int8_array_value() const
Definition: ov.h:950
octave_idx_type rows() const
Definition: ov.h:545
bool isreal() const
Definition: ov.h:738
bool is_string() const
Definition: ov.h:637
bool is_int8_type() const
Definition: ov.h:706
bool isjava() const
Definition: ov.h:667
bool is_range() const
Definition: ov.h:646
bool isempty() const
Definition: ov.h:601
bool is_uint8_type() const
Definition: ov.h:718
bool is_uint64_type() const
Definition: ov.h:727
bool is_uint16_type() const
Definition: ov.h:721
octave_function * function_value(bool silent=false) const
bool is_int16_type() const
Definition: ov.h:709
bool iscell() const
Definition: ov.h:604
octave_idx_type numel() const
Definition: ov.h:559
bool is_real_matrix() const
Definition: ov.h:613
std::string string_value(bool force=false) const
Definition: ov.h:974
bool is_int64_type() const
Definition: ov.h:715
bool is_matrix_type() const
Definition: ov.h:747
NDArray array_value(bool frc_str_conv=false) const
Definition: ov.h:859
octave_idx_type length() const
bool is_double_type() const
Definition: ov.h:695
bool is_int32_type() const
Definition: ov.h:712
bool is_bool_scalar() const
Definition: ov.h:622
octave_value convert_to_str(bool pad=false, bool force=false, char type='\'') const
Definition: ov.h:1307
uint8NDArray uint8_array_value() const
Definition: ov.h:962
std::string xstring_value(const char *fmt,...) const
bool iscellstr() const
Definition: ov.h:607
octave_idx_type columns() const
Definition: ov.h:547
Matrix matrix_value(bool frc_str_conv=false) const
Definition: ov.h:853
bool islogical() const
Definition: ov.h:735
Array< std::string > cellstr_value() const
Definition: ov.h:982
dim_vector dims() const
Definition: ov.h:541
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
std::string release()
void print_usage(void)
Definition: defun-int.h:72
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:56
void warning(const char *fmt,...)
Definition: error.cc:1063
void() error(const char *fmt,...)
Definition: error.cc:988
#define panic_impossible()
Definition: error.h:503
void err_disabled_feature(const std::string &fcn, const std::string &feature, const std::string &pkg)
Definition: errwarn.cc:53
void octave_set_default_fpucw(void)
octave::idx_vector idx_vector
Definition: idx-vector.h:1022
interpreter & __get_interpreter__()
F77_RET_T const F77_INT F77_CMPLX const F77_INT F77_CMPLX * B
F77_RET_T const F77_INT const F77_INT const F77_INT F77_DBLE const F77_INT F77_DBLE const F77_INT F77_DBLE const F77_INT F77_DBLE * Z
std::ifstream ifstream(const std::string &filename, const std::ios::openmode mode)
Definition: lo-sysdep.cc:621
char * strsave(const char *s)
Definition: lo-utils.cc:78
T octave_idx_type m
Definition: mx-inlines.cc:781
octave_idx_type n
Definition: mx-inlines.cc:761
T * r
Definition: mx-inlines.cc:781
std::string fcn_file_dir()
Definition: defaults.cc:345
std::string dir_sep_str()
Definition: file-ops.cc:242
std::string tilde_expand(const std::string &name)
Definition: file-ops.cc:289
octave_value set_internal_variable(bool &var, const octave_value_list &args, int nargout, const char *nm)
Definition: variables.cc:583
process_execution_result run_command_and_return_output(const std::string &cmd_str)
Definition: oct-process.cc:51
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))
#define LIBJVM_FILE_NAME
Definition: ov-java.cc:82
#define BOX_PRIMITIVE_ARRAY(JAVA_TYPE, JAVA_ID, JAVA_TYPE_CAP, OCTAVE_ID)
#define TO_JCLASS(obj)
Definition: ov-java.cc:86
JNIEXPORT jboolean JNICALL Java_org_octave_Octave_call(JNIEnv *, jclass, jstring, jobjectArray, jobjectArray)
bool Vjava_unsigned_autoconversion
Definition: ov-java.cc:223
java_local_ref< jthrowable > jthrowable_ref
Definition: ov-java.cc:153
#define UNBOX_PRIMITIVE_SCALAR(OCTAVE_T, METHOD_T, JAVA_T, JAVA_CON)
JNIEXPORT jboolean JNICALL Java_org_octave_Octave_needThreadedInvokation(JNIEnv *, jclass)
#define TO_JNIENV(env)
Definition: ov-java.cc:88
java_local_ref< jdoubleArray > jdoubleArray_ref
Definition: ov-java.cc:152
java_local_ref< jobjectArray > jobjectArray_ref
Definition: ov-java.cc:149
java_local_ref< jclass > jclass_ref
Definition: ov-java.cc:147
jint(JNICALL * JNI_CreateJavaVM_t)(JavaVM **pvm, JNIEnv **penv, void *args)
Definition: ov-java.cc:90
java_local_ref< jbyteArray > jbyteArray_ref
Definition: ov-java.cc:151
JNIEXPORT void JNICALL Java_org_octave_Octave_doInvoke(JNIEnv *, jclass, jint, jobjectArray)
JNIEXPORT void JNICALL Java_org_octave_Octave_doEvalString(JNIEnv *, jclass, jstring)
java_local_ref< jobject > jobject_ref
Definition: ov-java.cc:146
bool Vjava_matrix_autoconversion
Definition: ov-java.cc:222
#define UNBOX_PRIMITIVE_ARRAY(METHOD_T, OCTAVE_T, JAVA_T, JAVA_T_CAP)
#define TO_JAVA(obj)
JNIEXPORT void JNICALL Java_org_octave_OctaveReference_doFinalize(JNIEnv *, jclass, jint)
bool Vdebug_java
Definition: ov-java.cc:224
java_local_ref< jstring > jstring_ref
Definition: ov-java.cc:148
#define TO_JOBJECT(obj)
Definition: ov-java.cc:85
java_local_ref< jintArray > jintArray_ref
Definition: ov-java.cc:150
jint(JNICALL * JNI_GetCreatedJavaVMs_t)(JavaVM **pvm, jsize bufLen, jsize *nVMs)
Definition: ov-java.cc:93
void * voidptr
Definition: ov-java.h:36
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:219
#define octave_stdout
Definition: pager.h:309
F77_RET_T len
Definition: xerbla.cc:61