1// © 2017 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html#License
3package com.ibm.icu.text;
4
5import java.util.Locale;
6
7import com.ibm.icu.impl.CaseMapImpl;
8import com.ibm.icu.impl.UCaseProps;
9import com.ibm.icu.lang.UCharacter;
10import com.ibm.icu.util.ULocale;
11
12/**
13 * Low-level case mapping options and methods. Immutable.
14 * "Setters" return instances with the union of the current and new options set.
15 *
16 * This class is not intended for public subclassing.
17 *
18 * @draft ICU 59
19 * @provisional This API might change or be removed in a future release.
20 */
21public abstract class CaseMap {
22    /**
23     * @internal
24     * @deprecated This API is ICU internal only.
25     */
26    @Deprecated
27    protected int internalOptions;
28
29    private CaseMap(int opt) { internalOptions = opt; }
30
31    private static int getCaseLocale(Locale locale) {
32        if (locale == null) {
33            locale = Locale.getDefault();
34        }
35        return UCaseProps.getCaseLocale(locale);
36    }
37
38    /**
39     * @return Lowercasing object with default options.
40     * @draft ICU 59
41     * @provisional This API might change or be removed in a future release.
42     */
43    public static Lower toLower() { return Lower.DEFAULT; }
44    /**
45     * @return Uppercasing object with default options.
46     * @draft ICU 59
47     * @provisional This API might change or be removed in a future release.
48     */
49    public static Upper toUpper() { return Upper.DEFAULT; }
50    /**
51     * @return Titlecasing object with default options.
52     * @draft ICU 59
53     * @provisional This API might change or be removed in a future release.
54     */
55    public static Title toTitle() { return Title.DEFAULT; }
56    /**
57     * @return Case folding object with default options.
58     * @draft ICU 59
59     * @provisional This API might change or be removed in a future release.
60     */
61    public static Fold fold() { return Fold.DEFAULT; }
62
63    /**
64     * Returns an instance that behaves like this one but
65     * omits unchanged text when case-mapping with {@link Edits}.
66     *
67     * @return an options object with this option.
68     * @draft ICU 59
69     * @provisional This API might change or be removed in a future release.
70     */
71    public abstract CaseMap omitUnchangedText();
72
73    /**
74     * Lowercasing options and methods. Immutable.
75     *
76     * @see #toLower()
77     * @draft ICU 59
78     * @provisional This API might change or be removed in a future release.
79     */
80    public static final class Lower extends CaseMap {
81        private static final Lower DEFAULT = new Lower(0);
82        private static final Lower OMIT_UNCHANGED = new Lower(CaseMapImpl.OMIT_UNCHANGED_TEXT);
83        private Lower(int opt) { super(opt); }
84
85        /**
86         * {@inheritDoc}
87         * @draft ICU 59
88         * @provisional This API might change or be removed in a future release.
89         */
90        @Override
91        public Lower omitUnchangedText() {
92            return OMIT_UNCHANGED;
93        }
94
95        /**
96         * Lowercases a string and optionally records edits (see {@link #omitUnchangedText}).
97         * Casing is locale-dependent and context-sensitive.
98         * The result may be longer or shorter than the original.
99         *
100         * @param locale    The locale ID. Can be null for {@link Locale#getDefault}.
101         *                  (See {@link ULocale#toLocale}.)
102         * @param src       The original string.
103         * @param dest      A buffer for the result string. Must not be null.
104         * @param edits     Records edits for index mapping, working with styled text,
105         *                  and getting only changes (if any).
106         *                  This function calls edits.reset() first. edits can be null.
107         * @return dest with the result string (or only changes) appended.
108         *
109         * @see UCharacter#toLowerCase(Locale, String)
110         * @draft ICU 59
111         * @provisional This API might change or be removed in a future release.
112         */
113         public <A extends Appendable> A apply(
114                 Locale locale, CharSequence src, A dest, Edits edits) {
115             return CaseMapImpl.toLower(getCaseLocale(locale), internalOptions, src, dest, edits);
116         }
117    }
118
119    /**
120     * Uppercasing options and methods. Immutable.
121     *
122     * @see #toUpper()
123     * @draft ICU 59
124     * @provisional This API might change or be removed in a future release.
125     */
126    public static final class Upper extends CaseMap {
127        private static final Upper DEFAULT = new Upper(0);
128        private static final Upper OMIT_UNCHANGED = new Upper(CaseMapImpl.OMIT_UNCHANGED_TEXT);
129        private Upper(int opt) { super(opt); }
130
131        /**
132         * {@inheritDoc}
133         * @draft ICU 59
134         * @provisional This API might change or be removed in a future release.
135         */
136        @Override
137        public Upper omitUnchangedText() {
138            return OMIT_UNCHANGED;
139        }
140
141        /**
142         * Uppercases a string and optionally records edits (see {@link #omitUnchangedText}).
143         * Casing is locale-dependent and context-sensitive.
144         * The result may be longer or shorter than the original.
145         *
146         * @param locale    The locale ID. Can be null for {@link Locale#getDefault}.
147         *                  (See {@link ULocale#toLocale}.)
148         * @param src       The original string.
149         * @param dest      A buffer for the result string. Must not be null.
150         * @param edits     Records edits for index mapping, working with styled text,
151         *                  and getting only changes (if any).
152         *                  This function calls edits.reset() first. edits can be null.
153         * @return dest with the result string (or only changes) appended.
154         *
155         * @see UCharacter#toUpperCase(Locale, String)
156         * @draft ICU 59
157         * @provisional This API might change or be removed in a future release.
158         */
159         public <A extends Appendable> A apply(
160                 Locale locale, CharSequence src, A dest, Edits edits) {
161             return CaseMapImpl.toUpper(getCaseLocale(locale), internalOptions, src, dest, edits);
162         }
163    }
164
165    /**
166     * Titlecasing options and methods. Immutable.
167     *
168     * @see #toTitle()
169     * @draft ICU 59
170     * @provisional This API might change or be removed in a future release.
171     */
172    public static final class Title extends CaseMap {
173        private static final Title DEFAULT = new Title(0);
174        private static final Title OMIT_UNCHANGED = new Title(CaseMapImpl.OMIT_UNCHANGED_TEXT);
175        private Title(int opt) { super(opt); }
176
177        /**
178         * {@inheritDoc}
179         * @draft ICU 59
180         * @provisional This API might change or be removed in a future release.
181         */
182        @Override
183        public Title omitUnchangedText() {
184            if (internalOptions == 0 || internalOptions == CaseMapImpl.OMIT_UNCHANGED_TEXT) {
185                return OMIT_UNCHANGED;
186            }
187            return new Title(internalOptions | CaseMapImpl.OMIT_UNCHANGED_TEXT);
188        }
189
190        /**
191         * Returns an instance that behaves like this one but
192         * does not lowercase non-initial parts of words when titlecasing.
193         *
194         * <p>By default, titlecasing will titlecase the first cased character
195         * of a word and lowercase all other characters.
196         * With this option, the other characters will not be modified.
197         *
198         * @return an options object with this option.
199         * @see UCharacter#TITLECASE_NO_LOWERCASE
200         * @draft ICU 59
201         * @provisional This API might change or be removed in a future release.
202         */
203        public Title noLowercase() {
204            return new Title(internalOptions | UCharacter.TITLECASE_NO_LOWERCASE);
205        }
206
207        // TODO: update references to the Unicode Standard for recent version
208        /**
209         * Returns an instance that behaves like this one but
210         * does not adjust the titlecasing indexes from BreakIterator::next() indexes;
211         * titlecases exactly the characters at breaks from the iterator.
212         *
213         * <p>By default, titlecasing will take each break iterator index,
214         * adjust it by looking for the next cased character, and titlecase that one.
215         * Other characters are lowercased.
216         *
217         * <p>This follows Unicode 4 &amp; 5 section 3.13 Default Case Operations:
218         *
219         * R3  toTitlecase(X): Find the word boundaries based on Unicode Standard Annex
220         * #29, "Text Boundaries." Between each pair of word boundaries, find the first
221         * cased character F. If F exists, map F to default_title(F); then map each
222         * subsequent character C to default_lower(C).
223         *
224         * @return an options object with this option.
225         * @see UCharacter#TITLECASE_NO_BREAK_ADJUSTMENT
226         * @draft ICU 59
227         * @provisional This API might change or be removed in a future release.
228         */
229        public Title noBreakAdjustment() {
230            return new Title(internalOptions | UCharacter.TITLECASE_NO_BREAK_ADJUSTMENT);
231        }
232
233        /**
234         * Titlecases a string and optionally records edits (see {@link #omitUnchangedText}).
235         * Casing is locale-dependent and context-sensitive.
236         * The result may be longer or shorter than the original.
237         *
238         * <p>Titlecasing uses a break iterator to find the first characters of words
239         * that are to be titlecased. It titlecases those characters and lowercases
240         * all others. (This can be modified with options bits.)
241         *
242         * @param locale    The locale ID. Can be null for {@link Locale#getDefault}.
243         *                  (See {@link ULocale#toLocale}.)
244         * @param iter      A break iterator to find the first characters of words that are to be titlecased.
245         *                  It is set to the source string (setText())
246         *                  and used one or more times for iteration (first() and next()).
247         *                  If null, then a word break iterator for the locale is used
248         *                  (or something equivalent).
249         * @param src       The original string.
250         * @param dest      A buffer for the result string. Must not be null.
251         * @param edits     Records edits for index mapping, working with styled text,
252         *                  and getting only changes (if any).
253         *                  This function calls edits.reset() first. edits can be null.
254         * @return dest with the result string (or only changes) appended.
255         *
256         * @see UCharacter#toTitleCase(Locale, String, BreakIterator, int)
257         * @draft ICU 59
258         * @provisional This API might change or be removed in a future release.
259         */
260         public <A extends Appendable> A apply(
261                 Locale locale, BreakIterator iter, CharSequence src, A dest, Edits edits) {
262             if (iter == null) {
263                 iter = BreakIterator.getWordInstance(locale);
264             }
265             iter.setText(src.toString());
266             return CaseMapImpl.toTitle(
267                     getCaseLocale(locale), internalOptions, iter, src, dest, edits);
268         }
269    }
270
271    /**
272     * Case folding options and methods. Immutable.
273     *
274     * @see #fold()
275     * @draft ICU 59
276     * @provisional This API might change or be removed in a future release.
277     */
278    public static final class Fold extends CaseMap {
279        private static final Fold DEFAULT = new Fold(0);
280        private static final Fold TURKIC = new Fold(UCharacter.FOLD_CASE_EXCLUDE_SPECIAL_I);
281        private static final Fold OMIT_UNCHANGED = new Fold(CaseMapImpl.OMIT_UNCHANGED_TEXT);
282        private static final Fold TURKIC_OMIT_UNCHANGED = new Fold(
283                UCharacter.FOLD_CASE_EXCLUDE_SPECIAL_I | CaseMapImpl.OMIT_UNCHANGED_TEXT);
284        private Fold(int opt) { super(opt); }
285
286        /**
287         * {@inheritDoc}
288         * @draft ICU 59
289         * @provisional This API might change or be removed in a future release.
290         */
291        @Override
292        public Fold omitUnchangedText() {
293            return (internalOptions & UCharacter.FOLD_CASE_EXCLUDE_SPECIAL_I) == 0 ?
294                    OMIT_UNCHANGED : TURKIC_OMIT_UNCHANGED;
295        }
296
297        /**
298         * Returns an instance that behaves like this one but
299         * handles dotted I and dotless i appropriately for Turkic languages (tr, az).
300         *
301         * <p>Uses the Unicode CaseFolding.txt mappings marked with 'T' that
302         * are to be excluded for default mappings and
303         * included for the Turkic-specific mappings.
304         *
305         * @return an options object with this option.
306         * @see UCharacter#FOLD_CASE_EXCLUDE_SPECIAL_I
307         * @draft ICU 59
308         * @provisional This API might change or be removed in a future release.
309         */
310        public Fold turkic() {
311            return (internalOptions & CaseMapImpl.OMIT_UNCHANGED_TEXT) == 0 ?
312                    TURKIC : TURKIC_OMIT_UNCHANGED;
313        }
314
315        /**
316         * Case-folds a string and optionally records edits (see {@link #omitUnchangedText}).
317         *
318         * <p>Case-folding is locale-independent and not context-sensitive,
319         * but there is an option for whether to include or exclude mappings for dotted I
320         * and dotless i that are marked with 'T' in CaseFolding.txt.
321         *
322         * <p>The result may be longer or shorter than the original.
323         *
324         * @param src       The original string.
325         * @param dest      A buffer for the result string. Must not be null.
326         * @param edits     Records edits for index mapping, working with styled text,
327         *                  and getting only changes (if any).
328         *                  This function calls edits.reset() first. edits can be null.
329         * @return dest with the result string (or only changes) appended.
330         *
331         * @see UCharacter#foldCase(String, int)
332         * @draft ICU 59
333         * @provisional This API might change or be removed in a future release.
334         */
335         public <A extends Appendable> A apply(CharSequence src, A dest, Edits edits) {
336             return CaseMapImpl.fold(internalOptions, src, dest, edits);
337         }
338    }
339}
340