Coverage Summary for Class: ICU4Jv26TextNormalizer (com.acciente.oacc.normalizer.icu4j)

Class Method, % Line, %
ICU4Jv26TextNormalizer 100% (6/ 6) 90.9% (20/ 22)
ICU4Jv26TextNormalizer$LazyInitSingletonHolder 100% (2/ 2) 100% (2/ 2)
total 100% (8/ 8) 91.7% (22/ 24)


1 /* 2  * Copyright 2009-2017, Acciente LLC 3  * 4  * Acciente LLC licenses this file to you under the 5  * Apache License, Version 2.0 (the "License"); you 6  * may not use this file except in compliance with the 7  * License. You may obtain a copy of the License at 8  * 9  * http://www.apache.org/licenses/LICENSE-2.0 10  * 11  * Unless required by applicable law or agreed to in 12  * writing, software distributed under the License is 13  * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 14  * OR CONDITIONS OF ANY KIND, either express or implied. 15  * See the License for the specific language governing 16  * permissions and limitations under the License. 17  */ 18  19 package com.acciente.oacc.normalizer.icu4j; 20  21 import com.acciente.oacc.normalizer.TextNormalizer; 22 import com.ibm.icu.text.Normalizer; 23  24 public class ICU4Jv26TextNormalizer extends TextNormalizer { 25  // constants 26  private static final char ZERO_CHAR = '\0'; 27  28  // we use the singleton holder pattern to lazy initialize the singleton instance 29  // in a thread safe manner without the need for any explicit locking 30  // (see https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom). 31  private static class LazyInitSingletonHolder { 32  private static final TextNormalizer INSTANCE = new ICU4Jv26TextNormalizer(); 33  } 34  35  private ICU4Jv26TextNormalizer() { 36  // this "no-op" call to the Normalize class is *very* important, without it when the 37  // com.ibm.icu.text.Normalizer class is not present in the classpath a load of the 38  // class will not fail until it is attempted in the normalizeToNfc() method below -- which 39  // is too late. The class load needs to fail here to cause the getInstance() method below to 40  // propagate the class load exception and correctly trigger the fallback to the JDK based 41  // TextNormalizer implementation in the parent class's TextNormalizer#getInstance(). 42  Normalizer.normalize("", Normalizer.NFC, 0); 43  } 44  45  public static TextNormalizer getInstance() { 46  return LazyInitSingletonHolder.INSTANCE; 47  } 48  49  @Override 50  public char[] normalizeToNfc(char[] source) { 51  int destBufferSize = 3 * source.length; 52  char[] result = null; 53  do { 54  char[] destBuffer = new char[destBufferSize]; 55  try { 56  final int destBufferUsedCount = Normalizer.normalize(source, destBuffer, Normalizer.NFC, 0); 57  result = copyContents(destBuffer, destBufferUsedCount); 58  } 59  catch (IndexOutOfBoundsException e) { 60  // NOTE: since we allocate an initial buffer that is 3x of 61  // the source text length we never expect this to happen 62  63  // try the next loop iteration with a larger buffer 64  destBufferSize += source.length; 65  } 66  finally { 67  // zero out the current dest buffer 68  zeroOut(destBuffer); 69  } 70  } while (result == null); 71  72  return result; 73  } 74  75  /** 76  * Returns a copy of the contents of specified char array 77  * 78  * @param source char array to copy from 79  * @param countToCopy the number of characters to copy from the source 80  * 81  * @return a character array with 82  */ 83  private char[] copyContents(char[] source, int countToCopy) { 84  final char[] copy = new char[countToCopy]; 85  System.arraycopy(source, 0, copy, 0, countToCopy); 86  return copy; 87  } 88  89  /** 90  * Sets all contents in the specified char array to {@value ZERO_CHAR}. 91  * 92  * @param dest the char array to zero out 93  */ 94  private void zeroOut(char[] dest) { 95  for (int i = 0; i < dest.length; i++) { 96  dest[i] = ZERO_CHAR; 97  } 98  } 99 }