/** * Copyright (C) 2009-2013 FoundationDB, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.foundationdb.server.collation; import com.google.common.hash.HashFunction; import com.google.common.hash.Hashing; import com.ibm.icu.text.Collator; import com.persistit.Key; import com.persistit.util.Util; import java.util.Arrays; public class AkCollatorICU extends AkCollator { private final CollationSpecifier collationSpecifier; final ThreadLocal<Collator> collator = new ThreadLocal<Collator>() { protected Collator initialValue() { return AkCollatorFactory.forScheme(collationSpecifier); } }; /** * Create an AkCollator which may be used in across multiple threads. Each * instance of AkCollator has a ThreadLocal which optionally contains a * reference to a thread-private ICU4J Collator. * * @param name * Name given to AkCollatorFactory by which to look up the scheme * @param scheme * Formatted string containing Locale name, and collation string * strength. */ AkCollatorICU(final String scheme, final int collationId) { super(scheme, collationId); collationSpecifier = new CollationSpecifier(scheme); collator.get(); // force the collator to initialize (to test scheme) } @Override public boolean isRecoverable() { return false; } @Override public void append(Key key, String value) { if (value == null) { key.append(null); } else { key.append(encodeSortKeyBytes(value)); } } @Override public int compare(String source, String target) { return collator.get().compare(source, target); } @Override public boolean isCaseSensitive() { return collator.get().getStrength() > Collator.SECONDARY; } /** * Construct the sort key bytes for the given String value * * @param value * the String * @return sort key bytes */ @Override public byte[] encodeSortKeyBytes(String value) { byte[] bytes = collator.get().getCollationKey(value).toByteArray(); return Arrays.copyOf(bytes, bytes.length - 1); // Remove terminating null. } /** Decode the value to a string of hex digits. */ @Override String debugDecodeSortKeyBytes(byte[] bytes, int index, int length) { StringBuilder sb = new StringBuilder(); Util.bytesToHex(sb, bytes, index, length); return sb.toString(); } @Override public int hashCode(String string) { byte[] bytes = collator.get().getCollationKey(string).toByteArray(); bytes = Arrays.copyOfRange(bytes, 0, bytes.length-1); // remove null terminating character return hashCode(bytes); } @Override public int hashCode(byte[] bytes) { return hashFunction.hashBytes(bytes, 0, bytes.length).asInt(); } @Override public String toString() { return collationSpecifier.toString(); } @Override public String getScheme() { return collationSpecifier.toString(); } private static final HashFunction hashFunction = Hashing.goodFastHash(32); // Because we're returning ints }