1 /******************************************************************************
2  *
3  * Project:  OpenGIS Simple Features Reference Implementation
4  * Purpose:  Implements OGRMDBJavaEnv class.
5  * Author:   Even Rouault, <even dot rouault at spatialys.com>
6  *
7  ******************************************************************************
8  * Copyright (c) 2011, Even Rouault <even dot rouault at spatialys.com>
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  ****************************************************************************/
28 
29 #include "ogr_mdb.h"
30 #include "cpl_multiproc.h"
31 
32 CPL_CVSID("$Id: ogrmdbjackcess.cpp 355b41831cd2685c85d1aabe5b95665a2c6e99b7 2019-06-19 17:07:04 +0200 Even Rouault $")
33 
34 #if JVM_LIB_DLOPEN
35 #include <limits.h>
36 #include <stdio.h>
37 #endif
38 
39 static JavaVM *jvm_static = nullptr;
40 static CPLMutex* hMutex = nullptr;
41 
42 /************************************************************************/
43 /*                         OGRMDBJavaEnv()                              */
44 /************************************************************************/
45 
OGRMDBJavaEnv()46 OGRMDBJavaEnv::OGRMDBJavaEnv()
47 {
48 }
49 
50 /************************************************************************/
51 /*                        ~OGRMDBJavaEnv()                              */
52 /************************************************************************/
53 
~OGRMDBJavaEnv()54 OGRMDBJavaEnv::~OGRMDBJavaEnv()
55 {
56     if (jvm)
57     {
58         env->DeleteLocalRef(byteArray_class);
59 
60         env->DeleteLocalRef(file_class);
61         env->DeleteLocalRef(database_class);
62 
63         env->DeleteLocalRef(table_class);
64 
65         env->DeleteLocalRef(column_class);
66 
67         env->DeleteLocalRef(datatype_class);
68 
69         env->DeleteLocalRef(list_class);
70 
71         env->DeleteLocalRef(set_class);
72 
73         env->DeleteLocalRef(map_class);
74 
75         env->DeleteLocalRef(iterator_class);
76 
77         env->DeleteLocalRef(object_class);
78 
79         env->DeleteLocalRef(boolean_class);
80         env->DeleteLocalRef(byte_class);
81         env->DeleteLocalRef(short_class);
82         env->DeleteLocalRef(integer_class);
83         env->DeleteLocalRef(float_class);
84         env->DeleteLocalRef(double_class);
85 
86         /*if (!bCalledFromJava)
87         {
88             CPLDebug("MDB", "Destroying JVM");
89             int ret = jvm->DestroyJavaVM();
90             CPLDebug("MDB", "ret=%d", ret);
91         }*/
92     }
93 }
94 
95 #define CHECK(x, y) do {x = y; if (!x) { \
96       CPLError(CE_Failure, CPLE_AppDefined, #y " failed"); \
97       return FALSE;} } while( false )
98 
99 /************************************************************************/
100 /*                         CleanupMutex()                               */
101 /************************************************************************/
102 
CleanupMutex()103 void OGRMDBJavaEnv::CleanupMutex()
104 {
105     if( hMutex )
106         CPLDestroyMutex(hMutex);
107     hMutex = nullptr;
108 }
109 
110 /************************************************************************/
111 /*                           InitIfNeeded()                             */
112 /************************************************************************/
113 
InitIfNeeded()114 int OGRMDBJavaEnv::InitIfNeeded()
115 {
116     GIntBig nCurPID = CPLGetPID();
117     if( env == nullptr || bCalledFromJava || nLastPID != nCurPID )
118     {
119         nLastPID = nCurPID;
120         return Init();
121     }
122     return env != nullptr;
123 }
124 
125 /************************************************************************/
126 /*                              Init()                                  */
127 /************************************************************************/
128 
Init()129 int OGRMDBJavaEnv::Init()
130 {
131     CPLMutexHolderD(&hMutex);
132 
133     jvm = nullptr;
134     env = nullptr;
135 
136     if (jvm_static == nullptr)
137     {
138         JavaVM* vmBuf[1];
139         jsize nVMs;
140         int ret = 0;
141 
142 #if JVM_LIB_DLOPEN
143 #  if defined(__APPLE__) && defined(__MACH__)
144 #    define SO_EXT "dylib"
145 #  else
146 #    define SO_EXT "so"
147 #  endif
148         const char *jvmLibPtr = "libjvm." SO_EXT;
149         char jvmLib[PATH_MAX];
150 
151         /* libjvm.so's location is hard to predict so
152            ${JAVA_HOME}/bin/java -XshowSettings is executed to find
153            its location. If JAVA_HOME is not set then java is executed
154            from the PATH instead. This is POSIX-compliant code. */
155         FILE *javaCmd = popen("\"${JAVA_HOME}${JAVA_HOME:+/bin/}java\" -XshowSettings 2>&1 | grep 'sun.boot.library.path'", "r");
156 
157         if (javaCmd != nullptr)
158         {
159             char szTmp[PATH_MAX];
160             size_t javaCmdRead = fread(szTmp, 1, sizeof(szTmp), javaCmd);
161             ret = pclose(javaCmd);
162 
163             if (ret == 0 && javaCmdRead >= 2)
164             {
165                 /* Chomp the new line */
166                 szTmp[javaCmdRead - 1] = '\0';
167                 const char* pszPtr = strchr(szTmp, '=');
168                 if( pszPtr )
169                 {
170                     pszPtr ++;
171                     while( *pszPtr == ' ' )
172                         pszPtr ++;
173                     snprintf(jvmLib, sizeof(jvmLib), "%s/server/libjvm." SO_EXT, pszPtr);
174                     jvmLibPtr = jvmLib;
175                 }
176             }
177         }
178 
179         CPLDebug("MDB", "Trying %s", jvmLibPtr);
180         jint (*pfnJNI_GetCreatedJavaVMs)(JavaVM **, jsize, jsize *);
181         pfnJNI_GetCreatedJavaVMs = (jint (*)(JavaVM **, jsize, jsize *))
182             CPLGetSymbol(jvmLibPtr, "JNI_GetCreatedJavaVMs");
183 
184         if (pfnJNI_GetCreatedJavaVMs == nullptr)
185         {
186             CPLDebug("MDB", "Cannot find JNI_GetCreatedJavaVMs function");
187             return FALSE;
188         }
189         else
190         {
191             ret = pfnJNI_GetCreatedJavaVMs(vmBuf, 1, &nVMs);
192         }
193 #else
194         ret = JNI_GetCreatedJavaVMs(vmBuf, 1, &nVMs);
195 #endif
196 
197         /* Are we already called from Java ? */
198         if (ret == JNI_OK && nVMs == 1)
199         {
200             jvm = vmBuf[0];
201             if (jvm->GetEnv((void **)&env, JNI_VERSION_1_2) == JNI_OK)
202             {
203                 bCalledFromJava = TRUE;
204             }
205             else
206             {
207                 jvm = nullptr;
208                 env = nullptr;
209             }
210         }
211         else
212         {
213             JavaVMInitArgs args;
214             JavaVMOption options[1];
215             args.version = JNI_VERSION_1_2;
216             const char* pszClassPath = CPLGetConfigOption("CLASSPATH", nullptr);
217             char* pszClassPathOption = nullptr;
218             if (pszClassPath)
219             {
220                 args.nOptions = 1;
221                 pszClassPathOption = CPLStrdup(CPLSPrintf("-Djava.class.path=%s", pszClassPath));
222                 options[0].optionString = pszClassPathOption;
223                 args.options = options;
224             }
225             else
226                 args.nOptions = 0;
227             args.ignoreUnrecognized = JNI_FALSE;
228 
229 #if JVM_LIB_DLOPEN
230             jint (*pfnJNI_CreateJavaVM)(JavaVM **, void **, void *);
231             pfnJNI_CreateJavaVM = (jint (*)(JavaVM **, void **, void *))
232                 CPLGetSymbol(jvmLibPtr, "JNI_CreateJavaVM");
233 
234             if (pfnJNI_CreateJavaVM == nullptr)
235                 return FALSE;
236             else
237                 ret = pfnJNI_CreateJavaVM(&jvm, (void **)&env, &args);
238 #else
239             ret = JNI_CreateJavaVM(&jvm, (void **)&env, &args);
240 #endif
241 
242             CPLFree(pszClassPathOption);
243 
244             if (ret != JNI_OK || jvm == nullptr || env == nullptr)
245             {
246                 CPLError(CE_Failure, CPLE_AppDefined, "JNI_CreateJavaVM failed (%d)", ret);
247                 return FALSE;
248             }
249 
250             jvm_static = jvm;
251         }
252     }
253     else
254     {
255         jvm = jvm_static;
256     }
257 
258     if( jvm == nullptr )
259         return FALSE;
260 
261     if (jvm->GetEnv((void **)&env, JNI_VERSION_1_2) == JNI_EDETACHED )
262     {
263         jvm->AttachCurrentThread((void **)&env, nullptr);
264     }
265 
266     if( env == nullptr )
267         return FALSE;
268 
269     CHECK(byteArray_class, env->FindClass("[B"));
270     CHECK(file_class, env->FindClass("java/io/File"));
271     CHECK(file_constructor, env->GetMethodID(file_class, "<init>", "(Ljava/lang/String;)V"));
272     CHECK(database_class, env->FindClass("com/healthmarketscience/jackcess/Database"));
273 
274     CHECK(database_open, env->GetStaticMethodID(database_class, "open", "(Ljava/io/File;Z)Lcom/healthmarketscience/jackcess/Database;"));
275     CHECK(database_close, env->GetMethodID(database_class, "close", "()V"));
276     CHECK(database_getTableNames, env->GetMethodID(database_class, "getTableNames", "()Ljava/util/Set;"));
277     CHECK(database_getTable, env->GetMethodID(database_class, "getTable", "(Ljava/lang/String;)Lcom/healthmarketscience/jackcess/Table;"));
278 
279     CHECK(table_class, env->FindClass("com/healthmarketscience/jackcess/Table"));
280     CHECK(table_getColumns, env->GetMethodID(table_class, "getColumns", "()Ljava/util/List;"));
281     CHECK(table_iterator, env->GetMethodID(table_class, "iterator", "()Ljava/util/Iterator;"));
282     CHECK(table_getRowCount, env->GetMethodID(table_class, "getRowCount", "()I"));
283 
284     CHECK(column_class, env->FindClass("com/healthmarketscience/jackcess/Column"));
285     CHECK(column_getName, env->GetMethodID(column_class, "getName", "()Ljava/lang/String;"));
286     CHECK(column_getType, env->GetMethodID(column_class, "getType", "()Lcom/healthmarketscience/jackcess/DataType;"));
287     CHECK(column_getLength, env->GetMethodID(column_class, "getLength", "()S"));
288     CHECK(column_isVariableLength, env->GetMethodID(column_class, "isVariableLength", "()Z"));
289 
290     CHECK(datatype_class, env->FindClass("com/healthmarketscience/jackcess/DataType"));
291     CHECK(datatype_getValue, env->GetMethodID(datatype_class, "getValue", "()B"));
292 
293     CHECK(list_class, env->FindClass("java/util/List"));
294     CHECK(list_iterator, env->GetMethodID(list_class, "iterator", "()Ljava/util/Iterator;"));
295 
296     CHECK(set_class, env->FindClass("java/util/Set"));
297     CHECK(set_iterator, env->GetMethodID(set_class, "iterator", "()Ljava/util/Iterator;"));
298 
299     CHECK(map_class, env->FindClass("java/util/Map"));
300     CHECK(map_get, env->GetMethodID(map_class, "get", "(Ljava/lang/Object;)Ljava/lang/Object;"));
301 
302     CHECK(iterator_class,  env->FindClass("java/util/Iterator"));
303     CHECK(iterator_hasNext, env->GetMethodID(iterator_class, "hasNext", "()Z"));
304     CHECK(iterator_next, env->GetMethodID(iterator_class, "next", "()Ljava/lang/Object;"));
305 
306     CHECK(object_class,  env->FindClass("java/lang/Object"));
307     CHECK(object_toString, env->GetMethodID(object_class, "toString", "()Ljava/lang/String;"));
308     CHECK(object_getClass, env->GetMethodID(object_class, "getClass", "()Ljava/lang/Class;"));
309 
310     CHECK(boolean_class,  env->FindClass("java/lang/Boolean"));
311     CHECK(boolean_booleanValue, env->GetMethodID(boolean_class, "booleanValue", "()Z"));
312 
313     CHECK(byte_class,  env->FindClass("java/lang/Byte"));
314     CHECK(byte_byteValue, env->GetMethodID(byte_class, "byteValue", "()B"));
315 
316     CHECK(short_class,  env->FindClass("java/lang/Short"));
317     CHECK(short_shortValue, env->GetMethodID(short_class, "shortValue", "()S"));
318 
319     CHECK(integer_class,  env->FindClass("java/lang/Integer"));
320     CHECK(integer_intValue, env->GetMethodID(integer_class, "intValue", "()I"));
321 
322     CHECK(float_class,  env->FindClass("java/lang/Float"));
323     CHECK(float_floatValue, env->GetMethodID(float_class, "floatValue", "()F"));
324 
325     CHECK(double_class,  env->FindClass("java/lang/Double"));
326     CHECK(double_doubleValue, env->GetMethodID(integer_class, "doubleValue", "()D"));
327 
328     return TRUE;
329 }
330 
331 /************************************************************************/
332 /*                       ExceptionOccurred()                             */
333 /************************************************************************/
334 
ExceptionOccurred()335 int OGRMDBJavaEnv::ExceptionOccurred()
336 {
337     jthrowable exc = env->ExceptionOccurred();
338     if (exc)
339     {
340          env->ExceptionDescribe();
341          env->ExceptionClear();
342          return TRUE;
343     }
344     return FALSE;
345 }
346 
347 /************************************************************************/
348 /*                           OGRMDBDatabase()                           */
349 /************************************************************************/
350 
OGRMDBDatabase()351 OGRMDBDatabase::OGRMDBDatabase()
352 {
353 }
354 
355 /************************************************************************/
356 /*                          ~OGRMDBDatabase()                           */
357 /************************************************************************/
358 
~OGRMDBDatabase()359 OGRMDBDatabase::~OGRMDBDatabase()
360 {
361     if (database)
362     {
363         CPLDebug("MDB", "Closing database");
364         env->env->CallVoidMethod(database, env->database_close);
365 
366         env->env->DeleteGlobalRef(database);
367     }
368 }
369 
370 /************************************************************************/
371 /*                               Open()                                 */
372 /************************************************************************/
373 
Open(OGRMDBJavaEnv * env,const char * pszName)374 OGRMDBDatabase* OGRMDBDatabase::Open(OGRMDBJavaEnv* env, const char* pszName)
375 {
376     jstring jstr = env->env->NewStringUTF(pszName);
377     jobject file = env->env->NewObject(env->file_class, env->file_constructor, jstr);
378     if (env->ExceptionOccurred()) return nullptr;
379     env->env->ReleaseStringUTFChars(jstr, nullptr);
380 
381     jobject database = env->env->CallStaticObjectMethod(env->database_class, env->database_open, file, JNI_TRUE);
382 
383     env->env->DeleteLocalRef(file);
384 
385     if (env->ExceptionOccurred()) return nullptr;
386     if (database == nullptr)
387         return nullptr;
388 
389     OGRMDBDatabase* poDB = new OGRMDBDatabase();
390     poDB->env = env;
391     poDB->database = env->env->NewGlobalRef(database);
392     env->env->DeleteLocalRef(database);
393     return poDB;
394 }
395 
396 /************************************************************************/
397 /*                        FetchTableNames()                             */
398 /************************************************************************/
399 
FetchTableNames()400 int OGRMDBDatabase::FetchTableNames()
401 {
402     if( !env->InitIfNeeded() )
403         return FALSE;
404 
405     jobject table_set = env->env->CallObjectMethod(database, env->database_getTableNames);
406     if (env->ExceptionOccurred()) return FALSE;
407     jobject iterator = env->env->CallObjectMethod(table_set, env->set_iterator);
408     if (env->ExceptionOccurred()) return FALSE;
409 
410     while( env->env->CallBooleanMethod(iterator, env->iterator_hasNext) )
411     {
412         if (env->ExceptionOccurred()) return FALSE;
413         jstring table_name_jstring = (jstring) env->env->CallObjectMethod(iterator, env->iterator_next);
414         if (env->ExceptionOccurred()) return FALSE;
415         jboolean is_copy;
416         const char* table_name_str = env->env->GetStringUTFChars(table_name_jstring, &is_copy);
417 
418         apoTableNames.push_back(table_name_str);
419         //CPLDebug("MDB", "Table %s", table_name_str);
420 
421         env->env->ReleaseStringUTFChars(table_name_jstring, table_name_str);
422         env->env->DeleteLocalRef(table_name_jstring);
423     }
424     env->env->DeleteLocalRef(iterator);
425     env->env->DeleteLocalRef(table_set);
426     return TRUE;
427 }
428 
429 /************************************************************************/
430 /*                            GetTable()                                */
431 /************************************************************************/
432 
GetTable(const char * pszTableName)433 OGRMDBTable* OGRMDBDatabase::GetTable(const char* pszTableName)
434 {
435     if( !env->InitIfNeeded() )
436         return nullptr;
437 
438     jstring table_name_jstring = env->env->NewStringUTF(pszTableName);
439     jobject table = env->env->CallObjectMethod(database, env->database_getTable, table_name_jstring);
440     if (env->ExceptionOccurred()) return nullptr;
441     env->env->DeleteLocalRef(table_name_jstring);
442 
443     if (!table)
444         return nullptr;
445 
446     jobject global_table = env->env->NewGlobalRef(table);
447     env->env->DeleteLocalRef(table);
448     table = global_table;
449 
450     OGRMDBTable* poTable = new OGRMDBTable(env, this, table, pszTableName);
451     if (!poTable->FetchColumns())
452     {
453         delete poTable;
454         return nullptr;
455     }
456     return poTable;
457 }
458 
459 /************************************************************************/
460 /*                           OGRMDBTable()                              */
461 /************************************************************************/
462 
OGRMDBTable(OGRMDBJavaEnv * envIn,OGRMDBDatabase * poDBIn,jobject tableIn,const char * pszTableName)463 OGRMDBTable::OGRMDBTable(OGRMDBJavaEnv* envIn, OGRMDBDatabase* poDBIn,
464                          jobject tableIn, const char* pszTableName ) :
465     env(envIn), poDB(poDBIn), table(tableIn), osTableName( pszTableName )
466 {
467 }
468 
469 /************************************************************************/
470 /*                          ~OGRMDBTable()                              */
471 /************************************************************************/
472 
~OGRMDBTable()473 OGRMDBTable::~OGRMDBTable()
474 {
475     if (env && env->InitIfNeeded())
476     {
477         //CPLDebug("MDB", "Freeing table %s", osTableName.c_str());
478         int i;
479         for(i=0;i<(int)apoColumnNameObjects.size();i++)
480             env->env->DeleteGlobalRef(apoColumnNameObjects[i]);
481 
482         env->env->DeleteGlobalRef(table_iterator_obj);
483         env->env->DeleteGlobalRef(row);
484         env->env->DeleteGlobalRef(table);
485     }
486 }
487 
488 /************************************************************************/
489 /*                          FetchColumns()                              */
490 /************************************************************************/
491 
FetchColumns()492 int OGRMDBTable::FetchColumns()
493 {
494     if( !env->InitIfNeeded() )
495         return FALSE;
496 
497     jobject column_lists = env->env->CallObjectMethod(table, env->table_getColumns);
498     if (env->ExceptionOccurred()) return FALSE;
499 
500     jobject iterator_cols = env->env->CallObjectMethod(column_lists, env->list_iterator);
501     if (env->ExceptionOccurred()) return FALSE;
502 
503     while( env->env->CallBooleanMethod(iterator_cols, env->iterator_hasNext) )
504     {
505         if (env->ExceptionOccurred()) return FALSE;
506 
507         jobject column = env->env->CallObjectMethod(iterator_cols, env->iterator_next);
508         if (env->ExceptionOccurred()) return FALSE;
509 
510         jstring column_name_jstring = (jstring) env->env->CallObjectMethod(column, env->column_getName);
511         if (env->ExceptionOccurred()) return FALSE;
512         jboolean is_copy;
513         const char* column_name_str = env->env->GetStringUTFChars(column_name_jstring, &is_copy);
514         apoColumnNames.push_back(column_name_str);
515         env->env->ReleaseStringUTFChars(column_name_jstring, column_name_str);
516 
517         apoColumnNameObjects.push_back((jstring) env->env->NewGlobalRef(column_name_jstring));
518         env->env->DeleteLocalRef(column_name_jstring);
519 
520         jobject column_type = env->env->CallObjectMethod(column, env->column_getType);
521         if (env->ExceptionOccurred()) return FALSE;
522         int type = env->env->CallByteMethod(column_type, env->datatype_getValue);
523         if (env->ExceptionOccurred()) return FALSE;
524         apoColumnTypes.push_back(type);
525 
526         int isvariablelength = env->env->CallBooleanMethod(column, env->column_isVariableLength);
527         if (env->ExceptionOccurred()) return FALSE;
528         if (!isvariablelength)
529         {
530             int length = env->env->CallShortMethod(column, env->column_getLength);
531             if (env->ExceptionOccurred()) return FALSE;
532             apoColumnLengths.push_back(length);
533         }
534         else
535             apoColumnLengths.push_back(0);
536 
537         //CPLDebug("MDB", "Column %s, type = %d", apoColumnNames.back().c_str(), type);
538 
539         env->env->DeleteLocalRef(column_type);
540 
541         env->env->DeleteLocalRef(column);
542     }
543     env->env->DeleteLocalRef(iterator_cols);
544     env->env->DeleteLocalRef(column_lists);
545 
546     return TRUE;
547 }
548 
549 /************************************************************************/
550 /*                          ResetReading()                              */
551 /************************************************************************/
552 
ResetReading()553 void OGRMDBTable::ResetReading()
554 {
555     if( !env->InitIfNeeded() )
556         return;
557 
558     env->env->DeleteGlobalRef(table_iterator_obj);
559     table_iterator_obj = nullptr;
560     env->env->DeleteGlobalRef(row);
561     row = nullptr;
562 }
563 
564 /************************************************************************/
565 /*                           GetNextRow()                               */
566 /************************************************************************/
567 
GetNextRow()568 int OGRMDBTable::GetNextRow()
569 {
570     if( !env->InitIfNeeded() )
571         return FALSE;
572 
573     if (table_iterator_obj == nullptr)
574     {
575         table_iterator_obj = env->env->CallObjectMethod(table, env->table_iterator);
576         if (env->ExceptionOccurred()) return FALSE;
577         if (table_iterator_obj)
578         {
579             jobject global_table_iterator_obj = env->env->NewGlobalRef(table_iterator_obj);
580             env->env->DeleteLocalRef(table_iterator_obj);
581             table_iterator_obj = global_table_iterator_obj;
582         }
583     }
584     if (table_iterator_obj == nullptr)
585         return FALSE;
586 
587     if (!env->env->CallBooleanMethod(table_iterator_obj, env->iterator_hasNext))
588         return FALSE;
589     if (env->ExceptionOccurred()) return FALSE;
590 
591     if (row)
592     {
593         env->env->DeleteGlobalRef(row);
594         row = nullptr;
595     }
596 
597     row = env->env->CallObjectMethod(table_iterator_obj, env->iterator_next);
598     if (env->ExceptionOccurred()) return FALSE;
599     if (row == nullptr)
600         return FALSE;
601 
602     jobject global_row = env->env->NewGlobalRef(row);
603     env->env->DeleteLocalRef(row);
604     row = global_row;
605 
606     return TRUE;
607 }
608 
609 /************************************************************************/
610 /*                          GetColumnVal()                              */
611 /************************************************************************/
612 
GetColumnVal(int iCol)613 jobject OGRMDBTable::GetColumnVal(int iCol)
614 {
615     if (row == nullptr)
616         return nullptr;
617 
618     jobject val = env->env->CallObjectMethod(row, env->map_get, apoColumnNameObjects[iCol]);
619     if (env->ExceptionOccurred()) return nullptr;
620     return val;
621 }
622 
623 /************************************************************************/
624 /*                        GetColumnAsString()                           */
625 /************************************************************************/
626 
GetColumnAsString(int iCol)627 char* OGRMDBTable::GetColumnAsString(int iCol)
628 {
629     jobject val = GetColumnVal(iCol);
630     if (!val) return nullptr;
631 
632     jstring val_jstring = (jstring) env->env->CallObjectMethod(val, env->object_toString);
633     if (env->ExceptionOccurred()) return nullptr;
634     jboolean is_copy;
635     const char* val_str = env->env->GetStringUTFChars(val_jstring, &is_copy);
636     char* dup_str = (val_str) ? CPLStrdup(val_str) : nullptr;
637     env->env->ReleaseStringUTFChars(val_jstring, val_str);
638     env->env->DeleteLocalRef(val_jstring);
639 
640     env->env->DeleteLocalRef(val);
641 
642     return dup_str;
643 }
644 
645 /************************************************************************/
646 /*                          GetColumnAsInt()                            */
647 /************************************************************************/
648 
GetColumnAsInt(int iCol)649 int OGRMDBTable::GetColumnAsInt(int iCol)
650 {
651     jobject val = GetColumnVal(iCol);
652     if (!val) return 0;
653 
654     int int_val = 0;
655     if (apoColumnTypes[iCol] == MDB_Boolean)
656         int_val = env->env->CallBooleanMethod(val, env->boolean_booleanValue);
657     else if (apoColumnTypes[iCol] == MDB_Byte)
658         int_val = env->env->CallByteMethod(val, env->byte_byteValue);
659     else if (apoColumnTypes[iCol] == MDB_Short)
660         int_val = env->env->CallShortMethod(val, env->short_shortValue);
661     else if (apoColumnTypes[iCol] == MDB_Int)
662         int_val = env->env->CallIntMethod(val, env->integer_intValue);
663     if (env->ExceptionOccurred()) return 0;
664 
665     env->env->DeleteLocalRef(val);
666 
667     return int_val;
668 }
669 
670 /************************************************************************/
671 /*                        GetColumnAsDouble()                           */
672 /************************************************************************/
673 
GetColumnAsDouble(int iCol)674 double OGRMDBTable::GetColumnAsDouble(int iCol)
675 {
676     jobject val = GetColumnVal(iCol);
677     if (!val) return 0;
678 
679     double double_val = 0;
680     if (apoColumnTypes[iCol] == MDB_Double)
681         double_val = env->env->CallDoubleMethod(val, env->double_doubleValue);
682     else if (apoColumnTypes[iCol] == MDB_Float)
683         double_val = env->env->CallFloatMethod(val, env->float_floatValue);
684     if (env->ExceptionOccurred()) return 0;
685 
686     env->env->DeleteLocalRef(val);
687 
688     return double_val;
689 }
690 
691 /************************************************************************/
692 /*                        GetColumnAsBinary()                           */
693 /************************************************************************/
694 
GetColumnAsBinary(int iCol,int * pnBytes)695 GByte* OGRMDBTable::GetColumnAsBinary(int iCol, int* pnBytes)
696 {
697     *pnBytes = 0;
698 
699     jobject val = GetColumnVal(iCol);
700     if (!val) return nullptr;
701 
702     if (!env->env->IsInstanceOf(val, env->byteArray_class))
703         return nullptr;
704 
705     jbyteArray byteArray = (jbyteArray) val;
706     *pnBytes = env->env->GetArrayLength(byteArray);
707     if (env->ExceptionOccurred()) return nullptr;
708     jboolean is_copy;
709     jbyte* elts = env->env->GetByteArrayElements(byteArray, &is_copy);
710     if (env->ExceptionOccurred()) return nullptr;
711 
712     GByte* pData = (GByte*)CPLMalloc(*pnBytes);
713     memcpy(pData, elts, *pnBytes);
714 
715     env->env->ReleaseByteArrayElements(byteArray, elts, JNI_ABORT);
716 
717     env->env->DeleteLocalRef(val);
718 
719     return pData;
720 }
721 
722 /************************************************************************/
723 /*                              DumpTable()                             */
724 /************************************************************************/
725 
DumpTable()726 void OGRMDBTable::DumpTable()
727 {
728     ResetReading();
729     int iRow = 0;
730     int nCols = static_cast<int>(apoColumnNames.size());
731     while(GetNextRow())
732     {
733         printf("Row = %d\n", iRow ++);/*ok*/
734         for(int i=0;i<nCols;i++)
735         {
736             printf("%s = ", apoColumnNames[i].c_str());/*ok*/
737             if (apoColumnTypes[i] == MDB_Float ||
738                 apoColumnTypes[i] == MDB_Double)
739             {
740                 printf("%.15f\n", GetColumnAsDouble(i));/*ok*/
741             }
742             else if (apoColumnTypes[i] == MDB_Boolean ||
743                      apoColumnTypes[i] == MDB_Byte ||
744                      apoColumnTypes[i] == MDB_Short ||
745                      apoColumnTypes[i] == MDB_Int)
746             {
747                 printf("%d\n", GetColumnAsInt(i));/*ok*/
748             }
749             else if (apoColumnTypes[i] == MDB_Binary ||
750                      apoColumnTypes[i] == MDB_OLE)
751             {
752                 int nBytes;
753                 GByte* pData = GetColumnAsBinary(i, &nBytes);
754                 printf("(%d bytes)\n", nBytes);/*ok*/
755                 CPLFree(pData);
756             }
757             else
758             {
759                 char* val = GetColumnAsString(i);
760                 printf("'%s'\n", val);/*ok*/
761                 CPLFree(val);
762             }
763         }
764     }
765 }
766 
767 /************************************************************************/
768 /*                            GetColumnIndex()                          */
769 /************************************************************************/
770 
GetColumnIndex(const char * pszColName,int bEmitErrorIfNotFound)771 int OGRMDBTable::GetColumnIndex(const char* pszColName, int bEmitErrorIfNotFound)
772 {
773     int nCols = static_cast<int>(apoColumnNames.size());
774     CPLString osColName(pszColName);
775     for(int i=0;i<nCols;i++)
776     {
777         if (apoColumnNames[i] == osColName)
778             return i;
779     }
780     if (bEmitErrorIfNotFound)
781         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find column %s", pszColName);
782     return -1;
783 }
784 
785 /************************************************************************/
786 /*                             GetRowCount()                            */
787 /************************************************************************/
788 
GetRowCount()789 int OGRMDBTable::GetRowCount()
790 {
791     if( !env->InitIfNeeded() )
792         return 0;
793 
794     int nRowCount = env->env->CallIntMethod(table, env->table_getRowCount);
795     if (env->ExceptionOccurred()) return 0;
796     return nRowCount;
797 }
798