BUG-5410: optimize CaseInsensitiveMap
[yangtools.git] / third-party / xsd-regex / src / main / java / org / opendaylight / yangtools / xsd / regex / CaseInsensitiveMap.java
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the 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 writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 package org.opendaylight.yangtools.xsd.regex;
19
20 /**
21  * @xerces.internal
22  * 
23  * @version $Id: CaseInsensitiveMap.java 834653 2009-11-10 20:32:39Z mrglavas $
24  */
25 final class CaseInsensitiveMap {
26
27     private static final int CHUNK_SHIFT = 10;           /* 2^10 = 1k */
28     private static final int CHUNK_SIZE = (1<<CHUNK_SHIFT);
29     private static final int CHUNK_MASK = (CHUNK_SIZE-1);
30     private static final int INITIAL_CHUNK_COUNT = 64;   /* up to 0xFFFF */
31
32     private static final int[][][] caseInsensitiveMap;
33     
34     private static final int LOWER_CASE_MATCH = 1;
35     private static final int UPPER_CASE_MATCH = 2;
36
37     /**
38      *  Return a list of code point characters (not including the input value)
39      *  that can be substituted in a case insensitive match
40      */
41     static int[] get(int codePoint) {
42         return (codePoint < 0x10000) ? getMapping(codePoint) : null;
43     }
44
45     private static int[] getMapping(int codePoint) {
46         int chunk = codePoint >>> CHUNK_SHIFT;
47         int offset = codePoint & CHUNK_MASK;
48         
49         return caseInsensitiveMap[chunk][offset];
50     }
51     
52     static {
53         caseInsensitiveMap = new int[INITIAL_CHUNK_COUNT][CHUNK_SIZE][];
54         int lc, uc;
55         for (int i=0; i<0x10000; i++) {
56             lc = Character.toLowerCase((char) i);
57             uc = Character.toUpperCase((char) i);
58
59             // lower/upper case value is not the same as code point
60             if (lc != uc || lc != i) {
61                 int[] map = new int[2];
62                 int index = 0;
63
64                 if (lc != i) {
65                     map[index++] = lc;
66                     map[index++] = LOWER_CASE_MATCH;
67                     int[] lcMap = getMapping(lc);
68                     if (lcMap != null) {
69                         map = updateMap(i, map, lc, lcMap, LOWER_CASE_MATCH);
70                     }
71                 }
72                 
73                 if (uc != i) {
74                     if (index == map.length) {
75                         map = expandMap(map, 2);
76                     }
77                     map[index++] = uc;
78                     map[index++] = UPPER_CASE_MATCH;
79                     int[] ucMap = getMapping(uc);
80                     if (ucMap != null) {
81                         map = updateMap(i, map, uc, ucMap, UPPER_CASE_MATCH);
82                     }
83                 }
84                 
85                 set(i, map);
86             }
87         }
88     }
89     
90     private static int[] expandMap(int[] srcMap, int expandBy) {
91         final int oldLen = srcMap.length;
92         int[] newMap = new int[oldLen + expandBy];
93         
94         System.arraycopy(srcMap, 0, newMap, 0, oldLen);
95         return newMap;
96     }
97     
98     private static void set(int codePoint, int[] map) {
99         int chunk = codePoint >>> CHUNK_SHIFT;
100         int offset = codePoint & CHUNK_MASK;
101         
102         caseInsensitiveMap[chunk][offset] = map;
103     }
104
105     private static int[] updateMap(int codePoint, int[] codePointMap,
106             int ciCodePoint, int[] ciCodePointMap, int matchType) {
107         for (int i=0; i<ciCodePointMap.length; i+=2) {
108             int c = ciCodePointMap[i];
109             int[] cMap = getMapping(c);
110             if (cMap != null) {
111                 if (contains(cMap, ciCodePoint, matchType)) {
112                     if (!contains(cMap, codePoint)) {
113                         cMap = expandAndAdd(cMap, codePoint, matchType);
114                         set(c, cMap);
115                     }
116                     if (!contains(codePointMap, c)) {
117                         codePointMap = expandAndAdd(codePointMap, c,matchType);
118                     }
119                 }
120             }
121         }
122         
123         if (!contains(ciCodePointMap, codePoint)) {
124             ciCodePointMap = expandAndAdd(ciCodePointMap, codePoint, matchType);
125             set(ciCodePoint, ciCodePointMap);
126         }
127
128         return codePointMap;
129     }
130     
131     private static boolean contains(int[] map, int codePoint) {
132         for (int i=0; i<map.length; i += 2) {
133             if (map[i] == codePoint) {
134                 return true;
135             }
136         }
137         return false;
138     }
139
140     private static boolean contains(int[] map, int codePoint, int matchType) {
141         for (int i=0; i<map.length; i += 2) {
142             if (map[i] == codePoint && map[i+1] == matchType) {
143                 return true;
144             }
145         }
146         return false;
147     }
148     
149     private static int[] expandAndAdd(int[] srcMap, int codePoint, int matchType) {
150         final int oldLen = srcMap.length;
151         int[] newMap = new int[oldLen + 2];
152         
153         System.arraycopy(srcMap, 0, newMap, 0, oldLen);
154         newMap[oldLen] = codePoint;
155         newMap[oldLen+1] = matchType;
156         return newMap;
157     }
158 }