blob: f50f1cfc069b6a8a124d9d9ef88b4e1f7102b631 [file] [log] [blame]
/*
*******************************************************************************
* Copyright (C) 2009-2014, International Business Machines
* Corporation and others. All Rights Reserved.
*******************************************************************************
*/
package com.ibm.icu.impl;
import java.io.IOException;
import java.io.InputStream;
import com.ibm.icu.text.Normalizer;
import com.ibm.icu.text.Normalizer2;
import com.ibm.icu.util.ICUUncheckedIOException;
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 ICUUncheckedIOException(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 String getDecomposition(int c) {
return null;
}
// No need to override the default getRawDecomposition().
@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);
@Override
public String getDecomposition(int c) {
return impl.getDecomposition(c);
}
@Override
public String getRawDecomposition(int c) {
return impl.getRawDecomposition(c);
}
@Override
public int composePair(int a, int b) {
return impl.composePair(a, b);
}
@Override
public int getCombiningClass(int c) {
return impl.getCC(impl.getNorm16(c));
}
// 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<String, Norm2AllModes, InputStream> cache =
new SoftCache<String, Norm2AllModes, InputStream>() {
protected Norm2AllModes createInstance(String key, InputStream data) {
Normalizer2Impl impl;
if(data==null) {
impl=new Normalizer2Impl().load(ICUResourceBundle.ICU_BUNDLE+"/"+key+".nrm");
} else {
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() {
return getNFCInstance().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");
}
}