/* ******************************************************************************* * Copyright (C) 2009-2010, International Business Machines * Corporation and others. All Rights Reserved. ******************************************************************************* */ package com.ibm.icu.impl; import java.io.IOException; import java.io.InputStream; import java.util.MissingResourceException; import com.ibm.icu.text.Normalizer; import com.ibm.icu.text.Normalizer2; public final class Norm2AllModes { // Public API dispatch via Normalizer2 subclasses -------------------------- *** // Normalizer2 implementation for the old UNORM_NONE. public static final class NoopNormalizer2 extends Normalizer2 { @Override public StringBuilder normalize(CharSequence src, StringBuilder dest) { if(dest!=src) { dest.setLength(0); return dest.append(src); } else { throw new IllegalArgumentException(); } } @Override public Appendable normalize(CharSequence src, Appendable dest) { if(dest!=src) { try { return dest.append(src); } catch(IOException e) { throw new RuntimeException(e); // Avoid declaring "throws IOException". } } else { throw new IllegalArgumentException(); } } @Override public StringBuilder normalizeSecondAndAppend(StringBuilder first, CharSequence second) { if(first!=second) { return first.append(second); } else { throw new IllegalArgumentException(); } } @Override public StringBuilder append(StringBuilder first, CharSequence second) { if(first!=second) { return first.append(second); } else { throw new IllegalArgumentException(); } } @Override public boolean isNormalized(CharSequence s) { return true; } @Override public Normalizer.QuickCheckResult quickCheck(CharSequence s) { return Normalizer.YES; } @Override public int spanQuickCheckYes(CharSequence s) { return s.length(); } @Override public boolean hasBoundaryBefore(int c) { return true; } @Override public boolean hasBoundaryAfter(int c) { return true; } @Override public boolean isInert(int c) { return true; } } // Intermediate class: // Has Normalizer2Impl and does boilerplate argument checking and setup. public static abstract class Normalizer2WithImpl extends Normalizer2 { public Normalizer2WithImpl(Normalizer2Impl ni) { impl=ni; } // normalize @Override public StringBuilder normalize(CharSequence src, StringBuilder dest) { if(dest==src) { throw new IllegalArgumentException(); } dest.setLength(0); normalize(src, new Normalizer2Impl.ReorderingBuffer(impl, dest, src.length())); return dest; } @Override public Appendable normalize(CharSequence src, Appendable dest) { if(dest==src) { throw new IllegalArgumentException(); } Normalizer2Impl.ReorderingBuffer buffer= new Normalizer2Impl.ReorderingBuffer(impl, dest, src.length()); normalize(src, buffer); buffer.flush(); return dest; } protected abstract void normalize(CharSequence src, Normalizer2Impl.ReorderingBuffer buffer); // normalize and append @Override public StringBuilder normalizeSecondAndAppend(StringBuilder first, CharSequence second) { return normalizeSecondAndAppend(first, second, true); } @Override public StringBuilder append(StringBuilder first, CharSequence second) { return normalizeSecondAndAppend(first, second, false); } public StringBuilder normalizeSecondAndAppend( StringBuilder first, CharSequence second, boolean doNormalize) { if(first==second) { throw new IllegalArgumentException(); } normalizeAndAppend( second, doNormalize, new Normalizer2Impl.ReorderingBuffer(impl, first, first.length()+second.length())); return first; } protected abstract void normalizeAndAppend( CharSequence src, boolean doNormalize, Normalizer2Impl.ReorderingBuffer buffer); // quick checks @Override public boolean isNormalized(CharSequence s) { return s.length()==spanQuickCheckYes(s); } @Override public Normalizer.QuickCheckResult quickCheck(CharSequence s) { return isNormalized(s) ? Normalizer.YES : Normalizer.NO; } public int getQuickCheck(int c) { return 1; } public final Normalizer2Impl impl; } public static final class DecomposeNormalizer2 extends Normalizer2WithImpl { public DecomposeNormalizer2(Normalizer2Impl ni) { super(ni); } @Override protected void normalize(CharSequence src, Normalizer2Impl.ReorderingBuffer buffer) { impl.decompose(src, 0, src.length(), buffer); } @Override protected void normalizeAndAppend( CharSequence src, boolean doNormalize, Normalizer2Impl.ReorderingBuffer buffer) { impl.decomposeAndAppend(src, doNormalize, buffer); } @Override public int spanQuickCheckYes(CharSequence s) { return impl.decompose(s, 0, s.length(), null); } @Override public int getQuickCheck(int c) { return impl.isDecompYes(impl.getNorm16(c)) ? 1 : 0; } @Override public boolean hasBoundaryBefore(int c) { return impl.hasDecompBoundary(c, true); } @Override public boolean hasBoundaryAfter(int c) { return impl.hasDecompBoundary(c, false); } @Override public boolean isInert(int c) { return impl.isDecompInert(c); } } public static final class ComposeNormalizer2 extends Normalizer2WithImpl { public ComposeNormalizer2(Normalizer2Impl ni, boolean fcc) { super(ni); onlyContiguous=fcc; } @Override protected void normalize(CharSequence src, Normalizer2Impl.ReorderingBuffer buffer) { impl.compose(src, 0, src.length(), onlyContiguous, true, buffer); } @Override protected void normalizeAndAppend( CharSequence src, boolean doNormalize, Normalizer2Impl.ReorderingBuffer buffer) { impl.composeAndAppend(src, doNormalize, onlyContiguous, buffer); } @Override public boolean isNormalized(CharSequence s) { // 5: small destCapacity for substring normalization return impl.compose(s, 0, s.length(), onlyContiguous, false, new Normalizer2Impl.ReorderingBuffer(impl, new StringBuilder(), 5)); } @Override public Normalizer.QuickCheckResult quickCheck(CharSequence s) { int spanLengthAndMaybe=impl.composeQuickCheck(s, 0, s.length(), onlyContiguous, false); if((spanLengthAndMaybe&1)!=0) { return Normalizer.MAYBE; } else if((spanLengthAndMaybe>>>1)==s.length()) { return Normalizer.YES; } else { return Normalizer.NO; } } @Override public int spanQuickCheckYes(CharSequence s) { return impl.composeQuickCheck(s, 0, s.length(), onlyContiguous, true)>>>1; } @Override public int getQuickCheck(int c) { return impl.getCompQuickCheck(impl.getNorm16(c)); } @Override public boolean hasBoundaryBefore(int c) { return impl.hasCompBoundaryBefore(c); } @Override public boolean hasBoundaryAfter(int c) { return impl.hasCompBoundaryAfter(c, onlyContiguous, false); } @Override public boolean isInert(int c) { return impl.hasCompBoundaryAfter(c, onlyContiguous, true); } private final boolean onlyContiguous; } public static final class FCDNormalizer2 extends Normalizer2WithImpl { public FCDNormalizer2(Normalizer2Impl ni) { super(ni); } @Override protected void normalize(CharSequence src, Normalizer2Impl.ReorderingBuffer buffer) { impl.makeFCD(src, 0, src.length(), buffer); } @Override protected void normalizeAndAppend( CharSequence src, boolean doNormalize, Normalizer2Impl.ReorderingBuffer buffer) { impl.makeFCDAndAppend(src, doNormalize, buffer); } @Override public int spanQuickCheckYes(CharSequence s) { return impl.makeFCD(s, 0, s.length(), null); } @Override public int getQuickCheck(int c) { return impl.isDecompYes(impl.getNorm16(c)) ? 1 : 0; } @Override public boolean hasBoundaryBefore(int c) { return impl.hasFCDBoundaryBefore(c); } @Override public boolean hasBoundaryAfter(int c) { return impl.hasFCDBoundaryAfter(c); } @Override public boolean isInert(int c) { return impl.isFCDInert(c); } } // instance cache ---------------------------------------------------------- *** private Norm2AllModes(Normalizer2Impl ni) { impl=ni; comp=new ComposeNormalizer2(ni, false); decomp=new DecomposeNormalizer2(ni); fcd=new FCDNormalizer2(ni); fcc=new ComposeNormalizer2(ni, true); } public final Normalizer2Impl impl; public final ComposeNormalizer2 comp; public final DecomposeNormalizer2 decomp; public final FCDNormalizer2 fcd; public final ComposeNormalizer2 fcc; private static Norm2AllModes getInstanceFromSingleton(Norm2AllModesSingleton singleton) { if(singleton.exception!=null) { throw singleton.exception; } return singleton.allModes; } public static Norm2AllModes getNFCInstance() { return getInstanceFromSingleton(NFCSingleton.INSTANCE); } public static Norm2AllModes getNFKCInstance() { return getInstanceFromSingleton(NFKCSingleton.INSTANCE); } public static Norm2AllModes getNFKC_CFInstance() { return getInstanceFromSingleton(NFKC_CFSingleton.INSTANCE); } // For use in properties APIs. public static Normalizer2WithImpl getN2WithImpl(int index) { switch(index) { case 0: return getNFCInstance().decomp; // NFD case 1: return getNFKCInstance().decomp; // NFKD case 2: return getNFCInstance().comp; // NFC case 3: return getNFKCInstance().comp; // NFKC default: return null; } } public static Norm2AllModes getInstance(InputStream data, String name) { if(data==null) { Norm2AllModesSingleton singleton; if(name.equals("nfc")) { singleton=NFCSingleton.INSTANCE; } else if(name.equals("nfkc")) { singleton=NFKCSingleton.INSTANCE; } else if(name.equals("nfkc_cf")) { singleton=NFKC_CFSingleton.INSTANCE; } else { singleton=null; } if(singleton!=null) { if(singleton.exception!=null) { throw singleton.exception; } return singleton.allModes; } } return cache.getInstance(name, data); } private static CacheBase cache = new SoftCache() { protected Norm2AllModes createInstance(String key, InputStream data) { if(data==null) { throw new MissingResourceException( "No Normalizer2 data name \""+key+"\" cached, and InputStream is null", "Normalizer2", key); } Normalizer2Impl impl=new Normalizer2Impl().load(data); return new Norm2AllModes(impl); } }; public static final NoopNormalizer2 NOOP_NORMALIZER2=new NoopNormalizer2(); /** * Gets the FCD normalizer, with the FCD data initialized. * @return FCD normalizer */ public static Normalizer2 getFCDNormalizer2() { Norm2AllModes allModes=getNFCInstance(); allModes.impl.getFCDTrie(); return allModes.fcd; } private static final class Norm2AllModesSingleton { private Norm2AllModesSingleton(String name) { try { Normalizer2Impl impl=new Normalizer2Impl().load( ICUResourceBundle.ICU_BUNDLE+"/"+name+".nrm"); allModes=new Norm2AllModes(impl); } catch(RuntimeException e) { exception=e; } } private Norm2AllModes allModes; private RuntimeException exception; } private static final class NFCSingleton { private static final Norm2AllModesSingleton INSTANCE=new Norm2AllModesSingleton("nfc"); } private static final class NFKCSingleton { private static final Norm2AllModesSingleton INSTANCE=new Norm2AllModesSingleton("nfkc"); } private static final class NFKC_CFSingleton { private static final Norm2AllModesSingleton INSTANCE=new Norm2AllModesSingleton("nfkc_cf"); } }