1
/******************************************************************************
2
* $Id: ogrmdbjackcess.cpp 22156 2011-04-13 20:08:07Z rouault $
4
* Project: OpenGIS Simple Features Reference Implementation
5
* Purpose: Implements OGRMDBJavaEnv class.
6
* Author: Even Rouault, <even dot rouault at mines dash paris dot org>
8
******************************************************************************
9
* Copyright (c) 2011, Even Rouault, <even dot rouault at mines dash paris dot org>
11
* Permission is hereby granted, free of charge, to any person obtaining a
12
* copy of this software and associated documentation files (the "Software"),
13
* to deal in the Software without restriction, including without limitation
14
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
15
* and/or sell copies of the Software, and to permit persons to whom the
16
* Software is furnished to do so, subject to the following conditions:
18
* The above copyright notice and this permission notice shall be included
19
* in all copies or substantial portions of the Software.
21
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27
* DEALINGS IN THE SOFTWARE.
28
****************************************************************************/
32
CPL_CVSID("$Id: ogrmdbjackcess.cpp 22156 2011-04-13 20:08:07Z rouault $");
34
static JavaVM *jvm_static = NULL;
35
static JNIEnv *env_static = NULL;
37
/************************************************************************/
39
/************************************************************************/
41
OGRMDBJavaEnv::OGRMDBJavaEnv()
45
bCalledFromJava = FALSE;
47
byteArray_class = NULL;
50
file_constructor = NULL;
51
database_class = NULL;
53
database_close = NULL;
54
database_getTableNames = NULL;
55
database_getTable = NULL;
58
table_getColumns = NULL;
59
table_iterator = NULL;
60
table_getRowCount = NULL;
63
column_getName = NULL;
64
column_getType = NULL;
65
column_getLength = NULL;
66
column_isVariableLength = NULL;
68
datatype_class = NULL;
69
datatype_getValue = NULL;
80
iterator_class = NULL;
81
iterator_hasNext = NULL;
85
object_toString = NULL;
86
object_getClass = NULL;
89
boolean_booleanValue = NULL;
92
byte_byteValue = NULL;
95
short_shortValue = NULL;
98
integer_intValue = NULL;
101
float_floatValue = NULL;
104
double_doubleValue = NULL;
107
/************************************************************************/
108
/* ~OGRMDBJavaEnv() */
109
/************************************************************************/
111
OGRMDBJavaEnv::~OGRMDBJavaEnv()
115
env->DeleteLocalRef(byteArray_class);
117
env->DeleteLocalRef(file_class);
118
env->DeleteLocalRef(database_class);
120
env->DeleteLocalRef(table_class);
122
env->DeleteLocalRef(column_class);
124
env->DeleteLocalRef(datatype_class);
126
env->DeleteLocalRef(list_class);
128
env->DeleteLocalRef(set_class);
130
env->DeleteLocalRef(map_class);
132
env->DeleteLocalRef(iterator_class);
134
env->DeleteLocalRef(object_class);
136
env->DeleteLocalRef(boolean_class);
137
env->DeleteLocalRef(byte_class);
138
env->DeleteLocalRef(short_class);
139
env->DeleteLocalRef(integer_class);
140
env->DeleteLocalRef(float_class);
141
env->DeleteLocalRef(double_class);
143
/*if (!bCalledFromJava)
145
CPLDebug("MDB", "Destroying JVM");
146
int ret = jvm->DestroyJavaVM();
147
CPLDebug("MDB", "ret=%d", ret);
152
#define CHECK(x, y) do {x = y; if (!x) { CPLError(CE_Failure, CPLE_AppDefined, #y " failed"); return FALSE;} } while(0)
154
/************************************************************************/
156
/************************************************************************/
158
int OGRMDBJavaEnv::Init()
160
if (jvm_static == NULL)
165
/* Are we already called from Java ? */
166
if (JNI_GetCreatedJavaVMs(vmBuf, 1, &nVMs) == JNI_OK && nVMs == 1)
169
if (jvm->GetEnv((void **)&env, JNI_VERSION_1_2) == JNI_OK)
171
bCalledFromJava = TRUE;
182
JavaVMOption options[1];
183
args.version = JNI_VERSION_1_2;
184
const char* pszClassPath = CPLGetConfigOption("CLASSPATH", NULL);
185
CPLString osClassPathOption;
189
osClassPathOption.Printf("-Djava.class.path=%s", pszClassPath);
190
options[0].optionString = (char*) osClassPathOption.c_str();
191
args.options = options;
195
args.ignoreUnrecognized = JNI_FALSE;
197
int ret = JNI_CreateJavaVM(&jvm, (void **)&env, &args);
198
if (ret != 0 || jvm == NULL || env == NULL)
200
CPLError(CE_Failure, CPLE_AppDefined, "JNI_CreateJavaVM failed (%d)", ret);
214
CHECK(byteArray_class, env->FindClass("[B"));
215
CHECK(file_class, env->FindClass("java/io/File"));
216
CHECK(file_constructor, env->GetMethodID(file_class, "<init>", "(Ljava/lang/String;)V"));
217
CHECK(database_class, env->FindClass("com/healthmarketscience/jackcess/Database"));
219
CHECK(database_open, env->GetStaticMethodID(database_class, "open", "(Ljava/io/File;Z)Lcom/healthmarketscience/jackcess/Database;"));
220
CHECK(database_close, env->GetMethodID(database_class, "close", "()V"));
221
CHECK(database_getTableNames, env->GetMethodID(database_class, "getTableNames", "()Ljava/util/Set;"));
222
CHECK(database_getTable, env->GetMethodID(database_class, "getTable", "(Ljava/lang/String;)Lcom/healthmarketscience/jackcess/Table;"));
224
CHECK(table_class, env->FindClass("com/healthmarketscience/jackcess/Table"));
225
CHECK(table_getColumns, env->GetMethodID(table_class, "getColumns", "()Ljava/util/List;"));
226
CHECK(table_iterator, env->GetMethodID(table_class, "iterator", "()Ljava/util/Iterator;"));
227
CHECK(table_getRowCount, env->GetMethodID(table_class, "getRowCount", "()I"));
229
CHECK(column_class, env->FindClass("com/healthmarketscience/jackcess/Column"));
230
CHECK(column_getName, env->GetMethodID(column_class, "getName", "()Ljava/lang/String;"));
231
CHECK(column_getType, env->GetMethodID(column_class, "getType", "()Lcom/healthmarketscience/jackcess/DataType;"));
232
CHECK(column_getLength, env->GetMethodID(column_class, "getLength", "()S"));
233
CHECK(column_isVariableLength, env->GetMethodID(column_class, "isVariableLength", "()Z"));
235
CHECK(datatype_class, env->FindClass("com/healthmarketscience/jackcess/DataType"));
236
CHECK(datatype_getValue, env->GetMethodID(datatype_class, "getValue", "()B"));
238
CHECK(list_class, env->FindClass("java/util/List"));
239
CHECK(list_iterator, env->GetMethodID(list_class, "iterator", "()Ljava/util/Iterator;"));
241
CHECK(set_class, env->FindClass("java/util/Set"));
242
CHECK(set_iterator, env->GetMethodID(set_class, "iterator", "()Ljava/util/Iterator;"));
244
CHECK(map_class, env->FindClass("java/util/Map"));
245
CHECK(map_get, env->GetMethodID(map_class, "get", "(Ljava/lang/Object;)Ljava/lang/Object;"));
247
CHECK(iterator_class, env->FindClass("java/util/Iterator"));
248
CHECK(iterator_hasNext, env->GetMethodID(iterator_class, "hasNext", "()Z"));
249
CHECK(iterator_next, env->GetMethodID(iterator_class, "next", "()Ljava/lang/Object;"));
251
CHECK(object_class, env->FindClass("java/lang/Object"));
252
CHECK(object_toString, env->GetMethodID(object_class, "toString", "()Ljava/lang/String;"));
253
CHECK(object_getClass, env->GetMethodID(object_class, "getClass", "()Ljava/lang/Class;"));
255
CHECK(boolean_class, env->FindClass("java/lang/Boolean"));
256
CHECK(boolean_booleanValue, env->GetMethodID(boolean_class, "booleanValue", "()Z"));
258
CHECK(byte_class, env->FindClass("java/lang/Byte"));
259
CHECK(byte_byteValue, env->GetMethodID(byte_class, "byteValue", "()B"));
261
CHECK(short_class, env->FindClass("java/lang/Short"));
262
CHECK(short_shortValue, env->GetMethodID(short_class, "shortValue", "()S"));
264
CHECK(integer_class, env->FindClass("java/lang/Integer"));
265
CHECK(integer_intValue, env->GetMethodID(integer_class, "intValue", "()I"));
267
CHECK(float_class, env->FindClass("java/lang/Float"));
268
CHECK(float_floatValue, env->GetMethodID(float_class, "floatValue", "()F"));
270
CHECK(double_class, env->FindClass("java/lang/Double"));
271
CHECK(double_doubleValue, env->GetMethodID(integer_class, "doubleValue", "()D"));
277
/************************************************************************/
278
/* ExceptionOccured() */
279
/************************************************************************/
281
int OGRMDBJavaEnv::ExceptionOccured()
283
jthrowable exc = env->ExceptionOccurred();
286
env->ExceptionDescribe();
287
env->ExceptionClear();
294
/************************************************************************/
295
/* OGRMDBDatabase() */
296
/************************************************************************/
298
OGRMDBDatabase::OGRMDBDatabase()
304
/************************************************************************/
305
/* ~OGRMDBDatabase() */
306
/************************************************************************/
308
OGRMDBDatabase::~OGRMDBDatabase()
312
CPLDebug("MDB", "Closing database");
313
env->env->CallVoidMethod(database, env->database_close);
315
env->env->DeleteGlobalRef(database);
319
/************************************************************************/
321
/************************************************************************/
323
OGRMDBDatabase* OGRMDBDatabase::Open(OGRMDBJavaEnv* env, const char* pszName)
325
jstring jstr = env->env->NewStringUTF(pszName);
326
jobject file = env->env->NewObject(env->file_class, env->file_constructor, jstr);
327
if (env->ExceptionOccured()) return NULL;
328
env->env->ReleaseStringUTFChars(jstr, NULL);
330
jobject database = env->env->CallStaticObjectMethod(env->database_class, env->database_open, file, JNI_TRUE);
332
env->env->DeleteLocalRef(file);
334
if (env->ExceptionOccured()) return NULL;
335
if (database == NULL)
338
OGRMDBDatabase* poDB = new OGRMDBDatabase();
340
poDB->database = env->env->NewGlobalRef(database);
341
env->env->DeleteLocalRef(database);
345
/************************************************************************/
346
/* FetchTableNames() */
347
/************************************************************************/
349
int OGRMDBDatabase::FetchTableNames()
351
if (env->bCalledFromJava)
354
jobject table_set = env->env->CallObjectMethod(database, env->database_getTableNames);
355
if (env->ExceptionOccured()) return FALSE;
356
jobject iterator = env->env->CallObjectMethod(table_set, env->set_iterator);
357
if (env->ExceptionOccured()) return FALSE;
359
while( env->env->CallBooleanMethod(iterator, env->iterator_hasNext) )
361
if (env->ExceptionOccured()) return FALSE;
362
jstring table_name_jstring = (jstring) env->env->CallObjectMethod(iterator, env->iterator_next);
363
if (env->ExceptionOccured()) return FALSE;
365
const char* table_name_str = env->env->GetStringUTFChars(table_name_jstring, &is_copy);
367
apoTableNames.push_back(table_name_str);
368
//CPLDebug("MDB", "Table %s", table_name_str);
370
env->env->ReleaseStringUTFChars(table_name_jstring, table_name_str);
371
env->env->DeleteLocalRef(table_name_jstring);
373
env->env->DeleteLocalRef(iterator);
374
env->env->DeleteLocalRef(table_set);
378
/************************************************************************/
380
/************************************************************************/
382
OGRMDBTable* OGRMDBDatabase::GetTable(const char* pszTableName)
384
if (env->bCalledFromJava)
387
jstring table_name_jstring = env->env->NewStringUTF(pszTableName);
388
jobject table = env->env->CallObjectMethod(database, env->database_getTable, table_name_jstring);
389
if (env->ExceptionOccured()) return NULL;
390
env->env->DeleteLocalRef(table_name_jstring);
395
jobject global_table = env->env->NewGlobalRef(table);
396
env->env->DeleteLocalRef(table);
397
table = global_table;
399
OGRMDBTable* poTable = new OGRMDBTable(env, this, table, pszTableName);
400
if (!poTable->FetchColumns())
408
/************************************************************************/
410
/************************************************************************/
412
OGRMDBTable::OGRMDBTable(OGRMDBJavaEnv* env, OGRMDBDatabase* poDB, jobject table, const char* pszTableName )
417
osTableName = pszTableName;
418
table_iterator_obj = NULL;
422
/************************************************************************/
424
/************************************************************************/
426
OGRMDBTable::~OGRMDBTable()
430
//CPLDebug("MDB", "Freeing table %s", osTableName.c_str());
431
if (env->bCalledFromJava)
435
for(i=0;i<(int)apoColumnNameObjects.size();i++)
436
env->env->DeleteGlobalRef(apoColumnNameObjects[i]);
438
env->env->DeleteGlobalRef(table_iterator_obj);
439
env->env->DeleteGlobalRef(row);
440
env->env->DeleteGlobalRef(table);
444
/************************************************************************/
446
/************************************************************************/
448
int OGRMDBTable::FetchColumns()
450
if (env->bCalledFromJava)
453
jobject column_lists = env->env->CallObjectMethod(table, env->table_getColumns);
454
if (env->ExceptionOccured()) return FALSE;
456
jobject iterator_cols = env->env->CallObjectMethod(column_lists, env->list_iterator);
457
if (env->ExceptionOccured()) return FALSE;
459
while( env->env->CallBooleanMethod(iterator_cols, env->iterator_hasNext) )
461
if (env->ExceptionOccured()) return FALSE;
463
jobject column = env->env->CallObjectMethod(iterator_cols, env->iterator_next);
464
if (env->ExceptionOccured()) return FALSE;
466
jstring column_name_jstring = (jstring) env->env->CallObjectMethod(column, env->column_getName);
467
if (env->ExceptionOccured()) return FALSE;
469
const char* column_name_str = env->env->GetStringUTFChars(column_name_jstring, &is_copy);
470
apoColumnNames.push_back(column_name_str);
471
env->env->ReleaseStringUTFChars(column_name_jstring, column_name_str);
473
apoColumnNameObjects.push_back((jstring) env->env->NewGlobalRef(column_name_jstring));
474
env->env->DeleteLocalRef(column_name_jstring);
476
jobject column_type = env->env->CallObjectMethod(column, env->column_getType);
477
if (env->ExceptionOccured()) return FALSE;
478
int type = env->env->CallByteMethod(column_type, env->datatype_getValue);
479
if (env->ExceptionOccured()) return FALSE;
480
apoColumnTypes.push_back(type);
482
int isvariablelength = env->env->CallBooleanMethod(column, env->column_isVariableLength);
483
if (env->ExceptionOccured()) return FALSE;
484
if (!isvariablelength)
486
int length = env->env->CallShortMethod(column, env->column_getLength);
487
if (env->ExceptionOccured()) return FALSE;
488
apoColumnLengths.push_back(length);
491
apoColumnLengths.push_back(0);
493
//CPLDebug("MDB", "Column %s, type = %d", apoColumnNames[apoColumnNames.size()-1].c_str(), type);
495
env->env->DeleteLocalRef(column_type);
497
env->env->DeleteLocalRef(column);
499
env->env->DeleteLocalRef(iterator_cols);
500
env->env->DeleteLocalRef(column_lists);
505
/************************************************************************/
507
/************************************************************************/
509
void OGRMDBTable::ResetReading()
511
if (env->bCalledFromJava)
514
env->env->DeleteGlobalRef(table_iterator_obj);
515
table_iterator_obj = NULL;
516
env->env->DeleteGlobalRef(row);
520
/************************************************************************/
522
/************************************************************************/
524
int OGRMDBTable::GetNextRow()
526
if (env->bCalledFromJava)
529
if (table_iterator_obj == NULL)
531
table_iterator_obj = env->env->CallObjectMethod(table, env->table_iterator);
532
if (env->ExceptionOccured()) return FALSE;
533
if (table_iterator_obj)
535
jobject global_table_iterator_obj = env->env->NewGlobalRef(table_iterator_obj);
536
env->env->DeleteLocalRef(table_iterator_obj);
537
table_iterator_obj = global_table_iterator_obj;
540
if (table_iterator_obj == NULL)
543
if (!env->env->CallBooleanMethod(table_iterator_obj, env->iterator_hasNext))
545
if (env->ExceptionOccured()) return FALSE;
549
env->env->DeleteGlobalRef(row);
553
row = env->env->CallObjectMethod(table_iterator_obj, env->iterator_next);
554
if (env->ExceptionOccured()) return FALSE;
558
jobject global_row = env->env->NewGlobalRef(row);
559
env->env->DeleteLocalRef(row);
565
/************************************************************************/
567
/************************************************************************/
569
jobject OGRMDBTable::GetColumnVal(int iCol)
574
jobject val = env->env->CallObjectMethod(row, env->map_get, apoColumnNameObjects[iCol]);
575
if (env->ExceptionOccured()) return NULL;
579
/************************************************************************/
580
/* GetColumnAsString() */
581
/************************************************************************/
583
char* OGRMDBTable::GetColumnAsString(int iCol)
585
jobject val = GetColumnVal(iCol);
586
if (!val) return NULL;
588
jstring val_jstring = (jstring) env->env->CallObjectMethod(val, env->object_toString);
589
if (env->ExceptionOccured()) return NULL;
591
const char* val_str = env->env->GetStringUTFChars(val_jstring, &is_copy);
592
char* dup_str = (val_str) ? CPLStrdup(val_str) : NULL;
593
env->env->ReleaseStringUTFChars(val_jstring, val_str);
594
env->env->DeleteLocalRef(val_jstring);
596
env->env->DeleteLocalRef(val);
601
/************************************************************************/
602
/* GetColumnAsInt() */
603
/************************************************************************/
605
int OGRMDBTable::GetColumnAsInt(int iCol)
607
jobject val = GetColumnVal(iCol);
611
if (apoColumnTypes[iCol] == MDB_Boolean)
612
int_val = env->env->CallBooleanMethod(val, env->boolean_booleanValue);
613
else if (apoColumnTypes[iCol] == MDB_Byte)
614
int_val = env->env->CallByteMethod(val, env->byte_byteValue);
615
else if (apoColumnTypes[iCol] == MDB_Short)
616
int_val = env->env->CallShortMethod(val, env->short_shortValue);
617
else if (apoColumnTypes[iCol] == MDB_Int)
618
int_val = env->env->CallIntMethod(val, env->integer_intValue);
619
if (env->ExceptionOccured()) return 0;
621
env->env->DeleteLocalRef(val);
626
/************************************************************************/
627
/* GetColumnAsDouble() */
628
/************************************************************************/
630
double OGRMDBTable::GetColumnAsDouble(int iCol)
632
jobject val = GetColumnVal(iCol);
635
double double_val = 0;
636
if (apoColumnTypes[iCol] == MDB_Double)
637
double_val = env->env->CallDoubleMethod(val, env->double_doubleValue);
638
else if (apoColumnTypes[iCol] == MDB_Float)
639
double_val = env->env->CallFloatMethod(val, env->float_floatValue);
640
if (env->ExceptionOccured()) return 0;
642
env->env->DeleteLocalRef(val);
647
/************************************************************************/
648
/* GetColumnAsBinary() */
649
/************************************************************************/
651
GByte* OGRMDBTable::GetColumnAsBinary(int iCol, int* pnBytes)
655
jobject val = GetColumnVal(iCol);
656
if (!val) return NULL;
658
if (!env->env->IsInstanceOf(val, env->byteArray_class))
661
jbyteArray byteArray = (jbyteArray) val;
662
*pnBytes = env->env->GetArrayLength(byteArray);
663
if (env->ExceptionOccured()) return NULL;
665
jbyte* elts = env->env->GetByteArrayElements(byteArray, &is_copy);
666
if (env->ExceptionOccured()) return NULL;
668
GByte* pData = (GByte*)CPLMalloc(*pnBytes);
669
memcpy(pData, elts, *pnBytes);
671
env->env->ReleaseByteArrayElements(byteArray, elts, JNI_ABORT);
673
env->env->DeleteLocalRef(val);
678
/************************************************************************/
680
/************************************************************************/
682
void OGRMDBTable::DumpTable()
686
int nCols = apoColumnNames.size();
689
printf("Row = %d\n", iRow ++);
690
for(int i=0;i<nCols;i++)
692
printf("%s = ", apoColumnNames[i].c_str());
693
if (apoColumnTypes[i] == MDB_Float ||
694
apoColumnTypes[i] == MDB_Double)
696
printf("%.15f\n", GetColumnAsDouble(i));
698
else if (apoColumnTypes[i] == MDB_Boolean ||
699
apoColumnTypes[i] == MDB_Byte ||
700
apoColumnTypes[i] == MDB_Short ||
701
apoColumnTypes[i] == MDB_Int)
703
printf("%d\n", GetColumnAsInt(i));
705
else if (apoColumnTypes[i] == MDB_Binary ||
706
apoColumnTypes[i] == MDB_OLE)
709
GByte* pData = GetColumnAsBinary(i, &nBytes);
710
printf("(%d bytes)\n", nBytes);
715
char* val = GetColumnAsString(i);
716
printf("'%s'\n", val);
723
/************************************************************************/
724
/* GetColumnIndex() */
725
/************************************************************************/
727
int OGRMDBTable::GetColumnIndex(const char* pszColName, int bEmitErrorIfNotFound)
729
int nCols = apoColumnNames.size();
730
CPLString osColName(pszColName);
731
for(int i=0;i<nCols;i++)
733
if (apoColumnNames[i] == osColName)
736
if (bEmitErrorIfNotFound)
737
CPLError(CE_Failure, CPLE_AppDefined, "Cannot find column %s", pszColName);
741
/************************************************************************/
743
/************************************************************************/
745
int OGRMDBTable::GetRowCount()
747
if (env->bCalledFromJava)
749
int nRowCount = env->env->CallIntMethod(table, env->table_getRowCount);
750
if (env->ExceptionOccured()) return 0;