1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html#License
3/*
4 *******************************************************************************
5 * Copyright (C) 1996-2011, International Business Machines Corporation and    *
6 * others. All Rights Reserved.                                                *
7 *******************************************************************************
8 */
9package com.ibm.icu.text;
10
11import com.ibm.icu.impl.UCaseProps;
12import com.ibm.icu.lang.UCharacter;
13import com.ibm.icu.util.ULocale;
14
15/**
16 * A transliterator that performs locale-sensitive toLower()
17 * case mapping.
18 */
19class LowercaseTransliterator extends Transliterator{
20
21    /**
22     * Package accessible ID.
23     */
24    static final String _ID = "Any-Lower";
25
26    // TODO: Add variants for tr/az, lt, default = default locale: ICU ticket #12720
27
28    /**
29     * System registration hook.
30     */
31    static void register() {
32        Transliterator.registerFactory(_ID, new Transliterator.Factory() {
33            @Override
34            public Transliterator getInstance(String ID) {
35                return new LowercaseTransliterator(ULocale.US);
36            }
37        });
38
39        Transliterator.registerSpecialInverse("Lower", "Upper", true);
40    }
41
42    private final ULocale locale;
43
44    private final UCaseProps csp;
45    private ReplaceableContextIterator iter;
46    private StringBuilder result;
47    private int caseLocale;
48
49    /**
50     * Constructs a transliterator.
51     */
52
53    public LowercaseTransliterator(ULocale loc) {
54        super(_ID, null);
55        locale = loc;
56        csp=UCaseProps.INSTANCE;
57        iter=new ReplaceableContextIterator();
58        result = new StringBuilder();
59        caseLocale = UCaseProps.getCaseLocale(locale);
60    }
61
62    /**
63     * Implements {@link Transliterator#handleTransliterate}.
64     */
65    @Override
66    protected synchronized void handleTransliterate(Replaceable text,
67                                       Position offsets, boolean isIncremental) {
68        if(csp==null) {
69            return;
70        }
71
72        if(offsets.start >= offsets.limit) {
73            return;
74        }
75
76        iter.setText(text);
77        result.setLength(0);
78        int c, delta;
79
80        // Walk through original string
81        // If there is a case change, modify corresponding position in replaceable
82
83        iter.setIndex(offsets.start);
84        iter.setLimit(offsets.limit);
85        iter.setContextLimits(offsets.contextStart, offsets.contextLimit);
86        while((c=iter.nextCaseMapCP())>=0) {
87            c=csp.toFullLower(c, iter, result, caseLocale);
88
89            if(iter.didReachLimit() && isIncremental) {
90                // the case mapping function tried to look beyond the context limit
91                // wait for more input
92                offsets.start=iter.getCaseMapCPStart();
93                return;
94            }
95
96            /* decode the result */
97            if(c<0) {
98                /* c mapped to itself, no change */
99                continue;
100            } else if(c<=UCaseProps.MAX_STRING_LENGTH) {
101                /* replace by the mapping string */
102                delta=iter.replace(result.toString());
103                result.setLength(0);
104            } else {
105                /* replace by single-code point mapping */
106                delta=iter.replace(UTF16.valueOf(c));
107            }
108
109            if(delta!=0) {
110                offsets.limit += delta;
111                offsets.contextLimit += delta;
112            }
113        }
114        offsets.start = offsets.limit;
115    }
116
117    // NOTE: normally this would be static, but because the results vary by locale....
118    SourceTargetUtility sourceTargetUtility = null;
119
120    /* (non-Javadoc)
121     * @see com.ibm.icu.text.Transliterator#addSourceTargetSet(com.ibm.icu.text.UnicodeSet, com.ibm.icu.text.UnicodeSet, com.ibm.icu.text.UnicodeSet)
122     */
123    @Override
124    public void addSourceTargetSet(UnicodeSet inputFilter, UnicodeSet sourceSet, UnicodeSet targetSet) {
125        synchronized (this) {
126            if (sourceTargetUtility == null) {
127                sourceTargetUtility = new SourceTargetUtility(new Transform<String,String>() {
128                    @Override
129                    public String transform(String source) {
130                        return UCharacter.toLowerCase(locale, source);
131                    }
132                });
133            }
134        }
135        sourceTargetUtility.addSourceTargetSet(this, inputFilter, sourceSet, targetSet);
136    }
137}
138