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