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