GNU Octave 11.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-2026 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 <clocale>
42#include <fstream>
43#include <map>
44#include <string>
45#include <vector>
46
47#include "cmd-edit.h"
48#include "file-ops.h"
49#include "file-stat.h"
50#include "fpucw-wrappers.h"
51#include "oct-env.h"
52#include "oct-shlib.h"
53#include "oct-sysdep.h"
54
55#include "Cell.h"
56#include "builtin-defun-decls.h"
57#include "defaults.h"
58#include "defun.h"
59#include "error.h"
60#include "errwarn.h"
61#include "interpreter-private.h"
62#include "interpreter.h"
63#include "load-path.h"
64#include "oct-process.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 (std::next (idx.begin (), 2),
2301 idx.end ());
2302 u(0).subsasgn (type.substr (2), next_idx, rhs);
2303
2304 m_count++;
2305 retval = octave_value (this);
2306 }
2307 else if (type[1] == '.')
2308 {
2309 octave_value_list u = subsref (type.substr (0, 1), idx, 1);
2310
2311 std::list<octave_value_list> next_idx (std::next (idx.begin ()),
2312 idx.end ());
2313 u(0).subsasgn (type.substr (1), next_idx, rhs);
2314
2315 m_count++;
2316 retval = octave_value (this);
2317 }
2318 else
2319 error ("invalid indexing/assignment on Java object");
2320 break;
2321
2322 case '(':
2323 if (current_env)
2324 {
2325 set_array_elements (current_env, TO_JOBJECT (to_java ()),
2326 idx.front (), rhs);
2327
2328 m_count++;
2329 retval = octave_value (this);
2330 }
2331 break;
2332
2333 default:
2334 error ("Java object cannot be indexed with %c", type[0]);
2335 break;
2336 }
2337
2338 return retval;
2339
2340#else
2341
2342 octave_unused_parameter (type);
2343 octave_unused_parameter (idx);
2344 octave_unused_parameter (rhs);
2345
2346 // This shouldn't happen because construction of octave_java objects is
2347 // supposed to be impossible if Java is not available.
2348
2349 error_unexpected ("octave_java::subsasgn");
2350
2351#endif
2352}
2353
2356{
2357#if defined (HAVE_JAVA)
2358
2359 JNIEnv *current_env = thread_jni_env ();
2360
2361 if (current_env)
2362 return get_invoke_list (current_env, to_java ());
2363 else
2364 return string_vector ();
2365
2366#else
2367
2368 // This shouldn't happen because construction of octave_java objects is
2369 // supposed to be impossible if Java is not available.
2370
2371 error_unexpected ("octave_java::map_keys");
2372
2373#endif
2374}
2375
2377octave_java::convert_to_str_internal (bool, bool force, char type) const
2378{
2379#if defined (HAVE_JAVA)
2380
2381 JNIEnv *current_env = thread_jni_env ();
2382
2383 if (current_env)
2384 return convert_to_string (current_env, TO_JOBJECT (to_java ()), force,
2385 type);
2386 else
2387 return octave_value ("");
2388
2389#else
2390
2391 octave_unused_parameter (force);
2392 octave_unused_parameter (type);
2393
2394 // This shouldn't happen because construction of octave_java objects is
2395 // supposed to be impossible if Java is not available.
2396
2397 error_unexpected ("octave_java::convert_to_str_internal");
2398
2399#endif
2400}
2401
2402void
2403octave_java::print (std::ostream& os, bool)
2404{
2405 print_raw (os);
2406 newline (os);
2407}
2408
2409void
2410octave_java::print_raw (std::ostream& os, bool) const
2411{
2412 os << "<Java object: " << m_java_classname << '>';
2413}
2414
2415// FIXME: Need routines to actually save/load java objects through Serialize.
2416// See bug #42112.
2417
2418bool
2419octave_java::save_ascii (std::ostream& /* os */)
2420{
2421 warning ("save: unable to save java objects, skipping");
2422
2423 return true;
2424}
2425
2426bool
2427octave_java::load_ascii (std::istream& /* is */)
2428{
2429 // Silently skip over java object that was not saved
2430 return true;
2431}
2432
2433bool
2434octave_java::save_binary (std::ostream& /* os */, bool /* save_as_floats */)
2435{
2436 warning ("save: unable to save java objects, skipping");
2437
2438 return true;
2439}
2440
2441bool
2442octave_java::load_binary (std::istream& /* is */, bool /* swap*/,
2443 octave::mach_info::float_format /* fmt */)
2444{
2445 // Silently skip over java object that was not saved
2446 return true;
2447}
2448
2449bool
2450octave_java::save_hdf5 (octave_hdf5_id /* loc_id */, const char * /* name */,
2451 bool /* save_as_floats */)
2452{
2453 warning ("save: unable to save java objects, skipping");
2454
2455 return true;
2456}
2457
2458bool
2459octave_java::load_hdf5 (octave_hdf5_id /* loc_id */, const char * /* name */)
2460{
2461 // Silently skip object that was not saved
2462 return true;
2463}
2464
2466octave_java::do_javaMethod (void *jni_env_arg, const std::string& name,
2467 const octave_value_list& args)
2468{
2469#if defined (HAVE_JAVA)
2470
2471 octave_value retval;
2472
2473 JNIEnv *jni_env = TO_JNIENV (jni_env_arg);
2474
2475 if (jni_env)
2476 {
2477 jobjectArray_ref arg_objs (jni_env), arg_types (jni_env);
2478 if (unbox (jni_env, args, arg_objs, arg_types))
2479 {
2480 jclass_ref helperClass (jni_env, find_octave_class (jni_env, "org/octave/ClassHelper"));
2481 jmethodID mID = jni_env->GetStaticMethodID (helperClass, "invokeMethod",
2482 "(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;[Ljava/lang/Class;)Ljava/lang/Object;");
2483 jstring_ref methName (jni_env, jni_env->NewStringUTF (name.c_str ()));
2484 jobjectArray_ref resObj (jni_env,
2485 reinterpret_cast<jobjectArray> (jni_env->CallStaticObjectMethod (helperClass, mID,
2486 to_java (), jstring (methName), jobjectArray (arg_objs), jobjectArray (arg_types))));
2487 if (resObj)
2488 retval = box (jni_env, resObj);
2489 else
2490 retval = check_exception (jni_env);
2491 }
2492
2494 }
2495
2496 return retval;
2497
2498#else
2499
2500 octave_unused_parameter (jni_env_arg);
2501 octave_unused_parameter (name);
2502 octave_unused_parameter (args);
2503
2504 // This shouldn't happen because construction of octave_java objects is
2505 // supposed to be impossible if Java is not available.
2506
2507 error_unexpected ("octave_java::do_javaMethod");
2508
2509#endif
2510}
2511
2513octave_java::do_javaMethod (const std::string& name,
2514 const octave_value_list& args)
2515{
2516#if defined (HAVE_JAVA)
2517
2518 return do_javaMethod (thread_jni_env (), name, args);
2519
2520#else
2521
2522 octave_unused_parameter (name);
2523 octave_unused_parameter (args);
2524
2525 // This shouldn't happen because construction of octave_java
2526 // objects is supposed to be impossible if Java is not available.
2527
2528 error_unexpected ("octave_java::do_javaMethod");
2529
2530#endif
2531}
2532
2535 const std::string& class_name,
2536 const std::string& name,
2537 const octave_value_list& args)
2538{
2539#if defined (HAVE_JAVA)
2540
2541 octave_value retval;
2542
2543 JNIEnv *jni_env = TO_JNIENV (jni_env_arg);
2544
2545 if (jni_env)
2546 {
2547 jobjectArray_ref arg_objs (jni_env), arg_types (jni_env);
2548 if (unbox (jni_env, args, arg_objs, arg_types))
2549 {
2550 jclass_ref helperClass (jni_env,
2551 find_octave_class (jni_env,
2552 "org/octave/ClassHelper"));
2553 jmethodID mID = jni_env->GetStaticMethodID (helperClass,
2554 "invokeStaticMethod",
2555 "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;[Ljava/lang/Class;)Ljava/lang/Object;");
2556 jstring_ref methName (jni_env,
2557 jni_env->NewStringUTF (name.c_str ()));
2558 jstring_ref clsName (jni_env,
2559 jni_env->NewStringUTF (class_name.c_str ()));
2560 jobject_ref resObj (jni_env,
2561 jni_env->CallStaticObjectMethod (helperClass,
2562 mID,
2563 jstring (clsName),
2564 jstring (methName),
2565 jobjectArray (arg_objs),
2566 jobjectArray (arg_types)));
2567 if (resObj)
2568 retval = box (jni_env, resObj);
2569 else
2570 retval = check_exception (jni_env);
2571 }
2572
2574 }
2575
2576 return retval;
2577
2578#else
2579
2580 octave_unused_parameter (jni_env_arg);
2581 octave_unused_parameter (class_name);
2582 octave_unused_parameter (name);
2583 octave_unused_parameter (args);
2584
2585 // This shouldn't happen because construction of octave_java
2586 // objects is supposed to be impossible if Java is not available.
2587
2588 error_unexpected ("octave_java::do_javaMethod");
2589
2590#endif
2591}
2592
2594octave_java::do_javaMethod (const std::string& class_name,
2595 const std::string& name,
2596 const octave_value_list& args)
2597{
2598#if defined (HAVE_JAVA)
2599
2600 return do_javaMethod (thread_jni_env (), class_name, name, args);
2601
2602#else
2603
2604 octave_unused_parameter (class_name);
2605 octave_unused_parameter (name);
2606 octave_unused_parameter (args);
2607
2608 // This shouldn't happen because construction of octave_java
2609 // objects is supposed to be impossible if Java is not available.
2610
2611 error_unexpected ("octave_java::do_javaMethod");
2612
2613#endif
2614}
2615
2617octave_java::do_javaObject (void *jni_env_arg, const std::string& name,
2618 const octave_value_list& args)
2619{
2620#if defined (HAVE_JAVA)
2621
2622 octave_value retval;
2623
2624 JNIEnv *jni_env = TO_JNIENV (jni_env_arg);
2625
2626 if (jni_env)
2627 {
2628 jobjectArray_ref arg_objs (jni_env), arg_types (jni_env);
2629
2630 if (unbox (jni_env, args, arg_objs, arg_types))
2631 {
2632 jclass_ref helperClass (jni_env,
2633 find_octave_class (jni_env,
2634 "org/octave/ClassHelper"));
2635 jmethodID mID = jni_env->GetStaticMethodID (helperClass,
2636 "invokeConstructor",
2637 "(Ljava/lang/String;[Ljava/lang/Object;[Ljava/lang/Class;)Ljava/lang/Object;");
2638 jstring_ref clsName (jni_env,
2639 jni_env->NewStringUTF (name.c_str ()));
2640 jobject_ref resObj (jni_env,
2641 jni_env->CallStaticObjectMethod (helperClass,
2642 mID,
2643 jstring (clsName),
2644 jobjectArray (arg_objs),
2645 jobjectArray (arg_types)));
2646
2647 if (resObj)
2648 retval = octave_value (new octave_java (resObj, nullptr));
2649 else
2650 check_exception (jni_env);
2651 }
2652
2654 }
2655
2656 return retval;
2657
2658#else
2659
2660 octave_unused_parameter (jni_env_arg);
2661 octave_unused_parameter (name);
2662 octave_unused_parameter (args);
2663
2664 // This shouldn't happen because construction of octave_java
2665 // objects is supposed to be impossible if Java is not available.
2666
2667 error_unexpected ("octave_java::do_javaObject");
2668
2669#endif
2670}
2671
2673octave_java::do_javaObject (const std::string& name,
2674 const octave_value_list& args)
2675{
2676#if defined (HAVE_JAVA)
2677
2678 return do_javaObject (thread_jni_env (), name, args);
2679
2680#else
2681
2682 octave_unused_parameter (name);
2683 octave_unused_parameter (args);
2684
2685 // This shouldn't happen because construction of octave_java
2686 // objects is supposed to be impossible if Java is not available.
2687
2688 error_unexpected ("octave_java::do_javaObject");
2689
2690#endif
2691}
2692
2694octave_java::do_java_get (void *jni_env_arg, const std::string& name)
2695{
2696#if defined (HAVE_JAVA)
2697
2698 octave_value retval;
2699
2700 JNIEnv *jni_env = TO_JNIENV (jni_env_arg);
2701
2702 if (jni_env)
2703 {
2704 jclass_ref helperClass (jni_env,
2705 find_octave_class (jni_env,
2706 "org/octave/ClassHelper"));
2707 jmethodID mID = jni_env->GetStaticMethodID (helperClass, "getField",
2708 "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;");
2709 jstring_ref fName (jni_env, jni_env->NewStringUTF (name.c_str ()));
2710 jobject_ref resObj (jni_env,
2711 jni_env->CallStaticObjectMethod (helperClass,
2712 mID,
2713 to_java (),
2714 jstring (fName)));
2715
2716 if (resObj)
2717 retval = box (jni_env, resObj);
2718 else
2719 retval = check_exception (jni_env);
2720
2722 }
2723
2724 return retval;
2725
2726#else
2727
2728 octave_unused_parameter (jni_env_arg);
2729 octave_unused_parameter (name);
2730
2731 // This shouldn't happen because construction of octave_java
2732 // objects is supposed to be impossible if Java is not available.
2733
2734 error_unexpected ("octave_java::do_java_get");
2735
2736#endif
2737}
2738
2740octave_java::do_java_get (const std::string& name)
2741{
2742#if defined (HAVE_JAVA)
2743
2744 return do_java_get (thread_jni_env (), name);
2745
2746#else
2747
2748 octave_unused_parameter (name);
2749
2750 // This shouldn't happen because construction of octave_java
2751 // objects is supposed to be impossible if Java is not available.
2752
2753 error_unexpected ("octave_java::do_java_get");
2754
2755#endif
2756}
2757
2759octave_java::do_java_get (void *jni_env_arg, const std::string& class_name,
2760 const std::string& name)
2761{
2762#if defined (HAVE_JAVA)
2763
2764 octave_value retval;
2765
2766 JNIEnv *jni_env = TO_JNIENV (jni_env_arg);
2767
2768 if (jni_env)
2769 {
2770 jclass_ref helperClass (jni_env,
2771 find_octave_class (jni_env,
2772 "org/octave/ClassHelper"));
2773 jmethodID mID = jni_env->GetStaticMethodID (helperClass,
2774 "getStaticField",
2775 "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;");
2776 jstring_ref cName (jni_env, jni_env->NewStringUTF (class_name.c_str ()));
2777 jstring_ref fName (jni_env, jni_env->NewStringUTF (name.c_str ()));
2778 jobject_ref resObj (jni_env,
2779 jni_env->CallStaticObjectMethod (helperClass, mID,
2780 jstring (cName),
2781 jstring (fName)));
2782 if (resObj)
2783 retval = box (jni_env, resObj);
2784 else
2785 retval = check_exception (jni_env);
2786
2788 }
2789
2790 return retval;
2791
2792#else
2793
2794 octave_unused_parameter (jni_env_arg);
2795 octave_unused_parameter (class_name);
2796 octave_unused_parameter (name);
2797
2798 // This shouldn't happen because construction of octave_java
2799 // objects is supposed to be impossible if Java is not available.
2800
2801 error_unexpected ("octave_java::do_java_get");
2802
2803#endif
2804}
2805
2807octave_java::do_java_get (const std::string& class_name,
2808 const std::string& name)
2809{
2810#if defined (HAVE_JAVA)
2811
2812 return do_java_get (thread_jni_env (), class_name, name);
2813
2814#else
2815
2816 octave_unused_parameter (class_name);
2817 octave_unused_parameter (name);
2818
2819 // This shouldn't happen because construction of octave_java
2820 // objects is supposed to be impossible if Java is not available.
2821
2822 error_unexpected ("octave_java::do_java_get");
2823
2824#endif
2825}
2826
2828octave_java::do_java_set (void *jni_env_arg, const std::string& name,
2829 const octave_value& val)
2830{
2831#if defined (HAVE_JAVA)
2832
2833 octave_value retval;
2834
2835 JNIEnv *jni_env = TO_JNIENV (jni_env_arg);
2836
2837 if (jni_env)
2838 {
2839 jobject_ref jobj (jni_env);
2840 jclass_ref jcls (jni_env);
2841
2842 if (unbox (jni_env, val, jobj, jcls))
2843 {
2844 jclass_ref helperClass (jni_env,
2845 find_octave_class (jni_env,
2846 "org/octave/ClassHelper"));
2847 jmethodID mID = jni_env->GetStaticMethodID (helperClass, "setField",
2848 "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)V");
2849 jstring_ref fName (jni_env, jni_env->NewStringUTF (name.c_str ()));
2850 jni_env->CallStaticObjectMethod (helperClass, mID, to_java (),
2851 jstring (fName), jobject (jobj));
2852 check_exception (jni_env);
2853 }
2854
2856 }
2857
2858 return retval;
2859
2860#else
2861
2862 octave_unused_parameter (jni_env_arg);
2863 octave_unused_parameter (name);
2864 octave_unused_parameter (val);
2865
2866 // This shouldn't happen because construction of octave_java
2867 // objects is supposed to be impossible if Java is not available.
2868
2869 error_unexpected ("octave_java::do_java_set");
2870
2871#endif
2872}
2873
2875octave_java::do_java_set (const std::string& name, const octave_value& val)
2876{
2877#if defined (HAVE_JAVA)
2878
2879 return do_java_set (thread_jni_env (), name, val);
2880
2881#else
2882
2883 octave_unused_parameter (name);
2884 octave_unused_parameter (val);
2885
2886 // This shouldn't happen because construction of octave_java
2887 // objects is supposed to be impossible if Java is not available.
2888
2889 error_unexpected ("octave_java::do_java_set");
2890
2891#endif
2892}
2893
2895octave_java::do_java_set (void *jni_env_arg, const std::string& class_name,
2896 const std::string& name, const octave_value& val)
2897{
2898#if defined (HAVE_JAVA)
2899
2900 octave_value retval;
2901
2902 JNIEnv *jni_env = TO_JNIENV (jni_env_arg);
2903
2904 if (jni_env)
2905 {
2906 jobject_ref jobj (jni_env);
2907 jclass_ref jcls (jni_env);
2908
2909 if (unbox (jni_env, val, jobj, jcls))
2910 {
2911 jclass_ref helperClass (jni_env,
2912 find_octave_class (jni_env,
2913 "org/octave/ClassHelper"));
2914 jmethodID mID = jni_env->GetStaticMethodID (helperClass,
2915 "setStaticField",
2916 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V");
2917 jstring_ref cName (jni_env,
2918 jni_env->NewStringUTF (class_name.c_str ()));
2919 jstring_ref fName (jni_env, jni_env->NewStringUTF (name.c_str ()));
2920 jni_env->CallStaticObjectMethod (helperClass, mID, jstring (cName),
2921 jstring (fName), jobject (jobj));
2922 check_exception (jni_env);
2923 }
2924
2926 }
2927
2928 return retval;
2929
2930#else
2931
2932 octave_unused_parameter (jni_env_arg);
2933 octave_unused_parameter (class_name);
2934 octave_unused_parameter (name);
2935 octave_unused_parameter (val);
2936
2937 // This shouldn't happen because construction of octave_java
2938 // objects is supposed to be impossible if Java is not available.
2939
2940 error_unexpected ("octave_java::do_java_set");
2941
2942#endif
2943}
2944
2946octave_java::do_java_set (const std::string& class_name,
2947 const std::string& name,
2948 const octave_value& val)
2949{
2950#if defined (HAVE_JAVA)
2951
2952 return do_java_set (thread_jni_env (), class_name, name, val);
2953
2954#else
2955
2956 octave_unused_parameter (class_name);
2957 octave_unused_parameter (name);
2958 octave_unused_parameter (val);
2959
2960 // This shouldn't happen because construction of octave_java
2961 // objects is supposed to be impossible if Java is not available.
2962
2963 error_unexpected ("octave_java::do_java_set");
2964
2965#endif
2966}
2967
2968void
2969octave_java::init (void *jobj_arg, void *jcls_arg)
2970{
2971#if defined (HAVE_JAVA)
2972
2973 jobject jobj = TO_JOBJECT (jobj_arg);
2974 jclass jcls = TO_JCLASS (jcls_arg);
2975
2976 JNIEnv *current_env = thread_jni_env ();
2977
2978 if (current_env)
2979 {
2980 if (jobj)
2981 m_java_object = current_env->NewGlobalRef (jobj);
2982
2983 if (jcls)
2984 m_java_class = current_env->NewGlobalRef (jcls);
2985 else if (m_java_object)
2986 {
2987 jclass_ref ocls (current_env,
2988 current_env->GetObjectClass(TO_JOBJECT (m_java_object)));
2989 m_java_class = current_env->NewGlobalRef (jclass (ocls));
2990 }
2991
2992 if (m_java_class)
2993 {
2994 jclass_ref clsCls (current_env,
2995 current_env->GetObjectClass (TO_JCLASS (m_java_class)));
2996 jmethodID mID = current_env->GetMethodID (clsCls,
2997 "getCanonicalName",
2998 "()Ljava/lang/String;");
2999 jobject_ref resObj (current_env,
3000 current_env->CallObjectMethod (TO_JCLASS (m_java_class), mID));
3001 m_java_classname = jstring_to_string (current_env, resObj);
3002 }
3003 }
3004
3005#else
3006
3007 octave_unused_parameter (jobj_arg);
3008 octave_unused_parameter (jcls_arg);
3009
3010 // This shouldn't happen because construction of octave_java
3011 // objects is supposed to be impossible if Java is not available.
3012
3013 error_unexpected ("octave_java::init");
3014
3015#endif
3016}
3017
3018void
3019octave_java::release ()
3020{
3021#if defined (HAVE_JAVA)
3022
3023 JNIEnv *current_env = thread_jni_env ();
3024
3025 if (current_env)
3026 {
3027 if (m_java_object)
3028 current_env->DeleteGlobalRef (TO_JOBJECT (m_java_object));
3029
3030 if (m_java_class)
3031 current_env->DeleteGlobalRef (TO_JCLASS (m_java_class));
3032
3033 m_java_object = nullptr;
3034 m_java_class = nullptr;
3035 }
3036
3037#else
3038
3039 // This shouldn't happen because construction of octave_java objects is
3040 // supposed to be impossible if Java is not available.
3041
3042 error_unexpected ("octave_java::release");
3043
3044#endif
3045}
3046
3048
3049// DEFUN blocks below must be outside of HAVE_JAVA block so that documentation
3050// strings are always available, even when functions are not.
3051
3052DEFUN (__java_init__, , ,
3053 doc: /* -*- texinfo -*-
3054@deftypefn {} {@var{status} =} __java_init__ ()
3055Internal function used @strong{only} when debugging the Java interface.
3056
3057This function will directly call @code{initialize_java} to create an instance
3058of a JVM.
3059
3060The return variable @var{status} is 1 if the Java interface has been created,
3061and 0 otherwise.
3062@end deftypefn */)
3063{
3064#if defined (HAVE_JAVA)
3065
3066 octave_value retval = 0;
3067
3068 initialize_java ();
3069
3070 retval = 1;
3071
3072 return retval;
3073
3074#else
3075
3076 err_disabled_feature ("__java_init__", "Java");
3077
3078#endif
3079}
3080
3081DEFUN (__java_exit__, , ,
3082 doc: /* -*- texinfo -*-
3083@deftypefn {} {} __java_exit__ ()
3084Internal function used @strong{only} when debugging Java interface.
3085
3086Function will directly call terminate_jvm to destroy the current JVM instance.
3087@end deftypefn */)
3088{
3089#if defined (HAVE_JAVA)
3090
3091 terminate_jvm ();
3092
3093 return ovl ();
3094
3095#else
3096
3097 err_disabled_feature ("__java_exit__", "Java");
3098
3099#endif
3100}
3101
3102DEFUN (javaObject, args, ,
3103 doc: /* -*- texinfo -*-
3104@deftypefn {} {@var{jobj} =} javaObject (@var{classname})
3105@deftypefnx {} {@var{jobj} =} javaObject (@var{classname}, @var{arg1}, @dots{})
3106Create a Java object of class @var{classsname}, by calling the class
3107constructor with the arguments @var{arg1}, @enddots{}
3108
3109The first example below creates an uninitialized object, while the second
3110example supplies an initial argument to the constructor.
3111
3112@example
3113@group
3114x = javaObject ("java.lang.StringBuffer")
3115x = javaObject ("java.lang.StringBuffer", "Initial string")
3116@end group
3117@end example
3118
3119@seealso{javaMethod, javaArray}
3120@end deftypefn */)
3121{
3122#if defined (HAVE_JAVA)
3123
3124 if (args.length () == 0)
3125 print_usage ();
3126
3127 std::string classname = args(0).xstring_value ("javaObject: CLASSNAME must be a string");
3128
3129 initialize_java ();
3130
3131 JNIEnv *current_env = thread_jni_env ();
3132
3134 for (int i=1; i<args.length (); i++)
3135 tmp(i-1) = args(i);
3136
3137 return ovl (octave_java::do_javaObject (current_env, classname, tmp));
3138
3139#else
3140
3141 octave_unused_parameter (args);
3142
3143 err_disabled_feature ("javaObject", "Java");
3144
3145#endif
3146}
3147
3148/*
3149## The tests below merely check if javaObject works at all. Whether
3150## it works properly, i.e., creates the right values, is a matter of
3151## Java itself. Create a Short and check if it really is a short, i.e.,
3152## whether it overflows.
3153%!testif HAVE_JAVA; usejava ("jvm")
3154%! assert (javaObject ("java.lang.Short", 40000).doubleValue < 0);
3155*/
3156
3157DEFUN (javaMethod, args, ,
3158 doc: /* -*- texinfo -*-
3159@deftypefn {} {@var{ret} =} javaMethod (@var{methodname}, @var{obj})
3160@deftypefnx {} {@var{ret} =} javaMethod (@var{methodname}, @var{obj}, @var{arg1}, @dots{})
3161Invoke the method @var{methodname} on the Java object @var{obj} with the
3162arguments @var{arg1}, @enddots{}
3163
3164For static methods, @var{obj} can be a string representing the fully
3165qualified name of the corresponding class.
3166
3167When @var{obj} is a regular Java object, structure-like indexing can be
3168used as a shortcut syntax. For instance, the two following statements are
3169equivalent
3170
3171@example
3172@group
3173 ret = javaMethod ("method1", x, 1.0, "a string")
3174 ret = x.method1 (1.0, "a string")
3175@end group
3176@end example
3177
3178@code{javaMethod} returns the result of the method invocation.
3179
3180@seealso{methods, javaObject}
3181@end deftypefn */)
3182{
3183#if defined (HAVE_JAVA)
3184
3185 if (args.length () < 2)
3186 print_usage ();
3187
3188 std::string methodname = args(0).xstring_value ("javaMethod: METHODNAME must be a string");
3189
3190 initialize_java ();
3191
3192 JNIEnv *current_env = thread_jni_env ();
3193
3194 octave_value retval;
3195
3197 for (int i=2; i<args.length (); i++)
3198 tmp(i-2) = args(i);
3199
3200 if (args(1).isjava ())
3201 {
3202 octave_java *jobj = TO_JAVA (args(1));
3203 retval = jobj->do_javaMethod (current_env, methodname, tmp);
3204 }
3205 else if (args(1).is_string ())
3206 {
3207 std::string cls = args(1).string_value ();
3208 retval = octave_java::do_javaMethod (current_env, cls, methodname, tmp);
3209 }
3210 else
3211 error ("javaMethod: OBJ must be a Java object or a string");
3212
3213 return retval;
3214
3215#else
3216
3217 octave_unused_parameter (args);
3218
3219 err_disabled_feature ("javaMethod", "Java");
3220
3221#endif
3222}
3223
3224/*
3225%!testif HAVE_JAVA; usejava ("jvm")
3226%! jver = javaMethod ("getProperty", "java.lang.System", "java.version");
3227%! jver = strsplit (jver, ".");
3228%! if (numel (jver) > 1)
3229%! assert (isfinite (str2double (jver{1})));
3230%! assert (isfinite (str2double (jver{2})));
3231%! else
3232%! assert (isfinite (str2double (jver{1})));
3233%! endif
3234*/
3235
3236DEFUN (__java_get__, args, ,
3237 doc: /* -*- texinfo -*-
3238@deftypefn {} {@var{val} =} __java_get__ (@var{obj}, @var{name})
3239Get the value of the field @var{name} of the Java object @var{obj}.
3240
3241For static fields, @var{obj} can be a string representing the fully
3242qualified name of the corresponding class.
3243
3244When @var{obj} is a regular Java object, structure-like indexing can be used
3245as a shortcut syntax. For instance, the two following statements are
3246equivalent
3247
3248@example
3249@group
3250 __java_get__ (x, "field1")
3251 x.field1
3252@end group
3253@end example
3254
3255@seealso{__java_set__, javaMethod, javaObject}
3256@end deftypefn */)
3257{
3258#if defined (HAVE_JAVA)
3259
3260 if (args.length () != 2)
3261 print_usage ();
3262
3263 std::string name = args(1).xstring_value ("__java_get__: NAME must be a string");
3264
3265 initialize_java ();
3266
3267 JNIEnv *current_env = thread_jni_env ();
3268
3269 octave_value retval;
3270
3271 if (args(0).isjava ())
3272 {
3273 octave_java *jobj = TO_JAVA (args(0));
3274 retval = jobj->do_java_get (current_env, name);
3275 }
3276 else if (args(0).is_string ())
3277 {
3278 std::string cls = args(0).string_value ();
3279 retval = octave_java::do_java_get (current_env, cls, name);
3280 }
3281 else
3282 error ("__java_get__: OBJ must be a Java object or a string");
3283
3284 return retval;
3285
3286#else
3287
3288 octave_unused_parameter (args);
3289
3290 err_disabled_feature ("__java_get__", "Java");
3291
3292#endif
3293}
3294
3295DEFUN (__java_set__, args, ,
3296 doc: /* -*- texinfo -*-
3297@deftypefn {} {@var{obj} =} __java_set__ (@var{obj}, @var{name}, @var{val})
3298Set the value of the field @var{name} of the Java object @var{obj} to
3299@var{val}.
3300
3301For static fields, @var{obj} can be a string representing the fully
3302qualified named of the corresponding Java class.
3303
3304When @var{obj} is a regular Java object, structure-like indexing can be
3305used as a shortcut syntax. For instance, the two following statements are
3306equivalent
3307
3308@example
3309@group
3310 __java_set__ (x, "field1", val)
3311 x.field1 = val
3312@end group
3313@end example
3314
3315@seealso{__java_get__, javaMethod, javaObject}
3316@end deftypefn */)
3317{
3318#if defined (HAVE_JAVA)
3319
3320 if (args.length () != 3)
3321 print_usage ();
3322
3323 std::string name = args(1).xstring_value ("__java_set__: NAME must be a string");
3324
3325 initialize_java ();
3326
3327 JNIEnv *current_env = thread_jni_env ();
3328
3329 octave_value retval;
3330
3331 if (args(0).isjava ())
3332 {
3333 octave_java *jobj = TO_JAVA (args(0));
3334 retval = jobj->do_java_set (current_env, name, args(2));
3335 }
3336 else if (args(0).is_string ())
3337 {
3338 std::string cls = args(0).string_value ();
3339 retval = octave_java::do_java_set (current_env, cls, name, args(2));
3340 }
3341 else
3342 error ("__java_set__: OBJ must be a Java object or a string");
3343
3344 return retval;
3345
3346#else
3347
3348 octave_unused_parameter (args);
3349
3350 err_disabled_feature ("__java_set__", "Java");
3351
3352#endif
3353}
3354
3355DEFUN (__java2mat__, args, ,
3356 doc: /* -*- texinfo -*-
3357@deftypefn {} {@var{mat} =} __java2mat__ (@var{javaobj})
3358Undocumented internal function.
3359@end deftypefn */)
3360{
3361#if defined (HAVE_JAVA)
3362
3363 if (args.length () != 1)
3364 print_usage ();
3365
3366 initialize_java ();
3367
3368 JNIEnv *current_env = thread_jni_env ();
3369
3370 octave_value_list retval;
3371
3372 if (args(0).isjava ())
3373 {
3374 octave_java *jobj = TO_JAVA (args(0));
3375 retval = ovl (box_more (current_env, jobj->to_java (), nullptr));
3376 }
3377 else
3378 retval = ovl (args(0));
3379
3380 return retval;
3381
3382#else
3383
3384 octave_unused_parameter (args);
3385
3386 err_disabled_feature ("__java2mat__", "Java");
3387
3388#endif
3389}
3390
3391DEFUN (java_matrix_autoconversion, args, nargout,
3392 doc: /* -*- texinfo -*-
3393@deftypefn {} {@var{val} =} java_matrix_autoconversion ()
3394@deftypefnx {} {@var{old_val} =} java_matrix_autoconversion (@var{new_val})
3395@deftypefnx {} {@var{old_val} =} java_matrix_autoconversion (@var{new_val}, "local")
3396Query or set the internal variable that controls whether Java arrays are
3397automatically converted to Octave matrices.
3398
3399The default value is false.
3400
3401When called from inside a function with the @qcode{"local"} option, the
3402variable is changed locally for the function and any subroutines it calls.
3403The original variable value is restored when exiting the function.
3404@seealso{java_unsigned_autoconversion, debug_java}
3405@end deftypefn */)
3406{
3407#if defined (HAVE_JAVA)
3408
3410 "java_matrix_autoconversion");
3411
3412#else
3413
3414 octave_unused_parameter (args);
3415 octave_unused_parameter (nargout);
3416
3417 err_disabled_feature ("java_matrix_autoconversion", "Java");
3418
3419#endif
3420}
3421
3422DEFUN (java_unsigned_autoconversion, args, nargout,
3423 doc: /* -*- texinfo -*-
3424@deftypefn {} {@var{val} =} java_unsigned_autoconversion ()
3425@deftypefnx {} {@var{old_val} =} java_unsigned_autoconversion (@var{new_val})
3426@deftypefnx {} {@var{old_val} =} java_unsigned_autoconversion (@var{new_val}, "local")
3427Query or set the internal variable that controls how integer classes are
3428converted when @code{java_matrix_autoconversion} is enabled.
3429
3430When enabled, Java arrays of class Byte or Integer are converted to matrices
3431of class uint8 or uint32 respectively. The default value is true.
3432
3433When called from inside a function with the @qcode{"local"} option, the
3434variable is changed locally for the function and any subroutines it calls.
3435The original variable value is restored when exiting the function.
3436@seealso{java_matrix_autoconversion, debug_java}
3437@end deftypefn */)
3438{
3439#if defined (HAVE_JAVA)
3440
3442 "java_unsigned_autoconversion");
3443
3444#else
3445
3446 octave_unused_parameter (args);
3447 octave_unused_parameter (nargout);
3448
3449 err_disabled_feature ("java_unsigned_autoconversion", "Java");
3450
3451#endif
3452}
3453
3454DEFUN (debug_java, args, nargout,
3455 doc: /* -*- texinfo -*-
3456@deftypefn {} {@var{val} =} debug_java ()
3457@deftypefnx {} {@var{old_val} =} debug_java (@var{new_val})
3458@deftypefnx {} {@var{old_val} =} debug_java (@var{new_val}, "local")
3459Query or set the internal variable that determines whether extra debugging
3460information regarding the initialization of the JVM and any Java exceptions
3461is printed.
3462
3463When called from inside a function with the @qcode{"local"} option, the
3464variable is changed locally for the function and any subroutines it calls.
3465The original variable value is restored when exiting the function.
3466@seealso{java_matrix_autoconversion, java_unsigned_autoconversion}
3467@end deftypefn */)
3468{
3469#if defined (HAVE_JAVA)
3470
3471 return set_internal_variable (Vdebug_java, args, nargout, "debug_java");
3472
3473#else
3474
3475 octave_unused_parameter (args);
3476 octave_unused_parameter (nargout);
3477
3478 err_disabled_feature ("debug_java", "Java");
3479
3480#endif
3481}
3482
3483// Outside of #if defined (HAVE_JAVA) because it is desirable to be able
3484// to test for the presence of a Java object without having Java
3485// installed.
3486
3487DEFUN (isjava, args, ,
3488 doc: /* -*- texinfo -*-
3489@deftypefn {} {@var{tf} =} isjava (@var{x})
3490Return true if @var{x} is a Java object.
3491@seealso{class, typeinfo, isa, javaObject}
3492@end deftypefn */)
3493{
3494 if (args.length () != 1)
3495 print_usage ();
3496
3497 return ovl (args(0).isjava ());
3498}
3499
3500/*
3501## Check automatic conversion of java primitive arrays into octave types.
3502%!testif HAVE_JAVA; usejava ("jvm")
3503%! assert (javaObject ("java.lang.String", "hello").getBytes (),
3504%! int8 ([104 101 108 108 111]'));
3505
3506## Check automatic conversion of octave types into java primitive arrays.
3507## Note that uint8 is casted to int8.
3508%!testif HAVE_JAVA; usejava ("jvm")
3509%! assert (javaMethod ("binarySearch", "java.util.Arrays", [90 100 255], 255), 2);
3510%! assert (javaMethod ("binarySearch", "java.util.Arrays", uint8 ([90 100 255]), uint8 (255)) < 0);
3511%! assert (javaMethod ("binarySearch", "java.util.Arrays", uint8 ([90 100 128]), uint8 (128)) < 0);
3512%! assert (javaMethod ("binarySearch", "java.util.Arrays", uint8 ([90 100 127]), uint8 (127)), 2);
3513%! assert (javaMethod ("binarySearch", "java.util.Arrays", uint16 ([90 100 128]), uint16 (128)), 2);
3514
3515## Check we can create objects that wrap java literals
3516%!testif HAVE_JAVA; usejava ("jvm") <*38821>
3517%! assert (class (javaObject ("java.lang.Byte", uint8 (1))),
3518%! "java.lang.Byte");
3519%! assert (class (javaObject ("java.lang.Byte", int8 (1))),
3520%! "java.lang.Byte");
3521%! assert (class (javaObject ("java.lang.Short", uint16 (1))),
3522%! "java.lang.Short");
3523%! assert (class (javaObject ("java.lang.Short", int16 (1))),
3524%! "java.lang.Short");
3525%! assert (class (javaObject ("java.lang.Integer", uint32 (1))),
3526%! "java.lang.Integer");
3527%! assert (class (javaObject ("java.lang.Integer", int32 (1))),
3528%! "java.lang.Integer");
3529%! assert (class (javaObject ("java.lang.Long", uint64 (1))),
3530%! "java.lang.Long");
3531%! assert (class (javaObject ("java.lang.Long", int64 (1))),
3532%! "java.lang.Long");
3533
3534## More checks of java numeric and boolean class instances
3535%!testif HAVE_JAVA; usejava ("jvm")
3536%! n = javaObject ("java.lang.Double", 1.35);
3537%! assert (n.compareTo (1.0), 1);
3538%! assert (n.compareTo (1.35), 0);
3539%! assert (n.compareTo (10), -1);
3540%! assert (n.isInfinite (), false);
3541
3542%!testif HAVE_JAVA; usejava ("jvm") <51804>
3543%! n = javaObject ("java.lang.Float", 1.35);
3544%! assert (n.compareTo (1.0), 1);
3545%! assert (n.compareTo (1.35), 0);
3546%! assert (n.compareTo (10), -1);
3547%! assert (n.doubleValue (), 1.35, 1e7);
3548
3549%!testif HAVE_JAVA; usejava ("jvm")
3550%! n = javaObject ("java.lang.Long", (int64 (1)));
3551%! assert (n.compareTo (int64 (0)), 1);
3552%! assert (n.compareTo (int64 (1)), 0);
3553%! assert (n.compareTo (int64 (2)), -1);
3554%! assert (n.toString (), "1");
3555
3556%!testif HAVE_JAVA; usejava ("jvm") <51804>
3557%! n = javaObject ("java.lang.Integer", 1.35);
3558%! assert (n.compareTo (0), 1);
3559%! assert (n.compareTo (1), 0);
3560%! assert (n.compareTo (2), -1);
3561
3562%!testif HAVE_JAVA; usejava ("jvm") <51804>
3563%! n = javaObject ("java.lang.Short", 1.35);
3564%! assert (n.compareTo (0), 1);
3565%! assert (n.compareTo (1), 0);
3566%! assert (n.compareTo (2), -1);
3567
3568%!testif HAVE_JAVA; usejava ("jvm")
3569%! n = javaObject ("java.lang.Byte", int8 (17));
3570%! assert (n.compareTo (int8 (20)), -3);
3571%! assert (n.compareTo (int8 (10)), 7);
3572%! assert (n.compareTo (int8 (17)), 0);
3573
3574%!testif HAVE_JAVA; usejava ("jvm")
3575%! b = javaObject ("java.lang.Boolean", true);
3576%! assert (b.compareTo (true), 0);
3577%! assert (b.compareTo (false), 1);
3578%! b = javaObject ("java.lang.Boolean", false);
3579%! assert (b.compareTo (true), -1);
3580%! assert (b.compareTo (false), 0);
3581
3582## Test for automatic conversion of specific numeric classes
3583%!testif HAVE_JAVA; usejava ("jvm") <*48013>
3584%! assert (javaMethod ("valueOf", "java.lang.Byte", int8 (1)), 1);
3585%! assert (javaMethod ("valueOf", "java.lang.Short", int16 (1)), 1);
3586%! assert (javaMethod ("valueOf", "java.lang.Integer", int32 (1)), 1);
3587%! assert (javaMethod ("valueOf", "java.lang.Long", int64 (1)), 1);
3588%! assert (javaMethod ("valueOf", "java.lang.Float", single (1)), 1);
3589%! assert (javaMethod ("valueOf", "java.lang.Double", double (1)), 1);
3590%! assert (class (javaMethod ("valueOf", "java.math.BigDecimal", double (1))),
3591%! "java.math.BigDecimal")
3592%! assert (class (javaMethod ("valueOf", "java.math.BigInteger", int64 (1))),
3593%! "java.math.BigInteger")
3594
3595## Automatic conversion from string cell array into String[]
3596%!testif HAVE_JAVA; usejava ("jvm") <*45290>
3597%! assert (javaMethod ("binarySearch", "java.util.Arrays", {"aaa", "bbb", "ccc", "zzz"}, "aaa"), 0);
3598%! assert (javaMethod ("binarySearch", "java.util.Arrays", {"aaa", "bbb", "ccc", "zzz"}, "zzz"), 3);
3599%! assert (javaMethod ("binarySearch", "java.util.Arrays", {"aaa", "bbb", "ccc", "zzz"}, "hhh") < 0);
3600
3601## Test that Octave index syntax allows Java object method calls with args
3602%!testif HAVE_JAVA; usejava ("jvm") <*51152>
3603%! s = javaObject ("java.lang.String", "Octave");
3604%! assert (s.length (), 6);
3605%! assert (s.charAt (0), "O");
3606%! assert (s.charAt (5), "e");
3607%! assert (s.matches ("^Octave$"));
3608%! assert (s.startsWith ("Oct"));
3609%! ## same tests with Java object as part of another indexing expression
3610%! a(1).s = s;
3611%! assert (! a(1).s.isEmpty ());
3612%! assert (a(1).s.length (), 6);
3613%! assert (a(1).s.charAt (0), "O");
3614%! assert (a(1).s.charAt (5), "e");
3615%! assert (a(1).s.matches ("^Octave$"));
3616%! assert (a(1).s.startsWith ("Oct"));
3617
3618## Check for basic usability of the java awt library
3619## FIXME: Removed runtime test to skip checking on Mac OS (22/08/2025).
3620## IF problems are reported the "! ismac ()" runtime test can be re-instated.
3621%!testif HAVE_JAVA; usejava ("jvm") && usejava ("awt") && have_window_system ()
3622%! frame = javaObject ("java.awt.Frame");
3623%! frame.setResizable (true);
3624%! assert (frame.isResizable ());
3625*/
3626
3627OCTAVE_END_NAMESPACE(octave)
#define C(a, b)
Definition Faddeeva.cc:256
N Dimensional Array with copy-on-write semantics.
Definition Array-base.h:130
bool isempty() const
Size of the specified dimension.
Definition Array-base.h:674
T * rwdata()
Size of the specified dimension.
octave_idx_type numel() const
Number of elements in the array.
Definition Array-base.h:440
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:92
void resize(int n, int fill_value=0)
Definition dim-vector.h:278
octave_idx_type ndims() const
Number of dimensions.
Definition dim-vector.h:263
bool isvector() const
Definition dim-vector.h:432
Definition oct-env.h:37
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:2450
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:2617
octave_value do_java_set(void *jni_env, const std::string &name, const octave_value &val)
Definition ov-java.cc:2828
octave_java()
Ctor.
Definition ov-java.cc:2132
void print(std::ostream &os, bool pr_as_read_syntax=false)
Definition ov-java.cc:2403
string_vector map_keys() const
Definition ov-java.cc:2355
bool save_ascii(std::ostream &os)
Definition ov-java.cc:2419
bool load_hdf5(octave_hdf5_id loc_id, const char *name)
Definition ov-java.cc:2459
bool save_binary(std::ostream &os, bool save_as_floats)
Definition ov-java.cc:2434
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:2466
octave_value convert_to_str_internal(bool pad, bool force, char type) const
Definition ov-java.cc:2377
octave_value do_java_get(void *jni_env, const std::string &name)
Definition ov-java.cc:2694
void print_raw(std::ostream &os, bool pr_as_read_syntax=false) const
Definition ov-java.cc:2410
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:2442
bool load_ascii(std::istream &is)
Definition ov-java.cc:2427
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:766
bool isfloat() const
Definition ov.h:699
bool is_undefined() const
Definition ov.h:593
bool is_uint32_type() const
Definition ov.h:722
std::string class_name() const
Definition ov.h:1360
octave_function * function_value(bool silent=false) const
bool is_real_scalar() const
Definition ov.h:608
int32NDArray int32_array_value() const
Definition ov.h:963
Cell cell_value() const
int8NDArray int8_array_value() const
Definition ov.h:957
octave_idx_type rows() const
Definition ov.h:543
bool isreal() const
Definition ov.h:736
Array< std::string > cellstr_value() const
Definition ov.h:989
bool is_string() const
Definition ov.h:635
bool is_int8_type() const
Definition ov.h:704
bool isjava() const
Definition ov.h:665
bool is_range() const
Definition ov.h:644
bool isempty() const
Definition ov.h:599
bool is_uint8_type() const
Definition ov.h:716
bool is_uint64_type() const
Definition ov.h:725
bool is_uint16_type() const
Definition ov.h:719
bool is_int16_type() const
Definition ov.h:707
bool iscell() const
Definition ov.h:602
octave_idx_type numel() const
Definition ov.h:557
bool is_real_matrix() const
Definition ov.h:611
std::string string_value(bool force=false) const
Definition ov.h:981
bool is_int64_type() const
Definition ov.h:713
bool is_matrix_type() const
Definition ov.h:745
NDArray array_value(bool frc_str_conv=false) const
Definition ov.h:863
octave_idx_type length() const
bool is_double_type() const
Definition ov.h:693
bool is_int32_type() const
Definition ov.h:710
bool is_bool_scalar() const
Definition ov.h:620
octave_value convert_to_str(bool pad=false, bool force=false, char type='\'') const
Definition ov.h:1320
uint8NDArray uint8_array_value() const
Definition ov.h:969
std::string xstring_value(const char *fmt,...) const
bool iscellstr() const
Definition ov.h:605
octave_idx_type columns() const
Definition ov.h:545
Matrix matrix_value(bool frc_str_conv=false) const
Definition ov.h:857
bool islogical() const
Definition ov.h:733
dim_vector dims() const
Definition ov.h:539
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
std::string release()
Definition defaults.cc:150
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:1083
void error(const char *fmt,...)
Definition error.cc:1008
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