文件
crab-input-rtos/android/keyboard/src/main/java/com/sydxky/crabinput/Lexicon.java
T

89 行
5.3 KiB
Java

package com.sydxky.crabinput;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/** Full upstream dictionary, offline. Called only on the service's single worker. */
public final class Lexicon implements AutoCloseable {
public static final String[] GROUP14={"qw","er","ty","ui","op","as","df","gh","jk","l","zx","cv","bn","m"};
public static final String[] GROUP9={"abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
private final Context context;
private SQLiteDatabase db;
public Lexicon(Context context) { this.context=context.getApplicationContext(); }
public static String encode(String text,String mode) {
String[] groups=mode.equals("14")?GROUP14:GROUP9;String tokens=mode.equals("14")?"0123456789abcd":"23456789";
StringBuilder result=new StringBuilder();
for(char ch:text.toLowerCase(java.util.Locale.ROOT).toCharArray()) {
int index=-1;for(int i=0;i<groups.length;i++)if(groups[i].indexOf(ch)>=0){index=i;break;}
if(index<0)return "";result.append(tokens.charAt(index));
}
return result.toString();
}
private void open() throws Exception {
if(db!=null)return;
File directory=context.getNoBackupFilesDir();File target=new File(directory,"crab-v5-2.db");
if(!target.isFile()) {
File temporary=new File(directory,"crab-v5-2.tmp");
try(InputStream input=context.getAssets().open("dictionary.db");FileOutputStream output=new FileOutputStream(temporary)) {
byte[] buffer=new byte[32768];int length;while((length=input.read(buffer))!=-1)output.write(buffer,0,length);output.getFD().sync();
}
if(!temporary.renameTo(target))throw new java.io.IOException("Dictionary installation failed");
}
db=SQLiteDatabase.openDatabase(target.getPath(),null,SQLiteDatabase.OPEN_READONLY|SQLiteDatabase.NO_LOCALIZED_COLLATORS);
}
public List<Candidate> lookup(String input,String mode) throws Exception {
return lookup(input,mode,"");
}
public List<Candidate> predict(String context) throws Exception {
List<Candidate> result=new ArrayList<>();
java.util.regex.Matcher match=java.util.regex.Pattern.compile("[\\u3400-\\u9fff]{1,4}$").matcher(context==null?"":context);
if(!match.find())return result;
open();String tail=match.group();Set<String> seen=new HashSet<>();
for(int length=tail.length();length>0&&result.size()<8;length--){
try(Cursor cursor=db.rawQuery("SELECT word,frequency FROM associations WHERE prefix=? ORDER BY frequency DESC,word LIMIT 8",new String[]{tail.substring(tail.length()-length)})){
while(cursor.moveToNext()&&result.size()<8){String word=cursor.getString(0);if(seen.add(word))result.add(new Candidate(word,0,cursor.getInt(1),false));}
}
}
return result;
}
public List<Candidate> lookup(String input,String mode,String context) throws Exception {
List<Candidate> result=new ArrayList<>();
String pattern=mode.equals("14")?"[0-9a-d]{1,32}":mode.equals("t9")?"[2-9]{1,32}":"[a-z]{1,32}";
if(!input.matches(pattern))return result;open();
String column=mode.equals("14")?"code14":mode.equals("t9")?"code9":"pinyin";
StringBuilder marks=new StringBuilder();String[] prefixes=new String[input.length()];
for(int i=0;i<input.length();i++){if(i>0)marks.append(',');marks.append('?');prefixes[i]=input.substring(0,i+1);}
String order=mode.equals("py")?"length(e.pinyin) DESC,e.pinyin,w.ordinal":"length(e.pinyin) DESC,w.frequency DESC,e.pinyin,w.ordinal";
Set<String> seen=new HashSet<>();
try(Cursor cursor=db.rawQuery("SELECT w.word,length(e.pinyin),w.frequency FROM entries e JOIN words w ON w.entry=e.id WHERE e."+column+" IN ("+marks+") ORDER BY "+order,prefixes)) {
while(cursor.moveToNext()) {
if(Thread.currentThread().isInterrupted())return new ArrayList<>();
String word=cursor.getString(0);int consume=cursor.getInt(1);
if(seen.add(word+"@"+consume))result.add(new Candidate(word,consume,cursor.getInt(2),false));
}
}
if(input.length()>=2){
String sql="SELECT w.word,w.frequency FROM entries e JOIN words w ON w.entry=e.id WHERE e."+column+">? AND e."+column+"<? AND length(e.pinyin)<=? ORDER BY w.frequency DESC,length(e.pinyin),e.pinyin,w.ordinal LIMIT 128";
try(Cursor cursor=db.rawQuery(sql,new String[]{input,input+'\uffff',Integer.toString(input.length()+4)})){
int added=0;
while(cursor.moveToNext()&&added<12){
if(Thread.currentThread().isInterrupted())return new ArrayList<>();
String word=cursor.getString(0);
if(seen.add(word+"@"+input.length())){result.add(new Candidate(word,input.length(),cursor.getInt(1),true));added++;}
}
}
}
CandidateRanking.sort(result,input.length(),predict(context));
return result;
}
@Override public void close() { if(db!=null){db.close();db=null;} }
}