]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-52_1/main/classes/translit/src/com/ibm/icu/text/RuleBasedTransliterator.java
Upgrade ICU4J.
[Dictionary.git] / jars / icu4j-52_1 / main / classes / translit / src / com / ibm / icu / text / RuleBasedTransliterator.java
1 /*
2  *******************************************************************************
3  * Copyright (C) 1996-2011, International Business Machines Corporation and    *
4  * others. All Rights Reserved.                                                *
5  *******************************************************************************
6  */
7 package com.ibm.icu.text;
8
9 import java.util.HashMap;
10 import java.util.Map;
11
12 /**
13  * <code>RuleBasedTransliterator</code> is a transliterator
14  * that reads a set of rules in order to determine how to perform
15  * translations. Rule sets are stored in resource bundles indexed by
16  * name. Rules within a rule set are separated by semicolons (';').
17  * To include a literal semicolon, prefix it with a backslash ('\').
18  * Unicode Pattern_White_Space is ignored.
19  * If the first non-blank character on a line is '#',
20  * the entire line is ignored as a comment. </p>
21  *
22  * <p>Each set of rules consists of two groups, one forward, and one
23  * reverse. This is a convention that is not enforced; rules for one
24  * direction may be omitted, with the result that translations in
25  * that direction will not modify the source text. In addition,
26  * bidirectional forward-reverse rules may be specified for
27  * symmetrical transformations.</p>
28  *
29  * <p><b>Rule syntax</b> </p>
30  *
31  * <p>Rule statements take one of the following forms: </p>
32  *
33  * <dl>
34  *     <dt><code>$alefmadda=\u0622;</code></dt>
35  *     <dd><strong>Variable definition.</strong> The name on the
36  *         left is assigned the text on the right. In this example,
37  *         after this statement, instances of the left hand name,
38  *         &quot;<code>$alefmadda</code>&quot;, will be replaced by
39  *         the Unicode character U+0622. Variable names must begin
40  *         with a letter and consist only of letters, digits, and
41  *         underscores. Case is significant. Duplicate names cause
42  *         an exception to be thrown, that is, variables cannot be
43  *         redefined. The right hand side may contain well-formed
44  *         text of any length, including no text at all (&quot;<code>$empty=;</code>&quot;).
45  *         The right hand side may contain embedded <code>UnicodeSet</code>
46  *         patterns, for example, &quot;<code>$softvowel=[eiyEIY]</code>&quot;.</dd>
47  *     <dd>&nbsp;</dd>
48  *     <dt><code>ai&gt;$alefmadda;</code></dt>
49  *     <dd><strong>Forward translation rule.</strong> This rule
50  *         states that the string on the left will be changed to the
51  *         string on the right when performing forward
52  *         transliteration.</dd>
53  *     <dt>&nbsp;</dt>
54  *     <dt><code>ai&lt;$alefmadda;</code></dt>
55  *     <dd><strong>Reverse translation rule.</strong> This rule
56  *         states that the string on the right will be changed to
57  *         the string on the left when performing reverse
58  *         transliteration.</dd>
59  * </dl>
60  *
61  * <dl>
62  *     <dt><code>ai&lt;&gt;$alefmadda;</code></dt>
63  *     <dd><strong>Bidirectional translation rule.</strong> This
64  *         rule states that the string on the right will be changed
65  *         to the string on the left when performing forward
66  *         transliteration, and vice versa when performing reverse
67  *         transliteration.</dd>
68  * </dl>
69  *
70  * <p>Translation rules consist of a <em>match pattern</em> and an <em>output
71  * string</em>. The match pattern consists of literal characters,
72  * optionally preceded by context, and optionally followed by
73  * context. Context characters, like literal pattern characters,
74  * must be matched in the text being transliterated. However, unlike
75  * literal pattern characters, they are not replaced by the output
76  * text. For example, the pattern &quot;<code>abc{def}</code>&quot;
77  * indicates the characters &quot;<code>def</code>&quot; must be
78  * preceded by &quot;<code>abc</code>&quot; for a successful match.
79  * If there is a successful match, &quot;<code>def</code>&quot; will
80  * be replaced, but not &quot;<code>abc</code>&quot;. The final '<code>}</code>'
81  * is optional, so &quot;<code>abc{def</code>&quot; is equivalent to
82  * &quot;<code>abc{def}</code>&quot;. Another example is &quot;<code>{123}456</code>&quot;
83  * (or &quot;<code>123}456</code>&quot;) in which the literal
84  * pattern &quot;<code>123</code>&quot; must be followed by &quot;<code>456</code>&quot;.
85  * </p>
86  *
87  * <p>The output string of a forward or reverse rule consists of
88  * characters to replace the literal pattern characters. If the
89  * output string contains the character '<code>|</code>', this is
90  * taken to indicate the location of the <em>cursor</em> after
91  * replacement. The cursor is the point in the text at which the
92  * next replacement, if any, will be applied. The cursor is usually
93  * placed within the replacement text; however, it can actually be
94  * placed into the precending or following context by using the
95  * special character '<code>@</code>'. Examples:</p>
96  *
97  * <blockquote>
98  *     <p><code>a {foo} z &gt; | @ bar; # foo -&gt; bar, move cursor
99  *     before a<br>
100  *     {foo} xyz &gt; bar @@|; #&nbsp;foo -&gt; bar, cursor between
101  *     y and z</code></p>
102  * </blockquote>
103  *
104  * <p><b>UnicodeSet</b></p>
105  *
106  * <p><code>UnicodeSet</code> patterns may appear anywhere that
107  * makes sense. They may appear in variable definitions.
108  * Contrariwise, <code>UnicodeSet</code> patterns may themselves
109  * contain variable references, such as &quot;<code>$a=[a-z];$not_a=[^$a]</code>&quot;,
110  * or &quot;<code>$range=a-z;$ll=[$range]</code>&quot;.</p>
111  *
112  * <p><code>UnicodeSet</code> patterns may also be embedded directly
113  * into rule strings. Thus, the following two rules are equivalent:</p>
114  *
115  * <blockquote>
116  *     <p><code>$vowel=[aeiou]; $vowel&gt;'*'; # One way to do this<br>
117  *     [aeiou]&gt;'*';
118  *     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#
119  *     Another way</code></p>
120  * </blockquote>
121  *
122  * <p>See {@link UnicodeSet} for more documentation and examples.</p>
123  *
124  * <p><b>Segments</b></p>
125  *
126  * <p>Segments of the input string can be matched and copied to the
127  * output string. This makes certain sets of rules simpler and more
128  * general, and makes reordering possible. For example:</p>
129  *
130  * <blockquote>
131  *     <p><code>([a-z]) &gt; $1 $1;
132  *     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#
133  *     double lowercase letters<br>
134  *     ([:Lu:]) ([:Ll:]) &gt; $2 $1; # reverse order of Lu-Ll pairs</code></p>
135  * </blockquote>
136  *
137  * <p>The segment of the input string to be copied is delimited by
138  * &quot;<code>(</code>&quot; and &quot;<code>)</code>&quot;. Up to
139  * nine segments may be defined. Segments may not overlap. In the
140  * output string, &quot;<code>$1</code>&quot; through &quot;<code>$9</code>&quot;
141  * represent the input string segments, in left-to-right order of
142  * definition.</p>
143  *
144  * <p><b>Anchors</b></p>
145  *
146  * <p>Patterns can be anchored to the beginning or the end of the text. This is done with the
147  * special characters '<code>^</code>' and '<code>$</code>'. For example:</p>
148  *
149  * <blockquote>
150  *   <p><code>^ a&nbsp;&nbsp; &gt; 'BEG_A'; &nbsp;&nbsp;# match 'a' at start of text<br>
151  *   &nbsp; a&nbsp;&nbsp; &gt; 'A';&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # match other instances
152  *   of 'a'<br>
153  *   &nbsp; z $ &gt; 'END_Z'; &nbsp;&nbsp;# match 'z' at end of text<br>
154  *   &nbsp; z&nbsp;&nbsp; &gt; 'Z';&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # match other instances
155  *   of 'z'</code></p>
156  * </blockquote>
157  *
158  * <p>It is also possible to match the beginning or the end of the text using a <code>UnicodeSet</code>.
159  * This is done by including a virtual anchor character '<code>$</code>' at the end of the
160  * set pattern. Although this is usually the match chafacter for the end anchor, the set will
161  * match either the beginning or the end of the text, depending on its placement. For
162  * example:</p>
163  *
164  * <blockquote>
165  *   <p><code>$x = [a-z$]; &nbsp;&nbsp;# match 'a' through 'z' OR anchor<br>
166  *   $x 1&nbsp;&nbsp;&nbsp; &gt; 2;&nbsp;&nbsp; # match '1' after a-z or at the start<br>
167  *   &nbsp;&nbsp; 3 $x &gt; 4; &nbsp;&nbsp;# match '3' before a-z or at the end</code></p>
168  * </blockquote>
169  *
170  * <p><b>Example</b> </p>
171  *
172  * <p>The following example rules illustrate many of the features of
173  * the rule language. </p>
174  *
175  * <table border="0" cellpadding="4">
176  *     <tr>
177  *         <td valign="top">Rule 1.</td>
178  *         <td valign="top" nowrap><code>abc{def}&gt;x|y</code></td>
179  *     </tr>
180  *     <tr>
181  *         <td valign="top">Rule 2.</td>
182  *         <td valign="top" nowrap><code>xyz&gt;r</code></td>
183  *     </tr>
184  *     <tr>
185  *         <td valign="top">Rule 3.</td>
186  *         <td valign="top" nowrap><code>yz&gt;q</code></td>
187  *     </tr>
188  * </table>
189  *
190  * <p>Applying these rules to the string &quot;<code>adefabcdefz</code>&quot;
191  * yields the following results: </p>
192  *
193  * <table border="0" cellpadding="4">
194  *     <tr>
195  *         <td valign="top" nowrap><code>|adefabcdefz</code></td>
196  *         <td valign="top">Initial state, no rules match. Advance
197  *         cursor.</td>
198  *     </tr>
199  *     <tr>
200  *         <td valign="top" nowrap><code>a|defabcdefz</code></td>
201  *         <td valign="top">Still no match. Rule 1 does not match
202  *         because the preceding context is not present.</td>
203  *     </tr>
204  *     <tr>
205  *         <td valign="top" nowrap><code>ad|efabcdefz</code></td>
206  *         <td valign="top">Still no match. Keep advancing until
207  *         there is a match...</td>
208  *     </tr>
209  *     <tr>
210  *         <td valign="top" nowrap><code>ade|fabcdefz</code></td>
211  *         <td valign="top">...</td>
212  *     </tr>
213  *     <tr>
214  *         <td valign="top" nowrap><code>adef|abcdefz</code></td>
215  *         <td valign="top">...</td>
216  *     </tr>
217  *     <tr>
218  *         <td valign="top" nowrap><code>adefa|bcdefz</code></td>
219  *         <td valign="top">...</td>
220  *     </tr>
221  *     <tr>
222  *         <td valign="top" nowrap><code>adefab|cdefz</code></td>
223  *         <td valign="top">...</td>
224  *     </tr>
225  *     <tr>
226  *         <td valign="top" nowrap><code>adefabc|defz</code></td>
227  *         <td valign="top">Rule 1 matches; replace &quot;<code>def</code>&quot;
228  *         with &quot;<code>xy</code>&quot; and back up the cursor
229  *         to before the '<code>y</code>'.</td>
230  *     </tr>
231  *     <tr>
232  *         <td valign="top" nowrap><code>adefabcx|yz</code></td>
233  *         <td valign="top">Although &quot;<code>xyz</code>&quot; is
234  *         present, rule 2 does not match because the cursor is
235  *         before the '<code>y</code>', not before the '<code>x</code>'.
236  *         Rule 3 does match. Replace &quot;<code>yz</code>&quot;
237  *         with &quot;<code>q</code>&quot;.</td>
238  *     </tr>
239  *     <tr>
240  *         <td valign="top" nowrap><code>adefabcxq|</code></td>
241  *         <td valign="top">The cursor is at the end;
242  *         transliteration is complete.</td>
243  *     </tr>
244  * </table>
245  *
246  * <p>The order of rules is significant. If multiple rules may match
247  * at some point, the first matching rule is applied. </p>
248  *
249  * <p>Forward and reverse rules may have an empty output string.
250  * Otherwise, an empty left or right hand side of any statement is a
251  * syntax error. </p>
252  *
253  * <p>Single quotes are used to quote any character other than a
254  * digit or letter. To specify a single quote itself, inside or
255  * outside of quotes, use two single quotes in a row. For example,
256  * the rule &quot;<code>'&gt;'&gt;o''clock</code>&quot; changes the
257  * string &quot;<code>&gt;</code>&quot; to the string &quot;<code>o'clock</code>&quot;.
258  * </p>
259  *
260  * <p><b>Notes</b> </p>
261  *
262  * <p>While a RuleBasedTransliterator is being built, it checks that
263  * the rules are added in proper order. For example, if the rule
264  * &quot;a&gt;x&quot; is followed by the rule &quot;ab&gt;y&quot;,
265  * then the second rule will throw an exception. The reason is that
266  * the second rule can never be triggered, since the first rule
267  * always matches anything it matches. In other words, the first
268  * rule <em>masks</em> the second rule. </p>
269  *
270  * <p>Copyright (c) IBM Corporation 1999-2000. All rights reserved.</p>
271  *
272  * @author Alan Liu
273  * @internal
274  * @deprecated This API is ICU internal only.
275  */
276 public class RuleBasedTransliterator extends Transliterator {
277
278     private Data data;
279
280     /**
281      * Constructs a new transliterator from the given rules.
282      * @param rules rules, separated by ';'
283      * @param direction either FORWARD or REVERSE.
284      * @exception IllegalArgumentException if rules are malformed
285      * or direction is invalid.
286      * @internal
287      * @deprecated This API is ICU internal only.
288      */
289     /*public RuleBasedTransliterator(String ID, String rules, int direction,
290                                    UnicodeFilter filter) {
291         super(ID, filter);
292         if (direction != FORWARD && direction != REVERSE) {
293             throw new IllegalArgumentException("Invalid direction");
294         }
295
296         TransliteratorParser parser = new TransliteratorParser();
297         parser.parse(rules, direction);
298         if (parser.idBlockVector.size() != 0 ||
299             parser.compoundFilter != null) {
300             throw new IllegalArgumentException("::ID blocks illegal in RuleBasedTransliterator constructor");
301         }
302
303         data = (Data)parser.dataVector.get(0);
304         setMaximumContextLength(data.ruleSet.getMaximumContextLength());
305     }*/
306
307     /**
308      * Constructs a new transliterator from the given rules in the
309      * <code>FORWARD</code> direction.
310      * @param rules rules, separated by ';'
311      * @exception IllegalArgumentException if rules are malformed
312      * or direction is invalid.
313      * @internal
314      * @deprecated This API is ICU internal only.
315      */
316     /*public RuleBasedTransliterator(String ID, String rules) {
317         this(ID, rules, FORWARD, null);
318     }*/
319
320     RuleBasedTransliterator(String ID, Data data, UnicodeFilter filter) {
321         super(ID, filter);
322         this.data = data;
323         setMaximumContextLength(data.ruleSet.getMaximumContextLength());
324     }
325
326     /**
327      * Implements {@link Transliterator#handleTransliterate}.
328      * @internal
329      * @deprecated This API is ICU internal only.
330      */
331     protected void handleTransliterate(Replaceable text,
332                                        Position index, boolean incremental) {
333         /* We keep start and limit fixed the entire time,
334          * relative to the text -- limit may move numerically if text is
335          * inserted or removed.  The cursor moves from start to limit, with
336          * replacements happening under it.
337          *
338          * Example: rules 1. ab>x|y
339          *                2. yc>z
340          *
341          * |eabcd   start - no match, advance cursor
342          * e|abcd   match rule 1 - change text & adjust cursor
343          * ex|ycd   match rule 2 - change text & adjust cursor
344          * exz|d    no match, advance cursor
345          * exzd|    done
346          */
347
348         /* A rule like
349          *   a>b|a
350          * creates an infinite loop. To prevent that, we put an arbitrary
351          * limit on the number of iterations that we take, one that is
352          * high enough that any reasonable rules are ok, but low enough to
353          * prevent a server from hanging.  The limit is 16 times the
354          * number of characters n, unless n is so large that 16n exceeds a
355          * uint32_t.
356          */
357         synchronized(data)  {
358             int loopCount = 0;
359             int loopLimit = (index.limit - index.start) << 4;
360             if (loopLimit < 0) {
361                 loopLimit = 0x7FFFFFFF;
362             }
363
364             while (index.start < index.limit &&
365                     loopCount <= loopLimit &&
366                     data.ruleSet.transliterate(text, index, incremental)) {
367                 ++loopCount;
368             }
369         }
370     }
371
372
373     static class Data {
374         public Data() {
375             variableNames = new HashMap<String, char[]>();
376             ruleSet = new TransliterationRuleSet();
377         }
378
379         /**
380          * Rule table.  May be empty.
381          */
382         public TransliterationRuleSet ruleSet;
383
384         /**
385          * Map variable name (String) to variable (char[]).  A variable name
386          * corresponds to zero or more characters, stored in a char[] array in
387          * this hash.  One or more of these chars may also correspond to a
388          * UnicodeSet, in which case the character in the char[] in this hash is
389          * a stand-in: it is an index for a secondary lookup in
390          * data.variables.  The stand-in also represents the UnicodeSet in
391          * the stored rules.
392          */
393         Map<String, char[]> variableNames;
394
395         /**
396          * Map category variable (Character) to UnicodeMatcher or UnicodeReplacer.
397          * Variables that correspond to a set of characters are mapped
398          * from variable name to a stand-in character in data.variableNames.
399          * The stand-in then serves as a key in this hash to lookup the
400          * actual UnicodeSet object.  In addition, the stand-in is
401          * stored in the rule text to represent the set of characters.
402          * variables[i] represents character (variablesBase + i).
403          */
404         Object[] variables;
405
406         /**
407          * The character that represents variables[0].  Characters
408          * variablesBase through variablesBase +
409          * variables.length - 1 represent UnicodeSet objects.
410          */
411         char variablesBase;
412
413         /**
414          * Return the UnicodeMatcher represented by the given character, or
415          * null if none.
416          */
417         public UnicodeMatcher lookupMatcher(int standIn) {
418             int i = standIn - variablesBase;
419             return (i >= 0 && i < variables.length)
420                 ? (UnicodeMatcher) variables[i] : null;
421         }
422
423         /**
424          * Return the UnicodeReplacer represented by the given character, or
425          * null if none.
426          */
427         public UnicodeReplacer lookupReplacer(int standIn) {
428             int i = standIn - variablesBase;
429             return (i >= 0 && i < variables.length)
430                 ? (UnicodeReplacer) variables[i] : null;
431         }
432     }
433
434
435     /**
436      * Return a representation of this transliterator as source rules.
437      * These rules will produce an equivalent transliterator if used
438      * to construct a new transliterator.
439      * @param escapeUnprintable if TRUE then convert unprintable
440      * character to their hex escape representations, \\uxxxx or
441      * \\Uxxxxxxxx.  Unprintable characters are those other than
442      * U+000A, U+0020..U+007E.
443      * @return rules string
444      * @internal
445      * @deprecated This API is ICU internal only.
446      */
447     public String toRules(boolean escapeUnprintable) {
448         return data.ruleSet.toRules(escapeUnprintable);
449     }
450
451 //    /**
452 //     * Return the set of all characters that may be modified by this
453 //     * Transliterator, ignoring the effect of our filter.
454 //     * @internal
455 //     * @deprecated This API is ICU internal only.
456 //     */
457 //    protected UnicodeSet handleGetSourceSet() {
458 //        return data.ruleSet.getSourceTargetSet(false, unicodeFilter);
459 //    }
460 //
461 //    /**
462 //     * Returns the set of all characters that may be generated as
463 //     * replacement text by this transliterator.
464 //     * @internal
465 //     * @deprecated This API is ICU internal only.
466 //     */
467 //    public UnicodeSet getTargetSet() {
468 //        return data.ruleSet.getSourceTargetSet(true, unicodeFilter);
469 //    }
470     
471     /**
472      * @internal
473      */
474     @Override
475     public void addSourceTargetSet(UnicodeSet filter, UnicodeSet sourceSet, UnicodeSet targetSet) {
476         data.ruleSet.addSourceTargetSet(filter, sourceSet, targetSet);
477     }
478
479     /**
480      * Temporary hack for registry problem. Needs to be replaced by better architecture.
481      * @internal
482      * @deprecated This API is ICU internal only.
483      */
484     public Transliterator safeClone() {
485         UnicodeFilter filter = getFilter();
486         if (filter != null && filter instanceof UnicodeSet) {
487             filter = new UnicodeSet((UnicodeSet)filter);
488         }
489         return new RuleBasedTransliterator(getID(), data, filter);
490     }
491 }
492
493