]> gitweb.fperrin.net Git - DictionaryPC.git/blob - src/com/hughes/android/dictionary/parser/wiktionary/EnFunctionCallbacks.java
Fixed trailing ,s in italian verb tenses.
[DictionaryPC.git] / src / com / hughes / android / dictionary / parser / wiktionary / EnFunctionCallbacks.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 com.hughes.android.dictionary.engine.EntryTypeName;
18 import com.hughes.android.dictionary.engine.IndexBuilder;
19 import com.hughes.android.dictionary.parser.WikiTokenizer;
20 import com.hughes.android.dictionary.parser.wiktionary.AbstractWiktionaryParser.AppendAndIndexWikiCallback;
21 import com.hughes.android.dictionary.parser.wiktionary.AbstractWiktionaryParser.NameAndArgs;
22 import com.hughes.util.ListUtil;
23 import com.hughes.util.MapUtil;
24
25 import java.util.Arrays;
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
33 class EnFunctionCallbacks {
34   
35   static final Map<String,FunctionCallback<EnParser>> DEFAULT = new LinkedHashMap<String, FunctionCallback<EnParser>>();
36
37   static <T extends AbstractWiktionaryParser> void addGenericCallbacks(Map<String, FunctionCallback<T>> callbacks) {
38       FunctionCallback<T> callback = new Gender<T>();
39       callbacks.put("m", callback);
40       callbacks.put("f", callback);
41       callbacks.put("n", callback);
42       callbacks.put("p", callback);
43       callbacks.put("g", callback);
44       
45       callback = new EncodingCallback<T>();
46       Set<String> encodings = new LinkedHashSet<String>(Arrays.asList(
47           "IPA", "IPAchar",  // Not really encodings, but it works.
48           "zh-ts", "zh-tsp",
49           "sd-Arab", "ku-Arab", "Arab", "unicode", "Laoo", "ur-Arab", "Thai", 
50           "fa-Arab", "Khmr", "Cyrl", "ug-Arab", "ko-inline", 
51           "Jpan", "Kore", "Hebr", "rfscript", "Beng", "Mong", "Knda", "Cyrs",
52           "yue-tsj", "Mlym", "Tfng", "Grek", "yue-yue-j"));
53       for (final String encoding : encodings) {
54           callbacks.put(encoding, callback);
55       }
56       
57       callback = new Ignore<T>();
58       callbacks.put("trreq", callback);
59       callbacks.put("t-image", callback);
60       callbacks.put("defn", callback);
61       callbacks.put("rfdef", callback);
62       callbacks.put("rfdate", callback);
63       callbacks.put("rfex", callback);
64       callbacks.put("rfquote", callback);
65       callbacks.put("attention", callback);
66       callbacks.put("zh-attention", callback);
67       callbacks.put("top2", callback);
68       callbacks.put("mid2", callback);
69       callbacks.put("top3", callback);
70       callbacks.put("mid3", callback);
71       callbacks.put("bottom", callback);
72       callbacks.put("rel-mid", callback);
73       callbacks.put("rel-mid3", callback);
74       callbacks.put("rel-mid4", callback);
75       callbacks.put("rel-bottom", callback);
76       
77       callback = new AppendName<T>();
78       callbacks.put("...", callback);
79       
80       callbacks.put("qualifier", new QualifierCallback<T>());
81       callbacks.put("italbrac", new italbrac<T>());
82       callbacks.put("gloss", new gloss<T>());
83       callbacks.put("not used", new not_used<T>());
84       callbacks.put("wikipedia", new wikipedia<T>());
85       
86       final it_conj<T> it_conj_cb = new it_conj<T>();
87       callbacks.put("it-conj", it_conj_cb);
88       callbacks.put("it-conj-are", new it_conj_are<T>(it_conj_cb));
89       callbacks.put("it-conj-arsi", new it_conj_are<T>(it_conj_cb));
90       callbacks.put("it-conj-care", new it_conj_are<T>(it_conj_cb));
91       callbacks.put("it-conj-carsi", new it_conj_are<T>(it_conj_cb));
92       callbacks.put("it-conj-ciare", new it_conj_are<T>(it_conj_cb));
93       callbacks.put("it-conj-ciarsi", new it_conj_are<T>(it_conj_cb));
94       callbacks.put("it-conj-iare", new it_conj_are<T>(it_conj_cb));
95       callbacks.put("it-conj-iarsi", new it_conj_are<T>(it_conj_cb));
96       callbacks.put("it-conj-iare-b", new it_conj_are<T>(it_conj_cb));
97       callbacks.put("it-conj-iarsi-b", new it_conj_are<T>(it_conj_cb));
98       callbacks.put("it-conj-ire", new it_conj_ire<T>(it_conj_cb));
99       callbacks.put("it-conj-irsi", new it_conj_ire<T>(it_conj_cb));
100       callbacks.put("it-conj-ire-b", new it_conj_ire<T>(it_conj_cb));
101       callbacks.put("it-conj-irsi-b", new it_conj_ire<T>(it_conj_cb));
102       callbacks.put("it-conj-cire", new it_conj_ire<T>(it_conj_cb));
103       callbacks.put("it-conj-cirsi", new it_conj_ire<T>(it_conj_cb));
104       callbacks.put("it-conj-ire", new it_conj_ire<T>(it_conj_cb));
105       callbacks.put("it-conj-ere", new it_conj_ere<T>(it_conj_cb));
106       callbacks.put("it-conj-ersi", new it_conj_ere<T>(it_conj_cb));
107       callbacks.put("it-conj-urre", new it_conj_urre<T>(it_conj_cb));
108       callbacks.put("it-conj-ursi", new it_conj_urre<T>(it_conj_cb));
109       callbacks.put("it-conj-fare", new it_conj_fare<T>(it_conj_cb));
110
111       
112       //"{{it-conj-fare|putre|avere}}\n" + 
113
114       
115   }
116
117   static {
118     addGenericCallbacks(DEFAULT);
119       
120     FunctionCallback<EnParser> callback = new TranslationCallback<EnParser>();
121     DEFAULT.put("t", callback);
122     DEFAULT.put("t+", callback);
123     DEFAULT.put("t-", callback);
124     DEFAULT.put("tø", callback);
125     DEFAULT.put("apdx-t", callback);
126     
127     callback = new l_term();
128     DEFAULT.put("l", callback);
129     DEFAULT.put("term", callback);
130
131     //callback = new AppendArg0();
132
133     callback = new FormOf();
134     DEFAULT.put("form of", callback);
135     DEFAULT.put("conjugation of", callback);
136     DEFAULT.put("participle of", callback);
137     DEFAULT.put("present participle of", callback);
138     DEFAULT.put("past participle of", callback);
139     DEFAULT.put("feminine past participle of", callback);
140     DEFAULT.put("gerund of", callback);
141     DEFAULT.put("feminine of", callback);
142     DEFAULT.put("plural of", callback);
143     DEFAULT.put("feminine plural of", callback);
144     DEFAULT.put("inflected form of", callback);
145     DEFAULT.put("alternative form of", callback);
146     DEFAULT.put("dated form of", callback);
147     DEFAULT.put("apocopic form of", callback);
148     
149     callback = new InflOrHead();
150     DEFAULT.put("infl", callback);
151     DEFAULT.put("head", callback);
152   }
153   
154   static final NameAndArgs<EnParser> NAME_AND_ARGS = new NameAndArgs<EnParser>();
155
156   // ------------------------------------------------------------------
157
158   static final class TranslationCallback<T extends AbstractWiktionaryParser> implements FunctionCallback<T> {
159     @Override
160     public boolean onWikiFunction(final WikiTokenizer wikiTokenizer, final String name, final List<String> args,
161         final Map<String, String> namedArgs, final T parser,
162         final AppendAndIndexWikiCallback<T> appendAndIndexWikiCallback) {
163
164       final String transliteration = namedArgs.remove("tr");
165       final String alt = namedArgs.remove("alt");
166       namedArgs.keySet().removeAll(EnParser.USELESS_WIKI_ARGS);
167       if (args.size() < 2) {
168         if (!name.equals("ttbc")) {
169           EnParser.LOG.warning("{{t...}} with wrong args: title=" + parser.title + ", " + wikiTokenizer.token());
170         }
171         return false;
172       }
173       final String langCode = ListUtil.get(args, 0);
174       if (!appendAndIndexWikiCallback.langCodeToTCount.containsKey(langCode)) {
175         appendAndIndexWikiCallback.langCodeToTCount.put(langCode, new AtomicInteger());
176       }
177       appendAndIndexWikiCallback.langCodeToTCount.get(langCode).incrementAndGet();
178       final String word = ListUtil.get(args, 1);
179       appendAndIndexWikiCallback.dispatch(alt != null ? alt : word, EntryTypeName.WIKTIONARY_TITLE_MULTI);
180
181       // Genders...
182       if (args.size() > 2) {
183         appendAndIndexWikiCallback.builder.append(" {");
184         for (int i = 2; i < args.size(); ++i) {
185           if (i > 2) {
186             appendAndIndexWikiCallback.builder.append("|");
187           }
188           appendAndIndexWikiCallback.builder.append(args.get(i));
189         }
190         appendAndIndexWikiCallback.builder.append("}");
191       }
192
193       if (transliteration != null) {
194         appendAndIndexWikiCallback.builder.append(" (");
195         appendAndIndexWikiCallback.dispatch(transliteration, EntryTypeName.WIKTIONARY_TRANSLITERATION);
196         appendAndIndexWikiCallback.builder.append(")");
197       }
198       
199       if (alt != null) {
200         // If alt wasn't null, we appended alt instead of the actual word
201         // we're filing under..
202         appendAndIndexWikiCallback.builder.append(" (");
203         appendAndIndexWikiCallback.dispatch(word, EntryTypeName.WIKTIONARY_TITLE_MULTI);
204         appendAndIndexWikiCallback.builder.append(")");
205       }
206
207       // Catch-all for anything else...
208       if (!namedArgs.isEmpty()) {
209         appendAndIndexWikiCallback.builder.append(" {");
210         EnParser.appendNamedArgs(namedArgs, appendAndIndexWikiCallback);
211         appendAndIndexWikiCallback.builder.append("}");
212       }
213       
214       return true;
215     }
216   }
217
218   // ------------------------------------------------------------------
219   
220   static final class QualifierCallback<T extends AbstractWiktionaryParser> implements FunctionCallback<T> {
221     @Override
222     public boolean onWikiFunction(final WikiTokenizer wikiTokenizer, final String name, final List<String> args,
223         final Map<String, String> namedArgs,
224         final T parser,
225         final AppendAndIndexWikiCallback<T> appendAndIndexWikiCallback) {
226       if (args.size() != 1 || !namedArgs.isEmpty()) {
227         EnParser.LOG.warning("weird qualifier: ");
228         return false;
229       }
230       String qualifier = args.get(0);
231       appendAndIndexWikiCallback.builder.append("(");
232       appendAndIndexWikiCallback.dispatch(qualifier, null);
233       appendAndIndexWikiCallback.builder.append(")");
234       return true;
235     }
236   }
237
238   // ------------------------------------------------------------------
239   
240   static final class EncodingCallback<T extends AbstractWiktionaryParser> implements FunctionCallback<T> {
241     @Override
242     public boolean onWikiFunction(final WikiTokenizer wikiTokenizer, final String name, final List<String> args,
243         final Map<String, String> namedArgs,
244         final T parser,
245         final AppendAndIndexWikiCallback<T> appendAndIndexWikiCallback) {
246       //namedArgs.remove("lang");
247       if (!namedArgs.isEmpty()) {
248         EnParser.LOG.warning("weird encoding: " + wikiTokenizer.token());
249         return false;
250       }
251       if (args.size() == 0) {
252         // Things like "{{Jpan}}" exist.
253         return true;
254       }
255       
256       if (name.equals("IPA")) {
257           appendAndIndexWikiCallback.dispatch("IPA: ", null);
258       }
259       
260       for (int i = 0; i < args.size(); ++i) {
261         if (i > 0) {
262           appendAndIndexWikiCallback.builder.append(", ");
263         }
264         final String arg = args.get(i);
265 //        if (arg.equals(parser.title)) {
266 //          parser.titleAppended = true;
267 //        }
268         appendAndIndexWikiCallback.dispatch(arg, appendAndIndexWikiCallback.entryTypeName);
269       }
270       
271       return true;
272     }
273   }
274
275   // ------------------------------------------------------------------
276   
277   static final class Gender<T extends AbstractWiktionaryParser> implements FunctionCallback<T> {
278     @Override
279     public boolean onWikiFunction(final WikiTokenizer wikiTokenizer, final String name, final List<String> args,
280         final Map<String, String> namedArgs,
281         final T parser,
282         final AppendAndIndexWikiCallback<T> appendAndIndexWikiCallback) {
283       if (!namedArgs.isEmpty()) {
284         return false;
285       }
286       appendAndIndexWikiCallback.builder.append("{");
287       appendAndIndexWikiCallback.builder.append(name);
288       for (int i = 0; i < args.size(); ++i) {
289         appendAndIndexWikiCallback.builder.append("|").append(args.get(i));
290       }
291       appendAndIndexWikiCallback.builder.append("}");
292       return true;
293     }
294   }
295
296   // ------------------------------------------------------------------
297   
298   static final class l_term implements FunctionCallback<EnParser> {
299     @Override
300     public boolean onWikiFunction(final WikiTokenizer wikiTokenizer, final String name, final List<String> args,
301         final Map<String, String> namedArgs,
302         final EnParser parser,
303         final AppendAndIndexWikiCallback<EnParser> appendAndIndexWikiCallback) {
304       
305       // for {{l}}, lang is arg 0, but not for {{term}}
306       if (name.equals("term")) {
307         args.add(0, "");
308       }
309       
310       final EntryTypeName entryTypeName;
311       switch (parser.state) {
312       case TRANSLATION_LINE: entryTypeName = EntryTypeName.WIKTIONARY_TRANSLATION_OTHER_TEXT; break;
313       case ENGLISH_DEF_OF_FOREIGN: entryTypeName = EntryTypeName.WIKTIONARY_ENGLISH_DEF_WIKI_LINK; break;
314       default: throw new IllegalStateException("Invalid enum value: " + parser.state);
315       }
316       
317       final String langCode = args.get(0);
318       final IndexBuilder indexBuilder;
319       if ("".equals(langCode)) {
320         indexBuilder = parser.foreignIndexBuilder;
321       } else if ("en".equals(langCode)) {
322         indexBuilder = parser.enIndexBuilder;
323       } else {
324         indexBuilder = parser.foreignIndexBuilder;
325       }
326       
327       String displayText = ListUtil.get(args, 2, "");
328       if (displayText.equals("")) {
329         displayText = ListUtil.get(args, 1, null);
330       }
331       
332       if (displayText != null) {
333         appendAndIndexWikiCallback.dispatch(displayText, indexBuilder, entryTypeName);
334       } else {
335         EnParser.LOG.warning("no display text: " + wikiTokenizer.token());
336       }
337       
338       final String tr = namedArgs.remove("tr");
339       if (tr != null) {
340         appendAndIndexWikiCallback.builder.append(" (");
341         appendAndIndexWikiCallback.dispatch(tr, indexBuilder, EntryTypeName.WIKTIONARY_TRANSLITERATION);
342         appendAndIndexWikiCallback.builder.append(")");
343       }
344       
345       final String gloss = ListUtil.get(args, 3, "");
346       if (!gloss.equals("")) {
347         appendAndIndexWikiCallback.builder.append(" (");
348         appendAndIndexWikiCallback.dispatch(gloss, parser.enIndexBuilder, EntryTypeName.WIKTIONARY_ENGLISH_DEF);
349         appendAndIndexWikiCallback.builder.append(")");
350       }
351       
352       namedArgs.keySet().removeAll(EnParser.USELESS_WIKI_ARGS);
353       if (!namedArgs.isEmpty()) {
354         appendAndIndexWikiCallback.builder.append(" {").append(name);
355         EnParser.appendNamedArgs(namedArgs, appendAndIndexWikiCallback);
356         appendAndIndexWikiCallback.builder.append("}");
357       }
358
359       return true;
360     }
361   }
362
363   // ------------------------------------------------------------------
364   
365   static final class AppendArg0<T extends AbstractWiktionaryParser> implements FunctionCallback<EnParser> {
366     @Override
367     public boolean onWikiFunction(final WikiTokenizer wikiTokenizer, final String name, final List<String> args,
368         final Map<String, String> namedArgs,
369         final EnParser parser,
370         final AppendAndIndexWikiCallback<EnParser> appendAndIndexWikiCallback) {
371       if (args.size() != 1 || !namedArgs.isEmpty()) {
372         return false;
373       }
374       appendAndIndexWikiCallback.dispatch(args.get(0), EntryTypeName.WIKTIONARY_TRANSLATION_OTHER_TEXT);
375
376       final String tr = namedArgs.remove("tr");
377       if (tr != null) {
378         appendAndIndexWikiCallback.builder.append(" (");
379         appendAndIndexWikiCallback.dispatch(tr, EntryTypeName.WIKTIONARY_TRANSLATION_OTHER_TEXT);
380         appendAndIndexWikiCallback.builder.append(")");
381         parser.wordForms.add(tr);
382       }
383
384       return true;
385     }
386   }
387
388   // ------------------------------------------------------------------
389   
390   static final class italbrac<T extends AbstractWiktionaryParser> implements FunctionCallback<T> {
391     @Override
392     public boolean onWikiFunction(final WikiTokenizer wikiTokenizer, final String name, final List<String> args,
393         final Map<String, String> namedArgs,
394         final T parser,
395         final AppendAndIndexWikiCallback<T> appendAndIndexWikiCallback) {
396       if (args.size() != 1 || !namedArgs.isEmpty()) {
397         return false;
398       }
399       appendAndIndexWikiCallback.builder.append("(");
400       appendAndIndexWikiCallback.dispatch(args.get(0), EntryTypeName.WIKTIONARY_TRANSLATION_OTHER_TEXT);
401       appendAndIndexWikiCallback.builder.append(")");
402       return true;
403     }
404   }
405
406   // ------------------------------------------------------------------
407   
408   static final class gloss<T extends AbstractWiktionaryParser> implements FunctionCallback<T> {
409     @Override
410     public boolean onWikiFunction(final WikiTokenizer wikiTokenizer, final String name, final List<String> args,
411         final Map<String, String> namedArgs,
412         final T parser,
413         final AppendAndIndexWikiCallback<T> appendAndIndexWikiCallback) {
414       if (args.size() != 1 || !namedArgs.isEmpty()) {
415         return false;
416       }
417       appendAndIndexWikiCallback.builder.append("(");
418       appendAndIndexWikiCallback.dispatch(args.get(0), EntryTypeName.WIKTIONARY_TRANSLATION_OTHER_TEXT);
419       appendAndIndexWikiCallback.builder.append(")");
420       return true;
421     }
422   }
423   
424   // ------------------------------------------------------------------
425   
426   static final class Ignore<T extends AbstractWiktionaryParser> implements FunctionCallback<T> {
427     @Override
428     public boolean onWikiFunction(final WikiTokenizer wikiTokenizer, final String name, final List<String> args,
429         final Map<String, String> namedArgs,
430         final T parser,
431         final AppendAndIndexWikiCallback<T> appendAndIndexWikiCallback) {
432       return true;
433     }
434   }
435
436   // ------------------------------------------------------------------
437   
438   static final class not_used<T extends AbstractWiktionaryParser> implements FunctionCallback<T> {
439     @Override
440     public boolean onWikiFunction(final WikiTokenizer wikiTokenizer, final String name, final List<String> args,
441         final Map<String, String> namedArgs,
442         final T parser,
443         final AppendAndIndexWikiCallback<T> appendAndIndexWikiCallback) {
444       appendAndIndexWikiCallback.builder.append("(not used)");
445       return true;
446     }
447   }
448
449
450   // ------------------------------------------------------------------
451   
452   static final class AppendName<T extends AbstractWiktionaryParser> implements FunctionCallback<T> {
453     @Override
454     public boolean onWikiFunction(final WikiTokenizer wikiTokenizer, final String name, final List<String> args,
455         final Map<String, String> namedArgs,
456         final T parser,
457         final AppendAndIndexWikiCallback<T> appendAndIndexWikiCallback) {
458       if (!args.isEmpty() || !namedArgs.isEmpty()) {
459         return false;
460       }
461       appendAndIndexWikiCallback.builder.append(name);
462       return true;
463     }
464   }
465
466   // --------------------------------------------------------------------
467   // --------------------------------------------------------------------
468   
469
470   static final class FormOf implements FunctionCallback<EnParser> {
471     @Override
472     public boolean onWikiFunction(final WikiTokenizer wikiTokenizer, final String name, final List<String> args,
473         final Map<String, String> namedArgs,
474         final EnParser parser,
475         final AppendAndIndexWikiCallback<EnParser> appendAndIndexWikiCallback) {
476       parser.entryIsFormOfSomething = true;
477       String formName = name;
478       if (name.equals("form of")) {
479         formName = ListUtil.remove(args, 0, null);
480       }
481       if (formName == null) {
482         EnParser.LOG.warning("Missing form name: " + parser.title);
483         formName = "form of";
484       }
485       String baseForm = ListUtil.get(args, 1, "");
486       if ("".equals(baseForm)) {
487         baseForm = ListUtil.get(args, 0, null);
488         ListUtil.remove(args, 1, "");
489       } else {
490         ListUtil.remove(args, 0, null);
491       }
492       namedArgs.keySet().removeAll(EnParser.USELESS_WIKI_ARGS);
493       
494       appendAndIndexWikiCallback.builder.append("{");
495       NAME_AND_ARGS.onWikiFunction(wikiTokenizer, formName, args, namedArgs, parser, appendAndIndexWikiCallback);
496       appendAndIndexWikiCallback.builder.append("}");
497       if (baseForm != null && appendAndIndexWikiCallback.indexedEntry != null) {
498         parser.foreignIndexBuilder.addEntryWithString(appendAndIndexWikiCallback.indexedEntry, baseForm, EntryTypeName.WIKTIONARY_BASE_FORM_MULTI);
499       } else {
500         // null baseForm happens in Danish.
501         EnParser.LOG.warning("Null baseform: " + parser.title);
502       }
503       return true;
504     }
505   }
506   
507   static final EnFunctionCallbacks.FormOf FORM_OF = new FormOf();
508   
509
510   // --------------------------------------------------------------------
511   // --------------------------------------------------------------------
512   
513   static final class wikipedia<T extends AbstractWiktionaryParser> implements FunctionCallback<T> {
514     @Override
515     public boolean onWikiFunction(final WikiTokenizer wikiTokenizer, final String name, final List<String> args,
516         final Map<String, String> namedArgs,
517         final T parser,
518         final AppendAndIndexWikiCallback<T> appendAndIndexWikiCallback) {
519       namedArgs.remove("lang");
520       if (args.size() > 1 || !namedArgs.isEmpty()) {
521         // Unindexed!
522         return false;
523       } else if (args.size() == 1) {
524         return false;
525       } else {
526         return true;
527       }
528     }
529   }
530
531   static final class InflOrHead implements FunctionCallback<EnParser> {
532     @Override
533     public boolean onWikiFunction(final WikiTokenizer wikiTokenizer, final String name, final List<String> args,
534         final Map<String, String> namedArgs,
535         final EnParser parser,
536         final AppendAndIndexWikiCallback<EnParser> appendAndIndexWikiCallback) {
537       // See: http://en.wiktionary.org/wiki/Template:infl
538       // TODO: Actually these functions should start a new WordPOS:
539       // See: http://en.wiktionary.org/wiki/quattro
540       final String langCode = ListUtil.get(args, 0);
541       String head = namedArgs.remove("head");
542       if (head == null) {
543         head = namedArgs.remove("title"); // Bug
544       }
545       if (head == null) {
546         head = parser.title;
547       }
548       
549       namedArgs.keySet().removeAll(EnParser.USELESS_WIKI_ARGS);
550
551       final String tr = namedArgs.remove("tr");
552       String g = namedArgs.remove("g");
553       if (g == null) {
554         g = namedArgs.remove("gender");
555       }
556       final String g2 = namedArgs.remove("g2");
557       final String g3 = namedArgs.remove("g3");
558
559       // We might have already taken care of this in a generic way...
560       if (!parser.titleAppended) {
561         appendAndIndexWikiCallback.dispatch(head, EntryTypeName.WIKTIONARY_TITLE_MULTI);
562         parser.titleAppended = true;
563       }
564
565       if (g != null) {
566         appendAndIndexWikiCallback.builder.append(" {").append(g);
567         if (g2 != null) {
568           appendAndIndexWikiCallback.builder.append("|").append(g2);
569         }
570         if (g3 != null) {
571           appendAndIndexWikiCallback.builder.append("|").append(g3);
572         }
573         appendAndIndexWikiCallback.builder.append("}");
574       }
575
576       if (tr != null) {
577         appendAndIndexWikiCallback.builder.append(" (");
578         appendAndIndexWikiCallback.dispatch(tr, EntryTypeName.WIKTIONARY_TITLE_MULTI);
579         appendAndIndexWikiCallback.builder.append(")");
580         parser.wordForms.add(tr);
581       }
582
583       final String pos = ListUtil.get(args, 1);
584       if (pos != null) {
585         appendAndIndexWikiCallback.builder.append(" (").append(pos).append(")");
586       }
587       for (int i = 2; i < args.size(); i += 2) {
588         final String inflName = ListUtil.get(args, i);
589         final String inflValue = ListUtil.get(args, i + 1);
590         appendAndIndexWikiCallback.builder.append(", ");
591         appendAndIndexWikiCallback.dispatch(inflName, null, null);
592         if (inflValue != null && inflValue.length() > 0) {
593           appendAndIndexWikiCallback.builder.append(": ");
594           appendAndIndexWikiCallback.dispatch(inflValue, null, null);
595           parser.wordForms.add(inflValue);
596         }
597       }
598       for (final String key : namedArgs.keySet()) {
599         final String value = WikiTokenizer.toPlainText(namedArgs.get(key));
600         appendAndIndexWikiCallback.builder.append(" ");
601         appendAndIndexWikiCallback.dispatch(key, null, null);
602         appendAndIndexWikiCallback.builder.append("=");
603         appendAndIndexWikiCallback.dispatch(value, null, null);
604         parser.wordForms.add(value);
605       }
606       return true;
607     }
608   }
609   
610
611   static {
612     DEFAULT.put("it-noun", new it_noun());
613   } 
614   static final class it_noun implements FunctionCallback<EnParser> {
615     @Override
616     public boolean onWikiFunction(final WikiTokenizer wikiTokenizer, final String name, final List<String> args,
617         final Map<String, String> namedArgs,
618         final EnParser parser,
619         final AppendAndIndexWikiCallback<EnParser> appendAndIndexWikiCallback) {
620       parser.titleAppended = true;
621       final String base = ListUtil.get(args, 0);
622       final String gender = ListUtil.get(args, 1);
623       final String singular = base + ListUtil.get(args, 2, null);
624       final String plural = base + ListUtil.get(args, 3, null);
625       appendAndIndexWikiCallback.builder.append(" ");
626       appendAndIndexWikiCallback.dispatch(singular, null, null);
627       appendAndIndexWikiCallback.builder.append(" {").append(gender).append("}, ");
628       appendAndIndexWikiCallback.dispatch(plural, null, null);
629       appendAndIndexWikiCallback.builder.append(" {pl}");
630       final String f = namedArgs.remove("f");
631       if (f != null) {
632           appendAndIndexWikiCallback.builder.append(", ");
633           appendAndIndexWikiCallback.dispatch(f, null, null);
634           appendAndIndexWikiCallback.builder.append(" {f}");
635       }
636       final String m = namedArgs.remove("f");
637       if (m != null) {
638           appendAndIndexWikiCallback.builder.append(", ");
639           appendAndIndexWikiCallback.dispatch(m, null, null);
640           appendAndIndexWikiCallback.builder.append(" {m}");
641       }
642       parser.wordForms.add(singular);
643       parser.wordForms.add(plural);
644       if (!namedArgs.isEmpty() || args.size() > 4) {
645         EnParser.LOG.warning("Invalid it-noun: " + wikiTokenizer.token());
646       }
647       return true;
648     }
649   }
650
651   static {
652     DEFAULT.put("it-proper noun", new it_proper_noun<EnParser>());
653   } 
654   static final class it_proper_noun<T extends AbstractWiktionaryParser> implements FunctionCallback<T> {
655     @Override
656     public boolean onWikiFunction(final WikiTokenizer wikiTokenizer, final String name, final List<String> args,
657         final Map<String, String> namedArgs,
658         final T parser,
659         final AppendAndIndexWikiCallback<T> appendAndIndexWikiCallback) {
660       return false;
661     }
662   }
663   
664   // -----------------------------------------------------------------------
665   // Italian stuff
666   // -----------------------------------------------------------------------
667   
668 static final class it_conj_are<T extends AbstractWiktionaryParser> implements FunctionCallback<T> {
669     final it_conj<T> dest;
670     it_conj_are(it_conj<T> dest) {
671       this.dest = dest;
672     }
673     @Override
674       public boolean onWikiFunction(final WikiTokenizer wikiTokenizer, final String name, final List<String> args,
675           final Map<String, String> namedArgs,
676           final T parser,
677           final AppendAndIndexWikiCallback<T> appendAndIndexWikiCallback) {
678         final String h = name.equals("it-conj-care") || name.equals("it-conj-carsi") ? "h" : "";
679         final String i = name.equals("it-conj-ciare") || name.equals("it-conj-ciarsi") ? "i" : "";
680         final String i2 = name.equals("it-conj-iare") || name.equals("it-conj-iarsi") ? "" : "i";
681         final boolean si = name.equals("it-conj-arsi") || name.equals("it-conj-iarsi") || name.equals("it-conj-iarsi-b") || name.equals("it-conj-carsi") || name.equals("it-conj-ciarsi");
682         final String root = args.get(0);
683         passThroughOrFillIn(namedArgs, "inf", root + i + (si ? "arsi" : "are"), false);
684         namedArgs.put("aux", ListUtil.get(args, 1, ""));
685         passThroughOrFillIn(namedArgs, "ger", root + i + "ando" + (si ? "si" : ""), true);
686         passThroughOrFillIn(namedArgs, "presp", root + i + "ante"+ (si ? "si" : ""), true);
687         passThroughOrFillIn(namedArgs, "pastp", root + i + "ato", true);
688         if (si) {
689             passThroughOrFillIn(namedArgs, "pastp2", root + i + "atosi", true);
690         }
691         final String i2b = (name.equals("it-conj-iare-b") || name.equals("it-conj-iarsi-b")) ? "" : i2;
692         
693         it_conj_passMood(namedArgs, "pres", false, root, Arrays.asList(i + "o", h + i2, i + "a", h + i2 + "amo", i + "ate", i + "ano"));
694         it_conj_passMood(namedArgs, "imperf", false, root, Arrays.asList(i + "avo", i + "avi", i + "ava", i + "avamo", i + "avate", i + "avano"));
695         it_conj_passMood(namedArgs, "prem", false, root, Arrays.asList(i + "ai", i + "asti", i + "ò", i + "ammo", i + "aste", i + "arono"));
696         it_conj_passMood(namedArgs, "fut", true, root, Arrays.asList(h + "erò", h + "erai", h + "erà", h + "eremo", h + "erete", h + "eranno"));
697         it_conj_passMood(namedArgs, "cond", true, root, Arrays.asList(h + "erei", h + "eresti", h + "erebbe", h + "eremmo", h + "ereste", h + "erebbero"));
698         
699         passThroughOrFillIn(namedArgs, "sub123s", root + h + i2, false);
700         passThroughOrFillIn(namedArgs, "sub1p", root + h + i2b + "amo", false);
701         passThroughOrFillIn(namedArgs, "sub2p", root + h + i2b + "ate", false);
702         passThroughOrFillIn(namedArgs, "sub3p", root + h + i2 + "no", false);
703
704         passThroughOrFillIn(namedArgs, "impsub12s", root + i + "assi", false);
705         passThroughOrFillIn(namedArgs, "impsub3s", root + i + "asse", false);
706         passThroughOrFillIn(namedArgs, "impsub1p", root + i + "assimo", false);
707         passThroughOrFillIn(namedArgs, "impsub2p", root + i + "aste", false);
708         passThroughOrFillIn(namedArgs, "impsub3p", root + i + "assero", false);
709
710         passThroughOrFillIn(namedArgs, "imp2s", root + i + "a" + (si ? "ti" : ""), true);
711         passThroughOrFillIn(namedArgs, "imp3s", (si ? "si " : "") + root + h + i2, true);
712         passThroughOrFillIn(namedArgs, "imp1p", root + h + i2b + "amo" + (si ? "ci" : ""), true);
713         passThroughOrFillIn(namedArgs, "imp2p", root + i + "ate" + (si ? "vi" : ""), true);
714         passThroughOrFillIn(namedArgs, "imp3p", (si ? "si " : "") + root + h + i2 + "no", true);
715
716         return dest.onWikiFunction(wikiTokenizer, name, args, namedArgs, parser, appendAndIndexWikiCallback);
717       }
718     }
719
720   static final class it_conj_ire<T extends AbstractWiktionaryParser> implements FunctionCallback<T> {
721     final it_conj<T> dest;
722     it_conj_ire(it_conj<T> dest) {
723       this.dest = dest;
724     }
725     @Override
726       public boolean onWikiFunction(final WikiTokenizer wikiTokenizer, final String name, final List<String> args,
727           final Map<String, String> namedArgs,
728           final T parser,
729           final AppendAndIndexWikiCallback<T> appendAndIndexWikiCallback) {
730         final String root = args.get(0);
731         final String i = name.equals("it-conj-cire") || name.equals("it-conj-cirsi") ? "i" : "";
732         final boolean si = name.equals("it-conj-irsi") || name.equals("it-conj-irsi-b") || name.equals("it-conj-cirsi");
733
734         passThroughOrFillIn(namedArgs, "inf", root + (si ? "irsi" : "ire"), false);
735         namedArgs.put("aux", ListUtil.get(args, 1, ""));
736         passThroughOrFillIn(namedArgs, "ger", root + "endo" + (si ? "si" : ""), true);
737         passThroughOrFillIn(namedArgs, "presp", root + "ente" + (si ? "si" : ""), true);
738         passThroughOrFillIn(namedArgs, "pastp", root + "ito", true);
739         if (si) {
740             passThroughOrFillIn(namedArgs, "pastp2", root + "itosi", true);
741         }
742         if (!name.endsWith("-b")) {
743             it_conj_passMood(namedArgs, "pres", false, root, Arrays.asList(i + "o", "i", "e", "iamo", "ite", i + "ono"));
744         } else {
745             it_conj_passMood(namedArgs, "pres", false, root, Arrays.asList("isco", "isci", "isce", "iamo", "ite", "iscono"));
746         }
747         it_conj_passMood(namedArgs, "imperf", false, root, Arrays.asList("ivo", "ivi", "iva", "ivamo", "ivate", "ivano"));
748         it_conj_passMood(namedArgs, "prem", false, root, Arrays.asList("ii", "isti", "ì", "immo", "iste", "irono"));
749         // Regular past historic synonyms:
750         passThroughOrFillIn(namedArgs, "prem3s2", root + "é", true);
751         passThroughOrFillIn(namedArgs, "prem3p2", root + "erono", true);
752         it_conj_passMood(namedArgs, "fut", true, root, Arrays.asList("irò", "irai", "irà", "iremo", "irete", "iranno"));
753         it_conj_passMood(namedArgs, "cond", true, root, Arrays.asList("irei", "iresti", "irebbe", "iremmo", "ireste", "irebbero"));
754
755         if (!name.endsWith("-b")) {
756             passThroughOrFillIn(namedArgs, "sub123s", root + i + "a", false);
757             passThroughOrFillIn(namedArgs, "sub3p", root + i + "ano", false);
758         } else {
759             passThroughOrFillIn(namedArgs, "sub123s", root + "isca", false);
760             passThroughOrFillIn(namedArgs, "sub3p", root + "iscano", false);
761         }
762         passThroughOrFillIn(namedArgs, "sub1p", root + "iamo", false);
763         passThroughOrFillIn(namedArgs, "sub2p", root + "iate", false);
764
765         passThroughOrFillIn(namedArgs, "impsub12s", root + "issi", false);
766         passThroughOrFillIn(namedArgs, "impsub3s", root + "isse", false);
767         passThroughOrFillIn(namedArgs, "impsub1p", root + "issimo", false);
768         passThroughOrFillIn(namedArgs, "impsub2p", root + "iste", false);
769         passThroughOrFillIn(namedArgs, "impsub3p", root + "issero", false);
770
771         if (!name.endsWith("-b")) {
772             passThroughOrFillIn(namedArgs, "imp2s", root + "i" + (si ? "ti" : ""), true);
773             passThroughOrFillIn(namedArgs, "imp3s", (si ? "si " : "") + root + i + "a", true);
774             passThroughOrFillIn(namedArgs, "imp3p", (si ? "si " : "") + root + i + "ano", true);
775         } else {
776             passThroughOrFillIn(namedArgs, "imp2s", root + "isci" + (si ? "ti" : ""), true);
777             passThroughOrFillIn(namedArgs, "imp3s", (si ? "si " : "") + root + "isca", true);
778             passThroughOrFillIn(namedArgs, "imp3p", (si ? "si " : "") + root + "iscano", true);
779         }
780         passThroughOrFillIn(namedArgs, "imp1p", root + "iamo" + (si ? "ci" : ""), true);
781         passThroughOrFillIn(namedArgs, "imp2p", root + "ite" + (si ? "vi" : ""), true);
782
783         return dest.onWikiFunction(wikiTokenizer, name, args, namedArgs, parser, appendAndIndexWikiCallback);
784       }
785     }
786
787   
788   static final class it_conj_ere<T extends AbstractWiktionaryParser> implements FunctionCallback<T> {
789       final it_conj<T> dest;
790       it_conj_ere(it_conj<T> dest) {
791         this.dest = dest;
792       }
793       @Override
794         public boolean onWikiFunction(final WikiTokenizer wikiTokenizer, final String name, final List<String> args,
795             final Map<String, String> namedArgs,
796             final T parser,
797             final AppendAndIndexWikiCallback<T> appendAndIndexWikiCallback) {
798           final String root = args.get(0);
799           final boolean si = name.equals("it-conj-ersi");
800
801           passThroughOrFillIn(namedArgs, "inf", root + (si ? "ersi" : "ere"), false);
802           namedArgs.put("aux", ListUtil.get(args, 1, ""));
803           passThroughOrFillIn(namedArgs, "ger", root + "endo" + (si ? "si" : ""), true);
804           passThroughOrFillIn(namedArgs, "presp", root + "ente" + (si ? "si" : ""), true);
805           passThroughOrFillIn(namedArgs, "pastp", root + "uto", true);
806           if (si) {
807               passThroughOrFillIn(namedArgs, "pastp2", root + "utosi", true);
808           }
809           it_conj_passMood(namedArgs, "pres", false, root, Arrays.asList("o", "i", "e", "iamo", "ete", "ono"));
810           it_conj_passMood(namedArgs, "imperf", false, root, Arrays.asList("evo", "evi", "eva", "evamo", "evate", "evano"));
811           it_conj_passMood(namedArgs, "prem", false, root, Arrays.asList("ei", "esti", "ette", "emmo", "este", "ettero"));
812           // Regular past historic synonyms:
813           passThroughOrFillIn(namedArgs, "prem3s2", root + "é", true);
814           passThroughOrFillIn(namedArgs, "prem3p2", root + "erono", true);
815           it_conj_passMood(namedArgs, "fut", true, root, Arrays.asList("erò", "erai", "erà", "eremo", "erete", "eranno"));
816           it_conj_passMood(namedArgs, "cond", true, root, Arrays.asList("erei", "eresti", "erebbe", "eremmo", "ereste", "erebbero"));
817
818           passThroughOrFillIn(namedArgs, "sub123s", root + "a", false);
819           passThroughOrFillIn(namedArgs, "sub1p", root + "iamo", false);
820           passThroughOrFillIn(namedArgs, "sub2p", root + "iate", false);
821           passThroughOrFillIn(namedArgs, "sub3p", root + "ano", false);
822
823           passThroughOrFillIn(namedArgs, "impsub12s", root + "essi", false);
824           passThroughOrFillIn(namedArgs, "impsub3s", root + "esse", false);
825           passThroughOrFillIn(namedArgs, "impsub1p", root + "essimo", false);
826           passThroughOrFillIn(namedArgs, "impsub2p", root + "este", false);
827           passThroughOrFillIn(namedArgs, "impsub3p", root + "essero", false);
828
829           passThroughOrFillIn(namedArgs, "imp2s", root + "i" + (si ? "ti" : ""), true);
830           passThroughOrFillIn(namedArgs, "imp3s", (si ? "si " : "") + root + "a", true);
831           passThroughOrFillIn(namedArgs, "imp1p", root + "iamo" + (si ? "ci" : ""), true);
832           passThroughOrFillIn(namedArgs, "imp2p", root + "ete" + (si ? "vi" : ""), true);
833           passThroughOrFillIn(namedArgs, "imp3p", (si ? "si " : "") + root + "ano", true);
834
835           return dest.onWikiFunction(wikiTokenizer, name, args, namedArgs, parser, appendAndIndexWikiCallback);
836         }
837       }
838
839   static final class it_conj_urre<T extends AbstractWiktionaryParser> implements FunctionCallback<T> {
840       final it_conj<T> dest;
841       it_conj_urre(it_conj<T> dest) {
842         this.dest = dest;
843       }
844       @Override
845         public boolean onWikiFunction(final WikiTokenizer wikiTokenizer, final String name, final List<String> args,
846             final Map<String, String> namedArgs,
847             final T parser,
848             final AppendAndIndexWikiCallback<T> appendAndIndexWikiCallback) {
849           final String root = args.get(0);
850           final boolean si = name.equals("it-conj-ursi");
851
852           passThroughOrFillIn(namedArgs, "inf", root + (si ? "ursi" : "urre"), false);
853           namedArgs.put("aux", ListUtil.get(args, 1, ""));
854           passThroughOrFillIn(namedArgs, "ger", root + "ucendo" + (si ? "si" : ""), true);
855           passThroughOrFillIn(namedArgs, "presp", root + "ucente" + (si ? "si" : ""), true);
856           passThroughOrFillIn(namedArgs, "pastp", root + "otto", true);
857           if (si) {
858               passThroughOrFillIn(namedArgs, "pastp2", root + "ottosi", true);
859           }
860           it_conj_passMood(namedArgs, "pres", false, root, Arrays.asList("uco", "uci", "uce", "uciamo", "ucete", "ucono"));
861           it_conj_passMood(namedArgs, "imperf", false, root, Arrays.asList("ucevo", "ucevi", "uceva", "ucevamo", "ucevate", "ucevano"));
862           it_conj_passMood(namedArgs, "prem", false, root, Arrays.asList("ussi", "ucesti", "usse", "ucemmo", "uceste", "ussero"));
863           it_conj_passMood(namedArgs, "fut", true, root, Arrays.asList("urrò", "urrai", "urrà", "urremo", "urrete", "urranno"));
864           it_conj_passMood(namedArgs, "cond", true, root, Arrays.asList("urrei", "urresti", "urrebbe", "urremmo", "urreste", "urrebbero"));
865
866           passThroughOrFillIn(namedArgs, "sub123s", root + "uca", false);
867           passThroughOrFillIn(namedArgs, "sub1p", root + "uciamo", false);
868           passThroughOrFillIn(namedArgs, "sub2p", root + "uciate", false);
869           passThroughOrFillIn(namedArgs, "sub3p", root + "ucano", false);
870
871           passThroughOrFillIn(namedArgs, "impsub12s", root + "ucessi", false);
872           passThroughOrFillIn(namedArgs, "impsub3s", root + "ucesse", false);
873           passThroughOrFillIn(namedArgs, "impsub1p", root + "ucessimo", false);
874           passThroughOrFillIn(namedArgs, "impsub2p", root + "uceste", false);
875           passThroughOrFillIn(namedArgs, "impsub3p", root + "ucessero", false);
876
877           passThroughOrFillIn(namedArgs, "imp2s", root + "uci" + (si ? "ti" : ""), true);
878           passThroughOrFillIn(namedArgs, "imp3s", (si ? "si" : "") + root + "uca", true);
879           passThroughOrFillIn(namedArgs, "imp1p", root + "uciamo" + (si ? "ci" : ""), true);
880           passThroughOrFillIn(namedArgs, "imp2p", root + "ucete" + (si ? "vi" : ""), true);
881           passThroughOrFillIn(namedArgs, "imp3p", (si ? "si" : "") + root + "ucano", true);
882
883           return dest.onWikiFunction(wikiTokenizer, name, args, namedArgs, parser, appendAndIndexWikiCallback);
884         }
885       }
886
887   static final class it_conj_fare<T extends AbstractWiktionaryParser> implements FunctionCallback<T> {
888       final it_conj<T> dest;
889       it_conj_fare(it_conj<T> dest) {
890         this.dest = dest;
891       }
892       @Override
893         public boolean onWikiFunction(final WikiTokenizer wikiTokenizer, final String name, final List<String> args,
894             final Map<String, String> namedArgs,
895             final T parser,
896             final AppendAndIndexWikiCallback<T> appendAndIndexWikiCallback) {
897           final String root = args.get(0);
898           passThroughOrFillIn(namedArgs, "inf", root + "fare", false);
899           namedArgs.put("aux", ListUtil.get(args, 1, ""));
900           passThroughOrFillIn(namedArgs, "ger", root + "facendo", true);
901           passThroughOrFillIn(namedArgs, "presp", root + "facente", true);
902           passThroughOrFillIn(namedArgs, "pastp", root + "fatto", true);
903           it_conj_passMood(namedArgs, "pres", false, root, Arrays.asList("faccio", "fai", "fà", "facciamo", "fate", "fanno"));
904           passThroughOrFillIn(namedArgs, "pres1s2", root + "fò", true);
905           it_conj_passMood(namedArgs, "imperf", false, root, Arrays.asList("facevo", "facevi", "faceva", "facevamo", "facevate", "facevano"));
906           it_conj_passMood(namedArgs, "prem", false, root, Arrays.asList("feci", "facesti", "fece", "facemmo", "faceste", "fecero"));
907           it_conj_passMood(namedArgs, "fut", true, root, Arrays.asList("farò", "farai", "farà", "faremo", "farete", "faranno"));
908           it_conj_passMood(namedArgs, "cond", true, root, Arrays.asList("farei", "faresti", "farebbe", "faremmo", "fareste", "farebbero"));
909
910           passThroughOrFillIn(namedArgs, "sub123s", root + "faccia", false);
911           passThroughOrFillIn(namedArgs, "sub1p", root + "facciamo", false);
912           passThroughOrFillIn(namedArgs, "sub2p", root + "facciate", false);
913           passThroughOrFillIn(namedArgs, "sub3p", root + "facciano", false);
914
915           passThroughOrFillIn(namedArgs, "impsub12s", root + "facessi", false);
916           passThroughOrFillIn(namedArgs, "impsub3s", root + "facesse", false);
917           passThroughOrFillIn(namedArgs, "impsub1p", root + "facessimo", false);
918           passThroughOrFillIn(namedArgs, "impsub2p", root + "faceste", false);
919           passThroughOrFillIn(namedArgs, "impsub3p", root + "facessero", false);
920
921           passThroughOrFillIn(namedArgs, "imp2s", root + "fa", true);
922           passThroughOrFillIn(namedArgs, "imp3s", root + "faccia", true);
923           passThroughOrFillIn(namedArgs, "imp1p", root + "facciamo", true);
924           passThroughOrFillIn(namedArgs, "imp2p", root + "fate", true);
925           passThroughOrFillIn(namedArgs, "imp3p", root + "facciano", true);
926
927           return dest.onWikiFunction(wikiTokenizer, name, args, namedArgs, parser, appendAndIndexWikiCallback);
928         }
929       }
930
931   static final Map<String,String> it_indicativePronouns = new LinkedHashMap<String, String>();
932   static {
933       it_indicativePronouns.put("1s", "io");
934       it_indicativePronouns.put("2s", "tu");
935       it_indicativePronouns.put("3s", "lui/lei");
936       it_indicativePronouns.put("1p", "noi");
937       it_indicativePronouns.put("2p", "voi");
938       it_indicativePronouns.put("3p", "essi/esse");
939   }
940
941   static final Map<String,String> it_subjunctivePronouns = new LinkedHashMap<String, String>();
942   static {
943       it_subjunctivePronouns.put("1s", "che io");
944       it_subjunctivePronouns.put("2s", "che tu");
945       it_subjunctivePronouns.put("3s", "che lui/lei");
946       it_subjunctivePronouns.put("1p", "che noi");
947       it_subjunctivePronouns.put("2p", "che voi");
948       it_subjunctivePronouns.put("3p", "che essi/esse");
949   }
950
951   static final Map<String,String> it_imperativePronouns = new LinkedHashMap<String, String>();
952   static {
953       it_imperativePronouns.put("1s", "-");
954       it_imperativePronouns.put("2s", "tu");
955       it_imperativePronouns.put("3s", "lui/lei");
956       it_imperativePronouns.put("1p", "noi");
957       it_imperativePronouns.put("2p", "voi");
958       it_imperativePronouns.put("3p", "essi/esse");
959   }
960
961
962   static final class it_conj<T extends AbstractWiktionaryParser> implements FunctionCallback<T> {
963       @Override
964       public boolean onWikiFunction(final WikiTokenizer wikiTokenizer, final String name, final List<String> args,
965           final Map<String, String> namedArgs,
966           final T parser,
967           final AppendAndIndexWikiCallback<T> appendAndIndexWikiCallback) {
968         
969         final StringBuilder builder = appendAndIndexWikiCallback.builder;
970         
971         final String inf = namedArgs.get("inf");
972         
973         // TODO: center everything horizontally.
974         builder.append("<table style=\"background:#F0F0F0\">");
975         
976         builder.append("<tr>");
977         builder.append("<th colspan=\"1\" style=\"background:#e2e4c0\">infinito</th>");
978         builder.append("<td colspan=\"1\">");
979         appendAndIndexWikiCallback.dispatch(MapUtil.safeRemove(namedArgs, "inf", "-"), null);
980         builder.append("</td>");
981         builder.append("</tr>\n");
982
983         builder.append("<tr>");
984         builder.append("<th colspan=\"1\" style=\"background:#e2e4c0\">verbo ausiliare</th>");
985         builder.append("<td colspan=\"1\">");
986         appendAndIndexWikiCallback.dispatch(MapUtil.safeRemove(namedArgs, "aux", "-"), null);
987         builder.append("</td>");
988         builder.append("<th colspan=\"1\" style=\"background:#e2e4c0\">gerundio</th>");
989         builder.append("<td colspan=\"1\">");
990         outputKeyVariations(appendAndIndexWikiCallback, builder, "ger", namedArgs, true);
991         builder.append("</td>");
992         builder.append("</tr>\n");
993
994         builder.append("<tr>");
995         builder.append("<th colspan=\"1\" style=\"background:#e2e4c0\">participio presente</th>");
996         builder.append("<td colspan=\"1\">");
997         outputKeyVariations(appendAndIndexWikiCallback, builder, "presp", namedArgs, true);
998         builder.append("</td>");
999         builder.append("<th colspan=\"1\" style=\"background:#e2e4c0\">participio passato</th>");
1000         builder.append("<td colspan=\"1\">");
1001         outputKeyVariations(appendAndIndexWikiCallback, builder, "pastp", namedArgs, true);
1002         builder.append("</td>");
1003         builder.append("</tr>\n");
1004         
1005         final List<String> prefixes = (inf != null && inf.endsWith("si")) ? it_reflexive_pronouns : it_empty; 
1006
1007         String style = " style=\"background:#c0cfe4\"";
1008         outputDataRow(appendAndIndexWikiCallback, style, "indicativo", style, "th", "", new LinkedHashMap<String, String>(it_indicativePronouns), it_empty, false);
1009         outputDataRow(appendAndIndexWikiCallback, style, "presente", "", "td", "pres", namedArgs, prefixes, true);
1010         outputDataRow(appendAndIndexWikiCallback, style, "imperfetto", "", "td", "imperf", namedArgs, prefixes, true);
1011         outputDataRow(appendAndIndexWikiCallback, style, "passato remoto", "", "td", "prem", namedArgs, prefixes, true);
1012         outputDataRow(appendAndIndexWikiCallback, style, "futuro", "", "td", "fut", namedArgs, prefixes, true);
1013
1014         style = " style=\"background:#c0d8e4\"";
1015         outputDataRow(appendAndIndexWikiCallback, style, "condizionale", style, "th", "", new LinkedHashMap<String, String>(it_indicativePronouns), it_empty, false);
1016         outputDataRow(appendAndIndexWikiCallback, style, "presente", "", "td", "cond", namedArgs, prefixes, true);
1017
1018         style = " style=\"background:#c0e4c0\"";
1019         outputDataRow(appendAndIndexWikiCallback, style, "congiuntivo", style, "th", "", new LinkedHashMap<String, String>(it_subjunctivePronouns), it_empty, false);
1020         namedArgs.put("sub3s2", namedArgs.remove("sub3s"));
1021         namedArgs.put("sub1s", namedArgs.get("sub123s"));
1022         namedArgs.put("sub2s", namedArgs.get("sub123s"));
1023         namedArgs.put("sub3s", namedArgs.remove("sub123s"));
1024         namedArgs.put("sub1s2", namedArgs.get("sub123s2"));
1025         namedArgs.put("sub2s2", namedArgs.get("sub123s2"));
1026         namedArgs.put("sub3s2", namedArgs.remove("sub123s2"));
1027         outputDataRow(appendAndIndexWikiCallback, style, "presente", "", "td", "sub", namedArgs, prefixes, true);
1028         namedArgs.put("impsub1s", namedArgs.get("impsub12s"));
1029         namedArgs.put("impsub2s", namedArgs.remove("impsub12s"));
1030         namedArgs.put("impsub1s2", namedArgs.get("impsub12s2"));
1031         namedArgs.put("impsub2s2", namedArgs.remove("impsub12s2"));
1032         outputDataRow(appendAndIndexWikiCallback, style, "imperfetto", "", "td", "impsub", namedArgs, prefixes, true);
1033
1034         style = " style=\"background:#e4d4c0\"";
1035         outputDataRow(appendAndIndexWikiCallback, style, "imperativo", style, "th", "", new LinkedHashMap<String, String>(it_imperativePronouns), it_empty, false);
1036         outputDataRow(appendAndIndexWikiCallback, style, "", "", "td", "imp", namedArgs, it_empty, false);  // these are attached to the stem.
1037
1038         builder.append("</table>\n");
1039         
1040         if (!namedArgs.isEmpty()) {
1041             System.err.println("NON-EMPTY namedArgs: " + namedArgs);
1042             if ("muovesse".equals(namedArgs.get("impsib3s2"))) {
1043                 return false;
1044             }
1045             if ("percuotesse".equals(namedArgs.get("impsib3s2"))) {
1046                 return false;
1047             }
1048             // Too many to deal with:
1049             //assert false;
1050             return false;
1051         }
1052
1053         return true;
1054       }
1055
1056         private void outputDataRow(AppendAndIndexWikiCallback<T> appendAndIndexWikiCallback,
1057                 String col1Style, String headerName, 
1058                 String col2Style, final String type2, 
1059                 String moodName, Map<String, String> namedArgs, final List<String> prefixes, final boolean isForm) {
1060             final StringBuilder builder = appendAndIndexWikiCallback.builder;
1061             builder.append("<tr>");
1062             builder.append("<th colspan=\"1\"").append(col1Style).append(">").append(headerName).append("</th>");
1063             int i = 0;
1064             for (final String number : it_number_s_p) {
1065                 for (final String person : it_person_1_2_3) {
1066                     // Output <td> or <th>
1067                     builder.append("<").append(type2).append("").append(col2Style).append(">");
1068                     final String keyBase = String.format("%s%s%s", moodName, person, number);
1069                     appendAndIndexWikiCallback.dispatch(prefixes.get(i++), null);
1070                     outputKeyVariations(appendAndIndexWikiCallback, builder, keyBase, namedArgs, isForm);
1071                     // Output <td> or <th>
1072                     builder.append("</").append(type2).append(">");
1073                 }
1074             }
1075             builder.append("</tr>\n");
1076         }
1077     }
1078   
1079   static void passThroughOrFillIn(final Map<String,String> namedArgs, final String key, final String fillIn, final boolean quoteToEmpty) {
1080       final String value = namedArgs.get(key);
1081       if (quoteToEmpty && "''".equals(value)) {
1082           namedArgs.put(key, "");
1083           return;
1084       }
1085       if (value == null || value.equals("")) {
1086           namedArgs.put(key, fillIn);
1087       }
1088   }
1089   
1090   static final List<String> it_number_s_p = Arrays.asList("s", "p");
1091   static final List<String> it_person_1_2_3 = Arrays.asList("1", "2", "3");
1092   static final List<String> it_reflexive_pronouns = Arrays.asList("mi ", "ti ", "si ", "ci ", "vi ", "si ");
1093   static final List<String> it_empty = Arrays.asList("", "", "", "", "", "");
1094   static void it_conj_passMood(final Map<String,String> namedArgs, final String moodName, final boolean quoteToEmpty, final String root, final List<String> suffixes) {
1095       assert suffixes.size() == 6;
1096       int i = 0;
1097       for (final String number : it_number_s_p) {
1098           for (final String person : it_person_1_2_3) {
1099               passThroughOrFillIn(namedArgs, String.format("%s%s%s", moodName, person, number), root + suffixes.get(i), quoteToEmpty);
1100               ++i;
1101           }
1102       }
1103   }
1104
1105   private static <T extends AbstractWiktionaryParser> void outputKeyVariations(AppendAndIndexWikiCallback<T> appendAndIndexWikiCallback,
1106         final StringBuilder builder, final String keyBase, Map<String, String> namedArgs, boolean isForm) {
1107     for (int suffix = 0; suffix <= 4; ++suffix) {
1108         final String key = suffix == 0 ? keyBase : keyBase + suffix;
1109         final String val = namedArgs.remove(key);
1110         if (val != null && !val.trim().equals("")) {
1111             if (suffix > 0) {
1112                 builder.append(", ");
1113             }
1114             appendAndIndexWikiCallback.dispatch(val, null);
1115             if (isForm) {
1116                 appendAndIndexWikiCallback.parser.addLinkToCurrentEntry(val, EntryTypeName.WIKTIONARY_INFLECTED_FORM_MULTI);
1117             }
1118         }
1119     }
1120   }
1121
1122
1123 }