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