/* * MGPPSearchWrapperImpl.cpp * Copyright (C) 2007 New Zealand Digital Library, http://www.nzdl.org * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef __WIN32__ #include #include #include #else #ifdef __APPLE__ #include #include #else #include #endif #endif #include #include "org_greenstone_mgpp_MGPPSearchWrapper.h" #include "MGPPSearchWrapperImpl.h" #include "GSDLQueryParser.h" #include "MGQuery.h" // toggle debugging //#define _DEBUG MGPPSearchData::MGPPSearchData() { indexData = new IndexData(); queryInfo = new QueryInfo(); if (queryInfo==NULL) { cerr<<"couldn't allocate new query info\n"; if (indexData!=NULL) { delete indexData; } } // set all the default params SetCStr(queryInfo->docLevel, "Document"); // the level to search at queryInfo->maxDocs = 50; queryInfo->sortByRank = true; queryInfo->exactWeights = false; queryInfo->needRankInfo = true; queryInfo->needTermFreqs = true; UCArrayClear(level); SetCStr(level, "Document"); // the level to return docs at defaultStemMethod=0; defaultBoolCombine=0; maxNumeric = 4; } MGPPSearchData::~MGPPSearchData() { if (indexData !=NULL) { delete indexData; } if (queryInfo !=NULL) { delete queryInfo; } } // ******************************************** // initialisation stuff // ******************************************** // cached ids for java stuff jfieldID FID_mgpp_data = NULL; // MGPPSearchData jfieldID FID_query_result = NULL; // MGPPQueryResult jmethodID MID_addDoc=NULL; // MGPPQueryResult.addDoc() jmethodID MID_addTerm=NULL; // MGPPQueryResult.addTerm() jmethodID MID_setTotalDocs=NULL; // MGPPQueryResult.setTotalDocs() jmethodID MID_clearResult=NULL; //MGPPQueryResult.clear() jmethodID MID_setSyntaxError=NULL; // MGPPQueryResult.setSyntaxError() jclass CID_String=NULL; // class ID of String /* to access objects and methods on java side, need their field/method ids - this initialises them at the start to avoid recalculating them each time they are needed Note: the descriptors need to be exactly right, otherwise you get an error saying "no such field" but no reference to the fact that it has the right name but the wrong type. Note: apparently the jclass is a local ref and should only work in the method that created it. It seems to work ok, but I'll make it global cos the book said I should, and it may avoid future hassles. */ JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_initIDs (JNIEnv *j_env, jclass j_cls) { FID_mgpp_data = j_env->GetFieldID(j_cls, "mgpp_data_ptr_", "J"); //a long-"J" if (FID_mgpp_data==NULL) { cerr <<"MGPP JNI: field mgpp_data_ptr_ not found"<GetFieldID(j_cls, "mgpp_query_result_", "Lorg/greenstone/mgpp/MGPPQueryResult;"); // an object -"L;" if (FID_query_result==NULL) { cerr <<"MGPP JNI: field mgpp_query_result_ not found"<FindClass("org/greenstone/mgpp/MGPPQueryResult"); MID_addDoc = j_env->GetMethodID(JC_MGPPQueryResult, "addDoc", "(JF)V"); if (MID_addDoc==NULL) { cerr <<"MGPP JNI: addDoc method not found"<GetMethodID(JC_MGPPQueryResult, "addTerm", "(Ljava/lang/String;Ljava/lang/String;IJJ[Ljava/lang/String;)V"); if (MID_addTerm==NULL) { cerr <<"MGPP JNI: method addTerm not found"<GetMethodID(JC_MGPPQueryResult, "setTotalDocs", "(J)V"); if (MID_setTotalDocs==NULL) { cerr <<"MGPP JNI: method setTotalDocs not found"<GetMethodID(JC_MGPPQueryResult, "clear", "()V"); if (MID_clearResult==NULL) { cerr <<"MGPP JNI: method clear not found"<GetMethodID(JC_MGPPQueryResult, "setSyntaxError", "(Z)V"); if (MID_clearResult==NULL) { cerr <<"MGPP JNI: method setSyntaxError not found"<FindClass("java/lang/String"); if (local_CID_String==NULL) { cerr <<"MGPP JNI: java String class not found"<NewGlobalRef(local_CID_String); /* The local reference is no longer useful */ j_env->DeleteLocalRef(local_CID_String); /* Is the global reference created successfully? */ if (CID_String == NULL) { return; /* out of memory exception thrown */ } } } /* the java side MGPPSearchWrapper has a pointer to a C++ object - MGPPSearchData initialise this and set the pointer */ JNIEXPORT jboolean JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_initCppSide (JNIEnv *j_env, jobject j_obj){ #ifdef _DEBUG cerr << "**** JNI debugging for GS3. initCppSide: SetLongField()\n"; #endif MGPPSearchData * data = new MGPPSearchData(); #ifdef _DEBUG fprintf (stderr, "1a. data before SetLongField() is: %ld and as hex: %lX\n", data, data); fprintf (stderr, "1b. FID_mgpp_data before SetLongField() is: %ld and as hex: %lX\n", FID_mgpp_data, FID_mgpp_data); #endif j_env->SetLongField(j_obj, FID_mgpp_data, (long)data); #ifdef _DEBUG fprintf (stderr, "1a. data after SetLongField() is: %ld and as hex: %lX\n", data, data); fprintf (stderr, "1b. FID_mgpp_data after SetLongField() is: %ld and as hex: %lX\n", FID_mgpp_data, FID_mgpp_data); #endif return true; } //****************************************** // do a query // **************************************** /* load the IndexData - cached for querying */ JNIEXPORT jboolean JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_loadIndexData (JNIEnv *j_env, jobject j_obj, jstring j_index_name) { #ifdef _DEBUG fprintf (stderr, "in loadIndexData\n"); #endif jlong data_ptr = j_env->GetLongField(j_obj, FID_mgpp_data); #ifdef _DEBUG fprintf (stderr, "1. data_ptr at start is: %ld and as hex: %lX\n", data_ptr, data_ptr); #endif MGPPSearchData * data = (MGPPSearchData *)data_ptr; #ifdef __WIN32__ const char* base_dir = ""; #else const char* base_dir = "/"; #endif const char * index_name = j_env->GetStringUTFChars( j_index_name, NULL); if (index_name==NULL) { return false; } jboolean j_result=false; // why doesn't this complain about const?? if (data->indexData->LoadData(base_dir, index_name)) { j_result=true; } // release any gets j_env->ReleaseStringUTFChars(j_index_name, index_name); return j_result; } /* unload the data */ JNIEXPORT jboolean JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_unloadIndexData (JNIEnv *j_env, jobject j_obj) { #ifdef _DEBUG fprintf (stderr, "in unloadIndexData\n"); #endif jlong data_ptr = j_env->GetLongField(j_obj, FID_mgpp_data); #ifdef _DEBUG fprintf (stderr, "1. data_ptr at start is: %ld and as hex: %lX\n", data_ptr, data_ptr); #endif MGPPSearchData * data = (MGPPSearchData *)data_ptr; data->indexData->UnloadData(); return true; } /* do the actual query - the results are written to query_result held on the java side */ JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_runQuery (JNIEnv *j_env, jobject j_obj, jstring j_query){ jthrowable exc; // an exception - check if something funny has happened const char *query = j_env->GetStringUTFChars(j_query, NULL); if (query==NULL) { return; // exception already thrown } // turn to UCArray for mgpp and then release the string UCArray queryArray; SetCStr(queryArray, query); j_env->ReleaseStringUTFChars(j_query, query); // the query data MGPPSearchData * data = (MGPPSearchData *)j_env->GetLongField(j_obj, FID_mgpp_data); // the result to write to jobject result_ptr = j_env->GetObjectField(j_obj, FID_query_result); if (result_ptr==NULL) { cerr <<"couldn't access the result to write to"<CallVoidMethod(result_ptr, MID_clearResult); exc = j_env->ExceptionOccurred(); // this catches the exception I think - it //wont be thrown any further if (exc) { j_env->ExceptionDescribe(); return; } // the mgpp QueryResult that we will use ExtQueryResult queryResult; QueryNode * queryTree = NULL; // parse the query string into a tree structure queryTree = ParseQuery(queryArray, data->defaultBoolCombine, data->defaultStemMethod, data->maxNumeric); if (queryTree == NULL) { // invalid syntax j_env->CallVoidMethod(result_ptr, MID_setSyntaxError, true); cerr << "MGPPSearchWrapperImpl: invalid query syntax!!\n"; return; } // print the query PrintNode (cout, queryTree); // finally, do the query MGQuery(*(data->indexData), *(data->queryInfo), queryTree, queryResult, data->level); delete queryTree; // convert queryResult to the java side version // use levels rather than docs of ExtQueryResult // CallVoidMethod(obj, method id, args to method) for (int i=0; iCallVoidMethod(result_ptr, MID_addDoc, doc, rank); exc = j_env->ExceptionOccurred(); if (exc) { j_env->ExceptionDescribe(); return; } } // actual num of docs jlong total = queryResult.actualNumDocs; j_env->CallVoidMethod(result_ptr, MID_setTotalDocs, total); exc = j_env->ExceptionOccurred(); if (exc) { j_env->ExceptionDescribe(); return; } // the terms for (int j=0; jNewStringUTF(GetCStr(tf.term)); jstring tag = j_env->NewStringUTF(GetCStr(tf.tag)); jint stem = tf.stemMethod; jlong match = tf.matchDocs; jlong freq = tf.termFreq; jobjectArray equivs=NULL; jstring empty = j_env->NewStringUTF(""); // the initial object to fill the array jint num_equivs = tf.equivTerms.size(); equivs = j_env->NewObjectArray(num_equivs, CID_String, empty); if (equivs==NULL) { cerr<<"couldn't create object array"<NewStringUTF(GetCStr(tf.equivTerms[k])); j_env->SetObjectArrayElement(equivs, k, equiv); } j_env->CallVoidMethod(result_ptr, MID_addTerm, term, tag, stem, match, freq, equivs); exc = j_env->ExceptionOccurred(); if (exc) { j_env->ExceptionDescribe(); return; } } } } JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_setStem (JNIEnv *j_env, jobject j_obj, jboolean j_on) { MGPPSearchData * data = (MGPPSearchData *)j_env->GetLongField(j_obj, FID_mgpp_data); if (j_on) { data->defaultStemMethod |= 2; } else { data->defaultStemMethod &= 0xd; } } JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_setAccentFold (JNIEnv *j_env, jobject j_obj, jboolean j_on) { MGPPSearchData * data = (MGPPSearchData *)j_env->GetLongField(j_obj, FID_mgpp_data); if (j_on) { data->defaultStemMethod |= 4; } else { data->defaultStemMethod &= 0xb; } } JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_setCase (JNIEnv *j_env, jobject j_obj, jboolean j_on) { MGPPSearchData * data = (MGPPSearchData *)j_env->GetLongField(j_obj, FID_mgpp_data); if (j_on) { data->defaultStemMethod |= 1; } else { data->defaultStemMethod &= 0xe; } } JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_setMaxDocs (JNIEnv *j_env, jobject j_obj, jint j_max) { MGPPSearchData * data = (MGPPSearchData *)j_env->GetLongField(j_obj, FID_mgpp_data); data->queryInfo->maxDocs=j_max; } JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_setMaxNumeric (JNIEnv *j_env, jobject j_obj, jint j_max) { MGPPSearchData * data = (MGPPSearchData *)j_env->GetLongField(j_obj, FID_mgpp_data); data->maxNumeric=j_max; } JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_setSortByRank (JNIEnv *j_env, jobject j_obj, jboolean j_on) { MGPPSearchData * data = (MGPPSearchData *)j_env->GetLongField(j_obj, FID_mgpp_data); data->queryInfo->sortByRank=j_on; } JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_setReturnTerms(JNIEnv *j_env, jobject j_obj, jboolean j_on) { MGPPSearchData * data = (MGPPSearchData *)j_env->GetLongField(j_obj, FID_mgpp_data); data->queryInfo->needTermFreqs = j_on; } JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_setQueryLevel(JNIEnv *j_env, jobject j_obj, jstring j_level){ MGPPSearchData * data = (MGPPSearchData *)j_env->GetLongField(j_obj, FID_mgpp_data); const char * level = j_env->GetStringUTFChars(j_level, NULL); if (level==NULL) { return; // exception already thrown } data->queryInfo->docLevel.clear(); SetCStr(data->queryInfo->docLevel, level); // release the java stuff j_env->ReleaseStringUTFChars(j_level, level); } JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_setReturnLevel(JNIEnv *j_env, jobject j_obj, jstring j_level){ // print to stderr start of setReturnLevel, print out FID.. // %ld or %x -> need to print out pointer - // Later consider field containing unsignedlong instead of long #ifdef _DEBUG cerr << "In MGPPSearchWrapperImpl.setReturnLevel()\n"; fprintf (stderr, "1. FID_mgpp_data at start is: %ld and as hex: %lX\n", FID_mgpp_data, FID_mgpp_data); #endif jlong data_ptr = j_env->GetLongField(j_obj, FID_mgpp_data); #ifdef _DEBUG fprintf (stderr, "1a. data_ptr at start is: %ld and as hex: %lX\n", data_ptr, data_ptr); #endif MGPPSearchData * data = (MGPPSearchData *)j_env->GetLongField(j_obj, FID_mgpp_data); #ifdef _DEBUG fprintf (stderr, "2a. FID_mgpp_data after data instantiation: %ld and as hex: %lX\n", FID_mgpp_data, FID_mgpp_data); fprintf (stderr, "2b. Pointer value of data upon inst: %ld and as hex: %lX\n", data, data); #endif // print out FID again.. as long decimal and as hex // %ld or %x -> need to print out pointer - // in the C code in the Setlong bit in this file // And the place on the java side, maybe in the configure // org.greenstone.gsdl3.service.GS2MGPPSearch.configure(Lorg/w3c/dom/Element;Lorg/w3c/dom/Element;)Z+18 // find out what value we're SETTING the value of the pointer to const char * level = j_env->GetStringUTFChars(j_level, NULL); if (level==NULL) { return; // exception already thrown } #ifdef _DEBUG fprintf (stderr, "3a. FID_mgpp_data after level: %ld and as hex: %lX\n", FID_mgpp_data, FID_mgpp_data); fprintf (stderr, "3b. Pointer value of data after level: %ld and as hex: %lX\n", data, data); #endif data->level.clear(); #ifdef _DEBUG fprintf (stderr, "4a. FID_mgpp_data after data->level.clear(): %ld and as hex: %lX\n", FID_mgpp_data, FID_mgpp_data); fprintf (stderr, "4b. Pointer value of data after level.clear(): %ld and as hex: %lX\n", data, data); #endif SetCStr(data->level, level); #ifdef _DEBUG fprintf (stderr, "5a. FID_mgpp_data after SetCStr on data: %ld and as hex: %lX\n", FID_mgpp_data, FID_mgpp_data); fprintf (stderr, "5b. Pointer value of data after SetCStr: %ld and as hex: %lX\n", data, data); #endif // release the java stuff j_env->ReleaseStringUTFChars(j_level, level); #ifdef _DEBUG fprintf (stderr, "5a. FID_mgpp_data at end of setReturnLevel: %ld and as hex: %lX\n", FID_mgpp_data, FID_mgpp_data); fprintf (stderr, "5b. Pointer value of data at end of SetReturnLevel: %ld and as hex: %lX\n", data, data); #endif } JNIEXPORT void JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_setMatchMode (JNIEnv *j_env, jobject j_obj, jint j_mode){ MGPPSearchData * data = (MGPPSearchData *)j_env->GetLongField(j_obj, FID_mgpp_data); data->defaultBoolCombine=j_mode; } JNIEXPORT jstring JNICALL Java_org_greenstone_mgpp_MGPPSearchWrapper_getQueryParams (JNIEnv *j_env, jobject j_obj){ MGPPSearchData * data = (MGPPSearchData *)j_env->GetLongField(j_obj, FID_mgpp_data); // print the data to a stringstream, then convert to char*, then to //jstring stringstream output; output << "Query params:"<indexData->basePath<<"/"<indexData->filename<queryInfo->docLevel)<level)<defaultStemMethod&1)<defaultStemMethod&2)<queryInfo->sortByRank<defaultBoolCombine==1?"all":"some")<queryInfo->maxDocs<NewStringUTF(result); delete (char *)result; return j_result; }