/*
** Copyright 2010, The LimeIME Open Source Project
**
** Project Url: http://code.google.com/p/limeime/
** http://android.toload.net/
**
** 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package net.toload.main.hd;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;
import net.toload.main.hd.R;
import net.toload.main.hd.global.ImObj;
import net.toload.main.hd.global.KeyboardObj;
import net.toload.main.hd.global.LIME;
import net.toload.main.hd.global.LIMEPreferenceManager;
import net.toload.main.hd.global.LIMEUtilities;
import net.toload.main.hd.global.Mapping;
import net.toload.main.hd.limedb.LimeDB;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.os.RemoteException;
import android.util.Log;
import android.util.Pair;
public class SearchServer {
private final boolean DEBUG = false;
private final String TAG = "LIME.SearchServer";
private static LimeDB dbadapter = null; //Jeremy '12,5,1 shared single LIMEDB object
//Jeremy '12,4,6 Combine updatedb and quierydb into db,
//Jeremy '12,4,7 move db open/clsoe back to LimeDB
// since query always following with userdict and related learning and dual db connections cause exceptions.
//private SQLiteDatabase db = null;
//private LimeHanConverter hanConverter = null;
//private static LinkedList<Mapping> diclist = null;
private static List<Mapping> scorelist = null;
private static List<List<Mapping>> LDPhraseListArray = null;
private static List<Mapping> LDPhraseList = null;
private static StringBuffer selectedText = new StringBuffer();
private static String tablename = "";
//private NotificationManager notificationMgr;
private LIMEPreferenceManager mLIMEPref;
//private static SearchServiceImpl obj = null;
//private static int recAmount = 0;
private static boolean isPhysicalKeyboardPressed; // Sync to LIMEService and LIMEDB
//Jeremy '11,6,10
private static boolean hasNumberMapping;
private static boolean hasSymbolMapping;
//private static List<Mapping> preresultlist = null;
//private static String precode = null;
//Jeremy '11,6,6
private HashMap<String,String> imKeysMap = new HashMap<String,String>();
private HashMap<String,String> selKeyMap = new HashMap<String,String>();
//private HashMap<String,String> endKeyMap = new HashMap<String,String>();
private static ConcurrentHashMap<String, Pair<List<Mapping>,List<Mapping>>> cache = null;
private static ConcurrentHashMap<String, List<Mapping>> engcache = null;
private static ConcurrentHashMap<String, String> keynamecache = null;
/** Store the mapping of typing code and mapped code from query on db Jeremy '12,6,5 */
private static ConcurrentHashMap<String, List<String>> coderemapcache = null;
private Context mContext = null;
private static List<Pair<Integer, Integer>> codeLenthMap = new LinkedList<Pair<Integer, Integer>>();
//public class SearchServiceImpl extends ISearchService.Stub {
//Context ctx = null;
/*
SearchServiceImpl(Context ctx) {
this.ctx = ctx;
mLIMEPref = new LIMEPreferenceManager(ctx);
loadDBAdapter();
}
*/
public SearchServer(Context context) {
this.mContext = context;
mLIMEPref = new LIMEPreferenceManager(mContext.getApplicationContext());
if(dbadapter == null) dbadapter = new LimeDB(mContext);
initialCache();
}
public void setSelectedText(String text){
selectedText = new StringBuffer();
selectedText.append(text);
}
public String getSelectedText(){
if(selectedText != null){
return selectedText.toString().trim();
}else{
return "";
}
}
public String hanConvert(String input){
return dbadapter.hanConvert(input, mLIMEPref.getHanCovertOption());
}
public String getTablename(){
return tablename;
}
public void setTablename(String table, boolean numberMapping, boolean symbolMapping){
if(DEBUG)
Log.i(TAG,"SearchService.setTablename()");
dbadapter.setTablename(table);
tablename = table;
hasNumberMapping = numberMapping;
hasSymbolMapping = symbolMapping;
}
//Deprecated by Jeremy '12,4,7
/*
@Deprecated
private void openLimeDatabase()
{
boolean reload = mLIMEPref.getParameterBoolean("reload_database", false);
if(DEBUG) {
Log.i(TAG,"SearchService:openLimeDatabase(), reload = " + reload );
if(db != null)
Log.i(TAG, "db.isOpen()" + db.isOpen());
}
try{
if(reload && db != null && db.isOpen()){
mLIMEPref.setParameter("reload_database", false);
db.close();
}
//if(reload && db != null && db.isOpen()){
// mLIMEPref.setParameter("reload_database", false);
// db.close();
//}
}catch(Exception e){
}
//if(db == null || !db.isOpen()){
// db = getSqliteDb();
// initialCache();
//}
if(db == null || !db.isOpen()){
db = dbadapter.getSqliteDb(false);
initialCache();
}
}
*/
/*
private void loadDBAdapter()
{
if(DEBUG)
Log.i(TAG,"SearchService:loadDBAdapter()");
//if(dbadapter == null){
dbadapter = new LimeDB(ctx);
}
}
*/
//Modified by Jeremy '10,3 ,12 for more specific related word
//-----------------------------------------------------------
public List<Mapping> queryUserDic(String word, boolean getAllRecords) throws RemoteException {
List<Mapping> result = dbadapter.queryUserDict(word, getAllRecords);
return result;
}
//-----------------------------------------------------------
public Cursor getDictionaryAll(){
return dbadapter.getDictionaryAll();
}
//Add by jeremy '10, 4,1
public void rQuery(final String word) throws RemoteException {
Thread queryThread = new Thread(){
public void run() {
String result = dbadapter.getRMappingInConvertedKeynameString(word);
if(result!=null && !result.equals("")){
//displayNotificationMessage(result);
LIMEUtilities.showNotification(
mContext, true, R.drawable.icon, mContext.getText(R.string.ime_setting), result, new Intent(mContext, LIMEMenu.class));
}
}
};
queryThread.start();
}
private String cacheKey(String code){
String key ="";
//Jeremy '11,6,17 Seperate physical keyboard cache with keybaordtype
if(isPhysicalKeyboardPressed){
if(tablename.equals("phonetic")){
key = mLIMEPref.getPhysicalKeyboardType()+dbadapter.getTablename()
+ mLIMEPref.getPhoneticKeyboardType()+code;
}else{
key = mLIMEPref.getPhysicalKeyboardType()+dbadapter.getTablename()+code;
}
}else{
if(tablename.equals("phonetic"))
key = dbadapter.getTablename()+ mLIMEPref.getPhoneticKeyboardType()+code;
else
key = dbadapter.getTablename()+code;
}
return key;
}
public List<Mapping> query(String code, boolean softkeyboard, boolean getAllRecords) throws RemoteException {
if(DEBUG) Log.i(TAG, "query(): code="+code);
// Check if system need to reset cache
if(mLIMEPref.getResetCacheFlag(false)){
initialCache();
mLIMEPref.setResetCacheFlag(false);
}
codeLenthMap.clear();//Jeremy '12,6,2 reset the codeLengthMap
List<Mapping> result = new LinkedList<Mapping>();
if(code!=null) {
// clear mappingidx when user switching between softkeyboard and hard keyboard. Jeremy '11,6,11
if(isPhysicalKeyboardPressed == softkeyboard)
isPhysicalKeyboardPressed = !softkeyboard;
// Jeremy '11,9, 3 remove cached keyname when request full records
if(getAllRecords && keynamecache.get(cacheKey(code))!=null)
keynamecache.remove(cacheKey(code));
Mapping temp = new Mapping();
temp.setWord(code);
//code = code.toLowerCase(Locale.US); //Jeremy '12,4,1 moved to LimeDB.getMapping after remapping For XPERIA PRO BPMF
temp.setCode(code);
//result.add(temp); Jeremy '12,5,30 add later in the result buliding loop
int size = code.length();
boolean hasMore = false;
// 11'7,22 rewritten for 嚙踐�嚙賢�鞈g蕭�佇��批�頦蕭嚙賢�剛�豲
// 12,6,4 Jeremy. Ascending a ab abc... looking up db if the cache is not exist
for(int i =0; i<size; i++) {
String queryCode = code.substring(0,i+1);
String cacheKey = cacheKey(queryCode);
Pair<List<Mapping>,List<Mapping>> cacheTemp = cache.get(cacheKey);
if(DEBUG)
Log.i(TAG," query() check if cached exist on code = '" + queryCode + "'");
if(cacheTemp == null){
// 25/Jul/2011 by Art
// Just ignore error when something wrong with the result set
try{
cacheTemp = dbadapter.getMapping(queryCode, !isPhysicalKeyboardPressed, getAllRecords);
cache.put(cacheKey, cacheTemp);
//Jeremy '12,6,5 check if need to update code remap cache
if(cacheTemp!=null && cacheTemp.first!=null
&& cacheTemp.first.size()>0 && cacheTemp.first.get(0)!=null) {
String codeFromMapping = cacheTemp.first.get(0).getCode();
if(!queryCode.equals(codeFromMapping) ){
List<String> codeList = coderemapcache.get(codeFromMapping);
String key = cacheKey(codeFromMapping);
if(codeList == null){
List<String> newlist = new LinkedList<String>();
newlist.add(codeFromMapping); //put self in the list
newlist.add(queryCode);
coderemapcache.put(key, newlist);
if(DEBUG)
Log.i(TAG, "query() build new remap code = '"
+ codeFromMapping + "' to code = '" + queryCode +"'"
+ " coderemapcache.size()=" +coderemapcache.size());
}else{
codeList.add(queryCode);
coderemapcache.remove(key);
coderemapcache.put(key, codeList);
if(DEBUG)
Log.i(TAG, "query() codeFromMapping: add new remap code = '" + codeFromMapping + "' to code = '" + queryCode +"'");
}
}
}
}catch(SQLiteException ne){
ne.printStackTrace();
}catch(NullPointerException ne){
ne.printStackTrace();
}catch(Exception e){
e.printStackTrace();
}
}
}
// 11'7,22 rewritten for 嚙踐�嚙賢�鞈g蕭�佇��批�頦蕭嚙賢�剛�豲
// 12,6,4 Jeremy. Descending abc ab a... Build the result candidate list.
for(int i =0; i<size; i++) {
String cacheKey = cacheKey(code);
Pair<List<Mapping>,List<Mapping>> cacheTemp = cache.get(cacheKey);
if(cacheTemp != null){
List<Mapping> resultlist = cacheTemp.first;
List<Mapping> relatedtlist = cacheTemp.second;
if(getAllRecords &&
(resultlist.size()>1 &&
resultlist.get(resultlist.size()-1).getCode().equals("has_more_records")||
relatedtlist.size()>1&&
relatedtlist.get(relatedtlist.size()-1).getCode().equals("has_more_records") )){
try{
cacheTemp = dbadapter.getMapping(code, !isPhysicalKeyboardPressed, true);
cache.remove(cacheKey);
cache.put(cacheKey, cacheTemp);
}catch(Exception e){
e.printStackTrace();
}
}
}
if(cacheTemp != null){
List<Mapping> resultlist = cacheTemp.first;
List<Mapping> relatedtlist = cacheTemp.second;
if(DEBUG)
Log.i(TAG, "query() code=" + code + " resultlist.size()=" + resultlist.size()
+" relatedlist.size()=" + relatedtlist.size());
if(i==0) {//Jeremy add the mixed type English code in first loop
temp.setRelated(resultlist.size()==0); //Jeremy '12,5,31 setRelated true if the exact match code has zero result list size.
result.add(temp);
}
// Art '09.11.2011 ignore phonetic tone control
if(resultlist.size() > 0){
result.addAll(resultlist);
int rsize = result.size();
if(result.get(rsize-1).getCode().equals("has_more_records")){
result.remove(rsize-1);
hasMore = true;
if(DEBUG)
Log.i(TAG, "query() code=" + code + " resutl list added resultlist.size()="
+ resultlist.size());
}
}
if(relatedtlist.size()>0 && i==0 ){
result.addAll(relatedtlist);
int rsize = result.size();
if(result.get(rsize-1).getCode().equals("has_more_records")){
result.remove(rsize-1);
hasMore = true;
if(DEBUG)
Log.i(TAG, "query() code=" + code + " related list added relatedlist.size()="
+ relatedtlist.size());
}
}
}
codeLenthMap.add(new Pair<Integer, Integer>(code.length(), result.size())); //Jeremy 12,6,2 preserve the code length in each loop.
if(DEBUG)
Log.i(TAG,"query() codeLengthMap codelenth = " + code.length() + ", resultsize = " + result.size());
code= code.substring(0,code.length()-1);
}
if(DEBUG) Log.i(TAG, "query() code=" + code + " result.size()=" + result.size());
if(hasMore){
temp = new Mapping();
temp.setCode("has_more_records");
temp.setWord("...");
result.add(temp);
}
}
return result;
}
/**
* Get the real code length according to codeLenthMap
*/
int getRealCodeLength(int index){
if(DEBUG)
Log.i(TAG,"getRealCodeLength() index = " + index);
for(Pair<Integer, Integer> entry: codeLenthMap){
if(DEBUG)
Log.i(TAG,"getRealCodeLength() codelength = " + entry.first + ", resultsize = " + entry.second);
if(index < entry.second){
return entry.first;
}
}
return codeLenthMap.get(0).first ; // should not happen
}
/*
public void initial() throws RemoteException {
initialCache();
}
*/
/**
* This method is to initial/reset the cache of im.
*/
public void initialCache(){
cache = new ConcurrentHashMap<String, Pair<List<Mapping>,List<Mapping>>>(LIME.SEARCHSRV_RESET_CACHE_SIZE);
engcache = new ConcurrentHashMap<String, List<Mapping>>(LIME.SEARCHSRV_RESET_CACHE_SIZE);
keynamecache = new ConcurrentHashMap<String, String>(LIME.SEARCHSRV_RESET_CACHE_SIZE);
coderemapcache = new ConcurrentHashMap<String, List<String>>(LIME.SEARCHSRV_RESET_CACHE_SIZE);
}
/*public List<Mapping> sortArray(String precode, List<Mapping> src) {
// Modified by jeremy '10, 4, 5. Buf fix for 3row remap. code may not equal to precode.
if(src != null && src.size() > 1){
for (int i = 1; i < (src.size() - 1); i++) {
for (int j = i + 1; j < src.size(); j++) {
if (src.get(j).getScore() > src.get(i).getScore()) {
Mapping dummy = src.get(i);
if(!dummy.getCode().equals(precode) && !src.get(j).getCode().equals(precode)){
src.set(i, src.get(j));
src.set(j, dummy);
}
}
}
}
}
return src;
}*/
/*@Deprecated
public void addUserDict(String id, String code, String word,
String pword, int score, boolean isDictionary)
throws RemoteException {
if(diclist == null){diclist = new LinkedList<Mapping>();}
Mapping temp = new Mapping();
temp.setId(id);
temp.setCode(code);
temp.setWord(word);
temp.setPword(pword);
temp.setScore(score);
temp.setDictionary(isDictionary);
diclist.addLast(temp);
}*/
private void updateScoreCache(Mapping cachedMapping){
if(DEBUG) Log.i(TAG, "udpateScoreCache(): code=" + cachedMapping.getCode());
dbadapter.addScore(cachedMapping);
// Jeremy '11,7,29 update cached here
if (!cachedMapping.isDictionary()){
String code = cachedMapping.getCode().toLowerCase(Locale.US);
String cachekey = cacheKey(code);
Pair<List<Mapping>, List<Mapping>> cachedPair = cache.get(cachekey);
// null id denotes target is selected from the related list (not exact match)
if(cachedMapping.getId()==null
&& cachedPair!=null && cachedPair.second !=null && cachedPair.second.size()>0){
if(DEBUG) Log.i(TAG,"updateUserDict: updating related list");
if(cache.remove(cachekey)!=null){
Pair<List<Mapping>, List<Mapping>> newPair
= new Pair<List<Mapping>, List<Mapping>>(cachedPair.first, dbadapter.updateRelatedList(code)) ;
cache.put(cachekey, newPair);
}else{//Jeremy '12,6,5 code not in cahe do updateRelatedList and removed cached items of ramped codes.
dbadapter.updateRelatedList(code);
removeRemapedCodeCachedMappings(code);
}
// non null id denotes target is in exact match result list.
} else if(cachedMapping.getId()!=null && cachedPair!=null
&& cachedPair.first !=null && cachedPair.first.size()>0) {
boolean sort = true;
if(isPhysicalKeyboardPressed)
sort = mLIMEPref.getPhysicalKeyboardSortSuggestions();
else
sort = mLIMEPref.getSortSuggestions();
if(sort){ // Jeremy '12,5,22 do not update the order of exact match list if the sort option is off
List<Mapping> cachedList = cachedPair.first;
int size = cachedList.size();
if(DEBUG) Log.i(TAG,"updateUserDict(): cachedList.size:" + size);
// update exact match cache
for(int j=0; j< size; j++){
Mapping cm = cachedList.get(j);
if(DEBUG) Log.i(TAG,"updateUserDict(): cachedList at :" + j + ". score="+ cm.getScore());
if(cachedMapping.getId() == cm.getId()){
int score = cm.getScore() + 1 ;
if(DEBUG) Log.i(TAG,"updateUserDict(): cachedMapping found at :" + j +". new score=" +score );
cm.setScore(score);
if(j>0 && score > cachedList.get(j-1).getScore()){
cachedList.remove(j);
for(int k=0; k<j; k++){
if(cachedList.get(k).getScore() <= score){
cachedList.add(k,cm);
break;
}
}
}
break;
}
}
}
// Jeremy '11,7,31
// exact match score was changed, related list in similar codes should be rebuild
// (eg. d, de, and def for code, defg)
updateSimilarCodeRelatedList(code);
}else{//Jeremy '12,6,5 code not in cahe do updateRelatedList and removed cached items of ramped codes.
dbadapter.updateRelatedList(code);
removeRemapedCodeCachedMappings(code);
}
}
}
// '11,8,1 renamed from updateuserdict()
List<Mapping> scorelistSnapshot = null;
public void postFinishInput() throws RemoteException {
//if(dbadapter == null){dbadapter = new LimeDB(ctx);}
if(scorelistSnapshot==null) scorelistSnapshot = new LinkedList<Mapping>();
else scorelistSnapshot.clear();
if(DEBUG) Log.i(TAG,"postFinishInput(), creating offline updating thread");
// Jeremy '11,7,31 The updating process takes some time. Create a new thread to do this.
Thread UpadtingThread = new Thread(){
public void run() {
// for thread-safe operation, duplicate local copy of scorelist and LDphraselistarray
//List<Mapping> localScorelist = new LinkedList<Mapping>();
if(scorelist != null) {
scorelistSnapshot.addAll(scorelist);
scorelist.clear();
}
//Jeremy '11,7,28 combine to adduserdict and addscore
//Jeremy '11,6,12 do adduserdict and add score if diclist.size > 0 and only adduserdict if diclist.size >1
//Jeremy '11,6,11, always learn scores, but sorted according preference options
// Learn user dictionary (the consecutive two words as a userdict phrase).
learnUserDict(scorelistSnapshot);
ArrayList<List<Mapping>> localLDPhraseListArray = new ArrayList<List<Mapping>>();
if(LDPhraseListArray!=null){
localLDPhraseListArray.addAll(LDPhraseListArray);
LDPhraseListArray.clear();
}
// Learn LD Phrase
learnLDPhrase(localLDPhraseListArray);
}
};
UpadtingThread.start();
}
private void learnUserDict(List<Mapping> localScorelist){
if(localScorelist != null){
if(DEBUG)
Log.i(TAG,"learnUserDict(), localScorelist.size=" + localScorelist.size());
if(mLIMEPref.getLearnRelatedWord() && localScorelist.size() > 1){
for (int i = 0; i < localScorelist.size(); i++) {
Mapping unit = localScorelist.get(i);
if(unit == null){continue;}
if(i+1 <localScorelist.size()){
Mapping unit2 = localScorelist.get((i + 1));
if(unit2 == null){continue;}
if (//unit.getId()!=null
unit.getWord() != null && !unit.getWord().equals("")
&& !unit.getCode().equals(unit.getWord())//Jeremy '12,6,13 avoid learning mixed mode english
&& !unit2.getCode().equals(unit2.getWord())
//&& unit2.getId() !=null
&& unit2.getWord() != null && !unit2.getWord().equals("")
) {
int score = 0;
if(unit.getId()!=null && unit2.getId() !=null) //Jeremy '12,7,2 eliminate learing english words.
score = dbadapter.addOrUpdateUserdictRecord(unit.getWord(),unit2.getWord());
if(DEBUG)
Log.i(TAG, "learnUserDict(), the return score = " + score);
//Jeremy '12,6,7 learn LD phrase if the score of userdic is > 20
if( score >20 && mLIMEPref.getLearnPhrase()){
addLDPhrase(unit, false);
addLDPhrase(unit2, true);
}
}
}
}
}
}
}
/**
* Jeremy '12,6,9 Rewrited to support word with more than 1 characters
* @param localLDPhraseListArray
*/
private void learnLDPhrase(ArrayList<List<Mapping>> localLDPhraseListArray){
if(DEBUG)
Log.i(TAG,"learnLDPhrase()");
if(localLDPhraseListArray!=null && localLDPhraseListArray.size()>0){
if(DEBUG)
Log.i(TAG,"learnLDPhrase(): LDPhrase learning, arraysize =" + localLDPhraseListArray.size());
for(List<Mapping> phraselist : localLDPhraseListArray ){
if(DEBUG)
Log.i(TAG,"learnLDPhrase(): LDPhrase learning, current list size =" + phraselist.size());
if(phraselist.size()>0 && phraselist.size() <5){ //Jeremy '12,6,8 limit the phrase to have 4 chracters
String baseCode="", LDCode ="", QPCode ="", baseWord="";
Mapping unit1 = phraselist.get(0);
if(DEBUG)
Log.i(TAG,"learnLDPhrase(): unit1.getId() = " + unit1.getId()
+ ", unit1.getCode() =" + unit1.getCode()
+ ", unit1.getWord() =" + unit1.getWord());
if( unit1 == null || unit1.getWord().length()==0
|| unit1.getCode().equals(unit1.getWord())) //Jeremy '12,6,13 avoid learning mixed mode english
{break;}
baseCode = unit1.getCode();
baseWord = unit1.getWord();
if( baseWord.length()==1 ){
if(unit1.getId() == null //Jermy '12,6,7 break if id is null (selected from related list)
|| unit1.getCode() == null //Jermy '12,6,7 break if code is null (selected from userdict)
|| unit1.getCode().length()==0
|| unit1.isDictionary()){
List<Mapping> rMappingList = dbadapter.getRMapping(baseWord, tablename);
if(rMappingList.size()>0)
baseCode = rMappingList.get(0).getCode();
else
break; //look-up failed, abandon.
}
if(baseCode!=null && baseCode.length()>0)
QPCode += baseCode.substring(0,1);
else
break;//abandon the phrase learning process;
//if word length >0, lookup all codes and rebuild basecode and QPCode
}else if( baseWord.length() >1 && baseWord.length() < 5){
baseCode = "";
for(int i=0; i < baseWord.length(); i++){
String c = baseWord.substring(i,i+1);
List<Mapping> rMappingList = dbadapter.getRMapping(c, tablename);
if(rMappingList.size()>0){
baseCode += rMappingList.get(0).getCode();
QPCode += rMappingList.get(0).getCode().substring(0,1);
}else{
baseCode = ""; //r-lookup failed. abandon the phrase learning
break;
}
}
}
for (int i = 0; i < phraselist.size(); i++) {
if(i+1 <phraselist.size()){
Mapping unit2 = phraselist.get((i + 1));
if(unit2 == null || unit2.getWord().length()==0
|| unit2.getCode().equals(unit2.getWord())) //Jeremy '12,6,13 avoid learning mixed mode english
{break;}
String word2 = unit2.getWord();
String code2 = unit2.getCode();
baseWord += word2;
if( word2.length()==1 && baseWord.length() <5){ //limit the phrase size to 4
if(unit2.getId() == null //Jermy '12,6,7 break if id is null (selected from related list)
|| code2 == null //Jermy '12,6,7 break if code is null (selected from userdict)
|| code2.length()==0
|| unit2.isDictionary()){
List<Mapping> rMappingList = dbadapter.getRMapping(word2, tablename);
if(rMappingList.size()>0)
code2 = rMappingList.get(0).getCode();
else
break;
}
if(code2!=null && code2.length()>0){
baseCode+=code2;
QPCode += code2.substring(0,1);
}else
break; //abandon the phrase learning process;
//if word length >0, lookup all codes and rebuild basecode and QPCode
}else if( word2.length() > 1 && baseWord.length() < 5){
for(int j=0; j < word2.length(); j++){
String c = word2.substring(j,j+1);
List<Mapping> rMappingList = dbadapter.getRMapping(c, tablename);
if(rMappingList.size()>0){
baseCode += rMappingList.get(0).getCode();
QPCode += rMappingList.get(0).getCode().substring(0,1);
}else //r-lookup failed. abandon the phrase learning
break;
}
} else // abandon the learing process.
break;
if(DEBUG)
Log.i(TAG,"learnLDPhrase(): code1 = " + unit1.getCode()
+ ", code2 = '" + code2
+ "', word1 = " + unit1.getWord()
+ ", word2 = " + word2
+ ", basecode = '" + baseCode
+ "', baseWord = " + baseWord
+ ", QPcode = '" + QPCode
+ "'.");
if(i+1 == phraselist.size()-1){//only learn at the end of the phrase word '12,6,8
if(tablename.equals("phonetic")) {// remove tone symbol in phonetic table
LDCode = baseCode.replaceAll("[3467 ]", "").toLowerCase(Locale.US);
QPCode = QPCode.toLowerCase(Locale.US);
if(LDCode.length()>1){
dbadapter.addOrUpdateMappingRecord(LDCode, baseWord);
removeRemapedCodeCachedMappings(LDCode);
updateSimilarCodeRelatedList(LDCode);
}
if(QPCode.length()>1){
dbadapter.addOrUpdateMappingRecord(QPCode, baseWord);
removeRemapedCodeCachedMappings(QPCode);
updateSimilarCodeRelatedList(QPCode);
}
}else if(baseCode.length()>1){
baseCode = baseCode.toLowerCase(Locale.US);
dbadapter.addOrUpdateMappingRecord(baseCode, baseWord);
removeRemapedCodeCachedMappings(baseCode);
updateSimilarCodeRelatedList(baseCode);
}
if(DEBUG)
Log.i(TAG,"learnLDPhrase(): LDPhrase learning, baseCode = '" + baseCode
+ "', LDCode = '" + LDCode + "', QPCode=" + QPCode + "'."
+ ", baseWord" + baseWord );
}
}
}
}
}
}
}
/**
* @param code
*/
private void removeRemapedCodeCachedMappings(String code) {
if(DEBUG)
Log.i(TAG, "removeRemapedCodeCachedMappings() on code ='" + code + "' coderemapcache.size=" + coderemapcache.size());
List<String> codelist = coderemapcache.get(cacheKey(code));
if(codelist != null){
for(String entry: codelist){
if(DEBUG)
Log.i(TAG, "removeRemapedCodeCachedMappings() remove code= '" + entry + "' from cache.");
cache.remove(cacheKey(entry));
}
}else
cache.remove(cacheKey(code)); //Jeremy '12,6,6 no remap. remove the code mapping from cache.
}
private void updateSimilarCodeRelatedList(String code){
if(DEBUG)
Log.i(TAG, "updateSimilarCodeRelatedList(): code = '" + code + "'");
String cachekey = cacheKey(code);
Pair<List<Mapping>, List<Mapping>> cachedPair;// = cache.get(cachekey);
int len = code.length();
if(len > 5 ) len = 5; //Jeremy '12,6,7 change max backward level to 5.
for (int k = 1; k < len; k++) {
String key = code.substring(0, code.length() - k);
cachekey = cacheKey(key);
cachedPair = cache.get(cachekey);
if(DEBUG)
Log.i(TAG, "updateSimilarCodeRelatedList(): cachekey = '" + cachekey + "' cachedPair == null :" + (cachedPair ==null) );
if(cachedPair !=null){
if(DEBUG)
Log.i(TAG, "updateSimilarCodeRelatedList(): udpate to db cachekey = '" + cachekey + "'");
Pair<List<Mapping>, List<Mapping>> newPair
= new Pair<List<Mapping>, List<Mapping>>(cachedPair.first, dbadapter.updateRelatedList(key)) ;
cache.remove(cachekey);
cache.put(cachekey, newPair);
}else{
if(DEBUG)
Log.i(TAG, "updateSimilarCodeRelatedList(): code not in cache. udpate to db only on code = '" + key + "'");
dbadapter.updateRelatedList(key);
removeRemapedCodeCachedMappings(key);
}
}
}
public String keyToKeyname(String code){
//Jeremy '11,6,21 Build cache according using cachekey
String cacheKey = cacheKey(code);
String result = keynamecache.get(cacheKey);
if(result == null){
//loadDBAdapter(); openLimeDatabase();
result = dbadapter.keyToKeyname(code, tablename, true);
keynamecache.put(cacheKey, result);
}
return result;
}
/**
* Renamed from addUserDict and pass parameter with mapping directly Jeremy '12,6,5
* @param updateMapping
* @throws RemoteException
*/
public void addUserDictAndUpdateScore(Mapping updateMapping)
//String id, String code, String word,
//String pword, int score, boolean isDictionary)
throws RemoteException {
if(DEBUG) Log.i(TAG, "addUserDictAndUpdateScore()");
if(scorelist == null){scorelist = new ArrayList<Mapping>();}
// Temp final Mapping Object For updateMapping thread.
final Mapping updateMappingTemp = new Mapping(updateMapping);
// Jeremy '11,6,11. Always update score and sort according to preferences.
scorelist.add(updateMappingTemp);
Thread UpadtingThread = new Thread(){
public void run() {
updateScoreCache(updateMappingTemp);
}
};
UpadtingThread.start();
}
public void addLDPhrase(Mapping mapping,//String id, String code, String word, int score,
boolean ending){
if(LDPhraseListArray == null)
LDPhraseListArray = new ArrayList<List<Mapping>>();
if(LDPhraseList == null)
LDPhraseList = new LinkedList<Mapping>();
if(mapping != null){ // force interruped if mapping=null
LDPhraseList.add(mapping);
}
if(ending){
if(LDPhraseList.size()>1)
LDPhraseListArray.add(LDPhraseList);
LDPhraseList = new LinkedList<Mapping>();
}
if(DEBUG) Log.i(TAG,"addLDPhrase()"//+mapping.getCode() + ". id=" + mapping.getId()
+ ". engding:" + ending
+ ". LDPhraseListArray.size=" + LDPhraseListArray.size()
+ ". LDPhraseList.size=" + LDPhraseList.size());
}
public List<KeyboardObj> getKeyboardList() throws RemoteException {
//if(dbadapter == null){dbadapter = new LimeDB(ctx);}
List<KeyboardObj> result = dbadapter.getKeyboardList();
return result;
}
public List<ImObj> getImList() throws RemoteException {
//if(dbadapter == null){dbadapter = new LimeDB(ctx);}
List<ImObj> result = dbadapter.getImList();
return result;
}
public void clear() throws RemoteException {
if(scorelist != null){
scorelist.clear();
}
if(scorelist != null){
scorelist.clear();
}
if(cache != null){
cache.clear();
}
if(engcache != null){
engcache.clear();
}
if(keynamecache != null){
keynamecache.clear();
}
if(coderemapcache != null){
keynamecache.clear();
}
}
public List<Mapping> queryDictionary(String word) throws RemoteException {
List<Mapping> result = new LinkedList<Mapping>();
List<Mapping> cacheTemp = engcache.get(word);
if(cacheTemp != null){
result.addAll(cacheTemp);
}else{
//loadDBAdapter(); openLimeDatabase();
List<String> tempResult = dbadapter.queryDictionary(word);
for(String u: tempResult){
Mapping temp = new Mapping();
temp.setWord(u);
temp.setDictionary(true);
result.add(temp);
}
if(result.size() > 0){
engcache.put(word, result);
}
}
return result;
}
// public void close() throws RemoteException {
// if(dbadapter != null){
// try{
// dbadapter.close();
// }catch(Exception e){
// Log.i(TAG, "close(): Database Close error : "+e);
// }
// }
// }
public boolean isImKeys(char c) throws RemoteException {
if(imKeysMap.get(tablename)==null || imKeysMap.size()==0){
//if(dbadapter == null){dbadapter = new LimeDB(ctx);}
imKeysMap.put(tablename, dbadapter.getImInfo(tablename, "imkeys"));
}
String imkeys = imKeysMap.get(tablename);
if(!(imkeys==null || imkeys.equals(""))){
return (imkeys.indexOf(c)>=0);
}
return false;
}
/*
@Override
public int isSelkey(char c) throws RemoteException {
String selkey = "";
if(selKeyMap.get(tablename)==null || selKeyMap.size()==0){
//if(dbadapter == null){dbadapter = new LimeDB(ctx);}
selkey = dbadapter.getImInfo(tablename, "selkey");
if(selkey.equals("")) selkey = "'[]-\\^&*()";
selKeyMap.put(tablename, selkey);
}
selkey = selKeyMap.get(tablename);
if(!(selkey==null || selkey.equals(""))){
return selkey.indexOf(c);
}
return -1;
}
@Override
public boolean isEndkey(char c) throws RemoteException {
if(endKeyMap.get(tablename)==null || endKeyMap.size()==0){
//if(dbadapter == null){dbadapter = new LimeDB(ctx);}
endKeyMap.put(tablename, dbadapter.getImInfo(tablename, "endkey"));
}
String endkey = endKeyMap.get(tablename);
if(!(endkey==null || endkey.equals(""))){
return (endkey.indexOf(c)>=0);
}
return false;
}
*/
public String getSelkey() throws RemoteException {
if(DEBUG)
Log.i(TAG, "getSelkey():hasNumber:" + hasNumberMapping + "hasSymbol:" + hasSymbolMapping);
String selkey = "";
String table = tablename;
if(tablename.equals("phonetic")){
table = tablename + mLIMEPref.getPhoneticKeyboardType();
}
if(selKeyMap.get(table)==null || selKeyMap.size()==0){
//if(dbadapter == null){dbadapter = new LimeDB(ctx);}
selkey = dbadapter.getImInfo(tablename, "selkey");
if(DEBUG)
Log.i(TAG, "getSelkey():selkey from db:"+selkey);
boolean validSelkey = true;
if(selkey!=null && selkey.length()==10){
for(int i=0; i<10; i++){
if(Character.isLetter(selkey.charAt(i)) ||
(hasNumberMapping && Character.isDigit(selkey.charAt(i))))
validSelkey = false;
}
}else
validSelkey = false;
//Jeremy '11,6,19 Rewrote for IM has symbol mapping like ETEN
if(!validSelkey || tablename.equals("phonetic")){
if(hasNumberMapping && hasSymbolMapping){
if(tablename.equals("dayi")
||(tablename.equals("phonetic")&&mLIMEPref.getPhoneticKeyboardType().equals("standard"))){
selkey = "'[]-\\^&*()";
}else{
selkey = "!@#$%^&*()";
}
}else if (hasNumberMapping) {
selkey = "'[]-\\^&*()";
}else{
selkey = "1234567890";
}
}
if(DEBUG)
Log.i(TAG, "getSelkey():selkey:"+selkey);
selKeyMap.put(table, selkey);
}
return selKeyMap.get(table);
}
public int isSelkey(char c) throws RemoteException {
return getSelkey().indexOf(c);
}
/*
* This is the method to openDB from Service
*
@Deprecated
public SQLiteDatabase getSqliteDb(){
SQLiteDatabase db = null;
try{
String dbtarget = mLIMEPref.getParameterString("dbtarget");
db = SQLiteDatabase.openDatabase(getDBPath(dbtarget), null, SQLiteDatabase.OPEN_READONLY);
}catch(Exception e){return null;}
return db;
}
/*
* This is the method to openDB from Service
*
@Deprecated
public SQLiteDatabase getSqliteDbWritable(){
SQLiteDatabase db = null;
try{
String dbtarget = mLIMEPref.getParameterString("dbtarget");
db = SQLiteDatabase.openDatabase(getDBPath(dbtarget), null, SQLiteDatabase.OPEN_READWRITE);
}catch(Exception e){return null;}
return db;
}
@Deprecated
private String getDBPath(String dbTarget){
String dbLocationPrefix = (dbTarget.equals("sdcard"))
?LIME.DATABASE_DECOMPRESS_FOLDER_SDCARD:LIME.DATABASE_DECOMPRESS_FOLDER;
return dbLocationPrefix + File.separator + LIME.DATABASE_NAME;
}
*/
}
/*
@Override
public IBinder onBind(Intent arg0) {
if(obj == null){
obj = new SearchServiceImpl(this);
}
return obj;
}
/*
* (non-Javadoc)
*
* @see android.app.Service#onCreate()
*
@Override
public void onCreate() {
//notificationMgr =(NotificationManager)getSystemService(NOTIFICATION_SERVICE);
super.onCreate();
}
*/
/*
* (non-Javadoc)
*
* @see android.app.Service#onDestroy()
*
@Override
public void onDestroy() {
if(dbadapter != null){
dbadapter.close();
}
super.onDestroy();
}
/*
* (non-Javadoc)
*
* @see android.app.Service#onStart(android.content.Intent, int)
* Jeremy '12,4,23 onStart is deprecated since API 15. delete here since it do nothing but call the super.
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
}
/*
private void displayNotificationMessage(String message){
LIMEUtilities util = new LIMEUtilities();
util.showNotification(
this, true, R.drawable.icon, this.getText(R.string.ime_setting), message, new Intent(this, LIMEMenu.class));
/*
Notification notification = new Notification(R.drawable.icon, message, System.currentTimeMillis());
notification.flags |= Notification.FLAG_AUTO_CANCEL;
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,new Intent(this, LIMEMenu.class), 0);
notification.setLatestEventInfo(this, this.getText(R.string.ime_setting), message, contentIntent);
notificationMgr.notify(0, notification);
*
}
*/
//}