1 ///////////////////////////////////////////////////////
\r
3 // VTT Technical Research Centre of Finland LTD //
\r
4 // For internal use only. Do not redistribute. //
\r
7 // Antton Tapani ext-antton.tapani@vtt.fi //
\r
9 // Last modified by Antton Tapani 9.2016 //
\r
11 ///////////////////////////////////////////////////////
\r
13 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
\r
16 #include <Python.h> //header for system python import; add include paths
\r
17 #include <numpy/arrayobject.h>
\r
20 #include <Python.h> //header for system python import; add include paths
\r
21 #include <numpy/arrayobject.h>
\r
26 #include <jni.h> //java connection header
\r
28 #include <windows.h>
\r
30 #define JAVA_MAXINT (0x7fffffff)
\r
32 #define RUNTIME_EXCEPTION "java/lang/RuntimeException"
\r
33 #define ILLEGAL_ARGUMENT_EXCEPTION "java/lang/IllegalArgumentException"
\r
34 #define STRING_CLASS "java/lang/String"
\r
36 #define PACKAGE_PREFIX "org/simantics/pythonlink/"
\r
38 #define NDARRAY_CLASS (PACKAGE_PREFIX "NDArray")
\r
40 jint throwException( JNIEnv *env, char *className, char *message )
\r
42 jclass exClass = (*env)->FindClass( env, className);
\r
43 if (exClass == NULL) {
\r
47 return (*env)->ThrowNew( env, exClass, message );
\r
50 jint throwIllegalArgumentException( JNIEnv *env, char *message ) {
\r
51 return throwException( env, ILLEGAL_ARGUMENT_EXCEPTION, message );
\r
54 int moduleCount = 0;
\r
56 JNIEXPORT jlong JNICALL Java_org_simantics_pythonlink_PythonContext_createContextImpl(JNIEnv *env, jobject thisObj) {
\r
58 sprintf(name, "SCL_%d", ++moduleCount);
\r
61 PyObject *module = PyModule_New(name);
\r
62 PyObject *main = PyImport_AddModule("__main__");
\r
64 PyDict_Merge(PyModule_GetDict(module), PyModule_GetDict(main), 0);
\r
65 return (jlong)module;
\r
69 JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_deleteContextImpl(JNIEnv *env, jobject thisObj, jlong contextID) {
\r
70 PyObject *module = (PyObject*)contextID;
\r
74 PyObject *getPythonString(JNIEnv *env, jstring string) {
\r
75 jsize len = (*env)->GetStringLength(env, string);
\r
76 const jchar *chars = (*env)->GetStringChars(env, string, NULL);
\r
78 PyObject *value = PyUnicode_DecodeUTF16((char*)chars, 2*len, NULL, NULL);
\r
80 (*env)->ReleaseStringChars(env, string, chars);
\r
84 PyObject *getPythonStringList(JNIEnv *env, jobjectArray value) {
\r
85 jsize nitems = (*env)->GetArrayLength(env, value);
\r
86 jint *values = (*env)->GetIntArrayElements(env, value, NULL);
\r
87 jclass stringClass = (*env)->FindClass(env, STRING_CLASS);
\r
90 PyObject *result = PyList_New(nitems);
\r
91 for (i = 0; i < nitems; i++) {
\r
92 jobject item = (*env)->GetObjectArrayElement(env, value, i);
\r
93 if (item != NULL && (*env)->IsInstanceOf(env, item, stringClass)) {
\r
94 PyList_SetItem(result, i, getPythonString(env, (jstring)item));
\r
97 PyList_SetItem(result, i, Py_None);
\r
101 (*env)->ReleaseIntArrayElements(env, value, values, JNI_ABORT);
\r
105 PyObject *getPythonIntegerList(JNIEnv *env, jintArray value) {
\r
106 jsize nitems = (*env)->GetArrayLength(env, value);
\r
107 jint *values = (*env)->GetIntArrayElements(env, value, NULL);
\r
110 PyObject *result = PyList_New(nitems);
\r
111 for (i = 0; i < nitems; i++) {
\r
112 PyList_SetItem(result, i, PyLong_FromLong(values[i]));
\r
115 (*env)->ReleaseIntArrayElements(env, value, values, JNI_ABORT);
\r
119 PyObject *getPythonDoubleList(JNIEnv *env, jdoubleArray value) {
\r
120 jsize nitems = (*env)->GetArrayLength(env, value);
\r
121 double *values = (*env)->GetDoubleArrayElements(env, value, NULL);
\r
124 PyObject *result = PyList_New(nitems);
\r
125 for (i = 0; i < nitems; i++) {
\r
126 PyList_SetItem(result, i, PyFloat_FromDouble(values[i]));
\r
129 (*env)->ReleaseDoubleArrayElements(env, value, values, JNI_ABORT);
\r
133 PyObject *getPythonNDArray(JNIEnv *env, jobject value) {
\r
134 jclass ndarrayClass = (*env)->FindClass(env, NDARRAY_CLASS);
\r
135 jmethodID dimsMethod = (*env)->GetMethodID(env, ndarrayClass, "dims", "()[I");
\r
136 jmethodID getValuesMethod = (*env)->GetMethodID(env, ndarrayClass, "getValues", "()[D");
\r
138 jintArray jdims = (*env)->CallObjectMethod(env, value, dimsMethod);
\r
139 jsize ndims = (*env)->GetArrayLength(env, jdims);
\r
140 jint *dims = (*env)->GetIntArrayElements(env, jdims, NULL);
\r
142 jdoubleArray jvalues = (*env)->CallObjectMethod(env, value, getValuesMethod);
\r
143 jsize len = (*env)->GetArrayLength(env, jvalues);
\r
144 jdouble *values = (*env)->GetDoubleArrayElements(env, jvalues, NULL);
\r
146 npy_intp *pyDims = (npy_intp*)malloc(ndims * sizeof(npy_intp));
\r
148 jint i, nelem = ndims > 0 ? 1 : 0;
\r
149 for (i = 0; i < ndims; i++) {
\r
151 pyDims[i] = dims[i];
\r
154 len = min(len, nelem);
\r
157 PyObject *array = PyArray_EMPTY(ndims, pyDims, NPY_DOUBLE, 0);
\r
158 double *data = (double *)PyArray_DATA((PyArrayObject*)array);
\r
160 memcpy(data, values, len * sizeof(double));
\r
164 (*env)->ReleaseDoubleArrayElements(env, jvalues, values, JNI_ABORT);
\r
165 (*env)->ReleaseIntArrayElements(env, jdims, dims, JNI_ABORT);
\r
171 void setPythonVariable(PyObject *module, PyObject *name, PyObject *value) {
\r
172 if (name && value) {
\r
173 PyDict_SetItem(PyModule_GetDict(module), name, value);
\r
180 jstring pythonStringAsJavaString(JNIEnv *env, PyObject *string) {
\r
181 PyObject *utf16Value = PyUnicode_AsUTF16String(string);
\r
182 Py_ssize_t len = PyBytes_Size(utf16Value) / 2;
\r
183 char *bytes = PyBytes_AsString(utf16Value);
\r
185 // Create Java string, skipping the byte order mark in the beginning
\r
186 jstring result = (*env)->NewString(env, (jchar *)bytes + 1, (jsize)min(len, JAVA_MAXINT) - 1);
\r
188 Py_XDECREF(utf16Value);
\r
193 jobjectArray pythonStringListAsJavaArray(JNIEnv *env, PyObject *list) {
\r
194 Py_ssize_t len = PyList_Size(list);
\r
195 jsize jlen = (jsize)min(len, JAVA_MAXINT);
\r
196 jobjectArray array = (*env)->NewObjectArray(env, jlen, (*env)->FindClass(env, STRING_CLASS), NULL);
\r
200 for (i = 0; i < jlen; i++) {
\r
201 PyObject *item = PyList_GetItem(list, i);
\r
202 if (PyUnicode_Check(item)) {
\r
203 jstring value = pythonStringAsJavaString(env, item);
\r
204 (*env)->SetObjectArrayElement(env, array, i, value);
\r
207 throwException(env, RUNTIME_EXCEPTION, "List item not a string");
\r
215 jdoubleArray pythonListAsDoubleArray(JNIEnv *env, PyObject *list) {
\r
216 Py_ssize_t len = PyList_Size(list);
\r
217 jsize jlen = (jsize)min(len, JAVA_MAXINT);
\r
218 jdoubleArray array = (*env)->NewDoubleArray(env, jlen);
\r
222 for (i = 0; i < jlen; i++) {
\r
223 PyObject *item = PyList_GetItem(list, i);
\r
224 if (PyFloat_Check(item)) {
\r
225 double value = PyFloat_AsDouble(item);
\r
226 (*env)->SetDoubleArrayRegion(env, array, i, 1, &value);
\r
229 throwException(env, RUNTIME_EXCEPTION, "List item not a floating point value");
\r
237 npy_intp nContiguous(int d, int nd, npy_intp *strides, npy_intp *dims, npy_intp *ncont) {
\r
243 npy_intp n = nContiguous(d+1, nd, strides, dims, ncont);
\r
244 ncont[d] = n > 0 && strides[d] == sizeof(double) * n ? dims[d] * n : 0;
\r
249 void copyDoubleArrayValues(JNIEnv *env, jdoubleArray array, double *data, npy_intp *offset, int d, int nd, npy_intp *strides, npy_intp *dims, npy_intp *ncont) {
\r
250 if (ncont[d] > 0) {
\r
251 (*env)->SetDoubleArrayRegion(env, array, (jint)*offset, (jint)ncont[d], data);
\r
252 *offset += ncont[d];
\r
256 for (i = 0; i < dims[d]; i++) {
\r
257 copyDoubleArrayValues(env, array, (double*)((char*)data + strides[d] * i), offset, d+1, nd, strides, dims, ncont);
\r
262 jobject pythonArrayAsNDArray(JNIEnv *env, PyArrayObject *array) {
\r
263 jclass ndarrayClass = (*env)->FindClass(env, NDARRAY_CLASS);
\r
264 jmethodID constructor = (*env)->GetMethodID(env, ndarrayClass, "<init>", "([I[D)V");
\r
266 int ndims = PyArray_NDIM(array);
\r
267 npy_intp *dims = PyArray_DIMS(array);
\r
269 npy_intp len = PyArray_Size((PyObject*)array);
\r
270 double *values = (double*)PyArray_DATA(array);
\r
272 jboolean isFortran = PyArray_ISFORTRAN(array) != 0;
\r
276 if (len > JAVA_MAXINT) {
\r
277 throwException(env, RUNTIME_EXCEPTION, "Array too large");
\r
282 jintArray jdims = (*env)->NewIntArray(env, ndims);
\r
283 jdoubleArray jvalues = (*env)->NewDoubleArray(env, (jsize)len);
\r
285 for (i = 0; i < ndims; i++) {
\r
286 jint dim = (jint)dims[i];
\r
287 (*env)->SetIntArrayRegion(env, jdims, i, 1, &dim);
\r
290 if (PyArray_IS_C_CONTIGUOUS(array)) {
\r
291 (*env)->SetDoubleArrayRegion(env, jvalues, 0, (jsize)len, values);
\r
294 npy_intp offset = 0;
\r
295 npy_intp *strides = PyArray_STRIDES(array);
\r
296 npy_intp *ncont = (npy_intp*)malloc((ndims + 1) * sizeof(npy_intp));
\r
297 nContiguous(0, ndims, strides, dims, ncont);
\r
298 copyDoubleArrayValues(env, jvalues, values, &offset, 0, ndims, strides, dims, ncont);
\r
302 return (*env)->NewObject(env, ndarrayClass, constructor, jdims, jvalues, isFortran);
\r
306 jintArray pythonListAsIntegerArray(JNIEnv *env, PyObject *list) {
\r
307 Py_ssize_t len = PyList_Size(list);
\r
308 jsize jlen = (jsize)min(len, JAVA_MAXINT);
\r
309 jdoubleArray array = (*env)->NewIntArray(env, jlen);
\r
313 for (i = 0; i < jlen; i++) {
\r
314 PyObject *item = PyList_GetItem(list, i);
\r
315 if (PyLong_Check(item)) {
\r
316 jint value = PyLong_AsLong(item);
\r
317 (*env)->SetIntArrayRegion(env, array, i, 1, &value);
\r
320 throwException(env, RUNTIME_EXCEPTION, "List item not an integer");
\r
328 JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonIntegerVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jint value) {
\r
329 PyObject *module = (PyObject*)contextID;
\r
331 PyObject *pythonName = getPythonString(env, variableName);
\r
332 PyObject *val = PyLong_FromLong(value);
\r
334 setPythonVariable(module, pythonName, val);
\r
337 JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonIntegerArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jintArray value) {
\r
338 PyObject *module = (PyObject*)contextID;
\r
340 PyObject *pythonName = getPythonString(env, variableName);
\r
341 PyObject *val = getPythonIntegerList(env, value);
\r
343 setPythonVariable(module, pythonName, val);
\r
346 JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonDoubleVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jdouble value) {
\r
347 PyObject *module = (PyObject*)contextID;
\r
349 PyObject *pythonName = getPythonString(env, variableName);
\r
350 PyObject *val = PyFloat_FromDouble(value);
\r
352 setPythonVariable(module, pythonName, val);
\r
355 JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonDoubleArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jdoubleArray value) {
\r
356 PyObject *module = (PyObject*)contextID;
\r
358 PyObject *pythonName = getPythonString(env, variableName);
\r
359 PyObject *val = getPythonDoubleList(env, value);
\r
361 setPythonVariable(module, pythonName, val);
\r
364 JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonStringVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jstring value) {
\r
365 PyObject *module = (PyObject*)contextID;
\r
367 PyObject *pythonName = getPythonString(env, variableName);
\r
368 PyObject *val = getPythonString(env, value);
\r
370 setPythonVariable(module, pythonName, val);
\r
373 JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonStringArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jobjectArray value) {
\r
374 PyObject *module = (PyObject*)contextID;
\r
376 PyObject *pythonName = getPythonString(env, variableName);
\r
377 PyObject *val = getPythonStringList(env, value);
\r
379 setPythonVariable(module, pythonName, val);
\r
382 JNIEXPORT void JNICALL Java_org_simantics_pythonlink_PythonContext_setPythonNDArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName, jobject value) {
\r
383 PyObject *module = (PyObject*)contextID;
\r
385 if (_import_array() < 0) {
\r
386 throwException(env, RUNTIME_EXCEPTION, "Importing numpy failed");
\r
391 PyObject *pythonName = getPythonString(env, variableName);
\r
392 PyObject *val = getPythonNDArray(env, value);
\r
394 setPythonVariable(module, pythonName, val);
\r
398 JNIEXPORT jint JNICALL Java_org_simantics_pythonlink_PythonContext_executePythonStatementImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring statement) {
\r
399 PyObject *module = (PyObject*)contextID;
\r
401 const char *utfchars = (*env)->GetStringUTFChars(env, statement, NULL);
\r
407 globals = PyModule_GetDict(module);
\r
410 PyObject *result = PyRun_String(utfchars, Py_file_input, globals, globals);
\r
412 PyObject *exceptionType = PyErr_Occurred();
\r
413 if (exceptionType != NULL) {
\r
414 PyObject *exception, *stackTrace;
\r
416 PyErr_Fetch(&exceptionType, &exception, &stackTrace);
\r
417 message = PyUnicode_AsUTF8(exception);
\r
418 throwException(env, RUNTIME_EXCEPTION, message);
\r
421 // Py_XDECREF(globals);
\r
423 (*env)->ReleaseStringUTFChars(env, statement, utfchars);
\r
425 return result != NULL ? 0 : 1;
\r
430 JNIEXPORT jstring JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonStringVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) {
\r
431 PyObject *module = (PyObject*)contextID;
\r
433 PyObject *pythonName = getPythonString(env, variableName);
\r
435 PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName);
\r
436 if (value == NULL) {
\r
437 throwException(env, RUNTIME_EXCEPTION, "Python variable not found");
\r
441 if (!PyUnicode_Check(value)) {
\r
442 throwException(env, RUNTIME_EXCEPTION, "Python variable not a string");
\r
446 return pythonStringAsJavaString(env, value);
\r
449 JNIEXPORT jobjectArray JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonStringArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) {
\r
450 PyObject *module = (PyObject*)contextID;
\r
452 PyObject *pythonName = getPythonString(env, variableName);
\r
454 PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName);
\r
455 if (value == NULL) {
\r
456 throwException(env, RUNTIME_EXCEPTION, "Python variable not found");
\r
460 if (!PyList_Check(value)) {
\r
461 throwException(env, RUNTIME_EXCEPTION, "Python variable not a list");
\r
465 return pythonStringListAsJavaArray(env, value);
\r
468 JNIEXPORT jint JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonIntegerVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) {
\r
469 PyObject *module = (PyObject*)contextID;
\r
471 PyObject *pythonName = getPythonString(env, variableName);
\r
473 PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName);
\r
474 if (value == NULL) {
\r
475 throwException(env, RUNTIME_EXCEPTION, "Python variable not found");
\r
479 if (!PyLong_Check(value)) {
\r
480 throwException(env, RUNTIME_EXCEPTION, "Python variable not an integer");
\r
484 return PyLong_AsLong(value);
\r
487 JNIEXPORT jintArray JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonIntegerArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) {
\r
488 PyObject *module = (PyObject*)contextID;
\r
490 PyObject *pythonName = getPythonString(env, variableName);
\r
492 PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName);
\r
493 if (value == NULL) {
\r
494 throwException(env, RUNTIME_EXCEPTION, "Python variable not found");
\r
498 if (!PyList_Check(value)) {
\r
499 throwException(env, RUNTIME_EXCEPTION, "Python variable not a list");
\r
503 return pythonListAsIntegerArray(env, value);
\r
506 JNIEXPORT jdouble JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonDoubleVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) {
\r
507 PyObject *module = (PyObject*)contextID;
\r
509 PyObject *pythonName = getPythonString(env, variableName);
\r
511 PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName);
\r
512 if (value == NULL) {
\r
513 throwException(env, RUNTIME_EXCEPTION, "Python variable not found");
\r
517 if (!PyFloat_Check(value)) {
\r
518 throwException(env, RUNTIME_EXCEPTION, "Python variable not a float");
\r
522 return PyFloat_AsDouble(value);
\r
525 JNIEXPORT jdoubleArray JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonDoubleArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) {
\r
526 PyObject *module = (PyObject*)contextID;
\r
528 PyObject *pythonName = getPythonString(env, variableName);
\r
530 PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName);
\r
531 if (value == NULL) {
\r
532 throwException(env, RUNTIME_EXCEPTION, "Python variable not found");
\r
536 if (!PyList_Check(value)) {
\r
537 throwException(env, RUNTIME_EXCEPTION, "Python variable not a list");
\r
541 return pythonListAsDoubleArray(env, value);
\r
544 JNIEXPORT jobject JNICALL Java_org_simantics_pythonlink_PythonContext_getPythonNDArrayVariableImpl(JNIEnv *env, jobject thisObj, jlong contextID, jstring variableName) {
\r
545 PyObject *module = (PyObject*)contextID;
\r
547 if (_import_array() < 0) {
\r
548 throwException(env, RUNTIME_EXCEPTION, "Importing numpy failed");
\r
553 PyObject *pythonName = getPythonString(env, variableName);
\r
555 PyObject *value = PyDict_GetItem(PyModule_GetDict(module), pythonName);
\r
556 if (value == NULL) {
\r
557 throwException(env, RUNTIME_EXCEPTION, "Python variable not found");
\r
561 if (!PyArray_Check(value)) {
\r
562 throwException(env, RUNTIME_EXCEPTION, "Python variable not an ndarray");
\r
566 if (PyArray_TYPE((PyArrayObject*)value) != NPY_DOUBLE) {
\r
567 throwException(env, RUNTIME_EXCEPTION, "Only ndarrays of type double are supported");
\r
571 return pythonArrayAsNDArray(env, (PyArrayObject *)value);
\r
575 BOOL __stdcall DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
\r
576 //extern "C" DLL_EXPORT BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
\r
580 case DLL_PROCESS_ATTACH:
\r
581 // attach to process
\r
582 // return FALSE to fail DLL load
\r
586 case DLL_PROCESS_DETACH:
\r
587 // detach from process
\r
591 case DLL_THREAD_ATTACH:
\r
592 // attach to thread
\r
595 case DLL_THREAD_DETACH:
\r
596 // detach from thread
\r
599 return TRUE; // succesful
\r