89 行
5.3 KiB
Java
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;} }
|
|
}
|