]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_8_1_1/main/classes/core/src/com/ibm/icu/lang/UScriptRun.java
Added flags.
[Dictionary.git] / jars / icu4j-4_8_1_1 / main / classes / core / src / com / ibm / icu / lang / UScriptRun.java
1 /*
2  *******************************************************************************
3  *
4  *   Copyright (C) 1999-2009, International Business Machines
5  *   Corporation and others.  All Rights Reserved.
6  *
7  *******************************************************************************
8  */
9
10 package com.ibm.icu.lang;
11
12 import com.ibm.icu.text.UTF16;
13
14 /**
15  * <code>UScriptRun</code> is used to find runs of characters in
16  * the same script, as defined in the <code>UScript</code> class.
17  * It implements a simple iterator over an array of characters.
18  * The iterator will assign <code>COMMON</code> and <code>INHERITED</code>
19  * characters to the same script as the preceeding characters. If the
20  * COMMON and INHERITED characters are first, they will be assigned to
21  * the same script as the following characters.
22  *
23  * The iterator will try to match paired punctuation. If it sees an
24  * opening punctuation character, it will remember the script that
25  * was assigned to that character, and assign the same script to the
26  * matching closing punctuation.
27  *
28  * No attempt is made to combine related scripts into a single run. In
29  * particular, Hiragana, Katakana, and Han characters will appear in separate
30  * runs.
31
32  * Here is an example of how to iterate over script runs:
33  * <pre>
34  * void printScriptRuns(char[] text)
35  * {
36  *     UScriptRun scriptRun = new UScriptRun(text);
37  *
38  *     while (scriptRun.next()) {
39  *         int start  = scriptRun.getScriptStart();
40  *         int limit  = scriptRun.getScriptLimit();
41  *         int script = scriptRun.getScriptCode();
42  *
43  *         System.out.println("Script \"" + UScript.getName(script) + "\" from " +
44  *                            start + " to " + limit + ".");
45  *     }
46  *  }
47  * </pre>
48  *
49  * @internal
50  * @deprecated This API is ICU internal only.
51  */
52 public final class UScriptRun
53 {
54     /**
55      * Construct an empty <code>UScriptRun</code> object. The <code>next()</code>
56      * method will return <code>false</code> the first time it is called.
57      *
58      * @internal
59      * @deprecated This API is ICU internal only.
60      */
61     public UScriptRun()
62     {
63         char[] nullChars = null;
64         
65         reset(nullChars, 0, 0);
66     }
67     
68     /**
69      * Construct a <code>UScriptRun</code> object which iterates over the
70      * characters in the given string.
71      *
72      * @param text the string of characters over which to iterate.
73      *
74      * @internal
75      * @deprecated This API is ICU internal only.
76      */
77     public UScriptRun(String text)
78     {
79         reset (text);
80     }
81     
82     /**
83      * Construct a <code>UScriptRun</code> object which iterates over a subrange
84      * of the characetrs in the given string.
85      *
86      * @param text the string of characters over which to iterate.
87      * @param start the index of the first character over which to iterate
88      * @param count the number of characters over which to iterate
89      *
90      * @internal
91      * @deprecated This API is ICU internal only.
92      */
93     public UScriptRun(String text, int start, int count)
94     {
95         reset(text, start, count);
96     }
97
98     /**
99      * Construct a <code>UScriptRun</code> object which iterates over the given
100      * characetrs.
101      *
102      * @param chars the array of characters over which to iterate.
103      *
104      * @internal
105      * @deprecated This API is ICU internal only.
106      */
107     public UScriptRun(char[] chars)
108     {
109         reset(chars);
110     }
111
112     /**
113      * Construct a <code>UScriptRun</code> object which iterates over a subrange
114      * of the given characetrs.
115      *
116      * @param chars the array of characters over which to iterate.
117      * @param start the index of the first character over which to iterate
118      * @param count the number of characters over which to iterate
119      *
120      * @internal
121      * @deprecated This API is ICU internal only.
122      */
123     public UScriptRun(char[] chars, int start, int count)
124     {
125         reset(chars, start, count);
126     }
127
128
129     /**
130      * Reset the iterator to the start of the text.
131      *
132      * @internal
133      * @deprecated This API is ICU internal only.
134      */
135     public final void reset()
136     {
137         // empty any old parenStack contents.
138         // NOTE: this is not the most efficient way
139         // to do this, but it's the easiest to write...
140         while (stackIsNotEmpty()) {
141             pop();
142         }
143         
144         scriptStart = textStart;
145         scriptLimit = textStart;
146         scriptCode  = UScript.INVALID_CODE;
147         parenSP     = -1;
148         pushCount   =  0;
149         fixupCount  =  0;
150         
151         textIndex = textStart;
152     }
153
154     /**
155      * Reset the iterator to iterate over the given range of the text. Throws
156      * IllegalArgumentException if the range is outside of the bounds of the
157      * character array.
158      *
159      * @param start the index of the new first character over which to iterate
160      * @param count the new number of characters over which to iterate.
161      * @exception IllegalArgumentException If invalid arguments are passed.
162      *
163      * @internal
164      * @deprecated This API is ICU internal only.
165      */
166     public final void reset(int start, int count)
167     throws IllegalArgumentException
168     {
169         int len = 0;
170         
171         if (text != null) {
172             len = text.length;
173         }
174         
175         if (start < 0 || count < 0 || start > len - count) {
176             throw new IllegalArgumentException();
177         }
178         
179         textStart = start;
180         textLimit = start + count;
181
182         reset();
183     }
184
185     /**
186      * Reset the iterator to iterate over <code>count</code> characters
187      * in <code>chars</code> starting at <code>start</code>. This allows
188      * clients to reuse an iterator.
189      *
190      * @param chars the new array of characters over which to iterate.
191      * @param start the index of the first character over which to iterate.
192      * @param count the number of characters over which to iterate.
193      *
194      * @internal
195      * @deprecated This API is ICU internal only.
196      */
197     public final void reset(char[] chars, int start, int count)
198     {
199         if (chars == null) {
200             chars = emptyCharArray;
201         }
202         
203         text = chars;
204
205         reset(start, count);
206     }
207     
208     /**
209      * Reset the iterator to iterate over the characters
210      * in <code>chars</code>. This allows clients to reuse an iterator.
211      *
212      * @param chars the new array of characters over which to iterate.
213      *
214      * @internal
215      * @deprecated This API is ICU internal only.
216      */
217     public final void reset(char[] chars)
218     {
219         int length = 0;
220         
221         if (chars != null) {
222             length = chars.length;
223         }
224         
225         reset(chars, 0, length);
226     }
227     
228     /**
229      * Reset the iterator to iterate over <code>count</code> characters
230      * in <code>text</code> starting at <code>start</code>. This allows
231      * clients to reuse an iterator.
232      *
233      * @param str the new string of characters over which to iterate.
234      * @param start the index of the first character over which to iterate.
235      * @param count the nuber of characters over which to iterate.
236      *
237      * @internal
238      * @deprecated This API is ICU internal only.
239      */
240     public final void reset(String str, int start, int count)
241     {
242         char[] chars = null;
243         
244         if (str != null) {
245             chars = str.toCharArray();
246         }
247         
248         reset(chars, start, count);
249     }
250     
251     /**
252      * Reset the iterator to iterate over the characters
253      * in <code>text</code>. This allows clients to reuse an iterator.
254      *
255      * @param str the new string of characters over which to iterate.
256      *
257      * @internal
258      * @deprecated This API is ICU internal only.
259      */
260     public final void reset(String str)
261     {
262         int length   = 0;
263         
264         if (str != null) {
265             length = str.length();
266         }
267         
268         reset(str, 0, length);
269     }
270         
271
272
273     /**
274      * Get the starting index of the current script run.
275      *
276      * @return the index of the first character in the current script run.
277      *
278      * @internal
279      * @deprecated This API is ICU internal only.
280      */
281     public final int getScriptStart()
282     {
283         return scriptStart;
284     }
285
286     /**
287      * Get the index of the first character after the current script run.
288      *
289      * @return the index of the first character after the current script run.
290      *
291      * @internal
292      * @deprecated This API is ICU internal only.
293      */
294     public final int getScriptLimit()
295     {
296         return scriptLimit;
297     }
298
299     /**
300      * Get the script code for the script of the current script run.
301      *
302      * @return the script code for the script of the current script run.
303      * @see com.ibm.icu.lang.UScript
304      *
305      * @internal
306      * @deprecated This API is ICU internal only.
307      */
308     public final int getScriptCode()
309     {
310         return scriptCode;
311     }
312
313     /**
314      * Find the next script run. Returns <code>false</code> if there
315      * isn't another run, returns <code>true</code> if there is.
316      *
317      * @return <code>false</code> if there isn't another run, <code>true</code> if there is.
318      *
319      * @internal
320      * @deprecated This API is ICU internal only.
321      */
322     public final boolean next()
323     {
324         // if we've fallen off the end of the text, we're done
325         if (scriptLimit >= textLimit) {
326             return false;
327         }
328     
329         scriptCode  = UScript.COMMON;
330         scriptStart = scriptLimit;
331         
332         syncFixup();
333         
334         while (textIndex < textLimit) {
335             int ch = UTF16.charAt(text, textStart, textLimit, textIndex - textStart);
336             int codePointCount = UTF16.getCharCount(ch);
337             int sc = UScript.getScript(ch);
338             int pairIndex = getPairIndex(ch);
339
340             textIndex += codePointCount;
341             
342             // Paired character handling:
343             //
344             // if it's an open character, push it onto the stack.
345             // if it's a close character, find the matching open on the
346             // stack, and use that script code. Any non-matching open
347             // characters above it on the stack will be poped.
348             if (pairIndex >= 0) {
349                 if ((pairIndex & 1) == 0) {
350                     push(pairIndex, scriptCode);
351                 } else {
352                     int pi = pairIndex & ~1;
353
354                     while (stackIsNotEmpty() && top().pairIndex != pi) {
355                         pop();
356                     }
357
358                     if (stackIsNotEmpty()) {
359                         sc = top().scriptCode;
360                     }
361                 }
362             }
363
364             if (sameScript(scriptCode, sc)) {
365                 if (scriptCode <= UScript.INHERITED && sc > UScript.INHERITED) {
366                     scriptCode = sc;
367
368                     fixup(scriptCode);
369                 }
370
371                 // if this character is a close paired character,
372                 // pop the matching open character from the stack
373                 if (pairIndex >= 0 && (pairIndex & 1) != 0) {
374                     pop();
375                 }
376             } else {
377                 // We've just seen the first character of
378                 // the next run. Back over it so we'll see
379                 // it again the next time.
380                 textIndex -= codePointCount;
381                 break;
382             }
383         }
384
385         scriptLimit = textIndex;
386         return true;
387     }
388
389     /**
390      * Compare two script codes to see if they are in the same script. If one script is
391      * a strong script, and the other is INHERITED or COMMON, it will compare equal.
392      *
393      * @param scriptOne one of the script codes.
394      * @param scriptTwo the other script code.
395      * @return <code>true</code> if the two scripts are the same.
396      * @see com.ibm.icu.lang.UScript
397      */
398     private static boolean sameScript(int scriptOne, int scriptTwo)
399     {
400         return scriptOne <= UScript.INHERITED || scriptTwo <= UScript.INHERITED || scriptOne == scriptTwo;
401     }
402
403     /*
404      * An internal class which holds entries on the paren stack.
405      */
406     private static final class ParenStackEntry
407     {
408         int pairIndex;
409         int scriptCode;
410         
411         public ParenStackEntry(int thePairIndex, int theScriptCode)
412         {
413             pairIndex  = thePairIndex;
414             scriptCode = theScriptCode;
415         }
416     }
417     
418     private static final int mod(int sp)
419     {
420         return sp % PAREN_STACK_DEPTH;
421     }
422     
423     private static final int inc(int sp, int count)
424     {
425         return mod(sp + count);
426     }
427     
428     private static final int inc(int sp)
429     {
430         return inc(sp, 1);
431     }
432     
433     private static final int dec(int sp, int count)
434     {
435         return mod(sp + PAREN_STACK_DEPTH - count);
436     }
437     
438     private static final int dec(int sp)
439     {
440         return dec(sp, 1);
441     }
442     
443     private static final int limitInc(int count)
444     {
445         if (count < PAREN_STACK_DEPTH) {
446             count += 1;
447         }
448         
449         return count;
450     }
451     
452     private final boolean stackIsEmpty()
453     {
454         return pushCount <= 0;
455     }
456     
457     private final boolean stackIsNotEmpty()
458     {
459         return ! stackIsEmpty();
460     }
461     
462     private final void push(int pairIndex, int scrptCode)
463     {
464         pushCount  = limitInc(pushCount);
465         fixupCount = limitInc(fixupCount);
466         
467         parenSP = inc(parenSP);
468         parenStack[parenSP] = new ParenStackEntry(pairIndex, scrptCode);
469     }
470     
471     private final void pop()
472     {
473         
474         if (stackIsEmpty()) {
475             return;
476         }
477         
478         parenStack[parenSP] = null;
479         
480         if (fixupCount > 0) {
481             fixupCount -= 1;
482         }
483         
484         pushCount -= 1;
485         parenSP = dec(parenSP);
486         
487         // If the stack is now empty, reset the stack
488         // pointers to their initial values.
489         if (stackIsEmpty()) {
490             parenSP = -1;
491         }
492     }
493     
494     private final ParenStackEntry top()
495     {
496         return parenStack[parenSP];
497     }
498     
499     private final void syncFixup()
500     {
501         fixupCount = 0;
502     }
503     
504     private final void fixup(int scrptCode)
505     {
506         int fixupSP = dec(parenSP, fixupCount);
507         
508         while (fixupCount-- > 0) {
509             fixupSP = inc(fixupSP);
510             parenStack[fixupSP].scriptCode = scrptCode;
511         }
512     }
513     
514     private char[] emptyCharArray = {};
515
516     private char[] text;
517
518     private int textIndex;
519     private int  textStart;
520     private int  textLimit;
521     
522     private int  scriptStart;
523     private int  scriptLimit;
524     private int  scriptCode;
525
526     private static int PAREN_STACK_DEPTH = 32;
527     private static ParenStackEntry parenStack[] = new ParenStackEntry[PAREN_STACK_DEPTH];
528     private int parenSP = -1;
529     private int pushCount = 0;
530     private int fixupCount = 0;
531
532     /**
533      * Find the highest bit that's set in a word. Uses a binary search through
534      * the bits.
535      *
536      * @param n the word in which to find the highest bit that's set.
537      * @return the bit number (counting from the low order bit) of the highest bit.
538      */
539     private static final byte highBit(int n)
540     {
541         if (n <= 0) {
542             return -32;
543         }
544
545         byte bit = 0;
546
547         if (n >= 1 << 16) {
548             n >>= 16;
549             bit += 16;
550         }
551
552         if (n >= 1 << 8) {
553             n >>= 8;
554             bit += 8;
555         }
556
557         if (n >= 1 << 4) {
558             n >>= 4;
559             bit += 4;
560         }
561
562         if (n >= 1 << 2) {
563             n >>= 2;
564             bit += 2;
565         }
566
567         if (n >= 1 << 1) {
568             n >>= 1;
569             bit += 1;
570         }
571
572         return bit;
573     }
574
575     /**
576      * Search the pairedChars array for the given character.
577      *
578      * @param ch the character for which to search.
579      * @return the index of the character in the table, or -1 if it's not there.
580      */
581     private static int getPairIndex(int ch)
582     {
583         int probe = pairedCharPower;
584         int index = 0;
585
586         if (ch >= pairedChars[pairedCharExtra]) {
587             index = pairedCharExtra;
588         }
589
590         while (probe > (1 << 0)) {
591             probe >>= 1;
592
593             if (ch >= pairedChars[index + probe]) {
594                 index += probe;
595             }
596         }
597
598         if (pairedChars[index] != ch) {
599             index = -1;
600         }
601
602         return index;
603     }
604
605     private static int pairedChars[] = {
606         0x0028, 0x0029, // ascii paired punctuation
607         0x003c, 0x003e,
608         0x005b, 0x005d,
609         0x007b, 0x007d,
610         0x00ab, 0x00bb, // guillemets
611         0x2018, 0x2019, // general punctuation
612         0x201c, 0x201d,
613         0x2039, 0x203a,
614         0x3008, 0x3009, // chinese paired punctuation
615         0x300a, 0x300b,
616         0x300c, 0x300d,
617         0x300e, 0x300f,
618         0x3010, 0x3011,
619         0x3014, 0x3015,
620         0x3016, 0x3017,
621         0x3018, 0x3019,
622         0x301a, 0x301b
623     };
624
625     private static int pairedCharPower = 1 << highBit(pairedChars.length);
626     private static int pairedCharExtra = pairedChars.length - pairedCharPower;
627 }
628