1/*
2 *******************************************************************************
3 *   Copyright (C) 2009-2014, International Business Machines
4 *   Corporation and others.  All Rights Reserved.
5 *******************************************************************************
6 */
7
8package com.ibm.icu.impl;
9
10import java.io.IOException;
11import java.nio.ByteBuffer;
12
13import com.ibm.icu.text.Normalizer;
14import com.ibm.icu.text.Normalizer2;
15import com.ibm.icu.util.ICUUncheckedIOException;
16
17public final class Norm2AllModes {
18    // Public API dispatch via Normalizer2 subclasses -------------------------- ***
19
20    // Normalizer2 implementation for the old UNORM_NONE.
21    public static final class NoopNormalizer2 extends Normalizer2 {
22        @Override
23        public StringBuilder normalize(CharSequence src, StringBuilder dest) {
24            if(dest!=src) {
25                dest.setLength(0);
26                return dest.append(src);
27            } else {
28                throw new IllegalArgumentException();
29            }
30        }
31        @Override
32        public Appendable normalize(CharSequence src, Appendable dest) {
33            if(dest!=src) {
34                try {
35                    return dest.append(src);
36                } catch(IOException e) {
37                    throw new ICUUncheckedIOException(e);  // Avoid declaring "throws IOException".
38                }
39            } else {
40                throw new IllegalArgumentException();
41            }
42        }
43        @Override
44        public StringBuilder normalizeSecondAndAppend(StringBuilder first, CharSequence second) {
45            if(first!=second) {
46                return first.append(second);
47            } else {
48                throw new IllegalArgumentException();
49            }
50        }
51        @Override
52        public StringBuilder append(StringBuilder first, CharSequence second) {
53            if(first!=second) {
54                return first.append(second);
55            } else {
56                throw new IllegalArgumentException();
57            }
58        }
59        @Override
60        public String getDecomposition(int c) {
61            return null;
62        }
63        // No need to override the default getRawDecomposition().
64        @Override
65        public boolean isNormalized(CharSequence s) { return true; }
66        @Override
67        public Normalizer.QuickCheckResult quickCheck(CharSequence s) { return Normalizer.YES; }
68        @Override
69        public int spanQuickCheckYes(CharSequence s) { return s.length(); }
70        @Override
71        public boolean hasBoundaryBefore(int c) { return true; }
72        @Override
73        public boolean hasBoundaryAfter(int c) { return true; }
74        @Override
75        public boolean isInert(int c) { return true; }
76    }
77
78    // Intermediate class:
79    // Has Normalizer2Impl and does boilerplate argument checking and setup.
80    public static abstract class Normalizer2WithImpl extends Normalizer2 {
81        public Normalizer2WithImpl(Normalizer2Impl ni) {
82            impl=ni;
83        }
84
85        // normalize
86        @Override
87        public StringBuilder normalize(CharSequence src, StringBuilder dest) {
88            if(dest==src) {
89                throw new IllegalArgumentException();
90            }
91            dest.setLength(0);
92            normalize(src, new Normalizer2Impl.ReorderingBuffer(impl, dest, src.length()));
93            return dest;
94        }
95        @Override
96        public Appendable normalize(CharSequence src, Appendable dest) {
97            if(dest==src) {
98                throw new IllegalArgumentException();
99            }
100            Normalizer2Impl.ReorderingBuffer buffer=
101                new Normalizer2Impl.ReorderingBuffer(impl, dest, src.length());
102            normalize(src, buffer);
103            buffer.flush();
104            return dest;
105        }
106        protected abstract void normalize(CharSequence src, Normalizer2Impl.ReorderingBuffer buffer);
107
108        // normalize and append
109        @Override
110        public StringBuilder normalizeSecondAndAppend(StringBuilder first, CharSequence second) {
111            return normalizeSecondAndAppend(first, second, true);
112        }
113        @Override
114        public StringBuilder append(StringBuilder first, CharSequence second) {
115            return normalizeSecondAndAppend(first, second, false);
116        }
117        public StringBuilder normalizeSecondAndAppend(
118                StringBuilder first, CharSequence second, boolean doNormalize) {
119            if(first==second) {
120                throw new IllegalArgumentException();
121            }
122            normalizeAndAppend(
123                second, doNormalize,
124                new Normalizer2Impl.ReorderingBuffer(impl, first, first.length()+second.length()));
125            return first;
126        }
127        protected abstract void normalizeAndAppend(
128                CharSequence src, boolean doNormalize, Normalizer2Impl.ReorderingBuffer buffer);
129
130        @Override
131        public String getDecomposition(int c) {
132            return impl.getDecomposition(c);
133        }
134        @Override
135        public String getRawDecomposition(int c) {
136            return impl.getRawDecomposition(c);
137        }
138        @Override
139        public int composePair(int a, int b) {
140            return impl.composePair(a, b);
141        }
142
143        @Override
144        public int getCombiningClass(int c) {
145            return impl.getCC(impl.getNorm16(c));
146        }
147
148        // quick checks
149        @Override
150        public boolean isNormalized(CharSequence s) {
151            return s.length()==spanQuickCheckYes(s);
152        }
153        @Override
154        public Normalizer.QuickCheckResult quickCheck(CharSequence s) {
155            return isNormalized(s) ? Normalizer.YES : Normalizer.NO;
156        }
157
158        public int getQuickCheck(int c) {
159            return 1;
160        }
161
162        public final Normalizer2Impl impl;
163    }
164
165    public static final class DecomposeNormalizer2 extends Normalizer2WithImpl {
166        public DecomposeNormalizer2(Normalizer2Impl ni) {
167            super(ni);
168        }
169
170        @Override
171        protected void normalize(CharSequence src, Normalizer2Impl.ReorderingBuffer buffer) {
172            impl.decompose(src, 0, src.length(), buffer);
173        }
174        @Override
175        protected void normalizeAndAppend(
176                CharSequence src, boolean doNormalize, Normalizer2Impl.ReorderingBuffer buffer) {
177            impl.decomposeAndAppend(src, doNormalize, buffer);
178        }
179        @Override
180        public int spanQuickCheckYes(CharSequence s) {
181            return impl.decompose(s, 0, s.length(), null);
182        }
183        @Override
184        public int getQuickCheck(int c) {
185            return impl.isDecompYes(impl.getNorm16(c)) ? 1 : 0;
186        }
187        @Override
188        public boolean hasBoundaryBefore(int c) { return impl.hasDecompBoundary(c, true); }
189        @Override
190        public boolean hasBoundaryAfter(int c) { return impl.hasDecompBoundary(c, false); }
191        @Override
192        public boolean isInert(int c) { return impl.isDecompInert(c); }
193    }
194
195    public static final class ComposeNormalizer2 extends Normalizer2WithImpl {
196        public ComposeNormalizer2(Normalizer2Impl ni, boolean fcc) {
197            super(ni);
198            onlyContiguous=fcc;
199        }
200
201        @Override
202        protected void normalize(CharSequence src, Normalizer2Impl.ReorderingBuffer buffer) {
203            impl.compose(src, 0, src.length(), onlyContiguous, true, buffer);
204        }
205        @Override
206        protected void normalizeAndAppend(
207                CharSequence src, boolean doNormalize, Normalizer2Impl.ReorderingBuffer buffer) {
208            impl.composeAndAppend(src, doNormalize, onlyContiguous, buffer);
209        }
210
211        @Override
212        public boolean isNormalized(CharSequence s) {
213            // 5: small destCapacity for substring normalization
214            return impl.compose(s, 0, s.length(),
215                                onlyContiguous, false,
216                                new Normalizer2Impl.ReorderingBuffer(impl, new StringBuilder(), 5));
217        }
218        @Override
219        public Normalizer.QuickCheckResult quickCheck(CharSequence s) {
220            int spanLengthAndMaybe=impl.composeQuickCheck(s, 0, s.length(), onlyContiguous, false);
221            if((spanLengthAndMaybe&1)!=0) {
222                return Normalizer.MAYBE;
223            } else if((spanLengthAndMaybe>>>1)==s.length()) {
224                return Normalizer.YES;
225            } else {
226                return Normalizer.NO;
227            }
228        }
229        @Override
230        public int spanQuickCheckYes(CharSequence s) {
231            return impl.composeQuickCheck(s, 0, s.length(), onlyContiguous, true)>>>1;
232        }
233        @Override
234        public int getQuickCheck(int c) {
235            return impl.getCompQuickCheck(impl.getNorm16(c));
236        }
237        @Override
238        public boolean hasBoundaryBefore(int c) { return impl.hasCompBoundaryBefore(c); }
239        @Override
240        public boolean hasBoundaryAfter(int c) {
241            return impl.hasCompBoundaryAfter(c, onlyContiguous, false);
242        }
243        @Override
244        public boolean isInert(int c) {
245            return impl.hasCompBoundaryAfter(c, onlyContiguous, true);
246        }
247
248        private final boolean onlyContiguous;
249    }
250
251    public static final class FCDNormalizer2 extends Normalizer2WithImpl {
252        public FCDNormalizer2(Normalizer2Impl ni) {
253            super(ni);
254        }
255
256        @Override
257        protected void normalize(CharSequence src, Normalizer2Impl.ReorderingBuffer buffer) {
258            impl.makeFCD(src, 0, src.length(), buffer);
259        }
260        @Override
261        protected void normalizeAndAppend(
262                CharSequence src, boolean doNormalize, Normalizer2Impl.ReorderingBuffer buffer) {
263            impl.makeFCDAndAppend(src, doNormalize, buffer);
264        }
265        @Override
266        public int spanQuickCheckYes(CharSequence s) {
267            return impl.makeFCD(s, 0, s.length(), null);
268        }
269        @Override
270        public int getQuickCheck(int c) {
271            return impl.isDecompYes(impl.getNorm16(c)) ? 1 : 0;
272        }
273        @Override
274        public boolean hasBoundaryBefore(int c) { return impl.hasFCDBoundaryBefore(c); }
275        @Override
276        public boolean hasBoundaryAfter(int c) { return impl.hasFCDBoundaryAfter(c); }
277        @Override
278        public boolean isInert(int c) { return impl.isFCDInert(c); }
279    }
280
281    // instance cache ---------------------------------------------------------- ***
282
283    private Norm2AllModes(Normalizer2Impl ni) {
284        impl=ni;
285        comp=new ComposeNormalizer2(ni, false);
286        decomp=new DecomposeNormalizer2(ni);
287        fcd=new FCDNormalizer2(ni);
288        fcc=new ComposeNormalizer2(ni, true);
289    }
290
291    public final Normalizer2Impl impl;
292    public final ComposeNormalizer2 comp;
293    public final DecomposeNormalizer2 decomp;
294    public final FCDNormalizer2 fcd;
295    public final ComposeNormalizer2 fcc;
296
297    private static Norm2AllModes getInstanceFromSingleton(Norm2AllModesSingleton singleton) {
298        if(singleton.exception!=null) {
299            throw singleton.exception;
300        }
301        return singleton.allModes;
302    }
303    public static Norm2AllModes getNFCInstance() {
304        return getInstanceFromSingleton(NFCSingleton.INSTANCE);
305    }
306    public static Norm2AllModes getNFKCInstance() {
307        return getInstanceFromSingleton(NFKCSingleton.INSTANCE);
308    }
309    public static Norm2AllModes getNFKC_CFInstance() {
310        return getInstanceFromSingleton(NFKC_CFSingleton.INSTANCE);
311    }
312    // For use in properties APIs.
313    public static Normalizer2WithImpl getN2WithImpl(int index) {
314        switch(index) {
315        case 0: return getNFCInstance().decomp;  // NFD
316        case 1: return getNFKCInstance().decomp; // NFKD
317        case 2: return getNFCInstance().comp;    // NFC
318        case 3: return getNFKCInstance().comp;   // NFKC
319        default: return null;
320        }
321    }
322    public static Norm2AllModes getInstance(ByteBuffer bytes, String name) {
323        if(bytes==null) {
324            Norm2AllModesSingleton singleton;
325            if(name.equals("nfc")) {
326                singleton=NFCSingleton.INSTANCE;
327            } else if(name.equals("nfkc")) {
328                singleton=NFKCSingleton.INSTANCE;
329            } else if(name.equals("nfkc_cf")) {
330                singleton=NFKC_CFSingleton.INSTANCE;
331            } else {
332                singleton=null;
333            }
334            if(singleton!=null) {
335                if(singleton.exception!=null) {
336                    throw singleton.exception;
337                }
338                return singleton.allModes;
339            }
340        }
341        return cache.getInstance(name, bytes);
342    }
343    private static CacheBase<String, Norm2AllModes, ByteBuffer> cache =
344        new SoftCache<String, Norm2AllModes, ByteBuffer>() {
345            protected Norm2AllModes createInstance(String key, ByteBuffer bytes) {
346                Normalizer2Impl impl;
347                if(bytes==null) {
348                    impl=new Normalizer2Impl().load(key+".nrm");
349                } else {
350                    impl=new Normalizer2Impl().load(bytes);
351                }
352                return new Norm2AllModes(impl);
353            }
354        };
355
356    public static final NoopNormalizer2 NOOP_NORMALIZER2=new NoopNormalizer2();
357    /**
358     * Gets the FCD normalizer, with the FCD data initialized.
359     * @return FCD normalizer
360     */
361    public static Normalizer2 getFCDNormalizer2() {
362        return getNFCInstance().fcd;
363    }
364
365    private static final class Norm2AllModesSingleton {
366        private Norm2AllModesSingleton(String name) {
367            try {
368                Normalizer2Impl impl=new Normalizer2Impl().load(name+".nrm");
369                allModes=new Norm2AllModes(impl);
370            } catch(RuntimeException e) {
371                exception=e;
372            }
373        }
374
375        private Norm2AllModes allModes;
376        private RuntimeException exception;
377    }
378    private static final class NFCSingleton {
379        private static final Norm2AllModesSingleton INSTANCE=new Norm2AllModesSingleton("nfc");
380    }
381    private static final class NFKCSingleton {
382        private static final Norm2AllModesSingleton INSTANCE=new Norm2AllModesSingleton("nfkc");
383    }
384    private static final class NFKC_CFSingleton {
385        private static final Norm2AllModesSingleton INSTANCE=new Norm2AllModesSingleton("nfkc_cf");
386    }
387}
388