]> gitweb.fperrin.net Git - DictionaryPC.git/blob - src/com/hughes/android/dictionary/parser/wiktionary/AbstractWiktionaryParser.java
Support compressed input for parsers.
[DictionaryPC.git] / src / com / hughes / android / dictionary / parser / wiktionary / AbstractWiktionaryParser.java
1 // Copyright 2012 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package com.hughes.android.dictionary.parser.wiktionary;
16
17 import java.io.BufferedInputStream;
18 import java.io.DataInputStream;
19 import java.io.EOFException;
20 import java.io.File;
21 import java.io.FileInputStream;
22 import java.io.InputStream;
23 import java.io.IOException;
24 import java.util.LinkedHashMap;
25 import java.util.LinkedHashSet;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.SortedMap;
30 import java.util.TreeMap;
31 import java.util.concurrent.atomic.AtomicInteger;
32 import java.util.logging.Level;
33 import java.util.logging.Logger;
34 import java.util.regex.Pattern;
35
36 import org.apache.commons.compress.compressors.CompressorStreamFactory;
37 import org.apache.commons.compress.compressors.CompressorException;
38
39 import com.hughes.android.dictionary.engine.EntrySource;
40 import com.hughes.android.dictionary.engine.EntryTypeName;
41 import com.hughes.android.dictionary.engine.IndexBuilder;
42 import com.hughes.android.dictionary.engine.IndexedEntry;
43 import com.hughes.android.dictionary.engine.ReadAheadBuffer;
44 import com.hughes.android.dictionary.parser.Parser;
45 import com.hughes.android.dictionary.parser.WikiTokenizer;
46 import com.hughes.util.EnumUtil;
47
48 public abstract class AbstractWiktionaryParser implements Parser {
49
50     static final Logger LOG = Logger.getLogger("WiktionaryParser");
51
52     final SortedMap<String, AtomicInteger> counters = new TreeMap<String, AtomicInteger>();
53     final Set<String> pairsAdded = new LinkedHashSet<String>();
54
55     public EntrySource entrySource;
56     public String title;
57
58
59     abstract void parseSection(final String heading, final String text);
60
61     abstract void removeUselessArgs(final Map<String, String> namedArgs);
62
63     @Override
64     public void parse(final File file, final EntrySource entrySource, final int pageLimit) throws IOException {
65         this.entrySource = entrySource;
66         int pageCount = 0;
67         File input = new File(file.getPath() + ".bz2");
68         if (!input.exists()) input = new File(file.getPath() + ".gz");
69         if (!input.exists()) input = new File(file.getPath() + ".xz");
70         DataInputStream dis;
71         if (!input.exists()) {
72             // Fallback to uncompressed file
73             dis = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
74         } else {
75             InputStream compressedIn = new BufferedInputStream(new FileInputStream(input));
76             try {
77                 InputStream in = new CompressorStreamFactory().createCompressorInputStream(compressedIn);
78                 in = new ReadAheadBuffer(in, 20 * 1024 * 1024);
79                 dis = new DataInputStream(in);
80             } catch (CompressorException e) {
81                 throw new IOException(e);
82             }
83         }
84         try {
85             while (true) {
86                 if (pageLimit >= 0 && pageCount >= pageLimit) {
87                     return;
88                 }
89
90                 try {
91                     title = dis.readUTF();
92                 } catch (EOFException e) {
93                     LOG.log(Level.INFO, "EOF reading split.");
94                     dis.close();
95                     return;
96                 }
97                 final String heading = dis.readUTF();
98                 final int bytesLength = dis.readInt();
99                 final byte[] bytes = new byte[bytesLength];
100                 dis.readFully(bytes);
101                 final String text = new String(bytes, "UTF8");
102
103                 parseSection(heading, text);
104
105                 ++pageCount;
106                 if (pageCount % 1000 == 0) {
107                     LOG.info("pageCount=" + pageCount);
108                 }
109             }
110         } finally {
111             dis.close();
112             LOG.info("***COUNTERS***");
113             for (final Map.Entry<String, AtomicInteger> entry : counters.entrySet()) {
114                 LOG.info(entry.getKey() + ": " + entry.getValue());
115             }
116         }
117     }
118
119     static final Pattern whitespace = Pattern.compile("\\s+");
120     static String trim(final String s) {
121         return whitespace.matcher(s).replaceAll(" ").trim();
122     }
123
124     public void incrementCount(final String string) {
125         AtomicInteger counter = counters.get(string);
126         if (counter == null) {
127             counter = new AtomicInteger();
128             counters.put(string, counter);
129         }
130         counter.incrementAndGet();
131     }
132
133     public void addLinkToCurrentEntry(final String token, final String lang, final EntryTypeName entryTypeName) {
134         assert false : token + ", title=" + title;
135     }
136
137
138     // -------------------------------------------------------------------------
139
140     static class AppendAndIndexWikiCallback<T extends AbstractWiktionaryParser> implements WikiTokenizer.Callback {
141
142         final T parser;
143         StringBuilder builder;
144         IndexedEntry indexedEntry;
145         IndexBuilder indexBuilder;
146         final Map<String,FunctionCallback<T>> functionCallbacks = new LinkedHashMap<String, FunctionCallback<T>>();
147
148         boolean entryTypeNameSticks = false;
149         EntryTypeName entryTypeName = null;
150
151         final Map<String,AtomicInteger> langCodeToTCount = new LinkedHashMap<String, AtomicInteger>();
152
153         final NameAndArgs<T> nameAndArgs = new NameAndArgs<T>();
154
155         public AppendAndIndexWikiCallback(final T parser) {
156             this.parser = parser;
157         }
158
159         public void reset(final StringBuilder builder, final IndexedEntry indexedEntry) {
160             this.builder = builder;
161             this.indexedEntry = indexedEntry;
162             this.indexBuilder = null;
163             entryTypeName = null;
164             entryTypeNameSticks = false;
165         }
166
167         public void dispatch(final String wikiText, final IndexBuilder indexBuilder, final EntryTypeName entryTypeName) {
168             final IndexBuilder oldIndexBuilder = this.indexBuilder;
169             final EntryTypeName oldEntryTypeName = this.entryTypeName;
170             this.indexBuilder = indexBuilder;
171             if (!entryTypeNameSticks) {
172                 this.entryTypeName = EnumUtil.min(entryTypeName, this.entryTypeName);
173             }
174             if (entryTypeName == null) this.entryTypeName = null;
175             WikiTokenizer.dispatch(wikiText, false, this);
176             this.indexBuilder = oldIndexBuilder;
177             this.entryTypeName = oldEntryTypeName;
178         }
179
180         public String dispatch(final String wikiText, final EntryTypeName entryTypeName) {
181             final int start = builder.length();
182             dispatch(wikiText, this.indexBuilder, entryTypeName);
183             return builder.substring(start);
184         }
185
186         @Override
187         public void onPlainText(final String plainText) {
188             // The only non-recursive callback.  Just appends to the builder, and indexes.
189             builder.append(plainText);
190             if (indexBuilder != null && entryTypeName != null && indexedEntry != null) {
191                 indexBuilder.addEntryWithString(indexedEntry, plainText, entryTypeName);
192             }
193         }
194
195         @Override
196         public void onWikiLink(WikiTokenizer wikiTokenizer) {
197             final String text = wikiTokenizer.wikiLinkText();
198             @SuppressWarnings("unused")
199             final String link = wikiTokenizer.wikiLinkDest();
200             dispatch(text, entryTypeName);
201         }
202
203         @Override
204         public void onFunction(
205             final WikiTokenizer wikiTokenizer,
206             final String name,
207             final List<String> args,
208             final Map<String, String> namedArgs) {
209
210             FunctionCallback<T> functionCallback = functionCallbacks.get(name);
211             if (functionCallback == null || !functionCallback.onWikiFunction(wikiTokenizer, name, args, namedArgs, parser, this)) {
212                 // Default function handling:
213                 parser.removeUselessArgs(namedArgs);
214                 final boolean single = args.isEmpty() && namedArgs.isEmpty();
215                 builder.append(single ? "{" : "{{");
216
217                 final IndexBuilder oldIndexBuilder = indexBuilder;
218                 indexBuilder = null;
219                 nameAndArgs.onWikiFunction(wikiTokenizer, name, args, namedArgs, parser, this);
220                 indexBuilder = oldIndexBuilder;
221
222                 builder.append(single ? "}" : "}}");
223             }
224         }
225
226         @Override
227         public void onHtml(WikiTokenizer wikiTokenizer) {
228             if (wikiTokenizer.token().startsWith("<ref>")) {
229                 // Do nothing.
230                 return;
231             }
232             // Unindexed for now.
233             builder.append(wikiTokenizer.token());
234         }
235
236         @Override
237         public void onMarkup(WikiTokenizer wikiTokenizer) {
238             // Do nothing.
239         }
240
241         @Override
242         public final void onComment(WikiTokenizer wikiTokenizer) {
243             // Do nothing.
244         }
245
246         @Override
247         public void onNewline(WikiTokenizer wikiTokenizer) {
248             assert false;
249         }
250
251         @Override
252         public void onHeading(WikiTokenizer wikiTokenizer) {
253             assert false;
254         }
255
256         @Override
257         public void onListItem(WikiTokenizer wikiTokenizer) {
258             assert false;
259         }
260
261     }
262
263     // --------------------------------------------------------------------
264
265     static final class NameAndArgs<T extends AbstractWiktionaryParser> implements FunctionCallback<T> {
266         @Override
267         public boolean onWikiFunction(final WikiTokenizer wikiTokenizer, final String name, final List<String> args,
268                                       final Map<String, String> namedArgs, final T parser,
269                                       final AppendAndIndexWikiCallback<T> appendAndIndexWikiCallback) {
270
271             if (name != null) {
272                 appendAndIndexWikiCallback.dispatch(name, null);
273             }
274             for (int i = 0; i < args.size(); ++i) {
275                 if (args.get(i).length() > 0) {
276                     appendAndIndexWikiCallback.builder.append("|");
277                     appendAndIndexWikiCallback.dispatch(args.get(i), null, null);
278                 }
279             }
280             appendNamedArgs(namedArgs, appendAndIndexWikiCallback);
281             return true;
282         }
283     }
284     static NameAndArgs<AbstractWiktionaryParser> NAME_AND_ARGS = new NameAndArgs<AbstractWiktionaryParser>();
285
286     static void appendNamedArgs(final Map<String, String> namedArgs,
287                                 final AppendAndIndexWikiCallback<?> appendAndIndexWikiCallback) {
288         for (final Map.Entry<String, String> entry : namedArgs.entrySet()) {
289             appendAndIndexWikiCallback.builder.append("|");
290             appendAndIndexWikiCallback.dispatch(entry.getKey(), null, null);
291             appendAndIndexWikiCallback.builder.append("=");
292             EntryTypeName entryTypeName = null;
293             IndexBuilder indexBuilder = null;
294             // This doesn't work: we'd need to add to word-forms.
295 //      System.out.println(entry.getKey());
296 //      if (entry.getKey().equals("tr")) {
297 //        entryTypeName = EntryTypeName.WIKTIONARY_TRANSLITERATION;
298 //        indexBuilder = appendAndIndexWikiCallback.parser.foreignIndexBuilder;
299 //      }
300             appendAndIndexWikiCallback.dispatch(entry.getValue(), indexBuilder, entryTypeName);
301         }
302     }
303
304 }