]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_4_2-src/main/classes/core/src/com/ibm/icu/impl/UCaseProps.java
go
[Dictionary.git] / jars / icu4j-4_4_2-src / main / classes / core / src / com / ibm / icu / impl / UCaseProps.java
1 /*\r
2 *******************************************************************************\r
3 *\r
4 *   Copyright (C) 2004-2010, International Business Machines\r
5 *   Corporation and others.  All Rights Reserved.\r
6 *\r
7 *******************************************************************************\r
8 *   file name:  UCaseProps.java\r
9 *   encoding:   US-ASCII\r
10 *   tab size:   8 (not used)\r
11 *   indentation:4\r
12 *\r
13 *   created on: 2005jan29\r
14 *   created by: Markus W. Scherer\r
15 *\r
16 *   Low-level Unicode character/string case mapping code.\r
17 *   Java port of ucase.h/.c.\r
18 */\r
19 \r
20 package com.ibm.icu.impl;\r
21 \r
22 import java.io.BufferedInputStream;\r
23 import java.io.DataInputStream;\r
24 import java.io.IOException;\r
25 import java.io.InputStream;\r
26 \r
27 import com.ibm.icu.lang.UCharacter;\r
28 import com.ibm.icu.lang.UProperty;\r
29 import com.ibm.icu.text.UTF16;\r
30 import com.ibm.icu.text.UnicodeSet;\r
31 import com.ibm.icu.util.RangeValueIterator;\r
32 import com.ibm.icu.util.ULocale;\r
33 \r
34 public final class UCaseProps {\r
35 \r
36     // constructors etc. --------------------------------------------------- ***\r
37 \r
38     // port of ucase_openProps()\r
39     private UCaseProps() throws IOException {\r
40         InputStream is=ICUData.getRequiredStream(ICUResourceBundle.ICU_BUNDLE+"/"+DATA_FILE_NAME);\r
41         BufferedInputStream b=new BufferedInputStream(is, 4096 /* data buffer size */);\r
42         readData(b);\r
43         b.close();\r
44         is.close();\r
45     }\r
46 \r
47     private UCaseProps(boolean makeDummy) { // ignore makeDummy, only creates a unique signature\r
48         indexes=new int[IX_TOP];\r
49         indexes[0]=IX_TOP;\r
50         trie=new CharTrie(0, 0, null); // dummy trie, always returns 0\r
51     }\r
52 \r
53     private final void readData(InputStream is) throws IOException {\r
54         DataInputStream inputStream=new DataInputStream(is);\r
55 \r
56         // read the header\r
57         ICUBinary.readHeader(inputStream, FMT, new IsAcceptable());\r
58 \r
59         // read indexes[]\r
60         int i, count;\r
61         count=inputStream.readInt();\r
62         if(count<IX_INDEX_TOP) {\r
63             throw new IOException("indexes[0] too small in "+DATA_FILE_NAME);\r
64         }\r
65         indexes=new int[count];\r
66 \r
67         indexes[0]=count;\r
68         for(i=1; i<count; ++i) {\r
69             indexes[i]=inputStream.readInt();\r
70         }\r
71 \r
72         // read the trie\r
73         trie=new CharTrie(inputStream, null);\r
74 \r
75         // read exceptions[]\r
76         count=indexes[IX_EXC_LENGTH];\r
77         if(count>0) {\r
78             exceptions=new char[count];\r
79             for(i=0; i<count; ++i) {\r
80                 exceptions[i]=inputStream.readChar();\r
81             }\r
82         }\r
83 \r
84         // read unfold[]\r
85         count=indexes[IX_UNFOLD_LENGTH];\r
86         if(count>0) {\r
87             unfold=new char[count];\r
88             for(i=0; i<count; ++i) {\r
89                 unfold[i]=inputStream.readChar();\r
90             }\r
91         }\r
92     }\r
93 \r
94     // implement ICUBinary.Authenticate\r
95     private final class IsAcceptable implements ICUBinary.Authenticate {\r
96         public boolean isDataVersionAcceptable(byte version[]) {\r
97             return version[0]==1 &&\r
98                    version[2]==Trie.INDEX_STAGE_1_SHIFT_ && version[3]==Trie.INDEX_STAGE_2_SHIFT_;\r
99         }\r
100     }\r
101 \r
102     // port of ucase_getSingleton()\r
103     //\r
104     // Note: Do we really need this API?\r
105     public static UCaseProps getSingleton() throws IOException {\r
106         if (FULL_INSTANCE == null) {\r
107             synchronized (UCaseProps.class) {\r
108                 if (FULL_INSTANCE == null) {\r
109                     FULL_INSTANCE = new UCaseProps();\r
110                 }\r
111             }\r
112         }\r
113         return FULL_INSTANCE;\r
114     }\r
115 \r
116     /**\r
117      * Get a singleton dummy object, one that works with no real data.\r
118      * This can be used when the real data is not available.\r
119      * Using the dummy can reduce checks for available data after an initial failure.\r
120      * Port of ucase_getDummy().\r
121      */\r
122     // Note: do we really need this API?\r
123     public static UCaseProps getDummy() {\r
124         if (DUMMY_INSTANCE == null) {\r
125             synchronized (UCaseProps.class) {\r
126                 if (DUMMY_INSTANCE == null) {\r
127                     DUMMY_INSTANCE = new UCaseProps(true);\r
128                 }\r
129             }\r
130         }\r
131         return DUMMY_INSTANCE;\r
132     }\r
133 \r
134     // set of property starts for UnicodeSet ------------------------------- ***\r
135 \r
136     public final void addPropertyStarts(UnicodeSet set) {\r
137         /* add the start code point of each same-value range of the trie */\r
138         TrieIterator iter=new TrieIterator(trie);\r
139         RangeValueIterator.Element element=new RangeValueIterator.Element();\r
140 \r
141         while(iter.next(element)){\r
142             set.add(element.start);\r
143         }\r
144 \r
145         /* add code points with hardcoded properties, plus the ones following them */\r
146 \r
147         /* (none right now, see comment below) */\r
148 \r
149         /*\r
150          * Omit code points with hardcoded specialcasing properties\r
151          * because we do not build property UnicodeSets for them right now.\r
152          */\r
153     }\r
154 \r
155     // data access primitives ---------------------------------------------- ***\r
156     private static final int getExceptionsOffset(int props) {\r
157         return props>>EXC_SHIFT;\r
158     }\r
159 \r
160     private static final boolean propsHasException(int props) {\r
161         return (props&EXCEPTION)!=0;\r
162     }\r
163 \r
164     /* number of bits in an 8-bit integer value */\r
165     private static final byte flagsOffset[/*256*/]={\r
166         0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,\r
167         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,\r
168         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,\r
169         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,\r
170         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,\r
171         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,\r
172         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,\r
173         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,\r
174         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,\r
175         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,\r
176         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,\r
177         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,\r
178         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,\r
179         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,\r
180         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,\r
181         4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8\r
182     };\r
183 \r
184     private static final boolean hasSlot(int flags, int index) {\r
185         return (flags&(1<<index))!=0;\r
186     }\r
187     private static final byte slotOffset(int flags, int index) {\r
188         return flagsOffset[flags&((1<<index)-1)];\r
189     }\r
190 \r
191     /*\r
192      * Get the value of an optional-value slot where hasSlot(excWord, index).\r
193      *\r
194      * @param excWord (in) initial exceptions word\r
195      * @param index (in) desired slot index\r
196      * @param excOffset (in) offset into exceptions[] after excWord=exceptions[excOffset++];\r
197      * @return bits 31..0: slot value\r
198      *             63..32: modified excOffset, moved to the last char of the value, use +1 for beginning of next slot \r
199      */\r
200     private final long getSlotValueAndOffset(int excWord, int index, int excOffset) {\r
201         long value;\r
202         if((excWord&EXC_DOUBLE_SLOTS)==0) {\r
203             excOffset+=slotOffset(excWord, index);\r
204             value=exceptions[excOffset];\r
205         } else {\r
206             excOffset+=2*slotOffset(excWord, index);\r
207             value=exceptions[excOffset++];\r
208             value=(value<<16)|exceptions[excOffset];\r
209         }\r
210         return value |((long)excOffset<<32);\r
211     }\r
212 \r
213     /* same as getSlotValueAndOffset() but does not return the slot offset */\r
214     private final int getSlotValue(int excWord, int index, int excOffset) {\r
215         int value;\r
216         if((excWord&EXC_DOUBLE_SLOTS)==0) {\r
217             excOffset+=slotOffset(excWord, index);\r
218             value=exceptions[excOffset];\r
219         } else {\r
220             excOffset+=2*slotOffset(excWord, index);\r
221             value=exceptions[excOffset++];\r
222             value=(value<<16)|exceptions[excOffset];\r
223         }\r
224         return value;\r
225     }\r
226 \r
227     // simple case mappings ------------------------------------------------ ***\r
228 \r
229     public final int tolower(int c) {\r
230         int props=trie.getCodePointValue(c);\r
231         if(!propsHasException(props)) {\r
232             if(getTypeFromProps(props)>=UPPER) {\r
233                 c+=getDelta(props);\r
234             }\r
235         } else {\r
236             int excOffset=getExceptionsOffset(props);\r
237             int excWord=exceptions[excOffset++];\r
238             if(hasSlot(excWord, EXC_LOWER)) {\r
239                 c=getSlotValue(excWord, EXC_LOWER, excOffset);\r
240             }\r
241         }\r
242         return c;\r
243     }\r
244 \r
245     public final int toupper(int c) {\r
246         int props=trie.getCodePointValue(c);\r
247         if(!propsHasException(props)) {\r
248             if(getTypeFromProps(props)==LOWER) {\r
249                 c+=getDelta(props);\r
250             }\r
251         } else {\r
252             int excOffset=getExceptionsOffset(props);\r
253             int excWord=exceptions[excOffset++];\r
254             if(hasSlot(excWord, EXC_UPPER)) {\r
255                 c=getSlotValue(excWord, EXC_UPPER, excOffset);\r
256             }\r
257         }\r
258         return c;\r
259     }\r
260 \r
261     public final int totitle(int c) {\r
262         int props=trie.getCodePointValue(c);\r
263         if(!propsHasException(props)) {\r
264             if(getTypeFromProps(props)==LOWER) {\r
265                 c+=getDelta(props);\r
266             }\r
267         } else {\r
268             int excOffset=getExceptionsOffset(props);\r
269             int excWord=exceptions[excOffset++];\r
270             int index;\r
271             if(hasSlot(excWord, EXC_TITLE)) {\r
272                 index=EXC_TITLE;\r
273             } else if(hasSlot(excWord, EXC_UPPER)) {\r
274                 index=EXC_UPPER;\r
275             } else {\r
276                 return c;\r
277             }\r
278             c=getSlotValue(excWord, index, excOffset);\r
279         }\r
280         return c;\r
281     }\r
282 \r
283     /**\r
284      * Adds all simple case mappings and the full case folding for c to sa,\r
285      * and also adds special case closure mappings.\r
286      * c itself is not added.\r
287      * For example, the mappings\r
288      * - for s include long s\r
289      * - for sharp s include ss\r
290      * - for k include the Kelvin sign\r
291      */\r
292     public final void addCaseClosure(int c, UnicodeSet set) {\r
293         /*\r
294          * Hardcode the case closure of i and its relatives and ignore the\r
295          * data file data for these characters.\r
296          * The Turkic dotless i and dotted I with their case mapping conditions\r
297          * and case folding option make the related characters behave specially.\r
298          * This code matches their closure behavior to their case folding behavior.\r
299          */\r
300 \r
301         switch(c) {\r
302         case 0x49:\r
303             /* regular i and I are in one equivalence class */\r
304             set.add(0x69);\r
305             return;\r
306         case 0x69:\r
307             set.add(0x49);\r
308             return;\r
309         case 0x130:\r
310             /* dotted I is in a class with <0069 0307> (for canonical equivalence with <0049 0307>) */\r
311             set.add(iDot);\r
312             return;\r
313         case 0x131:\r
314             /* dotless i is in a class by itself */\r
315             return;\r
316         default:\r
317             /* otherwise use the data file data */\r
318             break;\r
319         }\r
320 \r
321         int props=trie.getCodePointValue(c);\r
322         if(!propsHasException(props)) {\r
323             if(getTypeFromProps(props)!=NONE) {\r
324                 /* add the one simple case mapping, no matter what type it is */\r
325                 int delta=getDelta(props);\r
326                 if(delta!=0) {\r
327                     set.add(c+delta);\r
328                 }\r
329             }\r
330         } else {\r
331             /*\r
332              * c has exceptions, so there may be multiple simple and/or\r
333              * full case mappings. Add them all.\r
334              */\r
335             int excOffset0, excOffset=getExceptionsOffset(props);\r
336             int closureOffset;\r
337             int excWord=exceptions[excOffset++];\r
338             int index, closureLength, fullLength, length;\r
339 \r
340             excOffset0=excOffset;\r
341 \r
342             /* add all simple case mappings */\r
343             for(index=EXC_LOWER; index<=EXC_TITLE; ++index) {\r
344                 if(hasSlot(excWord, index)) {\r
345                     excOffset=excOffset0;\r
346                     c=getSlotValue(excWord, index, excOffset);\r
347                     set.add(c);\r
348                 }\r
349             }\r
350 \r
351             /* get the closure string pointer & length */\r
352             if(hasSlot(excWord, EXC_CLOSURE)) {\r
353                 excOffset=excOffset0;\r
354                 long value=getSlotValueAndOffset(excWord, EXC_CLOSURE, excOffset);\r
355                 closureLength=(int)value&CLOSURE_MAX_LENGTH; /* higher bits are reserved */\r
356                 closureOffset=(int)(value>>32)+1; /* behind this slot, unless there are full case mappings */\r
357             } else {\r
358                 closureLength=0;\r
359                 closureOffset=0;\r
360             }\r
361 \r
362             /* add the full case folding */\r
363             if(hasSlot(excWord, EXC_FULL_MAPPINGS)) {\r
364                 excOffset=excOffset0;\r
365                 long value=getSlotValueAndOffset(excWord, EXC_FULL_MAPPINGS, excOffset);\r
366                 fullLength=(int)value;\r
367 \r
368                 /* start of full case mapping strings */\r
369                 excOffset=(int)(value>>32)+1;\r
370 \r
371                 fullLength&=0xffff; /* bits 16 and higher are reserved */\r
372 \r
373                 /* skip the lowercase result string */\r
374                 excOffset+=fullLength&FULL_LOWER;\r
375                 fullLength>>=4;\r
376 \r
377                 /* add the full case folding string */\r
378                 length=fullLength&0xf;\r
379                 if(length!=0) {\r
380                     set.add(new String(exceptions, excOffset, length));\r
381                     excOffset+=length;\r
382                 }\r
383 \r
384                 /* skip the uppercase and titlecase strings */\r
385                 fullLength>>=4;\r
386                 excOffset+=fullLength&0xf;\r
387                 fullLength>>=4;\r
388                 excOffset+=fullLength;\r
389 \r
390                 closureOffset=excOffset; /* behind full case mappings */\r
391             }\r
392 \r
393             /* add each code point in the closure string */\r
394             for(index=0; index<closureLength; index+=UTF16.getCharCount(c)) {\r
395                 c=UTF16.charAt(exceptions, closureOffset, exceptions.length, index);\r
396                 set.add(c);\r
397             }\r
398         }\r
399     }\r
400 \r
401     /*\r
402      * compare s, which has a length, with t=unfold[unfoldOffset..], which has a maximum length or is NUL-terminated\r
403      * must be s.length()>0 and max>0 and s.length()<=max\r
404      */\r
405     private final int strcmpMax(String s, int unfoldOffset, int max) {\r
406         int i1, length, c1, c2;\r
407 \r
408         length=s.length();\r
409         max-=length; /* we require length<=max, so no need to decrement max in the loop */\r
410         i1=0;\r
411         do {\r
412             c1=s.charAt(i1++);\r
413             c2=unfold[unfoldOffset++];\r
414             if(c2==0) {\r
415                 return 1; /* reached the end of t but not of s */\r
416             }\r
417             c1-=c2;\r
418             if(c1!=0) {\r
419                 return c1; /* return difference result */\r
420             }\r
421         } while(--length>0);\r
422         /* ends with length==0 */\r
423 \r
424         if(max==0 || unfold[unfoldOffset]==0) {\r
425             return 0; /* equal to length of both strings */\r
426         } else {\r
427             return -max; /* return lengh difference */\r
428         }\r
429     }\r
430 \r
431     /**\r
432      * Maps the string to single code points and adds the associated case closure\r
433      * mappings.\r
434      * The string is mapped to code points if it is their full case folding string.\r
435      * In other words, this performs a reverse full case folding and then\r
436      * adds the case closure items of the resulting code points.\r
437      * If the string is found and its closure applied, then\r
438      * the string itself is added as well as part of its code points' closure.\r
439      *\r
440      * @return true if the string was found\r
441      */\r
442     public final boolean addStringCaseClosure(String s, UnicodeSet set) {\r
443         int i, length, start, limit, result, unfoldOffset, unfoldRows, unfoldRowWidth, unfoldStringWidth;\r
444 \r
445         if(unfold==null || s==null) {\r
446             return false; /* no reverse case folding data, or no string */\r
447         }\r
448         length=s.length();\r
449         if(length<=1) {\r
450             /* the string is too short to find any match */\r
451             /*\r
452              * more precise would be:\r
453              * if(!u_strHasMoreChar32Than(s, length, 1))\r
454              * but this does not make much practical difference because\r
455              * a single supplementary code point would just not be found\r
456              */\r
457             return false;\r
458         }\r
459 \r
460         unfoldRows=unfold[UNFOLD_ROWS];\r
461         unfoldRowWidth=unfold[UNFOLD_ROW_WIDTH];\r
462         unfoldStringWidth=unfold[UNFOLD_STRING_WIDTH];\r
463         //unfoldCPWidth=unfoldRowWidth-unfoldStringWidth;\r
464 \r
465         if(length>unfoldStringWidth) {\r
466             /* the string is too long to find any match */\r
467             return false;\r
468         }\r
469 \r
470         /* do a binary search for the string */\r
471         start=0;\r
472         limit=unfoldRows;\r
473         while(start<limit) {\r
474             i=(start+limit)/2;\r
475             unfoldOffset=((i+1)*unfoldRowWidth); // +1 to skip the header values above\r
476             result=strcmpMax(s, unfoldOffset, unfoldStringWidth);\r
477 \r
478             if(result==0) {\r
479                 /* found the string: add each code point, and its case closure */\r
480                 int c;\r
481 \r
482                 for(i=unfoldStringWidth; i<unfoldRowWidth && unfold[unfoldOffset+i]!=0; i+=UTF16.getCharCount(c)) {\r
483                     c=UTF16.charAt(unfold, unfoldOffset, unfold.length, i);\r
484                     set.add(c);\r
485                     addCaseClosure(c, set);\r
486                 }\r
487                 return true;\r
488             } else if(result<0) {\r
489                 limit=i;\r
490             } else /* result>0 */ {\r
491                 start=i+1;\r
492             }\r
493         }\r
494 \r
495         return false; /* string not found */\r
496     }\r
497 \r
498     /** @return NONE, LOWER, UPPER, TITLE */\r
499     public final int getType(int c) {\r
500         return getTypeFromProps(trie.getCodePointValue(c));\r
501     }\r
502 \r
503     /** @return same as ucase_getType() and set bit 2 if c is case-ignorable */\r
504     public final int getTypeOrIgnorable(int c) {\r
505         int props=trie.getCodePointValue(c);\r
506         int type=getTypeFromProps(props);\r
507         if(propsHasException(props)) {\r
508             if((exceptions[getExceptionsOffset(props)]&EXC_CASE_IGNORABLE)!=0) {\r
509                 type|=4;\r
510             }\r
511         } else if(type==NONE && (props&CASE_IGNORABLE)!=0) {\r
512             type|=4;\r
513         }\r
514         return type;\r
515     }\r
516 \r
517     /** @return NO_DOT, SOFT_DOTTED, ABOVE, OTHER_ACCENT */\r
518     public final int getDotType(int c) {\r
519         int props=trie.getCodePointValue(c);\r
520         if(!propsHasException(props)) {\r
521             return props&DOT_MASK;\r
522         } else {\r
523             return (exceptions[getExceptionsOffset(props)]>>EXC_DOT_SHIFT)&DOT_MASK;\r
524         }\r
525     }\r
526 \r
527     public final boolean isSoftDotted(int c) {\r
528         return getDotType(c)==SOFT_DOTTED;\r
529     }\r
530 \r
531     public final boolean isCaseSensitive(int c) {\r
532         return (trie.getCodePointValue(c)&SENSITIVE)!=0;\r
533     }\r
534 \r
535     // string casing ------------------------------------------------------- ***\r
536 \r
537     /*\r
538      * These internal functions form the core of string case mappings.\r
539      * They map single code points to result code points or strings and take\r
540      * all necessary conditions (context, locale ID, options) into account.\r
541      *\r
542      * They do not iterate over the source or write to the destination\r
543      * so that the same functions are useful for non-standard string storage,\r
544      * such as in a Replaceable (for Transliterator) or UTF-8/32 strings etc.\r
545      * For the same reason, the "surrounding text" context is passed in as a\r
546      * ContextIterator which does not make any assumptions about\r
547      * the underlying storage.\r
548      *\r
549      * This section contains helper functions that check for conditions\r
550      * in the input text surrounding the current code point\r
551      * according to SpecialCasing.txt.\r
552      *\r
553      * Each helper function gets the index\r
554      * - after the current code point if it looks at following text\r
555      * - before the current code point if it looks at preceding text\r
556      *\r
557      * Unicode 3.2 UAX 21 "Case Mappings" defines the conditions as follows:\r
558      *\r
559      * Final_Sigma\r
560      *   C is preceded by a sequence consisting of\r
561      *     a cased letter and a case-ignorable sequence,\r
562      *   and C is not followed by a sequence consisting of\r
563      *     an ignorable sequence and then a cased letter.\r
564      *\r
565      * More_Above\r
566      *   C is followed by one or more characters of combining class 230 (ABOVE)\r
567      *   in the combining character sequence.\r
568      *\r
569      * After_Soft_Dotted\r
570      *   The last preceding character with combining class of zero before C\r
571      *   was Soft_Dotted,\r
572      *   and there is no intervening combining character class 230 (ABOVE).\r
573      *\r
574      * Before_Dot\r
575      *   C is followed by combining dot above (U+0307).\r
576      *   Any sequence of characters with a combining class that is neither 0 nor 230\r
577      *   may intervene between the current character and the combining dot above.\r
578      *\r
579      * The erratum from 2002-10-31 adds the condition\r
580      *\r
581      * After_I\r
582      *   The last preceding base character was an uppercase I, and there is no\r
583      *   intervening combining character class 230 (ABOVE).\r
584      *\r
585      *   (See Jitterbug 2344 and the comments on After_I below.)\r
586      *\r
587      * Helper definitions in Unicode 3.2 UAX 21:\r
588      *\r
589      * D1. A character C is defined to be cased\r
590      *     if it meets any of the following criteria:\r
591      *\r
592      *   - The general category of C is Titlecase Letter (Lt)\r
593      *   - In [CoreProps], C has one of the properties Uppercase, or Lowercase\r
594      *   - Given D = NFD(C), then it is not the case that:\r
595      *     D = UCD_lower(D) = UCD_upper(D) = UCD_title(D)\r
596      *     (This third criterium does not add any characters to the list\r
597      *      for Unicode 3.2. Ignored.)\r
598      *\r
599      * D2. A character C is defined to be case-ignorable\r
600      *     if it meets either of the following criteria:\r
601      *\r
602      *   - The general category of C is\r
603      *     Nonspacing Mark (Mn), or Enclosing Mark (Me), or Format Control (Cf), or\r
604      *     Letter Modifier (Lm), or Symbol Modifier (Sk)\r
605      *   - C is one of the following characters \r
606      *     U+0027 APOSTROPHE\r
607      *     U+00AD SOFT HYPHEN (SHY)\r
608      *     U+2019 RIGHT SINGLE QUOTATION MARK\r
609      *            (the preferred character for apostrophe)\r
610      *\r
611      * D3. A case-ignorable sequence is a sequence of\r
612      *     zero or more case-ignorable characters.\r
613      */\r
614 \r
615     /**\r
616      * Iterator for string case mappings, which need to look at the\r
617      * context (surrounding text) of a given character for conditional mappings.\r
618      *\r
619      * The iterator only needs to go backward or forward away from the\r
620      * character in question. It does not use any indexes on this interface.\r
621      * It does not support random access or an arbitrary change of\r
622      * iteration direction.\r
623      *\r
624      * The code point being case-mapped itself is never returned by\r
625      * this iterator.\r
626      */\r
627     public interface ContextIterator {\r
628         /**\r
629          * Reset the iterator for forward or backward iteration.\r
630          * @param dir >0: Begin iterating forward from the first code point\r
631          * after the one that is being case-mapped.\r
632          *            <0: Begin iterating backward from the first code point\r
633          * before the one that is being case-mapped.   \r
634          */\r
635         public void reset(int dir);\r
636         /**\r
637          * Iterate and return the next code point, moving in the direction\r
638          * determined by the reset() call.\r
639          * @return Next code point, or <0 when the iteration is done. \r
640          */\r
641         public int next();\r
642     }\r
643 \r
644     /**\r
645      * For string case mappings, a single character (a code point) is mapped\r
646      * either to itself (in which case in-place mapping functions do nothing),\r
647      * or to another single code point, or to a string.\r
648      * Aside from the string contents, these are indicated with a single int\r
649      * value as follows:\r
650      *\r
651      * Mapping to self: Negative values (~self instead of -self to support U+0000)\r
652      *\r
653      * Mapping to another code point: Positive values >MAX_STRING_LENGTH\r
654      *\r
655      * Mapping to a string: The string length (0..MAX_STRING_LENGTH) is\r
656      * returned. Note that the string result may indeed have zero length.\r
657      */\r
658     public static final int MAX_STRING_LENGTH=0x1f;\r
659 \r
660     private static final int LOC_UNKNOWN=0;\r
661     private static final int LOC_ROOT=1;\r
662     private static final int LOC_TURKISH=2;\r
663     private static final int LOC_LITHUANIAN=3;\r
664 \r
665     /*\r
666      * Checks and caches the type of locale ID as it is relevant for case mapping.\r
667      * If the locCache is not null, then it must be initialized with locCache[0]=0 .\r
668      */\r
669     private static final int getCaseLocale(ULocale locale, int[] locCache) {\r
670         int result;\r
671 \r
672         if(locCache!=null && (result=locCache[0])!=LOC_UNKNOWN) {\r
673             return result;\r
674         }\r
675 \r
676         result=LOC_ROOT;\r
677 \r
678         String language=locale.getLanguage();\r
679         if(language.equals("tr") || language.equals("tur") || language.equals("az") || language.equals("aze")) {\r
680             result=LOC_TURKISH;\r
681         } else if(language.equals("lt") || language.equals("lit")) {\r
682             result=LOC_LITHUANIAN;\r
683         }\r
684 \r
685         if(locCache!=null) {\r
686             locCache[0]=result;\r
687         }\r
688         return result;\r
689     }\r
690 \r
691     /* Is followed by {case-ignorable}* cased  ? (dir determines looking forward/backward) */\r
692     private final boolean isFollowedByCasedLetter(ContextIterator iter, int dir) {\r
693         int c;\r
694 \r
695         if(iter==null) {\r
696             return false;\r
697         }\r
698 \r
699         for(iter.reset(dir); (c=iter.next())>=0;) {\r
700             int type=getTypeOrIgnorable(c);\r
701             if((type&4)!=0) {\r
702                 /* case-ignorable, continue with the loop */\r
703             } else if(type!=NONE) {\r
704                 return true; /* followed by cased letter */\r
705             } else {\r
706                 return false; /* uncased and not case-ignorable */\r
707             }\r
708         }\r
709 \r
710         return false; /* not followed by cased letter */\r
711     }\r
712 \r
713     /* Is preceded by Soft_Dotted character with no intervening cc=230 ? */\r
714     private final boolean isPrecededBySoftDotted(ContextIterator iter) {\r
715         int c;\r
716         int dotType;\r
717 \r
718         if(iter==null) {\r
719             return false;\r
720         }\r
721 \r
722         for(iter.reset(-1); (c=iter.next())>=0;) {\r
723             dotType=getDotType(c);\r
724             if(dotType==SOFT_DOTTED) {\r
725                 return true; /* preceded by TYPE_i */\r
726             } else if(dotType!=OTHER_ACCENT) {\r
727                 return false; /* preceded by different base character (not TYPE_i), or intervening cc==230 */\r
728             }\r
729         }\r
730 \r
731         return false; /* not preceded by TYPE_i */\r
732     }\r
733 \r
734     /*\r
735      * See Jitterbug 2344:\r
736      * The condition After_I for Turkic-lowercasing of U+0307 combining dot above\r
737      * is checked in ICU 2.0, 2.1, 2.6 but was not in 2.2 & 2.4 because\r
738      * we made those releases compatible with Unicode 3.2 which had not fixed\r
739      * a related bug in SpecialCasing.txt.\r
740      *\r
741      * From the Jitterbug 2344 text:\r
742      * ... this bug is listed as a Unicode erratum\r
743      * from 2002-10-31 at http://www.unicode.org/uni2errata/UnicodeErrata.html\r
744      * <quote>\r
745      * There are two errors in SpecialCasing.txt.\r
746      * 1. Missing semicolons on two lines. ... [irrelevant for ICU]\r
747      * 2. An incorrect context definition. Correct as follows:\r
748      * < 0307; ; 0307; 0307; tr After_Soft_Dotted; # COMBINING DOT ABOVE\r
749      * < 0307; ; 0307; 0307; az After_Soft_Dotted; # COMBINING DOT ABOVE\r
750      * ---\r
751      * > 0307; ; 0307; 0307; tr After_I; # COMBINING DOT ABOVE\r
752      * > 0307; ; 0307; 0307; az After_I; # COMBINING DOT ABOVE\r
753      * where the context After_I is defined as:\r
754      * The last preceding base character was an uppercase I, and there is no\r
755      * intervening combining character class 230 (ABOVE).\r
756      * </quote>\r
757      *\r
758      * Note that SpecialCasing.txt even in Unicode 3.2 described the condition as:\r
759      *\r
760      * # When lowercasing, remove dot_above in the sequence I + dot_above, which will turn into i.\r
761      * # This matches the behavior of the canonically equivalent I-dot_above\r
762      *\r
763      * See also the description in this place in older versions of uchar.c (revision 1.100).\r
764      *\r
765      * Markus W. Scherer 2003-feb-15\r
766      */\r
767 \r
768     /* Is preceded by base character 'I' with no intervening cc=230 ? */\r
769     private final boolean isPrecededBy_I(ContextIterator iter) {\r
770         int c;\r
771         int dotType;\r
772 \r
773         if(iter==null) {\r
774             return false;\r
775         }\r
776 \r
777         for(iter.reset(-1); (c=iter.next())>=0;) {\r
778             if(c==0x49) {\r
779                 return true; /* preceded by I */\r
780             }\r
781             dotType=getDotType(c);\r
782             if(dotType!=OTHER_ACCENT) {\r
783                 return false; /* preceded by different base character (not I), or intervening cc==230 */\r
784             }\r
785         }\r
786 \r
787         return false; /* not preceded by I */\r
788     }\r
789 \r
790     /* Is followed by one or more cc==230 ? */\r
791     private final boolean isFollowedByMoreAbove(ContextIterator iter) {\r
792         int c;\r
793         int dotType;\r
794 \r
795         if(iter==null) {\r
796             return false;\r
797         }\r
798 \r
799         for(iter.reset(1); (c=iter.next())>=0;) {\r
800             dotType=getDotType(c);\r
801             if(dotType==ABOVE) {\r
802                 return true; /* at least one cc==230 following */\r
803             } else if(dotType!=OTHER_ACCENT) {\r
804                 return false; /* next base character, no more cc==230 following */\r
805             }\r
806         }\r
807 \r
808         return false; /* no more cc==230 following */\r
809     }\r
810 \r
811     /* Is followed by a dot above (without cc==230 in between) ? */\r
812     private final boolean isFollowedByDotAbove(ContextIterator iter) {\r
813         int c;\r
814         int dotType;\r
815 \r
816         if(iter==null) {\r
817             return false;\r
818         }\r
819 \r
820         for(iter.reset(1); (c=iter.next())>=0; ) {\r
821             if(c==0x307) {\r
822                 return true;\r
823             }\r
824             dotType=getDotType(c);\r
825             if(dotType!=OTHER_ACCENT) {\r
826                 return false; /* next base character or cc==230 in between */\r
827             }\r
828         }\r
829 \r
830         return false; /* no dot above following */\r
831     }\r
832 \r
833     private static final String\r
834         iDot=       "i\u0307",\r
835         jDot=       "j\u0307",\r
836         iOgonekDot= "\u012f\u0307",\r
837         iDotGrave=  "i\u0307\u0300",\r
838         iDotAcute=  "i\u0307\u0301",\r
839         iDotTilde=  "i\u0307\u0303";\r
840 \r
841     /**\r
842      * Get the full lowercase mapping for c.\r
843      *\r
844      * @param c Character to be mapped.\r
845      * @param iter Character iterator, used for context-sensitive mappings.\r
846      *             See ContextIterator for details.\r
847      *             If iter==null then a context-independent result is returned.\r
848      * @param out If the mapping result is a string, then it is appended to out.\r
849      * @param locale Locale ID for locale-dependent mappings.\r
850      * @param locCache Initialize locCache[0] to 0; may be used to cache the result of parsing\r
851      *                 the locale ID for subsequent calls.\r
852      *                 Can be null.\r
853      * @return Output code point or string length, see MAX_STRING_LENGTH.\r
854      *\r
855      * @see ContextIterator\r
856      * @see #MAX_STRING_LENGTH\r
857      * @internal\r
858      */\r
859     public final int toFullLower(int c, ContextIterator iter,\r
860                                  StringBuffer out,\r
861                                  ULocale locale, int[] locCache) {\r
862         int result, props;\r
863 \r
864         result=c;\r
865         props=trie.getCodePointValue(c);\r
866         if(!propsHasException(props)) {\r
867             if(getTypeFromProps(props)>=UPPER) {\r
868                 result=c+getDelta(props);\r
869             }\r
870         } else {\r
871             int excOffset=getExceptionsOffset(props), excOffset2;\r
872             int excWord=exceptions[excOffset++];\r
873             int full;\r
874 \r
875             excOffset2=excOffset;\r
876 \r
877             if((excWord&EXC_CONDITIONAL_SPECIAL)!=0) {\r
878                 /* use hardcoded conditions and mappings */\r
879                 int loc=getCaseLocale(locale, locCache);\r
880 \r
881                 /*\r
882                  * Test for conditional mappings first\r
883                  *   (otherwise the unconditional default mappings are always taken),\r
884                  * then test for characters that have unconditional mappings in SpecialCasing.txt,\r
885                  * then get the UnicodeData.txt mappings.\r
886                  */\r
887                 if( loc==LOC_LITHUANIAN &&\r
888                         /* base characters, find accents above */\r
889                         (((c==0x49 || c==0x4a || c==0x12e) &&\r
890                             isFollowedByMoreAbove(iter)) ||\r
891                         /* precomposed with accent above, no need to find one */\r
892                         (c==0xcc || c==0xcd || c==0x128))\r
893                 ) {\r
894                     /*\r
895                         # Lithuanian\r
896 \r
897                         # Lithuanian retains the dot in a lowercase i when followed by accents.\r
898 \r
899                         # Introduce an explicit dot above when lowercasing capital I's and J's\r
900                         # whenever there are more accents above.\r
901                         # (of the accents used in Lithuanian: grave, acute, tilde above, and ogonek)\r
902 \r
903                         0049; 0069 0307; 0049; 0049; lt More_Above; # LATIN CAPITAL LETTER I\r
904                         004A; 006A 0307; 004A; 004A; lt More_Above; # LATIN CAPITAL LETTER J\r
905                         012E; 012F 0307; 012E; 012E; lt More_Above; # LATIN CAPITAL LETTER I WITH OGONEK\r
906                         00CC; 0069 0307 0300; 00CC; 00CC; lt; # LATIN CAPITAL LETTER I WITH GRAVE\r
907                         00CD; 0069 0307 0301; 00CD; 00CD; lt; # LATIN CAPITAL LETTER I WITH ACUTE\r
908                         0128; 0069 0307 0303; 0128; 0128; lt; # LATIN CAPITAL LETTER I WITH TILDE\r
909                      */\r
910                     switch(c) {\r
911                     case 0x49:  /* LATIN CAPITAL LETTER I */\r
912                         out.append(iDot);\r
913                         return 2;\r
914                     case 0x4a:  /* LATIN CAPITAL LETTER J */\r
915                         out.append(jDot);\r
916                         return 2;\r
917                     case 0x12e: /* LATIN CAPITAL LETTER I WITH OGONEK */\r
918                         out.append(iOgonekDot);\r
919                         return 2;\r
920                     case 0xcc:  /* LATIN CAPITAL LETTER I WITH GRAVE */\r
921                         out.append(iDotGrave);\r
922                         return 3;\r
923                     case 0xcd:  /* LATIN CAPITAL LETTER I WITH ACUTE */\r
924                         out.append(iDotAcute);\r
925                         return 3;\r
926                     case 0x128: /* LATIN CAPITAL LETTER I WITH TILDE */\r
927                         out.append(iDotTilde);\r
928                         return 3;\r
929                     default:\r
930                         return 0; /* will not occur */\r
931                     }\r
932                 /* # Turkish and Azeri */\r
933                 } else if(loc==LOC_TURKISH && c==0x130) {\r
934                     /*\r
935                         # I and i-dotless; I-dot and i are case pairs in Turkish and Azeri\r
936                         # The following rules handle those cases.\r
937 \r
938                         0130; 0069; 0130; 0130; tr # LATIN CAPITAL LETTER I WITH DOT ABOVE\r
939                         0130; 0069; 0130; 0130; az # LATIN CAPITAL LETTER I WITH DOT ABOVE\r
940                      */\r
941                     return 0x69;\r
942                 } else if(loc==LOC_TURKISH && c==0x307 && isPrecededBy_I(iter)) {\r
943                     /*\r
944                         # When lowercasing, remove dot_above in the sequence I + dot_above, which will turn into i.\r
945                         # This matches the behavior of the canonically equivalent I-dot_above\r
946 \r
947                         0307; ; 0307; 0307; tr After_I; # COMBINING DOT ABOVE\r
948                         0307; ; 0307; 0307; az After_I; # COMBINING DOT ABOVE\r
949                      */\r
950                     return 0; /* remove the dot (continue without output) */\r
951                 } else if(loc==LOC_TURKISH && c==0x49 && !isFollowedByDotAbove(iter)) {\r
952                     /*\r
953                         # When lowercasing, unless an I is before a dot_above, it turns into a dotless i.\r
954 \r
955                         0049; 0131; 0049; 0049; tr Not_Before_Dot; # LATIN CAPITAL LETTER I\r
956                         0049; 0131; 0049; 0049; az Not_Before_Dot; # LATIN CAPITAL LETTER I\r
957                      */\r
958                     return 0x131;\r
959                 } else if(c==0x130) {\r
960                     /*\r
961                         # Preserve canonical equivalence for I with dot. Turkic is handled below.\r
962 \r
963                         0130; 0069 0307; 0130; 0130; # LATIN CAPITAL LETTER I WITH DOT ABOVE\r
964                      */\r
965                     out.append(iDot);\r
966                     return 2;\r
967                 } else if(  c==0x3a3 &&\r
968                             !isFollowedByCasedLetter(iter, 1) &&\r
969                             isFollowedByCasedLetter(iter, -1) /* -1=preceded */\r
970                 ) {\r
971                     /* greek capital sigma maps depending on surrounding cased letters (see SpecialCasing.txt) */\r
972                     /*\r
973                         # Special case for final form of sigma\r
974 \r
975                         03A3; 03C2; 03A3; 03A3; Final_Sigma; # GREEK CAPITAL LETTER SIGMA\r
976                      */\r
977                     return 0x3c2; /* greek small final sigma */\r
978                 } else {\r
979                     /* no known conditional special case mapping, use a normal mapping */\r
980                 }\r
981             } else if(hasSlot(excWord, EXC_FULL_MAPPINGS)) {\r
982                 long value=getSlotValueAndOffset(excWord, EXC_FULL_MAPPINGS, excOffset);\r
983                 full=(int)value&FULL_LOWER;\r
984                 if(full!=0) {\r
985                     /* start of full case mapping strings */\r
986                     excOffset=(int)(value>>32)+1;\r
987 \r
988                     /* set the output pointer to the lowercase mapping */\r
989                     out.append(exceptions, excOffset, full);\r
990 \r
991                     /* return the string length */\r
992                     return full;\r
993                 }\r
994             }\r
995 \r
996             if(hasSlot(excWord, EXC_LOWER)) {\r
997                 result=getSlotValue(excWord, EXC_LOWER, excOffset2);\r
998             }\r
999         }\r
1000 \r
1001         return (result==c) ? ~result : result;\r
1002     }\r
1003 \r
1004     /* internal */\r
1005     private final int toUpperOrTitle(int c, ContextIterator iter,\r
1006                                      StringBuffer out,\r
1007                                      ULocale locale, int[] locCache,\r
1008                                      boolean upperNotTitle) {\r
1009         int result;\r
1010         int props;\r
1011 \r
1012         result=c;\r
1013         props=trie.getCodePointValue(c);\r
1014         if(!propsHasException(props)) {\r
1015             if(getTypeFromProps(props)==LOWER) {\r
1016                 result=c+getDelta(props);\r
1017             }\r
1018         } else {\r
1019             int excOffset=getExceptionsOffset(props), excOffset2;\r
1020             int excWord=exceptions[excOffset++];\r
1021             int full, index;\r
1022 \r
1023             excOffset2=excOffset;\r
1024 \r
1025             if((excWord&EXC_CONDITIONAL_SPECIAL)!=0) {\r
1026                 /* use hardcoded conditions and mappings */\r
1027                 int loc=getCaseLocale(locale, locCache);\r
1028 \r
1029                 if(loc==LOC_TURKISH && c==0x69) {\r
1030                     /*\r
1031                         # Turkish and Azeri\r
1032 \r
1033                         # I and i-dotless; I-dot and i are case pairs in Turkish and Azeri\r
1034                         # The following rules handle those cases.\r
1035 \r
1036                         # When uppercasing, i turns into a dotted capital I\r
1037 \r
1038                         0069; 0069; 0130; 0130; tr; # LATIN SMALL LETTER I\r
1039                         0069; 0069; 0130; 0130; az; # LATIN SMALL LETTER I\r
1040                     */\r
1041                     return 0x130;\r
1042                 } else if(loc==LOC_LITHUANIAN && c==0x307 && isPrecededBySoftDotted(iter)) {\r
1043                     /*\r
1044                         # Lithuanian\r
1045 \r
1046                         # Lithuanian retains the dot in a lowercase i when followed by accents.\r
1047 \r
1048                         # Remove DOT ABOVE after "i" with upper or titlecase\r
1049 \r
1050                         0307; 0307; ; ; lt After_Soft_Dotted; # COMBINING DOT ABOVE\r
1051                      */\r
1052                     return 0; /* remove the dot (continue without output) */\r
1053                 } else {\r
1054                     /* no known conditional special case mapping, use a normal mapping */\r
1055                 }\r
1056             } else if(hasSlot(excWord, EXC_FULL_MAPPINGS)) {\r
1057                 long value=getSlotValueAndOffset(excWord, EXC_FULL_MAPPINGS, excOffset);\r
1058                 full=(int)value&0xffff;\r
1059 \r
1060                 /* start of full case mapping strings */\r
1061                 excOffset=(int)(value>>32)+1;\r
1062 \r
1063                 /* skip the lowercase and case-folding result strings */\r
1064                 excOffset+=full&FULL_LOWER;\r
1065                 full>>=4;\r
1066                 excOffset+=full&0xf;\r
1067                 full>>=4;\r
1068 \r
1069                 if(upperNotTitle) {\r
1070                     full&=0xf;\r
1071                 } else {\r
1072                     /* skip the uppercase result string */\r
1073                     excOffset+=full&0xf;\r
1074                     full=(full>>4)&0xf;\r
1075                 }\r
1076 \r
1077                 if(full!=0) {\r
1078                     /* set the output pointer to the result string */\r
1079                     out.append(exceptions, excOffset, full);\r
1080 \r
1081                     /* return the string length */\r
1082                     return full;\r
1083                 }\r
1084             }\r
1085 \r
1086             if(!upperNotTitle && hasSlot(excWord, EXC_TITLE)) {\r
1087                 index=EXC_TITLE;\r
1088             } else if(hasSlot(excWord, EXC_UPPER)) {\r
1089                 /* here, titlecase is same as uppercase */\r
1090                 index=EXC_UPPER;\r
1091             } else {\r
1092                 return ~c;\r
1093             }\r
1094             result=getSlotValue(excWord, index, excOffset2);\r
1095         }\r
1096 \r
1097         return (result==c) ? ~result : result;\r
1098     }\r
1099 \r
1100     public final int toFullUpper(int c, ContextIterator iter,\r
1101                                  StringBuffer out,\r
1102                                  ULocale locale, int[] locCache) {\r
1103         return toUpperOrTitle(c, iter, out, locale, locCache, true);\r
1104     }\r
1105 \r
1106     public final int toFullTitle(int c, ContextIterator iter,\r
1107                                  StringBuffer out,\r
1108                                  ULocale locale, int[] locCache) {\r
1109         return toUpperOrTitle(c, iter, out, locale, locCache, false);\r
1110     }\r
1111 \r
1112     /* case folding ------------------------------------------------------------- */\r
1113 \r
1114     /*\r
1115      * Case folding is similar to lowercasing.\r
1116      * The result may be a simple mapping, i.e., a single code point, or\r
1117      * a full mapping, i.e., a string.\r
1118      * If the case folding for a code point is the same as its simple (1:1) lowercase mapping,\r
1119      * then only the lowercase mapping is stored.\r
1120      *\r
1121      * Some special cases are hardcoded because their conditions cannot be\r
1122      * parsed and processed from CaseFolding.txt.\r
1123      *\r
1124      * Unicode 3.2 CaseFolding.txt specifies for its status field:\r
1125 \r
1126     # C: common case folding, common mappings shared by both simple and full mappings.\r
1127     # F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces.\r
1128     # S: simple case folding, mappings to single characters where different from F.\r
1129     # T: special case for uppercase I and dotted uppercase I\r
1130     #    - For non-Turkic languages, this mapping is normally not used.\r
1131     #    - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters.\r
1132     #\r
1133     # Usage:\r
1134     #  A. To do a simple case folding, use the mappings with status C + S.\r
1135     #  B. To do a full case folding, use the mappings with status C + F.\r
1136     #\r
1137     #    The mappings with status T can be used or omitted depending on the desired case-folding\r
1138     #    behavior. (The default option is to exclude them.)\r
1139 \r
1140      * Unicode 3.2 has 'T' mappings as follows:\r
1141 \r
1142     0049; T; 0131; # LATIN CAPITAL LETTER I\r
1143     0130; T; 0069; # LATIN CAPITAL LETTER I WITH DOT ABOVE\r
1144 \r
1145      * while the default mappings for these code points are:\r
1146 \r
1147     0049; C; 0069; # LATIN CAPITAL LETTER I\r
1148     0130; F; 0069 0307; # LATIN CAPITAL LETTER I WITH DOT ABOVE\r
1149 \r
1150      * U+0130 has no simple case folding (simple-case-folds to itself).\r
1151      */\r
1152 \r
1153     /**\r
1154      * Bit mask for getting just the options from a string compare options word\r
1155      * that are relevant for case folding (of a single string or code point).\r
1156      * @internal\r
1157      */\r
1158     private static final int FOLD_CASE_OPTIONS_MASK = 0xff;\r
1159     \r
1160     /* return the simple case folding mapping for c */\r
1161     public final int fold(int c, int options) {\r
1162         int props=trie.getCodePointValue(c);\r
1163         if(!propsHasException(props)) {\r
1164             if(getTypeFromProps(props)>=UPPER) {\r
1165                 c+=getDelta(props);\r
1166             }\r
1167         } else {\r
1168             int excOffset=getExceptionsOffset(props);\r
1169             int excWord=exceptions[excOffset++];\r
1170             int index;\r
1171             if((excWord&EXC_CONDITIONAL_FOLD)!=0) {\r
1172                 /* special case folding mappings, hardcoded */\r
1173                 if((options&FOLD_CASE_OPTIONS_MASK)==UCharacter.FOLD_CASE_DEFAULT) {\r
1174                     /* default mappings */\r
1175                     if(c==0x49) {\r
1176                         /* 0049; C; 0069; # LATIN CAPITAL LETTER I */\r
1177                         return 0x69;\r
1178                     } else if(c==0x130) {\r
1179                         /* no simple case folding for U+0130 */\r
1180                         return c;\r
1181                     }\r
1182                 } else {\r
1183                     /* Turkic mappings */\r
1184                     if(c==0x49) {\r
1185                         /* 0049; T; 0131; # LATIN CAPITAL LETTER I */\r
1186                         return 0x131;\r
1187                     } else if(c==0x130) {\r
1188                         /* 0130; T; 0069; # LATIN CAPITAL LETTER I WITH DOT ABOVE */\r
1189                         return 0x69;\r
1190                     }\r
1191                 }\r
1192             }\r
1193             if(hasSlot(excWord, EXC_FOLD)) {\r
1194                 index=EXC_FOLD;\r
1195             } else if(hasSlot(excWord, EXC_LOWER)) {\r
1196                 index=EXC_LOWER;\r
1197             } else {\r
1198                 return c;\r
1199             }\r
1200             c=getSlotValue(excWord, index, excOffset);\r
1201         }\r
1202         return c;\r
1203     }\r
1204 \r
1205     /*\r
1206      * Issue for canonical caseless match (UAX #21):\r
1207      * Turkic casefolding (using "T" mappings in CaseFolding.txt) does not preserve\r
1208      * canonical equivalence, unlike default-option casefolding.\r
1209      * For example, I-grave and I + grave fold to strings that are not canonically\r
1210      * equivalent.\r
1211      * For more details, see the comment in unorm_compare() in unorm.cpp\r
1212      * and the intermediate prototype changes for Jitterbug 2021.\r
1213      * (For example, revision 1.104 of uchar.c and 1.4 of CaseFolding.txt.)\r
1214      *\r
1215      * This did not get fixed because it appears that it is not possible to fix\r
1216      * it for uppercase and lowercase characters (I-grave vs. i-grave)\r
1217      * together in a way that they still fold to common result strings.\r
1218      */\r
1219 \r
1220     public final int toFullFolding(int c, StringBuffer out, int options) {\r
1221         int result;\r
1222         int props;\r
1223 \r
1224         result=c;\r
1225         props=trie.getCodePointValue(c);\r
1226         if(!propsHasException(props)) {\r
1227             if(getTypeFromProps(props)>=UPPER) {\r
1228                 result=c+getDelta(props);\r
1229             }\r
1230         } else {\r
1231             int excOffset=getExceptionsOffset(props), excOffset2;\r
1232             int excWord=exceptions[excOffset++];\r
1233             int full, index;\r
1234 \r
1235             excOffset2=excOffset;\r
1236 \r
1237             if((excWord&EXC_CONDITIONAL_FOLD)!=0) {\r
1238                 /* use hardcoded conditions and mappings */\r
1239                 if((options&FOLD_CASE_OPTIONS_MASK)==UCharacter.FOLD_CASE_DEFAULT) {\r
1240                     /* default mappings */\r
1241                     if(c==0x49) {\r
1242                         /* 0049; C; 0069; # LATIN CAPITAL LETTER I */\r
1243                         return 0x69;\r
1244                     } else if(c==0x130) {\r
1245                         /* 0130; F; 0069 0307; # LATIN CAPITAL LETTER I WITH DOT ABOVE */\r
1246                         out.append(iDot);\r
1247                         return 2;\r
1248                     }\r
1249                 } else {\r
1250                     /* Turkic mappings */\r
1251                     if(c==0x49) {\r
1252                         /* 0049; T; 0131; # LATIN CAPITAL LETTER I */\r
1253                         return 0x131;\r
1254                     } else if(c==0x130) {\r
1255                         /* 0130; T; 0069; # LATIN CAPITAL LETTER I WITH DOT ABOVE */\r
1256                         return 0x69;\r
1257                     }\r
1258                 }\r
1259             } else if(hasSlot(excWord, EXC_FULL_MAPPINGS)) {\r
1260                 long value=getSlotValueAndOffset(excWord, EXC_FULL_MAPPINGS, excOffset);\r
1261                 full=(int)value&0xffff;\r
1262 \r
1263                 /* start of full case mapping strings */\r
1264                 excOffset=(int)(value>>32)+1;\r
1265 \r
1266                 /* skip the lowercase result string */\r
1267                 excOffset+=full&FULL_LOWER;\r
1268                 full=(full>>4)&0xf;\r
1269 \r
1270                 if(full!=0) {\r
1271                     /* set the output pointer to the result string */\r
1272                     out.append(exceptions, excOffset, full);\r
1273 \r
1274                     /* return the string length */\r
1275                     return full;\r
1276                 }\r
1277             }\r
1278 \r
1279             if(hasSlot(excWord, EXC_FOLD)) {\r
1280                 index=EXC_FOLD;\r
1281             } else if(hasSlot(excWord, EXC_LOWER)) {\r
1282                 index=EXC_LOWER;\r
1283             } else {\r
1284                 return ~c;\r
1285             }\r
1286             result=getSlotValue(excWord, index, excOffset2);\r
1287         }\r
1288 \r
1289         return (result==c) ? ~result : result;\r
1290     }\r
1291 \r
1292     /* case mapping properties API ---------------------------------------------- */\r
1293 \r
1294     private static final int[] rootLocCache = { LOC_ROOT };\r
1295     /*\r
1296      * We need a StringBuffer for multi-code point output from the\r
1297      * full case mapping functions. However, we do not actually use that output,\r
1298      * we just check whether the input character was mapped to anything else.\r
1299      * We use a shared StringBuffer to avoid allocating a new one in each call.\r
1300      * We remove its contents each time so that it does not grow large over time.\r
1301      *\r
1302      * @internal\r
1303      */\r
1304     public static final StringBuffer dummyStringBuffer = new StringBuffer();\r
1305 \r
1306     public final boolean hasBinaryProperty(int c, int which) {\r
1307         switch(which) {\r
1308         case UProperty.LOWERCASE:\r
1309             return LOWER==getType(c);\r
1310         case UProperty.UPPERCASE:\r
1311             return UPPER==getType(c);\r
1312         case UProperty.SOFT_DOTTED:\r
1313             return isSoftDotted(c);\r
1314         case UProperty.CASE_SENSITIVE:\r
1315             return isCaseSensitive(c);\r
1316         case UProperty.CASED:\r
1317             return NONE!=getType(c);\r
1318         case UProperty.CASE_IGNORABLE:\r
1319             return (getTypeOrIgnorable(c)>>2)!=0;\r
1320         /*\r
1321          * Note: The following Changes_When_Xyz are defined as testing whether\r
1322          * the NFD form of the input changes when Xyz-case-mapped.\r
1323          * However, this simpler implementation of these properties,\r
1324          * ignoring NFD, passes the tests.\r
1325          * The implementation needs to be changed if the tests start failing.\r
1326          * When that happens, optimizations should be used to work with the\r
1327          * per-single-code point ucase_toFullXyz() functions unless\r
1328          * the NFD form has more than one code point,\r
1329          * and the property starts set needs to be the union of the\r
1330          * start sets for normalization and case mappings.\r
1331          */\r
1332         case UProperty.CHANGES_WHEN_LOWERCASED:\r
1333             dummyStringBuffer.setLength(0);\r
1334             return toFullLower(c, null, dummyStringBuffer, ULocale.ROOT, rootLocCache)>=0;\r
1335         case UProperty.CHANGES_WHEN_UPPERCASED:\r
1336             dummyStringBuffer.setLength(0);\r
1337             return toFullUpper(c, null, dummyStringBuffer, ULocale.ROOT, rootLocCache)>=0;\r
1338         case UProperty.CHANGES_WHEN_TITLECASED:\r
1339             dummyStringBuffer.setLength(0);\r
1340             return toFullTitle(c, null, dummyStringBuffer, ULocale.ROOT, rootLocCache)>=0;\r
1341         /* case UProperty.CHANGES_WHEN_CASEFOLDED: -- in UCharacterProperty.java */\r
1342         case UProperty.CHANGES_WHEN_CASEMAPPED:\r
1343             dummyStringBuffer.setLength(0);\r
1344             return\r
1345                 toFullLower(c, null, dummyStringBuffer, ULocale.ROOT, rootLocCache)>=0 ||\r
1346                 toFullUpper(c, null, dummyStringBuffer, ULocale.ROOT, rootLocCache)>=0 ||\r
1347                 toFullTitle(c, null, dummyStringBuffer, ULocale.ROOT, rootLocCache)>=0;\r
1348         default:\r
1349             return false;\r
1350         }\r
1351     }\r
1352 \r
1353     // data members -------------------------------------------------------- ***\r
1354     private int indexes[];\r
1355     private char exceptions[];\r
1356     private char unfold[];\r
1357 \r
1358     private CharTrie trie;\r
1359 \r
1360     // data format constants ----------------------------------------------- ***\r
1361     private static final String DATA_NAME="ucase";\r
1362     private static final String DATA_TYPE="icu";\r
1363     private static final String DATA_FILE_NAME=DATA_NAME+"."+DATA_TYPE;\r
1364 \r
1365     /* format "cAsE" */\r
1366     private static final byte FMT[]={ 0x63, 0x41, 0x53, 0x45 };\r
1367 \r
1368     /* indexes into indexes[] */\r
1369     private static final int IX_INDEX_TOP=0;\r
1370     //private static final int IX_LENGTH=1;\r
1371     //private static final int IX_TRIE_SIZE=2;\r
1372     private static final int IX_EXC_LENGTH=3;\r
1373     private static final int IX_UNFOLD_LENGTH=4;\r
1374 \r
1375     //private static final int IX_MAX_FULL_LENGTH=15;\r
1376     private static final int IX_TOP=16;\r
1377 \r
1378     // definitions for 16-bit case properties word ------------------------- ***\r
1379 \r
1380     /* 2-bit constants for types of cased characters */\r
1381     public static final int TYPE_MASK=3;\r
1382     public static final int NONE=0;\r
1383     public static final int LOWER=1;\r
1384     public static final int UPPER=2;\r
1385     public static final int TITLE=3;\r
1386 \r
1387     private static final int getTypeFromProps(int props) {\r
1388         return props&TYPE_MASK;\r
1389     }\r
1390 \r
1391     private static final int SENSITIVE=     4;\r
1392     private static final int EXCEPTION=     8;\r
1393 \r
1394     private static final int DOT_MASK=      0x30;\r
1395     //private static final int NO_DOT=        0;      /* normal characters with cc=0 */\r
1396     private static final int SOFT_DOTTED=   0x10;   /* soft-dotted characters with cc=0 */\r
1397     private static final int ABOVE=         0x20;   /* "above" accents with cc=230 */\r
1398     private static final int OTHER_ACCENT=  0x30;   /* other accent character (0<cc!=230) */\r
1399 \r
1400     /* no exception: bits 15..6 are a 10-bit signed case mapping delta */\r
1401     private static final int DELTA_SHIFT=   6;\r
1402     //private static final int DELTA_MASK=    0xffc0;\r
1403     //private static final int MAX_DELTA=     0x1ff;\r
1404     //private static final int MIN_DELTA=     (-MAX_DELTA-1);\r
1405 \r
1406     private static final int getDelta(int props) {\r
1407         return (short)props>>DELTA_SHIFT;\r
1408     }\r
1409 \r
1410     /* case-ignorable uses one of the delta bits, see gencase/store.c */\r
1411     private static final int CASE_IGNORABLE=0x40;\r
1412 \r
1413     /* exception: bits 15..4 are an unsigned 12-bit index into the exceptions array */\r
1414     private static final int EXC_SHIFT=     4;\r
1415     //private static final int EXC_MASK=      0xfff0;\r
1416     //private static final int MAX_EXCEPTIONS=0x1000;\r
1417 \r
1418     /* definitions for 16-bit main exceptions word ------------------------------ */\r
1419 \r
1420     /* first 8 bits indicate values in optional slots */\r
1421     private static final int EXC_LOWER=0;\r
1422     private static final int EXC_FOLD=1;\r
1423     private static final int EXC_UPPER=2;\r
1424     private static final int EXC_TITLE=3;\r
1425     //private static final int EXC_4=4;           /* reserved */\r
1426     //private static final int EXC_5=5;           /* reserved */\r
1427     private static final int EXC_CLOSURE=6;\r
1428     private static final int EXC_FULL_MAPPINGS=7;\r
1429     //private static final int EXC_ALL_SLOTS=8;   /* one past the last slot */\r
1430 \r
1431     /* each slot is 2 uint16_t instead of 1 */\r
1432     private static final int EXC_DOUBLE_SLOTS=          0x100;\r
1433 \r
1434     /* reserved: exception bits 10..9 */\r
1435 \r
1436     private static final int EXC_CASE_IGNORABLE=        0x800;\r
1437 \r
1438     /* EXC_DOT_MASK=DOT_MASK<<EXC_DOT_SHIFT */\r
1439     private static final int EXC_DOT_SHIFT=8;\r
1440 \r
1441     /* normally stored in the main word, but pushed out for larger exception indexes */\r
1442     //private static final int EXC_DOT_MASK=              0x3000;\r
1443     //private static final int EXC_NO_DOT=                0;\r
1444     //private static final int EXC_SOFT_DOTTED=           0x1000;\r
1445     //private static final int EXC_ABOVE=                 0x2000; /* "above" accents with cc=230 */\r
1446     //private static final int EXC_OTHER_ACCENT=          0x3000; /* other character (0<cc!=230) */\r
1447 \r
1448     /* complex/conditional mappings */\r
1449     private static final int EXC_CONDITIONAL_SPECIAL=   0x4000;\r
1450     private static final int EXC_CONDITIONAL_FOLD=      0x8000;\r
1451 \r
1452     /* definitions for lengths word for full case mappings */\r
1453     private static final int FULL_LOWER=    0xf;\r
1454     //private static final int FULL_FOLDING=  0xf0;\r
1455     //private static final int FULL_UPPER=    0xf00;\r
1456     //private static final int FULL_TITLE=    0xf000;\r
1457 \r
1458     /* maximum lengths */\r
1459     //private static final int FULL_MAPPINGS_MAX_LENGTH=4*0xf;\r
1460     private static final int CLOSURE_MAX_LENGTH=0xf;\r
1461 \r
1462     /* constants for reverse case folding ("unfold") data */\r
1463     private static final int UNFOLD_ROWS=0;\r
1464     private static final int UNFOLD_ROW_WIDTH=1;\r
1465     private static final int UNFOLD_STRING_WIDTH=2;\r
1466 \r
1467 \r
1468     /*\r
1469      * public singleton instance\r
1470      */\r
1471     public static final UCaseProps INSTANCE;\r
1472 \r
1473     private static volatile UCaseProps FULL_INSTANCE;\r
1474     private static volatile UCaseProps DUMMY_INSTANCE;\r
1475 \r
1476     // This static initializer block must be placed after\r
1477     // other static member initialization\r
1478     static {\r
1479         UCaseProps cp;\r
1480         try {\r
1481             cp = new UCaseProps();\r
1482             FULL_INSTANCE = cp;\r
1483         } catch (IOException e) {\r
1484             // creating dummy\r
1485             cp = new UCaseProps(true);\r
1486             DUMMY_INSTANCE = cp;\r
1487         }\r
1488         INSTANCE = cp;\r
1489     }\r
1490 }\r