]> gitweb.fperrin.net Git - Dictionary.git/blob - src/com/hughes/android/dictionary/Dictionary.java
a80c87469c7dc7c2acb2958c48d630ca4f7e1e22
[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   static final RAFSerializer<Entry> ENTRY_SERIALIZER = new RAFSerializableSerializer<Entry>(\r
20       Entry.RAF_FACTORY);\r
21   static final RAFSerializer<Row> ROW_SERIALIZER = new RAFSerializableSerializer<Row>(\r
22       Row.RAF_FACTORY);\r
23   static final RAFSerializer<IndexEntry> INDEX_ENTRY_SERIALIZER = new RAFSerializableSerializer<IndexEntry>(\r
24       IndexEntry.RAF_FACTORY);\r
25 \r
26   final List<Entry> entries;\r
27   final LanguageData[] languageDatas = new LanguageData[2];\r
28 \r
29   public Dictionary(final Language language0, final Language language1) {\r
30     languageDatas[0] = new LanguageData(this, language0, Entry.LANG1);\r
31     languageDatas[1] = new LanguageData(this, language1, Entry.LANG2);\r
32     entries = new ArrayList<Entry>();\r
33   }\r
34 \r
35   public Dictionary(final RandomAccessFile raf) throws IOException {\r
36     entries = CachingList.create(FileList.create(raf, ENTRY_SERIALIZER, raf\r
37         .getFilePointer()), 10000);\r
38     languageDatas[0] = new LanguageData(this, raf, Entry.LANG1);\r
39     languageDatas[1] = new LanguageData(this, raf, Entry.LANG2);\r
40   }\r
41 \r
42   public void write(RandomAccessFile raf) throws IOException {\r
43     FileList.write(raf, entries, ENTRY_SERIALIZER);\r
44     languageDatas[0].write(raf);\r
45     languageDatas[1].write(raf);\r
46   }\r
47 \r
48   final class LanguageData implements RAFSerializable<LanguageData> {\r
49     final Dictionary dictionary;\r
50     final Language language;\r
51     final byte lang;\r
52     final List<Row> rows;\r
53     final List<IndexEntry> sortedIndex;\r
54 \r
55     LanguageData(final Dictionary dictionary, final Language language, final byte lang) {\r
56       this.dictionary = dictionary;\r
57       this.language = language;\r
58       this.lang = lang;\r
59       rows = new ArrayList<Row>();\r
60       sortedIndex = new ArrayList<IndexEntry>();\r
61     }\r
62 \r
63     LanguageData(final Dictionary dictionary, final RandomAccessFile raf, final byte lang) throws IOException {\r
64       this.dictionary = dictionary;\r
65       language = Language.lookup(raf.readUTF());\r
66       if (language == null) {\r
67         throw new RuntimeException("Unknown language.");\r
68       }\r
69       this.lang = lang;\r
70       rows = CachingList.create(UniformFileList.create(raf, ROW_SERIALIZER, raf\r
71           .getFilePointer()), 10000);\r
72       sortedIndex = CachingList.create(FileList.create(raf,\r
73           INDEX_ENTRY_SERIALIZER, raf.getFilePointer()), 10000);\r
74     }\r
75 \r
76     public void write(final RandomAccessFile raf) throws IOException {\r
77       raf.writeUTF(language.symbol);\r
78       UniformFileList.write(raf, rows, ROW_SERIALIZER, 4);\r
79       FileList.write(raf, sortedIndex, INDEX_ENTRY_SERIALIZER);\r
80     }\r
81 \r
82     String rowToString(final Row row) {\r
83       return row.isToken() ? sortedIndex.get(row.getIndex()).word : entries\r
84           .get(row.getIndex()).toString();\r
85     }\r
86 \r
87     int lookup(String word, final AtomicBoolean interrupted) {\r
88       word = word.toLowerCase();\r
89 \r
90       int start = 0;\r
91       int end = sortedIndex.size();\r
92       while (start < end) {\r
93         final int mid = (start + end) / 2;\r
94         if (interrupted.get()) {\r
95           return mid;\r
96         }\r
97         final IndexEntry midEntry = sortedIndex.get(mid);\r
98 \r
99         final int comp = language.tokenComparator.compare(word, midEntry.word.toLowerCase());\r
100         if (comp == 0) {\r
101           int result = mid;\r
102           while (result > 0 && language.tokenComparator.compare(word, sortedIndex.get(result - 1).word.toLowerCase()) == 0) {\r
103             --result;\r
104             if (interrupted.get()) {\r
105               return result;\r
106             }\r
107           }\r
108           return result;\r
109         } else if (comp < 0) {\r
110           end = mid;\r
111         } else {\r
112           start = mid + 1;\r
113         }\r
114       }\r
115       return Math.min(sortedIndex.size() - 1, start);\r
116     }\r
117 \r
118     public IndexEntry getIndexEntryForRow(final int rowIndex) {\r
119       // TODO: this kinda blows.\r
120       int r = rowIndex;\r
121       Row row;\r
122       while (true) {\r
123         row = rows.get(r); \r
124         if (row.isToken() || row.indexEntry != null) {\r
125           break;\r
126         }\r
127         --r;\r
128       }\r
129       final IndexEntry indexEntry = row.isToken() ? sortedIndex.get(row.getIndex()) : row.indexEntry;\r
130       for (; r <= rowIndex; ++r) {\r
131         rows.get(r).indexEntry = indexEntry;\r
132       }\r
133       assert false && rows.get(indexEntry.startRow).isToken();\r
134       return indexEntry;\r
135     }\r
136   }\r
137 \r
138   public static final class Row implements RAFSerializable<Row> {\r
139     final int index;\r
140 \r
141     IndexEntry indexEntry = null;\r
142 \r
143     public Row(final int index) {\r
144       this.index = index;\r
145     }\r
146 \r
147     static final RAFFactory<Row> RAF_FACTORY = new RAFFactory<Row>() {\r
148       public Row create(RandomAccessFile raf) throws IOException {\r
149         return new Row(raf.readInt());\r
150       }\r
151     };\r
152 \r
153     public void write(RandomAccessFile raf) throws IOException {\r
154       raf.writeInt(index);\r
155     }\r
156 \r
157     boolean isToken() {\r
158       return index < 0;\r
159     }\r
160 \r
161     public int getIndex() {\r
162       if (index >= 0) {\r
163         return index;\r
164       }\r
165       return -index - 1;\r
166     }\r
167   }\r
168 \r
169   public static final class IndexEntry implements RAFSerializable<IndexEntry> {\r
170     final String word;\r
171     final int startRow;\r
172 \r
173     public IndexEntry(final String word, final int startRow) {\r
174       this.word = word;\r
175       this.startRow = startRow;\r
176     }\r
177 \r
178     static final RAFFactory<IndexEntry> RAF_FACTORY = new RAFFactory<IndexEntry>() {\r
179       public IndexEntry create(RandomAccessFile raf) throws IOException {\r
180         final String word = raf.readUTF();\r
181         final int startRow = raf.readInt();\r
182         return new IndexEntry(word, startRow);\r
183       }\r
184     };\r
185 \r
186     public void write(final RandomAccessFile raf) throws IOException {\r
187       raf.writeUTF(word);\r
188       raf.writeInt(startRow);\r
189     }\r
190 \r
191     @Override\r
192     public String toString() {\r
193       return word + "@" + startRow;\r
194     }\r
195 \r
196   }\r
197 \r
198 }\r