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-2018, 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 }