Index: /workspace/lucene-trunk/src/java/org/apache/lucene/search/StringComparatorSource.java =================================================================== --- /workspace/lucene-trunk/src/java/org/apache/lucene/search/StringComparatorSource.java (revision 0) +++ /workspace/lucene-trunk/src/java/org/apache/lucene/search/StringComparatorSource.java (revision 0) @@ -0,0 +1,121 @@ +package org.apache.lucene.search; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.IOException; +import java.text.Collator; +import java.util.Locale; + +import org.apache.lucene.index.IndexReader; + +/** + * SortComparatorSource which sorts hits by String comparison, either using a + * simple comparison or by a Locale-specific collator if a Locale-based sort is + * requested. + */ +public class StringComparatorSource implements SortComparatorSource { + private static final long serialVersionUID = 1L; + + /** + * Returns a comparator for sorting hits according to a field containing + * strings. + * + * @param reader Index to use. + * @param fieldname Fieldable containing string values. + * @param locale locale to use for sorting + * @return Comparator for sorting hits. + * @throws IOException If an error occurs reading the index. + */ + public ScoreDocComparator newComparator(IndexReader reader, String fieldname, + Locale locale) throws IOException { + final String field = fieldname.intern(); + if (locale == null) { + final FieldCache.StringIndex index = FieldCache.DEFAULT.getStringIndex( + reader, field); + return newLocaleInsensitiveComparator(index); + } else { + final String[] index = FieldCache.DEFAULT.getStrings(reader, field); + return newLocaleSensitiveComparator(index, locale); + } + } + + private ScoreDocComparator newLocaleSensitiveComparator( + final String[] index, Locale locale) { + final CollatorProvider collatorProvider = getCollatorProvider(locale); + + return new ScoreDocComparator() { + public final int compare(final ScoreDoc i, final ScoreDoc j) { + String is = index[i.doc]; + String js = index[j.doc]; + if (is == js) { + return 0; + } else if (is == null) { + return -1; + } else if (js == null) { + return 1; + } else { + return collatorProvider.getCollator().compare(is, js); + } + } + + public Comparable sortValue(final ScoreDoc i) { + return index[i.doc]; + } + + public int sortType() { + return SortField.STRING; + } + }; + } + + protected CollatorProvider getCollatorProvider(Locale locale) { + final Collator collator = Collator.getInstance(locale); + return new CollatorProvider() { + public Collator getCollator() { + return collator; + } + }; + } + + private ScoreDocComparator newLocaleInsensitiveComparator( + final FieldCache.StringIndex index) { + return new ScoreDocComparator() { + public final int compare(final ScoreDoc i, final ScoreDoc j) { + final int fi = index.order[i.doc]; + final int fj = index.order[j.doc]; + if (fi < fj) + return -1; + if (fi > fj) + return 1; + return 0; + } + + public Comparable sortValue(final ScoreDoc i) { + return index.lookup[index.order[i.doc]]; + } + + public int sortType() { + return SortField.STRING; + } + }; + } + + public static interface CollatorProvider { + public Collator getCollator(); + } +} \ No newline at end of file Index: /workspace/lucene-trunk/src/java/org/apache/lucene/search/ThreadLocalStringComparatorSource.java =================================================================== --- /workspace/lucene-trunk/src/java/org/apache/lucene/search/ThreadLocalStringComparatorSource.java (revision 0) +++ /workspace/lucene-trunk/src/java/org/apache/lucene/search/ThreadLocalStringComparatorSource.java (revision 0) @@ -0,0 +1,33 @@ +package org.apache.lucene.search; + +import java.text.Collator; +import java.util.Locale; + +/** + * Variation of StringComparatorSource which uses a ThreadLocal wrapper around + * the StringComparatorSource's collators. This provides substantial performance + * gains (at the cost when multiple threads are doing locale-sensitive sorting. + * By default, Collator.getInstance returns a RuleBasedCollator, access to which + * is completely synchronized. By using a ThreadLocal to get a Collator for each + * thread (the Collators are cached, but cloned upon retrieval so each is + * identity-unique) we can greatly reduce lock contention across multiple + * searches sorting on the same field of the same reader. + */ +public class ThreadLocalStringComparatorSource extends StringComparatorSource { + private static final long serialVersionUID = 1L; + + protected CollatorProvider getCollatorProvider(final Locale locale) { + return new CollatorProvider() { + private ThreadLocal collators = new ThreadLocal() { + protected Object initialValue() { + return Collator.getInstance(locale); + } + }; + + public Collator getCollator() { + return (Collator) collators.get(); + } + }; + } + +}