1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
/*
* Copyright 2004-2010 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.text.CollationKey;
import java.text.Collator;
import java.util.Locale;
import org.h2.constant.SysProperties;
import org.h2.util.SmallLRUCache;
import org.h2.util.StringUtils;
/**
* Instances of this class can compare strings.
* Case sensitive and case insensitive comparison is supported,
* and comparison using a collator.
*/
public class CompareMode {
/**
* This constant means there is no collator set,
* and the default string comparison is to be used.
*/
public static final String OFF = "OFF";
private static CompareMode lastUsed;
private final String name;
private final int strength;
private final Collator collator;
private final SmallLRUCache<String, CollationKey> collationKeys;
private CompareMode(String name, int strength) {
this.name = name;
this.strength = strength;
this.collator = CompareMode.getCollator(name);
int cacheSize = 0;
if (collator != null) {
this.collator.setStrength(strength);
cacheSize = SysProperties.getCollatorCacheSize();
}
if (cacheSize != 0) {
collationKeys = SmallLRUCache.newInstance(cacheSize);
} else {
collationKeys = null;
}
}
/**
* Create a new compare mode with the given collator and strength. If
* required, a new CompareMode is created, or if possible the last one is
* returned. A cache is used to speed up comparison when using a collator;
* CollationKey objects are cached.
*
* @param name the collation name or null
* @param strength the collation strength
* @return the compare mode
*/
public static synchronized CompareMode getInstance(String name, int strength) {
if (lastUsed != null) {
if (StringUtils.equals(lastUsed.name, name)) {
if (lastUsed.strength == strength) {
return lastUsed;
}
}
}
lastUsed = new CompareMode(name, strength);
return lastUsed;
}
/**
* Compare two characters in a string.
*
* @param a the first string
* @param ai the character index in the first string
* @param b the second string
* @param bi the character index in the second string
* @param ignoreCase true if a case-insensitive comparison should be made
* @return true if the characters are equals
*/
public boolean equalsChars(String a, int ai, String b, int bi, boolean ignoreCase) {
if (collator != null) {
return compareString(a.substring(ai, ai + 1), b.substring(bi, bi + 1), ignoreCase) == 0;
}
char ca = a.charAt(ai);
char cb = b.charAt(bi);
if (ignoreCase) {
ca = Character.toUpperCase(ca);
cb = Character.toUpperCase(cb);
}
return ca == cb;
}
/**
* Compare two strings.
*
* @param a the first string
* @param b the second string
* @param ignoreCase true if a case-insensitive comparison should be made
* @return -1 if the first string is 'smaller', 1 if the second string is
* smaller, and 0 if they are equal
*/
public int compareString(String a, String b, boolean ignoreCase) {
if (collator == null) {
if (ignoreCase) {
return a.compareToIgnoreCase(b);
}
return a.compareTo(b);
}
if (ignoreCase) {
// this is locale sensitive
a = a.toUpperCase();
b = b.toUpperCase();
}
int comp;
if (collationKeys != null) {
CollationKey aKey = getKey(a);
CollationKey bKey = getKey(b);
comp = aKey.compareTo(bKey);
} else {
comp = collator.compare(a, b);
}
return comp;
}
private CollationKey getKey(String a) {
synchronized (collationKeys) {
CollationKey key = collationKeys.get(a);
if (key == null) {
key = collator.getCollationKey(a);
collationKeys.put(a, key);
}
return key;
}
}
/**
* Get the collation name.
*
* @param l the locale
* @return the name of the collation
*/
public static String getName(Locale l) {
Locale english = Locale.ENGLISH;
String name = l.getDisplayLanguage(english) + ' ' + l.getDisplayCountry(english) + ' ' + l.getVariant();
name = StringUtils.toUpperEnglish(name.trim().replace(' ', '_'));
return name;
}
private static boolean compareLocaleNames(Locale locale, String name) {
return name.equalsIgnoreCase(locale.toString()) || name.equalsIgnoreCase(getName(locale));
}
/**
* Get the collator object for the given language name or language / country
* combination.
*
* @param name the language name
* @return the collator
*/
public static Collator getCollator(String name) {
if (name == null || name.equals(OFF)) {
return null;
}
Collator result = null;
if (name.length() == 2) {
Locale locale = new Locale(name.toLowerCase(), "");
if (compareLocaleNames(locale, name)) {
result = Collator.getInstance(locale);
}
} else if (name.length() == 5) {
// LL_CC (language_country)
int idx = name.indexOf('_');
if (idx >= 0) {
String language = name.substring(0, idx).toLowerCase();
String country = name.substring(idx + 1);
Locale locale = new Locale(language, country);
if (compareLocaleNames(locale, name)) {
result = Collator.getInstance(locale);
}
}
}
if (result == null) {
for (Locale locale : Collator.getAvailableLocales()) {
if (compareLocaleNames(locale, name)) {
result = Collator.getInstance(locale);
break;
}
}
}
return result;
}
public String getName() {
return name == null ? OFF : name;
}
public int getStrength() {
return strength;
}
}