// adapters are immutable! (plus returns a modified clone) abstract sclass Adapter { abstract bool canMatch(S in, S out); Adapter plus(S in, S out) { ret this; } S get(S in) { ret in; } double size() { ret 0; } } sclass WordAdapter extends Adapter { new Map wordMap; L tok(S s) { ret nlTok2(dropPunctuation2(s)); } bool canMatch(S in, S out) { L t1 = tok(in), t2 = tok(out); ret l(t1) == l(t2); } Adapter plus(S in, S out) { L t1 = tok(in), t2 = tok(out); if (l(t1) != l(t2)) ret this; WordAdapter a = cast nuObject(getClass()); a.wordMap = cloneMap(wordMap); for (int i = 1; i < l(t1); i += 2) { S w1 = t1.get(i), w2 = t2.get(i); if (!eqic(w1, w2)) // just overwrite - be flexible! a.wordMap.put(w1.toLowerCase(), w2.toLowerCase()); } ret a; } S get(S s) { L tok = nlTok2(s); for (int i = 1; i < l(tok); i += 2) { S w = lookupToken(tok.get(i)); if (nempty(w)) tok.set(i, w); } ret join(tok); } S lookupToken(S s) { ret wordMap.get(s.toLowerCase()); } double size() { ret l(wordMap); } public S toString() { ret structure(wordMap); } }