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
  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
  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
  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
  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
  87         public String getDecomposition(int c) {
  88             return null;
  89         }
  90 
  91         // No need to override the default getRawDecomposition().
  92         @Override
  93         public boolean isNormalized(CharSequence s) { return true; }
  94 
  95         @Override
  96         public int spanQuickCheckYes(CharSequence s) { return s.length(); }
  97 
  98         @Override
  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 {
 105         public Normalizer2WithImpl(NormalizerImpl ni) {
 106             impl=ni;
 107         }
 108 
 109         // normalize
 110         @Override
 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
 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 
 132         protected abstract void normalize(CharSequence src, NormalizerImpl.ReorderingBuffer buffer);
 133 
 134         // normalize and append
 135         @Override
 136         public StringBuilder normalizeSecondAndAppend(StringBuilder first, CharSequence second) {
 137             return normalizeSecondAndAppend(first, second, true);
 138         }
 139 
 140         @Override
 141         public StringBuilder append(StringBuilder first, CharSequence second) {
 142             return normalizeSecondAndAppend(first, second, false);
 143         }
 144 
 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 
 156         protected abstract void normalizeAndAppend(
 157                 CharSequence src, boolean doNormalize, NormalizerImpl.ReorderingBuffer buffer);
 158 
 159         @Override
 160         public String getDecomposition(int c) {
 161             return impl.getDecomposition(c);
 162         }
 163 
 164         @Override
 165         public int getCombiningClass(int c) {
 166             return impl.getCC(impl.getNorm16(c));
 167         }
 168 
 169         // quick checks
 170         @Override
 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 {
 179         public DecomposeNormalizer2(NormalizerImpl ni) {
 180             super(ni);
 181         }
 182 
 183         @Override
 184         protected void normalize(CharSequence src, NormalizerImpl.ReorderingBuffer buffer) {
 185             impl.decompose(src, 0, src.length(), buffer);
 186         }
 187 
 188         @Override
 189         protected void normalizeAndAppend(
 190                 CharSequence src, boolean doNormalize, NormalizerImpl.ReorderingBuffer buffer) {
 191             impl.decomposeAndAppend(src, doNormalize, buffer);
 192         }
 193 
 194         @Override
 195         public int spanQuickCheckYes(CharSequence s) {
 196             return impl.decompose(s, 0, s.length(), null);
 197         }
 198 
 199         @Override
 200         public boolean hasBoundaryBefore(int c) { return impl.hasDecompBoundaryBefore(c); }
 201     }
 202 
 203     public static final class ComposeNormalizer2 extends Normalizer2WithImpl {
 204         public ComposeNormalizer2(NormalizerImpl ni, boolean fcc) {
 205             super(ni);
 206             onlyContiguous=fcc;
 207         }
 208 
 209         @Override
 210         protected void normalize(CharSequence src, NormalizerImpl.ReorderingBuffer buffer) {
 211             impl.compose(src, 0, src.length(), onlyContiguous, true, buffer);
 212         }
 213 
 214         @Override
 215         protected void normalizeAndAppend(
 216                 CharSequence src, boolean doNormalize, NormalizerImpl.ReorderingBuffer buffer) {
 217             impl.composeAndAppend(src, doNormalize, onlyContiguous, buffer);
 218         }
 219 
 220         @Override
 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
 229         public int spanQuickCheckYes(CharSequence s) {
 230             return impl.composeQuickCheck(s, 0, s.length(), onlyContiguous, true)>>>1;
 231         }
 232 
 233         @Override
 234         public boolean hasBoundaryBefore(int c) { return impl.hasCompBoundaryBefore(c); }
 235 
 236         private final boolean onlyContiguous;
 237     }
 238 
 239     // instance cache ---------------------------------------------------------- ***
 240 
 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 
 251     private static Norm2AllModes getInstanceFromSingleton(Norm2AllModesSingleton singleton) {
 252         if(singleton.exception!=null) {
 253             throw singleton.exception;
 254         }
 255         return singleton.allModes;
 256     }
 257 
 258     public static Norm2AllModes getNFCInstance() {
 259         return getInstanceFromSingleton(NFCSingleton.INSTANCE);
 260     }
 261 
 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 {
 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 }