]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_4_2-src/main/classes/translit/src/com/ibm/icu/text/TitlecaseTransliterator.java
go
[Dictionary.git] / jars / icu4j-4_4_2-src / main / classes / translit / src / com / ibm / icu / text / TitlecaseTransliterator.java
1 /*\r
2  * Copyright (C) 1996-2010, International Business Machines Corporation and\r
3  * others. All Rights Reserved.\r
4  *\r
5  */\r
6 package com.ibm.icu.text;\r
7 \r
8 import java.io.IOException;\r
9 \r
10 import com.ibm.icu.impl.UCaseProps;\r
11 import com.ibm.icu.util.ULocale;\r
12 \r
13 /**\r
14  * A transliterator that converts all letters (as defined by\r
15  * <code>UCharacter.isLetter()</code>) to lower case, except for those\r
16  * letters preceded by non-letters.  The latter are converted to title\r
17  * case using <code>UCharacter.toTitleCase()</code>.\r
18  * @author Alan Liu\r
19  */\r
20 class TitlecaseTransliterator extends Transliterator {\r
21 \r
22     static final String _ID = "Any-Title";\r
23 \r
24     /**\r
25      * System registration hook.\r
26      */\r
27     static void register() {\r
28         Transliterator.registerFactory(_ID, new Transliterator.Factory() {\r
29             public Transliterator getInstance(String ID) {\r
30                 return new TitlecaseTransliterator(ULocale.US);\r
31             }\r
32         });\r
33 \r
34         registerSpecialInverse("Title", "Lower", false);\r
35     }\r
36 \r
37     private ULocale locale;\r
38 \r
39     private UCaseProps csp;\r
40     private ReplaceableContextIterator iter;\r
41     private StringBuffer result;\r
42     private int[] locCache;\r
43 \r
44    /**\r
45      * Constructs a transliterator.\r
46      */\r
47     public TitlecaseTransliterator(ULocale loc) {\r
48         super(_ID, null);\r
49         locale = loc;\r
50         // Need to look back 2 characters in the case of "can't"\r
51         setMaximumContextLength(2);\r
52         try {\r
53             csp=UCaseProps.getSingleton();\r
54         } catch (IOException e) {\r
55             csp=null;\r
56         }\r
57         iter=new ReplaceableContextIterator();\r
58         result = new StringBuffer();\r
59         locCache = new int[1];\r
60         locCache[0]=0;\r
61     }\r
62      \r
63     /**\r
64      * Implements {@link Transliterator#handleTransliterate}.\r
65      */\r
66     protected void handleTransliterate(Replaceable text,\r
67                                        Position offsets, boolean isIncremental) {\r
68         // TODO reimplement, see ustrcase.c\r
69         // using a real word break iterator\r
70         //   instead of just looking for a transition between cased and uncased characters\r
71         // call CaseMapTransliterator::handleTransliterate() for lowercasing? (set fMap)\r
72         // needs to take isIncremental into account because case mappings are context-sensitive\r
73         //   also detect when lowercasing function did not finish because of context\r
74 \r
75         if (offsets.start >= offsets.limit) {\r
76             return;\r
77         }\r
78 \r
79         // case type: >0 cased (UCaseProps.LOWER etc.)  ==0 uncased  <0 case-ignorable\r
80         int type;\r
81 \r
82         // Our mode; we are either converting letter toTitle or\r
83         // toLower.\r
84         boolean doTitle = true;\r
85 \r
86         // Determine if there is a preceding context of cased case-ignorable*,\r
87         // in which case we want to start in toLower mode.  If the\r
88         // prior context is anything else (including empty) then start\r
89         // in toTitle mode.\r
90         int c, start;\r
91         for (start = offsets.start - 1; start >= offsets.contextStart; start -= UTF16.getCharCount(c)) {\r
92             c = text.char32At(start);\r
93             type=csp.getTypeOrIgnorable(c);\r
94             if(type>0) { // cased\r
95                 doTitle=false;\r
96                 break;\r
97             } else if(type==0) { // uncased but not ignorable\r
98                 break;\r
99             }\r
100             // else (type<0) case-ignorable: continue\r
101         }\r
102 \r
103         // Convert things after a cased character toLower; things\r
104         // after a uncased, non-case-ignorable character toTitle.  Case-ignorable\r
105         // characters are copied directly and do not change the mode.\r
106 \r
107         iter.setText(text);\r
108         iter.setIndex(offsets.start);\r
109         iter.setLimit(offsets.limit);\r
110         iter.setContextLimits(offsets.contextStart, offsets.contextLimit);\r
111 \r
112         result.setLength(0);\r
113 \r
114         // Walk through original string\r
115         // If there is a case change, modify corresponding position in replaceable\r
116         int delta;\r
117 \r
118         while((c=iter.nextCaseMapCP())>=0) {\r
119             type=csp.getTypeOrIgnorable(c);\r
120             if(type>=0) { // not case-ignorable\r
121                 if(doTitle) {\r
122                     c=csp.toFullTitle(c, iter, result, locale, locCache);\r
123                 } else {\r
124                     c=csp.toFullLower(c, iter, result, locale, locCache);\r
125                 }\r
126                 doTitle = type==0; // doTitle=isUncased\r
127 \r
128                 if(iter.didReachLimit() && isIncremental) {\r
129                     // the case mapping function tried to look beyond the context limit\r
130                     // wait for more input\r
131                     offsets.start=iter.getCaseMapCPStart();\r
132                     return;\r
133                 }\r
134 \r
135                 /* decode the result */\r
136                 if(c<0) {\r
137                     /* c mapped to itself, no change */\r
138                     continue;\r
139                 } else if(c<=UCaseProps.MAX_STRING_LENGTH) {\r
140                     /* replace by the mapping string */\r
141                     delta=iter.replace(result.toString());\r
142                     result.setLength(0);\r
143                 } else {\r
144                     /* replace by single-code point mapping */\r
145                     delta=iter.replace(UTF16.valueOf(c));\r
146                 }\r
147 \r
148                 if(delta!=0) {\r
149                     offsets.limit += delta;\r
150                     offsets.contextLimit += delta;\r
151                 }\r
152             }\r
153         }\r
154         offsets.start = offsets.limit;\r
155     }\r
156 }\r