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