]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_8_1_1/main/classes/translit/src/com/ibm/icu/text/CompoundTransliterator.java
Added flags.
[Dictionary.git] / jars / icu4j-4_8_1_1 / main / classes / translit / src / com / ibm / icu / text / CompoundTransliterator.java
1 /*
2  *******************************************************************************
3  * Copyright (C) 1996-2010, International Business Machines Corporation and    *
4  * others. All Rights Reserved.                                                *
5  *******************************************************************************
6  */
7 package com.ibm.icu.text;
8
9 import java.util.List;
10
11 import com.ibm.icu.impl.Utility;
12 import com.ibm.icu.impl.UtilityExtensions;
13
14 /**
15  * A transliterator that is composed of two or more other
16  * transliterator objects linked together.  For example, if one
17  * transliterator transliterates from script A to script B, and
18  * another transliterates from script B to script C, the two may be
19  * combined to form a new transliterator from A to C.
20  *
21  * <p>Composed transliterators may not behave as expected.  For
22  * example, inverses may not combine to form the identity
23  * transliterator.  See the class documentation for {@link
24  * Transliterator} for details.
25  *
26  * <p>Copyright &copy; IBM Corporation 1999.  All rights reserved.
27  *
28  * @author Alan Liu
29  */
30 class CompoundTransliterator extends Transliterator {
31
32     private Transliterator[] trans;
33
34     private int numAnonymousRBTs = 0;
35
36     /**
37      * Constructs a new compound transliterator given an array of
38      * transliterators.  The array of transliterators may be of any
39      * length, including zero or one, however, useful compound
40      * transliterators have at least two components.
41      * @param transliterators array of <code>Transliterator</code>
42      * objects
43      * @param filter the filter.  Any character for which
44      * <tt>filter.contains()</tt> returns <tt>false</tt> will not be
45      * altered by this transliterator.  If <tt>filter</tt> is
46      * <tt>null</tt> then no filtering is applied.
47      */
48     /*public CompoundTransliterator(Transliterator[] transliterators,
49                                   UnicodeFilter filter) {
50         super(joinIDs(transliterators), filter);
51         trans = new Transliterator[transliterators.length];
52         System.arraycopy(transliterators, 0, trans, 0, trans.length);
53         computeMaximumContextLength();
54     }*/
55
56     /**
57      * Constructs a new compound transliterator given an array of
58      * transliterators.  The array of transliterators may be of any
59      * length, including zero or one, however, useful compound
60      * transliterators have at least two components.
61      * @param transliterators array of <code>Transliterator</code>
62      * objects
63      */
64     /*public CompoundTransliterator(Transliterator[] transliterators) {
65         this(transliterators, null);
66     }*/
67
68     /**
69      * Constructs a new compound transliterator.
70      * @param ID compound ID
71      * @param direction either Transliterator.FORWARD or Transliterator.REVERSE
72      * @param filter a global filter for this compound transliterator
73      * or null
74      */
75     /*public CompoundTransliterator(String ID, int direction,
76                                   UnicodeFilter filter) {
77         super(ID, filter);
78         init(ID, direction, true);
79     }*/
80
81     /**
82      * Constructs a new compound transliterator with no filter.
83      * @param ID compound ID
84      * @param direction either Transliterator.FORWARD or Transliterator.REVERSE
85      */
86     /*public CompoundTransliterator(String ID, int direction) {
87         this(ID, direction, null);
88     }*/
89
90     /**
91      * Constructs a new forward compound transliterator with no filter.
92      * @param ID compound ID
93      */
94     /*public CompoundTransliterator(String ID) {
95         this(ID, FORWARD, null);
96     }*/
97
98     /**
99      * Package private constructor for Transliterator from a vector of
100      * transliterators.  The caller is responsible for fixing up the
101      * ID.
102      */
103     CompoundTransliterator(List<Transliterator> list) {
104         this(list, 0);
105     }
106
107     CompoundTransliterator(List<Transliterator> list, int numAnonymousRBTs) {
108         super("", null);
109         trans = null;
110         init(list, FORWARD, false);
111         this.numAnonymousRBTs = numAnonymousRBTs;
112         // assume caller will fixup ID
113     }
114
115     /**
116      * Internal method for safeClone...
117      * @param id
118      * @param filter2 
119      * @param trans2
120      * @param numAnonymousRBTs2
121      */
122     CompoundTransliterator(String id, UnicodeFilter filter2, Transliterator[] trans2, int numAnonymousRBTs2) {
123         super(id, filter2);
124         trans = trans2;
125         numAnonymousRBTs = numAnonymousRBTs2;
126     }
127     
128     /**
129      * Finish constructing a transliterator: only to be called by
130      * constructors.  Before calling init(), set trans and filter to NULL.
131      * @param id the id containing ';'-separated entries
132      * @param direction either FORWARD or REVERSE
133      * @param idSplitPoint the index into id at which the
134      * splitTrans should be inserted, if there is one, or
135      * -1 if there is none.
136      * @param splitTrans a transliterator to be inserted
137      * before the entry at offset idSplitPoint in the id string.  May be
138      * NULL to insert no entry.
139      * @param fixReverseID if TRUE, then reconstruct the ID of reverse
140      * entries by calling getID() of component entries.  Some constructors
141      * do not require this because they apply a facade ID anyway.
142      */
143     /*private void init(String id,
144                       int direction,
145                       boolean fixReverseID) {
146         // assert(trans == 0);
147
148         Vector list = new Vector();
149         UnicodeSet[] compoundFilter = new UnicodeSet[1];
150         StringBuffer regenID = new StringBuffer();
151         if (!TransliteratorIDParser.parseCompoundID(id, direction,
152                  regenID, list, compoundFilter)) {
153             throw new IllegalArgumentException("Invalid ID " + id);
154         }
155
156         TransliteratorIDParser.instantiateList(list);
157
158         init(list, direction, fixReverseID);
159
160         if (compoundFilter[0] != null) {
161             setFilter(compoundFilter[0]);
162         }
163     }*/
164
165
166     /**
167      * Finish constructing a transliterator: only to be called by
168      * constructors.  Before calling init(), set trans and filter to NULL.
169      * @param list a vector of transliterator objects to be adopted.  It
170      * should NOT be empty.  The list should be in declared order.  That
171      * is, it should be in the FORWARD order; if direction is REVERSE then
172      * the list order will be reversed.
173      * @param direction either FORWARD or REVERSE
174      * @param fixReverseID if TRUE, then reconstruct the ID of reverse
175      * entries by calling getID() of component entries.  Some constructors
176      * do not require this because they apply a facade ID anyway.
177      */
178     private void init(List<Transliterator> list,
179                       int direction,
180                       boolean fixReverseID) {
181         // assert(trans == 0);
182
183         // Allocate array
184         int count = list.size();
185         trans = new Transliterator[count];
186
187         // Move the transliterators from the vector into an array.
188         // Reverse the order if necessary.
189         int i;
190         for (i=0; i<count; ++i) {
191             int j = (direction == FORWARD) ? i : count - 1 - i;
192             trans[i] = list.get(j);
193         }
194
195         // If the direction is UTRANS_REVERSE then we may need to fix the
196         // ID.
197         if (direction == REVERSE && fixReverseID) {
198             StringBuilder newID = new StringBuilder();
199             for (i=0; i<count; ++i) {
200                 if (i > 0) {
201                     newID.append(ID_DELIM);
202                 }
203                 newID.append(trans[i].getID());
204             }
205             setID(newID.toString());
206         }
207
208         computeMaximumContextLength();
209     }
210
211     /**
212      * Return the IDs of the given list of transliterators, concatenated
213      * with ';' delimiting them.  Equivalent to the perlish expression
214      * join(';', map($_.getID(), transliterators).
215      */
216     /*private static String joinIDs(Transliterator[] transliterators) {
217         StringBuffer id = new StringBuffer();
218         for (int i=0; i<transliterators.length; ++i) {
219             if (i > 0) {
220                 id.append(';');
221             }
222             id.append(transliterators[i].getID());
223         }
224         return id.toString();
225     }*/
226
227     /**
228      * Returns the number of transliterators in this chain.
229      * @return number of transliterators in this chain.
230      */
231     public int getCount() {
232         return trans.length;
233     }
234
235     /**
236      * Returns the transliterator at the given index in this chain.
237      * @param index index into chain, from 0 to <code>getCount() - 1</code>
238      * @return transliterator at the given index
239      */
240     public Transliterator getTransliterator(int index) {
241         return trans[index];
242     }
243
244     /**
245      * Append c to buf, unless buf is empty or buf already ends in c.
246      */
247     private static void _smartAppend(StringBuilder buf, char c) {
248         if (buf.length() != 0 &&
249             buf.charAt(buf.length() - 1) != c) {
250             buf.append(c);
251         }
252     }
253
254     /**
255      * Override Transliterator:
256      * Create a rule string that can be passed to createFromRules()
257      * to recreate this transliterator.
258      * @param escapeUnprintable if TRUE then convert unprintable
259      * character to their hex escape representations, \\uxxxx or
260      * \\Uxxxxxxxx.  Unprintable characters are those other than
261      * U+000A, U+0020..U+007E.
262      * @return the rule string
263      */
264     public String toRules(boolean escapeUnprintable) {
265         // We do NOT call toRules() on our component transliterators, in
266         // general.  If we have several rule-based transliterators, this
267         // yields a concatenation of the rules -- not what we want.  We do
268         // handle compound RBT transliterators specially -- those for which
269         // compoundRBTIndex >= 0.  For the transliterator at compoundRBTIndex,
270         // we do call toRules() recursively.
271         StringBuilder rulesSource = new StringBuilder();
272         if (numAnonymousRBTs >= 1 && getFilter() != null) {
273             // If we are a compound RBT and if we have a global
274             // filter, then emit it at the top.
275             rulesSource.append("::").append(getFilter().toPattern(escapeUnprintable)).append(ID_DELIM);
276         }
277         for (int i=0; i<trans.length; ++i) {
278             String rule;
279
280             // Anonymous RuleBasedTransliterators (inline rules and
281             // ::BEGIN/::END blocks) are given IDs that begin with
282             // "%Pass": use toRules() to write all the rules to the output
283             // (and insert "::Null;" if we have two in a row)
284             if (trans[i].getID().startsWith("%Pass")) {
285                 rule = trans[i].toRules(escapeUnprintable);
286                 if (numAnonymousRBTs > 1 && i > 0 && trans[i - 1].getID().startsWith("%Pass"))
287                     rule = "::Null;" + rule;
288
289             // we also use toRules() on CompoundTransliterators (which we
290             // check for by looking for a semicolon in the ID)-- this gets
291             // the list of their child transliterators output in the right
292             // format
293             } else if (trans[i].getID().indexOf(';') >= 0) {
294                 rule = trans[i].toRules(escapeUnprintable);
295
296             // for everything else, use baseToRules()
297             } else {
298                 rule = trans[i].baseToRules(escapeUnprintable);
299             }
300             _smartAppend(rulesSource, '\n');
301             rulesSource.append(rule);
302             _smartAppend(rulesSource, ID_DELIM);
303         }
304         return rulesSource.toString();
305     }
306
307     /**
308      * @internal
309      */
310     @Override
311     public void addSourceTargetSet(UnicodeSet filter, UnicodeSet sourceSet, UnicodeSet targetSet) {
312         UnicodeSet myFilter = new UnicodeSet(getFilterAsUnicodeSet(filter));
313         UnicodeSet tempTargetSet = new UnicodeSet();
314         for (int i=0; i<trans.length; ++i) {
315             // each time we produce targets, those can be used by subsequent items, despite the filter.
316             // so we get just those items, and add them to the filter each time.
317             tempTargetSet.clear();
318             trans[i].addSourceTargetSet(myFilter, sourceSet, tempTargetSet);
319             targetSet.addAll(tempTargetSet);
320             myFilter.addAll(tempTargetSet);
321         }
322     }
323
324 //    /**
325 //     * Returns the set of all characters that may be generated as
326 //     * replacement text by this transliterator.
327 //     */
328 //    public UnicodeSet getTargetSet() {
329 //        UnicodeSet set = new UnicodeSet();
330 //        for (int i=0; i<trans.length; ++i) {
331 //            // This is a heuristic, and not 100% reliable.
332 //            set.addAll(trans[i].getTargetSet());
333 //        }
334 //        return set;
335 //    }
336
337     /**
338      * Implements {@link Transliterator#handleTransliterate}.
339      */
340     protected void handleTransliterate(Replaceable text,
341                                        Position index, boolean incremental) {
342         /* Call each transliterator with the same start value and
343          * initial cursor index, but with the limit index as modified
344          * by preceding transliterators.  The cursor index must be
345          * reset for each transliterator to give each a chance to
346          * transliterate the text.  The initial cursor index is known
347          * to still point to the same place after each transliterator
348          * is called because each transliterator will not change the
349          * text between start and the initial value of cursor.
350          *
351          * IMPORTANT: After the first transliterator, each subsequent
352          * transliterator only gets to transliterate text committed by
353          * preceding transliterators; that is, the cursor (output
354          * value) of transliterator i becomes the limit (input value)
355          * of transliterator i+1.  Finally, the overall limit is fixed
356          * up before we return.
357          *
358          * Assumptions we make here:
359          * (1) contextStart <= start <= limit <= contextLimit <= text.length()
360          * (2) start <= start' <= limit'  ;cursor doesn't move back
361          * (3) start <= limit'            ;text before cursor unchanged
362          * - start' is the value of start after calling handleKT
363          * - limit' is the value of limit after calling handleKT
364          */
365
366         /**
367          * Example: 3 transliterators.  This example illustrates the
368          * mechanics we need to implement.  C, S, and L are the contextStart,
369          * start, and limit.  gl is the globalLimit.  contextLimit is
370          * equal to limit throughout.
371          *
372          * 1. h-u, changes hex to Unicode
373          *
374          *    4  7  a  d  0      4  7  a
375          *    abc/u0061/u    =>  abca/u
376          *    C  S       L       C   S L   gl=f->a
377          *
378          * 2. upup, changes "x" to "XX"
379          *
380          *    4  7  a       4  7  a
381          *    abca/u    =>  abcAA/u
382          *    C  SL         C    S
383          *                       L    gl=a->b
384          * 3. u-h, changes Unicode to hex
385          *
386          *    4  7  a        4  7  a  d  0  3
387          *    abcAA/u    =>  abc/u0041/u0041/u
388          *    C  S L         C              S
389          *                                  L   gl=b->15
390          * 4. return
391          *
392          *    4  7  a  d  0  3
393          *    abc/u0041/u0041/u
394          *    C S L
395          */
396
397         if (trans.length < 1) {
398             index.start = index.limit;
399             return; // Short circuit for empty compound transliterators
400         }
401
402         // compoundLimit is the limit value for the entire compound
403         // operation.  We overwrite index.limit with the previous
404         // index.start.  After each transliteration, we update
405         // compoundLimit for insertions or deletions that have happened.
406         int compoundLimit = index.limit;
407
408         // compoundStart is the start for the entire compound
409         // operation.
410         int compoundStart = index.start;
411
412         int delta = 0; // delta in length
413
414         StringBuffer log = null;
415         ///CLOVER:OFF
416         if (DEBUG) {
417             log = new StringBuffer("CompoundTransliterator{" + getID() +
418                                    (incremental ? "}i: IN=" : "}: IN="));
419             UtilityExtensions.formatInput(log, text, index);
420             System.out.println(Utility.escape(log.toString()));
421         }
422         ///CLOVER:ON
423
424         // Give each transliterator a crack at the run of characters.
425         // See comments at the top of the method for more detail.
426         for (int i=0; i<trans.length; ++i) {
427             index.start = compoundStart; // Reset start
428             int limit = index.limit;
429
430             if (index.start == index.limit) {
431                 // Short circuit for empty range
432                 ///CLOVER:OFF
433                 if (DEBUG) {
434                     System.out.println("CompoundTransliterator[" + i +
435                                        ".." + (trans.length-1) +
436                                        (incremental ? "]i: " : "]: ") +
437                                        UtilityExtensions.formatInput(text, index) +
438                                        " (NOTHING TO DO)");
439                 }
440                 ///CLOVER:ON
441                 break;
442             }
443
444             ///CLOVER:OFF
445             if (DEBUG) {
446                 log.setLength(0);
447                 log.append("CompoundTransliterator[" + i + "=" +
448                            trans[i].getID() +
449                            (incremental ? "]i: " : "]: "));
450                 UtilityExtensions.formatInput(log, text, index);
451             }
452             ///CLOVER:ON
453
454             trans[i].filteredTransliterate(text, index, incremental);
455
456             // In a properly written transliterator, start == limit after
457             // handleTransliterate() returns when incremental is false.
458             // Catch cases where the subclass doesn't do this, and throw
459             // an exception.  (Just pinning start to limit is a bad idea,
460             // because what's probably happening is that the subclass
461             // isn't transliterating all the way to the end, and it should
462             // in non-incremental mode.)
463             if (!incremental && index.start != index.limit) {
464                 throw new RuntimeException("ERROR: Incomplete non-incremental transliteration by " + trans[i].getID());
465             }
466
467             ///CLOVER:OFF
468             if (DEBUG) {
469                 log.append(" => ");
470                 UtilityExtensions.formatInput(log, text, index);
471                 System.out.println(Utility.escape(log.toString()));
472             }
473             ///CLOVER:ON
474
475             // Cumulative delta for insertions/deletions
476             delta += index.limit - limit;
477
478             if (incremental) {
479                 // In the incremental case, only allow subsequent
480                 // transliterators to modify what has already been
481                 // completely processed by prior transliterators.  In the
482                 // non-incrmental case, allow each transliterator to
483                 // process the entire text.
484                 index.limit = index.start;
485             }
486         }
487
488         compoundLimit += delta;
489
490         // Start is good where it is -- where the last transliterator left
491         // it.  Limit needs to be put back where it was, modulo
492         // adjustments for deletions/insertions.
493         index.limit = compoundLimit;
494
495         ///CLOVER:OFF
496         if (DEBUG) {
497             log.setLength(0);
498             log.append("CompoundTransliterator{" + getID() +
499                        (incremental ? "}i: OUT=" : "}: OUT="));
500             UtilityExtensions.formatInput(log, text, index);
501             System.out.println(Utility.escape(log.toString()));
502         }
503         ///CLOVER:ON
504     }
505
506     /**
507      * Compute and set the length of the longest context required by this transliterator.
508      * This is <em>preceding</em> context.
509      */
510     private void computeMaximumContextLength() {
511         int max = 0;
512         for (int i=0; i<trans.length; ++i) {
513             int len = trans[i].getMaximumContextLength();
514             if (len > max) {
515                 max = len;
516             }
517         }
518         setMaximumContextLength(max);
519     }
520
521     /**
522      * Temporary hack for registry problem. Needs to be replaced by better architecture.
523      */
524     public Transliterator safeClone() {
525         UnicodeFilter filter = getFilter();
526         if (filter != null && filter instanceof UnicodeSet) {
527             filter = new UnicodeSet((UnicodeSet)filter);
528         }
529         return new CompoundTransliterator(getID(), filter, trans, numAnonymousRBTs);
530     }
531 }