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