/* Glazed Lists (c) 2003-2006 */
/* http://publicobject.com/glazedlists/ publicobject.com,*/
/* O'Dell Engineering Ltd.*/
package ca.odell.glazedlists.impl.filter;
import ca.odell.glazedlists.matchers.TextMatcherEditor;
import com.ibm.icu.text.Collator;
import com.ibm.icu.text.RuleBasedCollator;
import com.ibm.icu.text.StringSearch;
import java.text.StringCharacterIterator;
/**
* This implementation of {@link TextSearchStrategy} is a thin wrapper around
* ICU4J's <a href="http://icu.sourceforge.net/userguide/searchString.html">StringSearch</a>.
*
* This provides us with locale-sensitive text searching. ICU4J handles
* complicated text matching scenarios like
*
* <ul>
* <li>'�' matches "ae"
* <li>'�' matches "ss"
* <li>"resume" matches "r�sum�"
* </ul>
*
* Since text matching algorithms is not the focus of Glazed Lists, it makes
* sense for us to use the open-source, IBM-backed ICU4J project to accomplish
* this highly specialized behaviour.
*
* @author James Lemieux
*/
public class UnicodeCaseInsensitiveTextSearchStrategy implements TextSearchStrategy {
private static final RuleBasedCollator COLLATOR;
static {
COLLATOR = (RuleBasedCollator) Collator.getInstance();
// setting the strength property of a Collator determines the minimum
// level of difference considered significant during comparison
//
// PRIMARY is the strongest collator strength value. It is typically
// used to denote differences between base characters. We use it to
// make the StringSearch case insensitive.
COLLATOR.setStrength(Collator.PRIMARY);
}
/** The string to locate within a larger text. */
private String pattern;
/** one of {@link TextMatcherEditor#CONTAINS} or {@link TextMatcherEditor#STARTS_WITH} */
private final int mode;
/**
* Construct a UnicodeCaseInsensitiveTextSearchStrategy that matches in one of two ways
* depending on the value of <code>mode</code>.
*
* @param mode one of {@link TextMatcherEditor#CONTAINS} or {@link TextMatcherEditor#STARTS_WITH}
*/
public UnicodeCaseInsensitiveTextSearchStrategy(int mode) {
this.mode = mode;
}
/**
* @throws UnsupportedOperationException since UnicodeCaseInsensitiveTextSearchStrategy
* does not support remapping characters
*/
public void setCharacterMap(char[] charMap) {
throw new UnsupportedOperationException("character maps are not supported by the UnicodeCaseInsensitiveTextSearchStrategy");
}
/**
* Sets the subtext to locate when {@link #indexOf(String)} is called.
*
* @param subtext the String to locate in {@link #indexOf(String)}
*/
public void setSubtext(String subtext) {
this.pattern = subtext;
}
/**
* Returns the index of the first occurrence of <code>subtext</code> within
* <code>text</code>; or <code>-1</code> if <code>subtext</code> does not
* occur within <code>text</code>.
*
* @param text String in which to locate <code>subtext</code>
* @return the index of the first occurrence of <code>subtext</code> within
* <code>text</code>; or <code>-1</code>
* @throws IllegalStateException if no subtext has been set
*/
public int indexOf(String text) {
if (pattern == null)
throw new IllegalStateException("setSubtext must be called with a valid value before this method can operate");
if (text.length() == 0)
return -1;
final int index = new StringSearch(pattern, new StringCharacterIterator(text), COLLATOR).first();
return mode == TextMatcherEditor.STARTS_WITH && index != 0 ? -1 : index;
}
}