]> gitweb.fperrin.net Git - Dictionary.git/blob - src/com/hughes/android/dictionary/Dictionary.java
dfca94617d2327b67a307c48436ffada79dbd9af
[Dictionary.git] / src / com / hughes / android / dictionary / Dictionary.java
1 package com.hughes.android.dictionary;\r
2 \r
3 import java.io.IOException;\r
4 import java.io.RandomAccessFile;\r
5 import java.util.ArrayList;\r
6 import java.util.List;\r
7 import java.util.concurrent.atomic.AtomicBoolean;\r
8 \r
9 import com.hughes.util.CachingList;\r
10 import com.hughes.util.raf.FileList;\r
11 import com.hughes.util.raf.RAFFactory;\r
12 import com.hughes.util.raf.RAFSerializable;\r
13 import com.hughes.util.raf.RAFSerializableSerializer;\r
14 import com.hughes.util.raf.RAFSerializer;\r
15 import com.hughes.util.raf.UniformFileList;\r
16 \r
17 public final class Dictionary implements RAFSerializable<Dictionary> {\r
18   \r
19   private static final String VERSION_CODE = "DictionaryVersion=1.5";\r
20 \r
21   static final RAFSerializer<Entry> ENTRY_SERIALIZER = new RAFSerializableSerializer<Entry>(\r
22       Entry.RAF_FACTORY);\r
23   static final RAFSerializer<Row> ROW_SERIALIZER = new RAFSerializableSerializer<Row>(\r
24       Row.RAF_FACTORY);\r
25   static final RAFSerializer<IndexEntry> INDEX_ENTRY_SERIALIZER = new RAFSerializableSerializer<IndexEntry>(\r
26       IndexEntry.RAF_FACTORY);\r
27 \r
28   final String dictionaryInfo;\r
29   final List<Entry> entries;\r
30   final LanguageData[] languageDatas = new LanguageData[2];\r
31 \r
32   public Dictionary(final String dictionaryInfo, final Language language0, final Language language1) {\r
33     this.dictionaryInfo = dictionaryInfo;\r
34     languageDatas[0] = new LanguageData(this, language0, Entry.LANG1);\r
35     languageDatas[1] = new LanguageData(this, language1, Entry.LANG2);\r
36     entries = new ArrayList<Entry>();\r
37   }\r
38 \r
39   public Dictionary(final RandomAccessFile raf) throws IOException {\r
40     dictionaryInfo = raf.readUTF();\r
41     entries = CachingList.create(FileList.create(raf, ENTRY_SERIALIZER, raf\r
42         .getFilePointer()), 10000);\r
43     languageDatas[0] = new LanguageData(this, raf, Entry.LANG1);\r
44     languageDatas[1] = new LanguageData(this, raf, Entry.LANG2);\r
45     if (!VERSION_CODE.equals(raf.readUTF())) {\r
46       throw new IOException("Invalid dictionary version, expected: " + VERSION_CODE);\r
47     }\r
48   }\r
49 \r
50   public void write(RandomAccessFile raf) throws IOException {\r
51     raf.writeUTF(dictionaryInfo);\r
52     FileList.write(raf, entries, ENTRY_SERIALIZER);\r
53     languageDatas[0].write(raf);\r
54     languageDatas[1].write(raf);\r
55     raf.writeUTF(VERSION_CODE);\r
56   }\r
57 \r
58   final class LanguageData implements RAFSerializable<LanguageData> {\r
59     final Dictionary dictionary;\r
60     final Language language;\r
61     final byte lang;\r
62     final List<Row> rows;\r
63     final List<IndexEntry> sortedIndex;\r
64 \r
65     LanguageData(final Dictionary dictionary, final Language language, final byte lang) {\r
66       this.dictionary = dictionary;\r
67       this.language = language;\r
68       this.lang = lang;\r
69       rows = new ArrayList<Row>();\r
70       sortedIndex = new ArrayList<IndexEntry>();\r
71     }\r
72 \r
73     LanguageData(final Dictionary dictionary, final RandomAccessFile raf, final byte lang) throws IOException {\r
74       this.dictionary = dictionary;\r
75       language = Language.lookup(raf.readUTF());\r
76       if (language == null) {\r
77         throw new RuntimeException("Unknown language.");\r
78       }\r
79       this.lang = lang;\r
80       rows = CachingList.create(UniformFileList.create(raf, ROW_SERIALIZER, raf\r
81           .getFilePointer()), 10000);\r
82       sortedIndex = CachingList.create(FileList.create(raf,\r
83           INDEX_ENTRY_SERIALIZER, raf.getFilePointer()), 10000);\r
84     }\r
85 \r
86     public void write(final RandomAccessFile raf) throws IOException {\r
87       raf.writeUTF(language.symbol);\r
88       UniformFileList.write(raf, rows, ROW_SERIALIZER, 4);\r
89       FileList.write(raf, sortedIndex, INDEX_ENTRY_SERIALIZER);\r
90     }\r
91 \r
92     String rowToString(final Row row, final boolean onlyFirstSubentry) {\r
93       return row.isToken() ? sortedIndex.get(row.getIndex()).word : entries\r
94           .get(row.getIndex()).getRawText(onlyFirstSubentry);\r
95     }\r
96 \r
97     int lookup(String word, final AtomicBoolean interrupted) {\r
98       word = word.toLowerCase();\r
99 \r
100       int start = 0;\r
101       int end = sortedIndex.size();\r
102       while (start < end) {\r
103         final int mid = (start + end) / 2;\r
104         if (interrupted.get()) {\r
105           return mid;\r
106         }\r
107         final IndexEntry midEntry = sortedIndex.get(mid);\r
108         if (midEntry.word.equals("pre-print")) {\r
109           System.out.println();\r
110         }\r
111 \r
112         final int comp = language.sortComparator.compare(word, midEntry.word.toLowerCase());\r
113         if (comp == 0) {\r
114           int result = mid;\r
115           while (result > 0 && language.findComparator.compare(word, sortedIndex.get(result - 1).word.toLowerCase()) == 0) {\r
116             --result;\r
117             if (interrupted.get()) {\r
118               return result;\r
119             }\r
120           }\r
121           return result;\r
122         } else if (comp < 0) {\r
123 //          Log.d("THAD", "Upper bound: " + midEntry);\r
124           end = mid;\r
125         } else {\r
126 //          Log.d("THAD", "Lower bound: " + midEntry);\r
127           start = mid + 1;\r
128         }\r
129       }\r
130       return Math.min(sortedIndex.size() - 1, start);\r
131     }\r
132     \r
133     public int getPrevTokenRow(final int rowIndex) {\r
134       final IndexEntry indexEntry = getIndexEntryForRow(rowIndex);\r
135       final Row tokenRow = rows.get(indexEntry.startRow);\r
136       assert tokenRow.isToken();\r
137       final int prevTokenIndex = tokenRow.getIndex() - 1;\r
138       if (indexEntry.startRow == rowIndex && prevTokenIndex >= 0) {\r
139         return sortedIndex.get(prevTokenIndex).startRow;\r
140       }\r
141       return indexEntry.startRow;\r
142     }\r
143 \r
144     public int getNextTokenRow(final int rowIndex) {\r
145       final IndexEntry indexEntry = getIndexEntryForRow(rowIndex);\r
146       final Row tokenRow = rows.get(indexEntry.startRow);\r
147       assert tokenRow.isToken();\r
148       final int nextTokenIndex = tokenRow.getIndex() + 1;\r
149       if (nextTokenIndex < sortedIndex.size()) {\r
150         return sortedIndex.get(nextTokenIndex).startRow;\r
151       }\r
152       return rows.size() - 1;\r
153     }\r
154 \r
155     public IndexEntry getIndexEntryForRow(final int rowIndex) {\r
156       // TODO: this kinda blows.\r
157       int r = rowIndex;\r
158       Row row;\r
159       while (true) {\r
160         row = rows.get(r); \r
161         if (row.isToken() || row.indexEntry != null) {\r
162           break;\r
163         }\r
164         --r;\r
165       }\r
166       final IndexEntry indexEntry = row.isToken() ? sortedIndex.get(row.getIndex()) : row.indexEntry;\r
167       for (; r <= rowIndex; ++r) {\r
168         rows.get(r).indexEntry = indexEntry;\r
169       }\r
170       assert rows.get(indexEntry.startRow).isToken();\r
171       return indexEntry;\r
172     }\r
173   }\r
174 \r
175   public static final class Row implements RAFSerializable<Row> {\r
176     final int index;\r
177 \r
178     IndexEntry indexEntry = null;\r
179 \r
180     public Row(final int index) {\r
181       this.index = index;\r
182     }\r
183 \r
184     static final RAFFactory<Row> RAF_FACTORY = new RAFFactory<Row>() {\r
185       public Row create(RandomAccessFile raf) throws IOException {\r
186         return new Row(raf.readInt());\r
187       }\r
188     };\r
189 \r
190     public void write(RandomAccessFile raf) throws IOException {\r
191       raf.writeInt(index);\r
192     }\r
193 \r
194     boolean isToken() {\r
195       return index < 0;\r
196     }\r
197 \r
198     public int getIndex() {\r
199       if (index >= 0) {\r
200         return index;\r
201       }\r
202       return -index - 1;\r
203     }\r
204   }\r
205 \r
206   public static final class IndexEntry implements RAFSerializable<IndexEntry> {\r
207     final String word;\r
208     final int startRow;\r
209 \r
210     public IndexEntry(final String word, final int startRow) {\r
211       this.word = word;\r
212       this.startRow = startRow;\r
213     }\r
214 \r
215     static final RAFFactory<IndexEntry> RAF_FACTORY = new RAFFactory<IndexEntry>() {\r
216       public IndexEntry create(RandomAccessFile raf) throws IOException {\r
217         final String word = raf.readUTF();\r
218         final int startRow = raf.readInt();\r
219         return new IndexEntry(word, startRow);\r
220       }\r
221     };\r
222 \r
223     public void write(final RandomAccessFile raf) throws IOException {\r
224       raf.writeUTF(word);\r
225       raf.writeInt(startRow);\r
226     }\r
227 \r
228     @Override\r
229     public String toString() {\r
230       return word + "@" + startRow;\r
231     }\r
232 \r
233   }\r
234 \r
235 }\r