]> gitweb.fperrin.net Git - Dictionary.git/blob - src/com/hughes/android/dictionary/engine/Index.java
go
[Dictionary.git] / src / com / hughes / android / dictionary / engine / Index.java
1 /**
2  * 
3  */
4 package com.hughes.android.dictionary.engine;
5
6 import java.io.IOException;
7 import java.io.PrintStream;
8 import java.io.RandomAccessFile;
9 import java.util.ArrayList;
10 import java.util.Collection;
11 import java.util.List;
12 import java.util.concurrent.atomic.AtomicBoolean;
13
14 import com.hughes.util.CachingList;
15 import com.hughes.util.raf.RAFList;
16 import com.hughes.util.raf.RAFSerializable;
17 import com.hughes.util.raf.RAFSerializer;
18 import com.hughes.util.raf.UniformRAFList;
19 import com.ibm.icu.text.Collator;
20
21 public final class Index implements RAFSerializable<Index> {
22   
23   static final int CACHE_SIZE = 5000;
24   
25   final Dictionary dict;
26   
27   final String shortName;
28   final String longName;
29   
30   // persisted: tells how the entries are sorted.
31   final Language sortLanguage;
32     
33   // persisted
34   final List<IndexEntry> sortedIndexEntries;
35
36   // One big list!
37   // Various sub-types.
38   // persisted
39   final List<RowBase> rows;
40   
41   // --------------------------------------------------------------------------
42   
43   public Index(final Dictionary dict, final String shortName, final String longName, final Language sortLanguage) {
44     this.dict = dict;
45     this.shortName = shortName;
46     this.longName = longName;
47     this.sortLanguage = sortLanguage;
48     sortedIndexEntries = new ArrayList<IndexEntry>();
49     rows = new ArrayList<RowBase>();
50   }
51   
52   public Index(final Dictionary dict, final RandomAccessFile raf) throws IOException {
53     this.dict = dict;
54     shortName = raf.readUTF();
55     longName = raf.readUTF();
56     final String languageCode = raf.readUTF();
57     sortLanguage = Language.lookup(languageCode);
58     if (sortLanguage == null) {
59       throw new IOException("Unsupported language: " + languageCode);
60     }
61     sortedIndexEntries = CachingList.create(RAFList.create(raf, IndexEntry.SERIALIZER, raf.getFilePointer()), CACHE_SIZE);
62     rows = CachingList.create(UniformRAFList.create(raf, new RowBase.Serializer(this), raf.getFilePointer()), CACHE_SIZE);
63   }
64   
65   public void print(final PrintStream out) {
66     for (final RowBase row : rows) {
67       row.print(out);
68     }
69   }
70   
71   @Override
72   public void write(final RandomAccessFile raf) throws IOException {
73     raf.writeUTF(shortName);
74     raf.writeUTF(longName);
75     raf.writeUTF(sortLanguage.getSymbol());
76     RAFList.write(raf, sortedIndexEntries, IndexEntry.SERIALIZER);
77     UniformRAFList.write(raf, (Collection<RowBase>) rows, new RowBase.Serializer(this), 5);
78   }
79
80   
81   static final class IndexEntry implements RAFSerializable<Index.IndexEntry> {
82     String token;
83     int startRow;
84     
85     static final RAFSerializer<IndexEntry> SERIALIZER = new RAFSerializer<IndexEntry> () {
86       @Override
87       public IndexEntry read(RandomAccessFile raf) throws IOException {
88         return new IndexEntry(raf);
89       }
90       @Override
91       public void write(RandomAccessFile raf, IndexEntry t) throws IOException {
92         t.write(raf);
93       }};
94       
95     public IndexEntry(final String token, final int startRow) {
96       assert token.equals(token.trim());
97       assert token.length() > 0;
98       this.token = token;
99       this.startRow = startRow;
100     }
101     
102     public IndexEntry(final RandomAccessFile raf) throws IOException {
103       token = raf.readUTF();
104       startRow = raf.readInt();
105     }
106     
107     public void write(RandomAccessFile raf) throws IOException {
108       raf.writeUTF(token);
109       raf.writeInt(startRow);
110     }
111
112     public String toString() {
113       return token + "@" + startRow;
114     }
115 }
116   
117
118   private TokenRow sortedIndexToToken(final int sortedIndex) {
119     final IndexEntry indexEntry = sortedIndexEntries.get(sortedIndex);
120     return (TokenRow) rows.get(indexEntry.startRow);
121   }
122
123   public TokenRow find(String token, final AtomicBoolean interrupted) {
124     token = sortLanguage.textNorm(token, true);
125
126     int start = 0;
127     int end = sortedIndexEntries.size();
128     
129     final Collator sortCollator = sortLanguage.getSortCollator();
130     while (start < end) {
131       final int mid = (start + end) / 2;
132       if (interrupted.get()) {
133         return sortedIndexToToken(mid);
134       }
135       final IndexEntry midEntry = sortedIndexEntries.get(mid);
136
137       final int comp = sortCollator.compare(token, sortLanguage.textNorm(midEntry.token, true));
138       if (comp == 0) {
139         final int result = windBack(token, mid, sortCollator, interrupted);
140         return sortedIndexToToken(result);
141       } else if (comp < 0) {
142 //        Log.d("THAD", "Upper bound: " + midEntry);
143         end = mid;
144       } else {
145 //        Log.d("THAD", "Lower bound: " + midEntry);
146         start = mid + 1;
147       }
148     }
149     int result = Math.min(start, sortedIndexEntries.size() - 1);
150     result = windBack(token, result, sortCollator, interrupted);
151     if (result > 0 && sortCollator.compare(sortLanguage.textNorm(sortedIndexEntries.get(result).token, true), token) > 0) {
152       result = windBack(sortLanguage.textNorm(sortedIndexEntries.get(result - 1).token, true), result, sortCollator, interrupted);
153     }
154     return sortedIndexToToken(result);
155   }
156   
157   private final int windBack(final String token, int result, final Collator sortCollator, final AtomicBoolean interrupted) {
158     while (result > 0 && sortCollator.compare(sortLanguage.textNorm(sortedIndexEntries.get(result - 1).token, true), token) >= 0) {
159       --result;
160       if (interrupted.get()) {
161         return result;
162       }
163     }
164     return result;
165   }
166
167 }