1 /*
   2  * Copyright (c) 2015, 2019, 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 sun.text.normalizer;
  34 
  35 import java.io.IOException;
  36 
  37 final class Norm2AllModes {
  38     // Public API dispatch via Normalizer2 subclasses -------------------------- ***
  39 
  40     // Normalizer2 implementation for the old UNORM_NONE.
  41     public static final class NoopNormalizer2 extends Normalizer2 {
  42         @Override
  43         public StringBuilder normalize(CharSequence src, StringBuilder dest) {
  44             if(dest!=src) {
  45                 dest.setLength(0);
  46                 return dest.append(src);
  47             } else {
  48                 throw new IllegalArgumentException();
  49             }
  50         }
  51 
  52         @Override
  53         public Appendable normalize(CharSequence src, Appendable dest) {
  54             if(dest!=src) {
  55                 try {
  56                     return dest.append(src);
  57                 } catch(IOException e) {
  58                     throw new InternalError(e.toString(), e);
  59                 }
  60             } else {
  61                 throw new IllegalArgumentException();
  62             }
  63         }
  64 
  65         @Override
  66         public StringBuilder normalizeSecondAndAppend(StringBuilder first, CharSequence second) {
  67             if(first!=second) {
  68                 return first.append(second);
  69             } else {
  70                 throw new IllegalArgumentException();
  71             }
  72         }
  73 
  74         @Override
  75         public StringBuilder append(StringBuilder first, CharSequence second) {
  76             if(first!=second) {
  77                 return first.append(second);
  78             } else {
  79                 throw new IllegalArgumentException();
  80             }
  81         }
  82 
  83         @Override
  84         public String getDecomposition(int c) {
  85             return null;
  86         }
  87 
  88         // No need to override the default getRawDecomposition().
  89         @Override
  90         public boolean isNormalized(CharSequence s) { return true; }
  91 
  92         @Override
  93         public int spanQuickCheckYes(CharSequence s) { return s.length(); }
  94 
  95         @Override
  96         public boolean hasBoundaryBefore(int c) { return true; }
  97     }
  98 
  99     // Intermediate class:
 100     // Has NormalizerImpl and does boilerplate argument checking and setup.
 101     public abstract static class Normalizer2WithImpl extends Normalizer2 {
 102         public Normalizer2WithImpl(NormalizerImpl ni) {
 103             impl=ni;
 104         }
 105 
 106         // normalize
 107         @Override
 108         public StringBuilder normalize(CharSequence src, StringBuilder dest) {
 109             if(dest==src) {
 110                 throw new IllegalArgumentException();
 111             }
 112             dest.setLength(0);
 113             normalize(src, new NormalizerImpl.ReorderingBuffer(impl, dest, src.length()));
 114             return dest;
 115         }
 116 
 117         @Override
 118         public Appendable normalize(CharSequence src, Appendable dest) {
 119             if(dest==src) {
 120                 throw new IllegalArgumentException();
 121             }
 122             NormalizerImpl.ReorderingBuffer buffer=
 123                 new NormalizerImpl.ReorderingBuffer(impl, dest, src.length());
 124             normalize(src, buffer);
 125             buffer.flush();
 126             return dest;
 127         }
 128 
 129         protected abstract void normalize(CharSequence src, NormalizerImpl.ReorderingBuffer buffer);
 130 
 131         // normalize and append
 132         @Override
 133         public StringBuilder normalizeSecondAndAppend(StringBuilder first, CharSequence second) {
 134             return normalizeSecondAndAppend(first, second, true);
 135         }
 136 
 137         @Override
 138         public StringBuilder append(StringBuilder first, CharSequence second) {
 139             return normalizeSecondAndAppend(first, second, false);
 140         }
 141 
 142         public StringBuilder normalizeSecondAndAppend(
 143                 StringBuilder first, CharSequence second, boolean doNormalize) {
 144             if(first==second) {
 145                 throw new IllegalArgumentException();
 146             }
 147             normalizeAndAppend(
 148                 second, doNormalize,
 149                 new NormalizerImpl.ReorderingBuffer(impl, first, first.length()+second.length()));
 150             return first;
 151         }
 152 
 153         protected abstract void normalizeAndAppend(
 154                 CharSequence src, boolean doNormalize, NormalizerImpl.ReorderingBuffer buffer);
 155 
 156         @Override
 157         public String getDecomposition(int c) {
 158             return impl.getDecomposition(c);
 159         }
 160 
 161         @Override
 162         public int getCombiningClass(int c) {
 163             return impl.getCC(impl.getNorm16(c));
 164         }
 165 
 166         // quick checks
 167         @Override
 168         public boolean isNormalized(CharSequence s) {
 169             return s.length()==spanQuickCheckYes(s);
 170         }
 171 
 172         public final NormalizerImpl impl;
 173     }
 174 
 175     public static final class DecomposeNormalizer2 extends Normalizer2WithImpl {
 176         public DecomposeNormalizer2(NormalizerImpl ni) {
 177             super(ni);
 178         }
 179 
 180         @Override
 181         protected void normalize(CharSequence src, NormalizerImpl.ReorderingBuffer buffer) {
 182             impl.decompose(src, 0, src.length(), buffer);
 183         }
 184 
 185         @Override
 186         protected void normalizeAndAppend(
 187                 CharSequence src, boolean doNormalize, NormalizerImpl.ReorderingBuffer buffer) {
 188             impl.decomposeAndAppend(src, doNormalize, buffer);
 189         }
 190 
 191         @Override
 192         public int spanQuickCheckYes(CharSequence s) {
 193             return impl.decompose(s, 0, s.length(), null);
 194         }
 195 
 196         @Override
 197         public boolean hasBoundaryBefore(int c) { return impl.hasDecompBoundaryBefore(c); }
 198     }
 199 
 200     public static final class ComposeNormalizer2 extends Normalizer2WithImpl {
 201         public ComposeNormalizer2(NormalizerImpl ni, boolean fcc) {
 202             super(ni);
 203             onlyContiguous=fcc;
 204         }
 205 
 206         @Override
 207         protected void normalize(CharSequence src, NormalizerImpl.ReorderingBuffer buffer) {
 208             impl.compose(src, 0, src.length(), onlyContiguous, true, buffer);
 209         }
 210 
 211         @Override
 212         protected void normalizeAndAppend(
 213                 CharSequence src, boolean doNormalize, NormalizerImpl.ReorderingBuffer buffer) {
 214             impl.composeAndAppend(src, doNormalize, onlyContiguous, buffer);
 215         }
 216 
 217         @Override
 218         public boolean isNormalized(CharSequence s) {
 219             // 5: small destCapacity for substring normalization
 220             return impl.compose(s, 0, s.length(),
 221                                 onlyContiguous, false,
 222                                 new NormalizerImpl.ReorderingBuffer(impl, new StringBuilder(), 5));
 223         }
 224 
 225         @Override
 226         public int spanQuickCheckYes(CharSequence s) {
 227             return impl.composeQuickCheck(s, 0, s.length(), onlyContiguous, true)>>>1;
 228         }
 229 
 230         @Override
 231         public boolean hasBoundaryBefore(int c) { return impl.hasCompBoundaryBefore(c); }
 232 
 233         private final boolean onlyContiguous;
 234     }
 235 
 236     // instance cache ---------------------------------------------------------- ***
 237 
 238     private Norm2AllModes(NormalizerImpl ni) {
 239         impl=ni;
 240         comp=new ComposeNormalizer2(ni, false);
 241         decomp=new DecomposeNormalizer2(ni);
 242     }
 243 
 244     public final NormalizerImpl impl;
 245     public final ComposeNormalizer2 comp;
 246     public final DecomposeNormalizer2 decomp;
 247 
 248     private static Norm2AllModes getInstanceFromSingleton(Norm2AllModesSingleton singleton) {
 249         if(singleton.exception!=null) {
 250             throw singleton.exception;
 251         }
 252         return singleton.allModes;
 253     }
 254 
 255     public static Norm2AllModes getNFCInstance() {
 256         return getInstanceFromSingleton(NFCSingleton.INSTANCE);
 257     }
 258 
 259     public static Norm2AllModes getNFKCInstance() {
 260         return getInstanceFromSingleton(NFKCSingleton.INSTANCE);
 261     }
 262 
 263     public static final NoopNormalizer2 NOOP_NORMALIZER2=new NoopNormalizer2();
 264 
 265     private static final class Norm2AllModesSingleton {
 266         private Norm2AllModesSingleton(String name) {
 267             try {
 268                 String DATA_FILE_NAME = "/sun/text/resources/" + name + ".nrm";
 269                 NormalizerImpl impl=new NormalizerImpl().load(DATA_FILE_NAME);
 270                 allModes=new Norm2AllModes(impl);
 271             } catch (RuntimeException e) {
 272                 exception=e;
 273             }
 274         }
 275 
 276         private Norm2AllModes allModes;
 277         private RuntimeException exception;
 278     }
 279 
 280     private static final class NFCSingleton {
 281         private static final Norm2AllModesSingleton INSTANCE=new Norm2AllModesSingleton("nfc");
 282     }
 283 
 284     private static final class NFKCSingleton {
 285         private static final Norm2AllModesSingleton INSTANCE=new Norm2AllModesSingleton("nfkc");
 286     }
 287 }