1 /*
2  * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 /*
27  *******************************************************************************
28  *   Copyright (C) 2009-2014, International Business Machines
29  *   Corporation and others.  All Rights Reserved.
30  *******************************************************************************
31  */
32 
33 package jdk.internal.icu.impl;
34 
35 import java.io.IOException;
36 
37 import jdk.internal.icu.text.Normalizer2;
38 import jdk.internal.icu.util.VersionInfo;
39 
40 public final class Norm2AllModes {
41     // Public API dispatch via Normalizer2 subclasses -------------------------- ***
42 
43     // Normalizer2 implementation for the old UNORM_NONE.
44     public static final class NoopNormalizer2 extends Normalizer2 {
45         @Override
normalize(CharSequence src, StringBuilder dest)46         public StringBuilder normalize(CharSequence src, StringBuilder dest) {
47             if(dest!=src) {
48                 dest.setLength(0);
49                 return dest.append(src);
50             } else {
51                 throw new IllegalArgumentException();
52             }
53         }
54 
55         @Override
normalize(CharSequence src, Appendable dest)56         public Appendable normalize(CharSequence src, Appendable dest) {
57             if(dest!=src) {
58                 try {
59                     return dest.append(src);
60                 } catch(IOException e) {
61                     throw new InternalError(e.toString(), e);
62                 }
63             } else {
64                 throw new IllegalArgumentException();
65             }
66         }
67 
68         @Override
normalizeSecondAndAppend(StringBuilder first, CharSequence second)69         public StringBuilder normalizeSecondAndAppend(StringBuilder first, CharSequence second) {
70             if(first!=second) {
71                 return first.append(second);
72             } else {
73                 throw new IllegalArgumentException();
74             }
75         }
76 
77         @Override
append(StringBuilder first, CharSequence second)78         public StringBuilder append(StringBuilder first, CharSequence second) {
79             if(first!=second) {
80                 return first.append(second);
81             } else {
82                 throw new IllegalArgumentException();
83             }
84         }
85 
86         @Override
getDecomposition(int c)87         public String getDecomposition(int c) {
88             return null;
89         }
90 
91         // No need to override the default getRawDecomposition().
92         @Override
isNormalized(CharSequence s)93         public boolean isNormalized(CharSequence s) { return true; }
94 
95         @Override
spanQuickCheckYes(CharSequence s)96         public int spanQuickCheckYes(CharSequence s) { return s.length(); }
97 
98         @Override
hasBoundaryBefore(int c)99         public boolean hasBoundaryBefore(int c) { return true; }
100     }
101 
102     // Intermediate class:
103     // Has NormalizerImpl and does boilerplate argument checking and setup.
104     public abstract static class Normalizer2WithImpl extends Normalizer2 {
Normalizer2WithImpl(NormalizerImpl ni)105         public Normalizer2WithImpl(NormalizerImpl ni) {
106             impl=ni;
107         }
108 
109         // normalize
110         @Override
normalize(CharSequence src, StringBuilder dest)111         public StringBuilder normalize(CharSequence src, StringBuilder dest) {
112             if(dest==src) {
113                 throw new IllegalArgumentException();
114             }
115             dest.setLength(0);
116             normalize(src, new NormalizerImpl.ReorderingBuffer(impl, dest, src.length()));
117             return dest;
118         }
119 
120         @Override
normalize(CharSequence src, Appendable dest)121         public Appendable normalize(CharSequence src, Appendable dest) {
122             if(dest==src) {
123                 throw new IllegalArgumentException();
124             }
125             NormalizerImpl.ReorderingBuffer buffer=
126                 new NormalizerImpl.ReorderingBuffer(impl, dest, src.length());
127             normalize(src, buffer);
128             buffer.flush();
129             return dest;
130         }
131 
normalize(CharSequence src, NormalizerImpl.ReorderingBuffer buffer)132         protected abstract void normalize(CharSequence src, NormalizerImpl.ReorderingBuffer buffer);
133 
134         // normalize and append
135         @Override
normalizeSecondAndAppend(StringBuilder first, CharSequence second)136         public StringBuilder normalizeSecondAndAppend(StringBuilder first, CharSequence second) {
137             return normalizeSecondAndAppend(first, second, true);
138         }
139 
140         @Override
append(StringBuilder first, CharSequence second)141         public StringBuilder append(StringBuilder first, CharSequence second) {
142             return normalizeSecondAndAppend(first, second, false);
143         }
144 
normalizeSecondAndAppend( StringBuilder first, CharSequence second, boolean doNormalize)145         public StringBuilder normalizeSecondAndAppend(
146                 StringBuilder first, CharSequence second, boolean doNormalize) {
147             if(first==second) {
148                 throw new IllegalArgumentException();
149             }
150             normalizeAndAppend(
151                 second, doNormalize,
152                 new NormalizerImpl.ReorderingBuffer(impl, first, first.length()+second.length()));
153             return first;
154         }
155 
normalizeAndAppend( CharSequence src, boolean doNormalize, NormalizerImpl.ReorderingBuffer buffer)156         protected abstract void normalizeAndAppend(
157                 CharSequence src, boolean doNormalize, NormalizerImpl.ReorderingBuffer buffer);
158 
159         @Override
getDecomposition(int c)160         public String getDecomposition(int c) {
161             return impl.getDecomposition(c);
162         }
163 
164         @Override
getCombiningClass(int c)165         public int getCombiningClass(int c) {
166             return impl.getCC(impl.getNorm16(c));
167         }
168 
169         // quick checks
170         @Override
isNormalized(CharSequence s)171         public boolean isNormalized(CharSequence s) {
172             return s.length()==spanQuickCheckYes(s);
173         }
174 
175         public final NormalizerImpl impl;
176     }
177 
178     public static final class DecomposeNormalizer2 extends Normalizer2WithImpl {
DecomposeNormalizer2(NormalizerImpl ni)179         public DecomposeNormalizer2(NormalizerImpl ni) {
180             super(ni);
181         }
182 
183         @Override
normalize(CharSequence src, NormalizerImpl.ReorderingBuffer buffer)184         protected void normalize(CharSequence src, NormalizerImpl.ReorderingBuffer buffer) {
185             impl.decompose(src, 0, src.length(), buffer);
186         }
187 
188         @Override
normalizeAndAppend( CharSequence src, boolean doNormalize, NormalizerImpl.ReorderingBuffer buffer)189         protected void normalizeAndAppend(
190                 CharSequence src, boolean doNormalize, NormalizerImpl.ReorderingBuffer buffer) {
191             impl.decomposeAndAppend(src, doNormalize, buffer);
192         }
193 
194         @Override
spanQuickCheckYes(CharSequence s)195         public int spanQuickCheckYes(CharSequence s) {
196             return impl.decompose(s, 0, s.length(), null);
197         }
198 
199         @Override
hasBoundaryBefore(int c)200         public boolean hasBoundaryBefore(int c) { return impl.hasDecompBoundaryBefore(c); }
201     }
202 
203     public static final class ComposeNormalizer2 extends Normalizer2WithImpl {
ComposeNormalizer2(NormalizerImpl ni, boolean fcc)204         public ComposeNormalizer2(NormalizerImpl ni, boolean fcc) {
205             super(ni);
206             onlyContiguous=fcc;
207         }
208 
209         @Override
normalize(CharSequence src, NormalizerImpl.ReorderingBuffer buffer)210         protected void normalize(CharSequence src, NormalizerImpl.ReorderingBuffer buffer) {
211             impl.compose(src, 0, src.length(), onlyContiguous, true, buffer);
212         }
213 
214         @Override
normalizeAndAppend( CharSequence src, boolean doNormalize, NormalizerImpl.ReorderingBuffer buffer)215         protected void normalizeAndAppend(
216                 CharSequence src, boolean doNormalize, NormalizerImpl.ReorderingBuffer buffer) {
217             impl.composeAndAppend(src, doNormalize, onlyContiguous, buffer);
218         }
219 
220         @Override
isNormalized(CharSequence s)221         public boolean isNormalized(CharSequence s) {
222             // 5: small destCapacity for substring normalization
223             return impl.compose(s, 0, s.length(),
224                                 onlyContiguous, false,
225                                 new NormalizerImpl.ReorderingBuffer(impl, new StringBuilder(), 5));
226         }
227 
228         @Override
spanQuickCheckYes(CharSequence s)229         public int spanQuickCheckYes(CharSequence s) {
230             return impl.composeQuickCheck(s, 0, s.length(), onlyContiguous, true)>>>1;
231         }
232 
233         @Override
hasBoundaryBefore(int c)234         public boolean hasBoundaryBefore(int c) { return impl.hasCompBoundaryBefore(c); }
235 
236         private final boolean onlyContiguous;
237     }
238 
239     // instance cache ---------------------------------------------------------- ***
240 
Norm2AllModes(NormalizerImpl ni)241     private Norm2AllModes(NormalizerImpl ni) {
242         impl=ni;
243         comp=new ComposeNormalizer2(ni, false);
244         decomp=new DecomposeNormalizer2(ni);
245     }
246 
247     public final NormalizerImpl impl;
248     public final ComposeNormalizer2 comp;
249     public final DecomposeNormalizer2 decomp;
250 
getInstanceFromSingleton(Norm2AllModesSingleton singleton)251     private static Norm2AllModes getInstanceFromSingleton(Norm2AllModesSingleton singleton) {
252         if(singleton.exception!=null) {
253             throw singleton.exception;
254         }
255         return singleton.allModes;
256     }
257 
getNFCInstance()258     public static Norm2AllModes getNFCInstance() {
259         return getInstanceFromSingleton(NFCSingleton.INSTANCE);
260     }
261 
getNFKCInstance()262     public static Norm2AllModes getNFKCInstance() {
263         return getInstanceFromSingleton(NFKCSingleton.INSTANCE);
264     }
265 
266     public static final NoopNormalizer2 NOOP_NORMALIZER2=new NoopNormalizer2();
267 
268     private static final class Norm2AllModesSingleton {
Norm2AllModesSingleton(String name)269         private Norm2AllModesSingleton(String name) {
270             try {
271                 @SuppressWarnings("deprecation")
272                 String DATA_FILE_NAME = "/jdk/internal/icu/impl/data/icudt" +
273                     VersionInfo.ICU_DATA_VERSION_PATH + "/" + name + ".nrm";
274                 NormalizerImpl impl=new NormalizerImpl().load(DATA_FILE_NAME);
275                 allModes=new Norm2AllModes(impl);
276             } catch (RuntimeException e) {
277                 exception=e;
278             }
279         }
280 
281         private Norm2AllModes allModes;
282         private RuntimeException exception;
283     }
284 
285     private static final class NFCSingleton {
286         private static final Norm2AllModesSingleton INSTANCE=new Norm2AllModesSingleton("nfc");
287     }
288 
289     private static final class NFKCSingleton {
290         private static final Norm2AllModesSingleton INSTANCE=new Norm2AllModesSingleton("nfkc");
291     }
292 }
293