]> gitweb.fperrin.net Git - DictionaryPC.git/blob - src/com/hughes/android/dictionary/parser/enwiktionary/EnWiktionaryXmlParser.java
Stoplist, more languages...
[DictionaryPC.git] / src / com / hughes / android / dictionary / parser / enwiktionary / EnWiktionaryXmlParser.java
1 // Copyright 2011 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.enwiktionary;
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.IOException;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.LinkedHashMap;
27 import java.util.LinkedHashSet;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
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 com.hughes.android.dictionary.engine.EntrySource;
37 import com.hughes.android.dictionary.engine.EntryTypeName;
38 import com.hughes.android.dictionary.engine.IndexBuilder;
39 import com.hughes.android.dictionary.engine.IndexedEntry;
40 import com.hughes.android.dictionary.engine.PairEntry;
41 import com.hughes.android.dictionary.engine.PairEntry.Pair;
42 import com.hughes.android.dictionary.parser.WikiTokenizer;
43
44 public class EnWiktionaryXmlParser {
45   
46   static final Logger LOG = Logger.getLogger(EnWiktionaryXmlParser.class.getName());
47   
48   // TODO: process {{ttbc}} lines
49   
50   static final Pattern partOfSpeechHeader = Pattern.compile(
51       "Noun|Verb|Adjective|Adverb|Pronoun|Conjunction|Interjection|" +
52       "Preposition|Proper noun|Article|Prepositional phrase|Acronym|" +
53       "Abbreviation|Initialism|Contraction|Prefix|Suffix|Symbol|Letter|" +
54       "Ligature|Idiom|Phrase|\\{\\{acronym\\}\\}|\\{\\{initialism\\}\\}|" +
55       "\\{\\{abbreviation\\}\\}|" +
56       // These are @deprecated:
57       "Noun form|Verb form|Adjective form|Nominal phrase|Noun phrase|" +
58       "Verb phrase|Transitive verb|Intransitive verb|Reflexive verb|" +
59       // These are extras I found:
60       "Determiner|Numeral|Number|Cardinal number|Ordinal number|Proverb|" +
61       "Particle|Interjection|Pronominal adverb" +
62       "Han character|Hanzi|Hanja|Kanji|Katakana character|Syllable");
63   
64   EntrySource entrySource;
65   final IndexBuilder enIndexBuilder;
66   final IndexBuilder foreignIndexBuilder;
67   final Pattern langPattern;
68   final Pattern langCodePattern;
69   final boolean swap;
70   
71   // State used while parsing.
72   enum State {
73     TRANSLATION_LINE,
74     ENGLISH_DEF_OF_FOREIGN,
75     ENGLISH_EXAMPLE,
76     FOREIGN_EXAMPLE,
77   }
78   State state = null;
79   String title;
80
81   public EnWiktionaryXmlParser(final IndexBuilder enIndexBuilder, final IndexBuilder otherIndexBuilder, final Pattern langPattern, final Pattern langCodePattern, final boolean swap) {
82     this.enIndexBuilder = enIndexBuilder;
83     this.foreignIndexBuilder = otherIndexBuilder;
84     this.langPattern = langPattern;
85     this.langCodePattern = langCodePattern;
86     this.swap = swap;
87   }
88
89   
90   public void parse(final File file, final EntrySource entrySource, final int pageLimit) throws IOException {
91     this.entrySource = entrySource;
92     int pageCount = 0;
93     final DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
94     try {
95     while (true) {
96       if (pageLimit >= 0 && pageCount >= pageLimit) {
97         return;
98       }
99       
100       try {
101         title = dis.readUTF();
102       } catch (EOFException e) {
103         LOG.log(Level.INFO, "EOF reading split.");
104         dis.close();
105         return;
106       }
107       final String heading = dis.readUTF();
108       final int bytesLength = dis.readInt();
109       final byte[] bytes = new byte[bytesLength];
110       dis.readFully(bytes);
111       final String text = new String(bytes, "UTF8");
112       
113       parseSection(heading, text);
114
115       ++pageCount;
116       if (pageCount % 1000 == 0) {
117         LOG.info("pageCount=" + pageCount);
118       }
119     }
120     } finally {
121       System.out.println("lang Counts: " + appendAndIndexWikiCallback.langCodeToTCount);
122       appendAndIndexWikiCallback.langCodeToTCount.keySet().removeAll(EnWiktionaryLangs.isoCodeToWikiName.keySet());
123       System.out.println("unused Counts: " + appendAndIndexWikiCallback.langCodeToTCount);
124       System.out.println("lang Counts: " + langNameToTCount);
125       langNameToTCount.keySet().removeAll(EnWiktionaryLangs.isoCodeToWikiName.values());
126       System.out.println("unknown counts: " + langNameToTCount);
127     }
128   }
129   
130   private void parseSection(String heading, final String text) {
131     if (title.startsWith("Wiktionary:") ||
132         title.startsWith("Template:") ||
133         title.startsWith("Appendix:") ||
134         title.startsWith("Category:") ||
135         title.startsWith("Index:") ||
136         title.startsWith("MediaWiki:") ||
137         title.startsWith("TransWiki:") ||
138         title.startsWith("Citations:") ||
139         title.startsWith("Concordance:") ||
140         title.startsWith("Help:")) {
141       return;
142     }
143     
144     heading = heading.replaceAll("=", "").trim(); 
145     if (heading.equals("English")) {
146       doEnglishWord(text);
147     } else if (langPattern.matcher(heading).find()){
148       doForeignWord(heading, text);
149     }
150         
151   }  // endPage()
152   
153   // -------------------------------------------------------------------------
154   
155   private void doEnglishWord(String text) {
156     
157     String pos = null;
158     int posDepth = -1;
159
160     final WikiTokenizer wikiTokenizer = new WikiTokenizer(text);
161     while (wikiTokenizer.nextToken() != null) {
162       
163       if (wikiTokenizer.isHeading()) {
164         final String headerName = wikiTokenizer.headingWikiText();
165         
166         if (wikiTokenizer.headingDepth() <= posDepth) {
167           pos = null;
168           posDepth = -1;
169         }
170         
171         if (partOfSpeechHeader.matcher(headerName).matches()) {
172           posDepth = wikiTokenizer.headingDepth();
173           pos = wikiTokenizer.headingWikiText();
174           // TODO: if we're inside the POS section, we should handle the first title line...
175           
176         } else if (headerName.equals("Translations")) {
177           if (pos == null) {
178             LOG.info("Translations without POS (but using anyway): " + title);
179           }
180           doTranslations(wikiTokenizer, pos);
181         } else if (headerName.equals("Pronunciation")) {
182           //doPronunciation(wikiLineReader);
183         }
184       } else if (wikiTokenizer.isFunction()) {
185         final String name = wikiTokenizer.functionName();
186         if (name.equals("head") && pos == null) {
187           LOG.warning("{{head}} without POS: " + title);
188         }
189       }
190     }
191   }
192   
193   final AppendAndIndexWikiCallback appendAndIndexWikiCallback = new AppendAndIndexWikiCallback(this);
194   {
195     appendAndIndexWikiCallback.functionCallbacks.putAll(FunctionCallbacksDefault.DEFAULT);
196   }
197   
198   final Map<String,AtomicInteger> langNameToTCount = new LinkedHashMap<String, AtomicInteger>();
199   
200   private void doTranslations(final WikiTokenizer wikiTokenizer, final String pos) {
201     if (title.equals("absolutely")) {
202       //System.out.println();
203     }
204     
205     String topLevelLang = null;
206     String sense = null;
207     boolean done = false;
208     while (wikiTokenizer.nextToken() != null) {
209       if (wikiTokenizer.isHeading()) {
210         wikiTokenizer.returnToLineStart();
211         return;
212       }
213       if (done) {
214         continue;
215       }
216       
217       // Check whether we care about this line:
218       
219       if (wikiTokenizer.isFunction()) {
220         final String functionName = wikiTokenizer.functionName();
221         final List<String> positionArgs = wikiTokenizer.functionPositionArgs();
222         
223         if (functionName.equals("trans-top")) {
224           sense = null;
225           if (wikiTokenizer.functionPositionArgs().size() >= 1) {
226             sense = positionArgs.get(0);
227             // TODO: could emphasize words in [[brackets]] inside sense.
228             sense = WikiTokenizer.toPlainText(sense);
229             //LOG.info("Sense: " + sense);
230           }
231         } else if (functionName.equals("trans-bottom")) {
232           sense = null;
233         } else if (functionName.equals("trans-mid")) {
234         } else if (functionName.equals("trans-see")) {
235           // TODO: would also be nice...
236         } else if (functionName.startsWith("picdic")) {
237         } else if (functionName.startsWith("checktrans")) {
238           done = true;
239         } else if (functionName.startsWith("ttbc")) {
240           wikiTokenizer.nextLine();
241           // TODO: would be great to handle ttbc
242           // TODO: Check this: done = true;
243         } else {
244           LOG.warning("Unexpected translation wikifunction: " + wikiTokenizer.token() + ", title=" + title);
245         }
246       } else if (wikiTokenizer.isListItem()) {
247         final String line = wikiTokenizer.listItemWikiText();
248         // This line could produce an output...
249         
250 //        if (line.contains("ich hoan dich gear")) {
251 //          //System.out.println();
252 //        }
253         
254         // First strip the language and check whether it matches.
255         // And hold onto it for sub-lines.
256         final int colonIndex = line.indexOf(":");
257         if (colonIndex == -1) {
258           continue;
259         }
260         
261         final String lang = trim(WikiTokenizer.toPlainText(line.substring(0, colonIndex)));
262         if (!langNameToTCount.containsKey(lang)) {
263           langNameToTCount.put(lang, new AtomicInteger());
264         }
265         langNameToTCount.get(lang).incrementAndGet();
266         final boolean appendLang;
267         if (wikiTokenizer.listItemPrefix().length() == 1) {
268           topLevelLang = lang;
269           final boolean thisFind = langPattern.matcher(lang).find();
270           if (!thisFind) {
271             continue;
272           }
273           appendLang = !langPattern.matcher(lang).matches();
274         } else if (topLevelLang == null) {
275           continue;
276         } else {
277           // Two-level -- the only way we won't append is if this second level matches exactly.
278           if (!langPattern.matcher(lang).matches() && !langPattern.matcher(topLevelLang).find()) {
279             continue;
280           }
281           appendLang = !langPattern.matcher(lang).matches();
282         }
283         
284         String rest = line.substring(colonIndex + 1).trim();
285         if (rest.length() > 0) {
286           doTranslationLine(line, appendLang ? lang : null, pos, sense, rest);
287         }
288         
289       } else if (wikiTokenizer.remainderStartsWith("''See''")) {
290         wikiTokenizer.nextLine();
291         LOG.fine("Skipping See line: " + wikiTokenizer.token());
292       } else if (wikiTokenizer.isWikiLink()) {
293         final String wikiLink = wikiTokenizer.wikiLinkText();
294         if (wikiLink.contains(":") && wikiLink.contains(title)) {
295         } else if (wikiLink.contains("Category:")) {
296         } else  {
297           LOG.warning("Unexpected wikiLink: " + wikiTokenizer.token() + ", title=" + title);
298         }
299       } else if (wikiTokenizer.isNewline() || wikiTokenizer.isMarkup() || wikiTokenizer.isComment()) {
300       } else {
301         final String token = wikiTokenizer.token();
302         if (token.equals("----")) { 
303         } else {
304           LOG.warning("Unexpected translation token: " + wikiTokenizer.token() + ", title=" + title);
305         }
306       }
307       
308     }
309   }
310   
311   private void doTranslationLine(final String line, final String lang, final String pos, final String sense, final String rest) {
312     state = State.TRANSLATION_LINE;
313     // Good chance we'll actually file this one...
314     final PairEntry pairEntry = new PairEntry(entrySource);
315     final IndexedEntry indexedEntry = new IndexedEntry(pairEntry);
316     
317     final StringBuilder foreignText = new StringBuilder();
318     appendAndIndexWikiCallback.reset(foreignText, indexedEntry);
319     appendAndIndexWikiCallback.dispatch(rest, foreignIndexBuilder, EntryTypeName.WIKTIONARY_TRANSLATION_OTHER_TEXT);
320     
321     if (foreignText.length() == 0) {
322       LOG.warning("Empty foreignText: " + line);
323       return;
324     }
325     
326     if (lang != null) {
327       foreignText.insert(0, String.format("(%s) ", lang));
328     }
329     
330     StringBuilder englishText = new StringBuilder();
331     
332     englishText.append(title);
333     if (sense != null) {
334       englishText.append(" (").append(sense).append(")");
335       enIndexBuilder.addEntryWithString(indexedEntry, sense, EntryTypeName.WIKTIONARY_TRANSLATION_SENSE);
336     }
337     if (pos != null) {
338       englishText.append(" (").append(pos.toLowerCase()).append(")");
339     }
340     enIndexBuilder.addEntryWithString(indexedEntry, title, EntryTypeName.WIKTIONARY_TITLE_MULTI);
341     
342     final Pair pair = new Pair(trim(englishText.toString()), trim(foreignText.toString()), swap);
343     pairEntry.pairs.add(pair);
344     if (!pairsAdded.add(pair.toString())) {
345       LOG.warning("Duplicate pair: " + pair.toString());
346     }
347   }
348
349
350   Set<String> pairsAdded = new LinkedHashSet<String>();
351   
352   // -------------------------------------------------------------------------
353   
354   private void doForeignWord(final String lang, final String text) {
355     final WikiTokenizer wikiTokenizer = new WikiTokenizer(text);
356     while (wikiTokenizer.nextToken() != null) {
357       if (wikiTokenizer.isHeading()) {
358         final String headingName = wikiTokenizer.headingWikiText();
359         if (headingName.equals("Translations")) {
360           LOG.warning("Translations not in English section: " + title);
361         } else if (headingName.equals("Pronunciation")) {
362           //doPronunciation(wikiLineReader);
363         } else if (partOfSpeechHeader.matcher(headingName).matches()) {
364           doForeignPartOfSpeech(lang, headingName, wikiTokenizer.headingDepth(), wikiTokenizer);
365         }
366       } else {
367       }
368     }
369   }
370   
371   static final class ListSection {
372     final String firstPrefix;
373     final String firstLine;
374     final List<String> nextPrefixes = new ArrayList<String>();
375     final List<String> nextLines = new ArrayList<String>();
376     
377     public ListSection(String firstPrefix, String firstLine) {
378       this.firstPrefix = firstPrefix;
379       this.firstLine = firstLine;
380     }
381
382     @Override
383     public String toString() {
384       return firstPrefix + firstLine + "{ " + nextPrefixes + "}";
385     }
386   }
387
388
389   int foreignCount = 0;
390   final Collection<String> wordForms = new ArrayList<String>();
391   boolean titleAppended = false;
392
393   private void doForeignPartOfSpeech(final String lang, String posHeading, final int posDepth, WikiTokenizer wikiTokenizer) {
394     if (++foreignCount % 1000 == 0) {
395       LOG.info("***" + lang + ", " + title + ", pos=" + posHeading + ", foreignCount=" + foreignCount);
396     }
397     if (title.equals("6")) {
398       System.out.println();
399     }
400     
401     final StringBuilder foreignBuilder = new StringBuilder();
402     final List<ListSection> listSections = new ArrayList<ListSection>();
403     
404     appendAndIndexWikiCallback.reset(foreignBuilder, null);
405     this.state = State.ENGLISH_DEF_OF_FOREIGN;  // TODO: this is wrong, need new category....
406     titleAppended = false;
407     wordForms.clear();
408     
409     try {
410     
411     ListSection lastListSection = null;
412     
413     int currentHeadingDepth = posDepth;
414     while (wikiTokenizer.nextToken() != null) {
415       if (wikiTokenizer.isHeading()) {
416         currentHeadingDepth = wikiTokenizer.headingDepth();
417         
418         if (currentHeadingDepth <= posDepth) {
419           wikiTokenizer.returnToLineStart();
420           return;
421         }
422       }
423       
424       if (currentHeadingDepth > posDepth) {
425         // TODO: deal with other neat info sections
426         continue;
427       }
428       
429       if (wikiTokenizer.isFunction()) {
430         final String name = wikiTokenizer.functionName();
431         final List<String> args = wikiTokenizer.functionPositionArgs();
432         final Map<String,String> namedArgs = wikiTokenizer.functionNamedArgs();
433         // First line is generally a repeat of the title with some extra information.
434         // We need to build up the left side (foreign text, tokens) separately from the
435         // right side (English).  The left-side may get paired with multiple right sides.
436         // The left side should get filed under every form of the word in question (singular, plural).
437         
438         // For verbs, the conjugation comes later on in a deeper section.
439         // Ideally, we'd want to file every English entry with the verb
440         // under every verb form coming from the conjugation.
441         // Ie. under "fa": see: "make :: fare" and "do :: fare"
442         // But then where should we put the conjugation table?
443         // I think just under fare.  But then we need a way to link to the entry (actually the row, since entries doesn't show up!)
444         // for the conjugation table from "fa".
445         // Would like to be able to link to a lang#token.
446         
447         appendAndIndexWikiCallback.onFunction(wikiTokenizer, name, args, namedArgs);
448         
449       } else if (wikiTokenizer.isListItem()) {
450         final String prefix = wikiTokenizer.listItemPrefix();
451         if (lastListSection != null && 
452             prefix.startsWith(lastListSection.firstPrefix) && 
453             prefix.length() > lastListSection.firstPrefix.length()) {
454           lastListSection.nextPrefixes.add(prefix);
455           lastListSection.nextLines.add(wikiTokenizer.listItemWikiText());
456         } else {
457           lastListSection = new ListSection(prefix, wikiTokenizer.listItemWikiText());
458           listSections.add(lastListSection);
459         }
460       } else if (lastListSection != null) {
461         // Don't append anything after the lists, because there's crap.
462       } else if (wikiTokenizer.isWikiLink()) {
463         // Unindexed!
464         foreignBuilder.append(wikiTokenizer.wikiLinkText());
465         
466       } else if (wikiTokenizer.isPlainText()) {
467         // Unindexed!
468         foreignBuilder.append(wikiTokenizer.token());
469         
470       } else if (wikiTokenizer.isMarkup() || wikiTokenizer.isNewline() || wikiTokenizer.isComment()) {
471         // Do nothing.
472       } else {
473         LOG.warning("Unexpected token: " + wikiTokenizer.token());
474       }
475     }
476     
477     } finally {
478       // Here's where we exit.
479       // Should we make an entry even if there are no foreign list items?
480       String foreign = foreignBuilder.toString().trim();
481       if (!titleAppended && !foreign.toLowerCase().startsWith(title.toLowerCase())) {
482         foreign = String.format("%s %s", title, foreign);
483       }
484       if (!langPattern.matcher(lang).matches()) {
485         foreign = String.format("(%s) %s", lang, foreign);
486       }
487       for (final ListSection listSection : listSections) {
488         doForeignListSection(foreign, title, wordForms, listSection);
489       }
490     }
491   }
492   
493   
494   // Might only want to remove "lang" if it's equal to "zh", for example.
495   static final Set<String> USELESS_WIKI_ARGS = new LinkedHashSet<String>(
496       Arrays.asList(
497           "lang",
498           "sc",
499           "sort",
500           "cat",
501           "xs",
502           "nodot"));
503
504   public boolean entryIsFormOfSomething = false;
505
506   private void doForeignListSection(final String foreignText, String title, final Collection<String> forms, final ListSection listSection) {
507     state = State.ENGLISH_DEF_OF_FOREIGN;
508     final String prefix = listSection.firstPrefix;
509     if (prefix.length() > 1) {
510       // Could just get looser and say that any prefix longer than first is a sublist.
511       LOG.warning("Prefix too long: " + listSection);
512       return;
513     }
514     
515     final PairEntry pairEntry = new PairEntry(entrySource);
516     final IndexedEntry indexedEntry = new IndexedEntry(pairEntry);
517
518     entryIsFormOfSomething = false;
519     final StringBuilder englishBuilder = new StringBuilder();
520     final String mainLine = listSection.firstLine;
521     appendAndIndexWikiCallback.reset(englishBuilder, indexedEntry);
522     appendAndIndexWikiCallback.dispatch(mainLine, enIndexBuilder, EntryTypeName.WIKTIONARY_ENGLISH_DEF);
523
524     final String english = trim(englishBuilder.toString());
525     if (english.length() > 0) {
526       final Pair pair = new Pair(english, trim(foreignText), this.swap);
527       pairEntry.pairs.add(pair);
528       foreignIndexBuilder.addEntryWithString(indexedEntry, title, entryIsFormOfSomething ? EntryTypeName.WIKTIONARY_IS_FORM_OF_SOMETHING_ELSE : EntryTypeName.WIKTIONARY_TITLE_MULTI);
529       for (final String form : forms) {
530         foreignIndexBuilder.addEntryWithString(indexedEntry, form, EntryTypeName.WIKTIONARY_INFLECTED_FORM_MULTI);
531       }
532     }
533     
534     // Do examples.
535     String lastForeign = null;
536     for (int i = 0; i < listSection.nextPrefixes.size(); ++i) {
537       final String nextPrefix = listSection.nextPrefixes.get(i);
538       final String nextLine = listSection.nextLines.get(i);
539
540       // TODO: This splitting is not sensitive to wiki code.
541       int dash = nextLine.indexOf("&mdash;");
542       int mdashLen = 7;
543       if (dash == -1) {
544         dash = nextLine.indexOf("—");
545         mdashLen = 1;
546       }
547       if (dash == -1) {
548         dash = nextLine.indexOf(" - ");
549         mdashLen = 3;
550       }
551       
552       if ((nextPrefix.equals("#:") || nextPrefix.equals("##:")) && dash != -1) {
553         final String foreignEx = nextLine.substring(0, dash);
554         final String englishEx = nextLine.substring(dash + mdashLen);
555         final Pair pair = new Pair(formatAndIndexExampleString(englishEx, enIndexBuilder, indexedEntry), formatAndIndexExampleString(foreignEx, foreignIndexBuilder, indexedEntry), swap);
556         if (pair.lang1 != "--" && pair.lang1 != "--") {
557           pairEntry.pairs.add(pair);
558         }
559         lastForeign = null;
560       } else if (nextPrefix.equals("#:") || nextPrefix.equals("##:")){
561         final Pair pair = new Pair("--", formatAndIndexExampleString(nextLine, null, indexedEntry), swap);
562         lastForeign = nextLine;
563         if (pair.lang1 != "--" && pair.lang1 != "--") {
564           pairEntry.pairs.add(pair);
565         }
566       } else if (nextPrefix.equals("#::") || nextPrefix.equals("#**")) {
567         if (lastForeign != null && pairEntry.pairs.size() > 0) {
568           pairEntry.pairs.remove(pairEntry.pairs.size() - 1);
569           final Pair pair = new Pair(formatAndIndexExampleString(nextLine, enIndexBuilder, indexedEntry), formatAndIndexExampleString(lastForeign, foreignIndexBuilder, indexedEntry), swap);
570           if (pair.lang1 != "--" || pair.lang2 != "--") {
571             pairEntry.pairs.add(pair);
572           }
573           lastForeign = null;
574         } else {
575           LOG.warning("TODO: English example with no foreign: " + title + ", " + nextLine);
576           final Pair pair = new Pair("--", formatAndIndexExampleString(nextLine, null, indexedEntry), swap);
577           if (pair.lang1 != "--" || pair.lang2 != "--") {
578             pairEntry.pairs.add(pair);
579           }
580         }
581       } else if (nextPrefix.equals("#*")) {
582         // Can't really index these.
583         final Pair pair = new Pair("--", formatAndIndexExampleString(nextLine, null, indexedEntry), swap);
584         lastForeign = nextLine;
585         if (pair.lang1 != "--" || pair.lang2 != "--") {
586           pairEntry.pairs.add(pair);
587         }
588       } else if (nextPrefix.equals("#::*") || nextPrefix.equals("##") || nextPrefix.equals("#*:") || nextPrefix.equals("#:*") || true) {
589         final Pair pair = new Pair("--", formatAndIndexExampleString(nextLine, null, indexedEntry), swap);
590         if (pair.lang1 != "--" || pair.lang2 != "--") {
591           pairEntry.pairs.add(pair);
592         }
593 //      } else {
594 //        assert false;
595       }
596     }
597   }
598   
599   private String formatAndIndexExampleString(final String example, final IndexBuilder indexBuilder, final IndexedEntry indexedEntry) {
600     // TODO:
601 //    if (wikiTokenizer.token().equals("'''")) {
602 //      insideTripleQuotes = !insideTripleQuotes;
603 //    }
604     final StringBuilder builder = new StringBuilder();
605     appendAndIndexWikiCallback.reset(builder, indexedEntry);
606     appendAndIndexWikiCallback.entryTypeName = EntryTypeName.WIKTIONARY_EXAMPLE;
607     appendAndIndexWikiCallback.entryTypeNameSticks = true;
608     try {
609       // TODO: this is a hack needed because we don't safely split on the dash.
610       appendAndIndexWikiCallback.dispatch(example, indexBuilder, EntryTypeName.WIKTIONARY_EXAMPLE);
611     } catch (AssertionError e) {
612       return "--";
613     }
614     final String result = trim(builder.toString());
615     return result.length() > 0 ? result : "--";
616   }
617
618
619   private void itConjAre(List<String> args, Map<String, String> namedArgs) {
620     final String base = args.get(0);
621     final String aux = args.get(1);
622     
623     putIfMissing(namedArgs, "inf", base + "are");
624     putIfMissing(namedArgs, "aux", aux);
625     putIfMissing(namedArgs, "ger", base + "ando");
626     putIfMissing(namedArgs, "presp", base + "ante");
627     putIfMissing(namedArgs, "pastp", base + "ato");
628     // Present
629     putIfMissing(namedArgs, "pres1s", base + "o");
630     putIfMissing(namedArgs, "pres2s", base + "i");
631     putIfMissing(namedArgs, "pres3s", base + "a");
632     putIfMissing(namedArgs, "pres1p", base + "iamo");
633     putIfMissing(namedArgs, "pres2p", base + "ate");
634     putIfMissing(namedArgs, "pres3p", base + "ano");
635     // Imperfect
636     putIfMissing(namedArgs, "imperf1s", base + "avo");
637     putIfMissing(namedArgs, "imperf2s", base + "avi");
638     putIfMissing(namedArgs, "imperf3s", base + "ava");
639     putIfMissing(namedArgs, "imperf1p", base + "avamo");
640     putIfMissing(namedArgs, "imperf2p", base + "avate");
641     putIfMissing(namedArgs, "imperf3p", base + "avano");
642     // Passato remoto
643     putIfMissing(namedArgs, "prem1s", base + "ai");
644     putIfMissing(namedArgs, "prem2s", base + "asti");
645     putIfMissing(namedArgs, "prem3s", base + "ò");
646     putIfMissing(namedArgs, "prem1p", base + "ammo");
647     putIfMissing(namedArgs, "prem2p", base + "aste");
648     putIfMissing(namedArgs, "prem3p", base + "arono");
649     // Future
650     putIfMissing(namedArgs, "fut1s", base + "erò");
651     putIfMissing(namedArgs, "fut2s", base + "erai");
652     putIfMissing(namedArgs, "fut3s", base + "erà");
653     putIfMissing(namedArgs, "fut1p", base + "eremo");
654     putIfMissing(namedArgs, "fut2p", base + "erete");
655     putIfMissing(namedArgs, "fut3p", base + "eranno");
656     // Conditional
657     putIfMissing(namedArgs, "cond1s", base + "erei");
658     putIfMissing(namedArgs, "cond2s", base + "eresti");
659     putIfMissing(namedArgs, "cond3s", base + "erebbe");
660     putIfMissing(namedArgs, "cond1p", base + "eremmo");
661     putIfMissing(namedArgs, "cond2p", base + "ereste");
662     putIfMissing(namedArgs, "cond3p", base + "erebbero");
663     // Subjunctive / congiuntivo
664     putIfMissing(namedArgs, "sub123s", base + "i");
665     putIfMissing(namedArgs, "sub1p", base + "iamo");
666     putIfMissing(namedArgs, "sub2p", base + "iate");
667     putIfMissing(namedArgs, "sub3p", base + "ino");
668     // Imperfect subjunctive
669     putIfMissing(namedArgs, "impsub12s", base + "assi");
670     putIfMissing(namedArgs, "impsub3s", base + "asse");
671     putIfMissing(namedArgs, "impsub1p", base + "assimo");
672     putIfMissing(namedArgs, "impsub2p", base + "aste");
673     putIfMissing(namedArgs, "impsub3p", base + "assero");
674     // Imperative
675     putIfMissing(namedArgs, "imp2s", base + "a");
676     putIfMissing(namedArgs, "imp3s", base + "i");
677     putIfMissing(namedArgs, "imp1p", base + "iamo");
678     putIfMissing(namedArgs, "imp2p", base + "ate");
679     putIfMissing(namedArgs, "imp3p", base + "ino");
680
681
682     itConj(args, namedArgs);
683   }
684
685
686   private void itConj(List<String> args, Map<String, String> namedArgs) {
687     // TODO Auto-generated method stub
688     
689   }
690
691
692   private static void putIfMissing(final Map<String, String> namedArgs, final String key,
693       final String value) {
694     final String oldValue = namedArgs.get(key);
695     if (oldValue == null || oldValue.length() == 0) {
696       namedArgs.put(key, value);
697     }
698   }
699   
700   // TODO: check how ='' and =| are manifested....
701   // TODO: get this right in -are
702   private static void putOrNullify(final Map<String, String> namedArgs, final String key,
703       final String value) {
704     final String oldValue = namedArgs.get(key);
705     if (oldValue == null/* || oldValue.length() == 0*/) {
706       namedArgs.put(key, value);
707     } else {
708       if (oldValue.equals("''")) {
709         namedArgs.put(key, "");
710       }
711     }
712   }
713
714   static final Pattern whitespace = Pattern.compile("\\s+");
715   static String trim(final String s) {
716     return whitespace.matcher(s).replaceAll(" ").trim();
717   }
718
719   
720 }