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