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