package er.extensions.eof.qualifiers; import java.util.StringTokenizer; import com.webobjects.eocontrol.EOClassDescription; import com.webobjects.eocontrol.EOQualifier; import com.webobjects.eocontrol.EOQualifierEvaluation; import com.webobjects.foundation.NSArray; import com.webobjects.foundation.NSDictionary; import com.webobjects.foundation.NSKeyValueCodingAdditions; import com.webobjects.foundation.NSMutableSet; import com.webobjects.foundation.NSSelector; /** * ERXFullTextQualifier provides a qualifier implementation for searching with a * fulltext index. This depends on ERXSQLHelper.sqlForFullTextQuery providing an * implementation for your particular database. Note that because of differences * in implementions of fulltext indexing on various databases, the results you * obtain using this qualifier will vary across implementations. Additionally, * executing this qualifier on an array of EO's may differ from executing the * qualifier in your database. When qualifying on an array, the qualifier does * not address stemming and stop words. * * @author mschrag */ public class ERXFullTextQualifier extends EOQualifier implements Cloneable, EOQualifierEvaluation { /** * Do I need to update serialVersionUID? * See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the * <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a> */ private static final long serialVersionUID = 1L; public static final String FullTextContainsSelectorName = "fulltextContains"; public static final NSSelector FullTextContainsSelector = new NSSelector(ERXFullTextQualifier.FullTextContainsSelectorName, new Class[] { String.class }); public static enum MatchType { /** * All search terms must match for the qualifier to match. */ ALL, /** * Only one search term must match for the qualifier to match. */ ANY } private String _keyPath; private String _indexName; private MatchType _matchType; private NSArray<String> _terms; /** * Constructs an ERXFullTextQualifier. * * @param keyPath * the keypath to qualify on (some databases may ignore this) * @param indexName * the name of the index to use (some databases may ignore this) * @param matchType * ANY or ALL * @param terms * the array of search terms */ public ERXFullTextQualifier(String keyPath, String indexName, MatchType matchType, NSArray<String> terms) { _keyPath = keyPath; _indexName = indexName; _matchType = matchType; _terms = terms; } /** * Constructs an ERXFullTextQualifier. * * @param keyPath * the keypath to qualify on (some databases may ignore this) * @param indexName * the name of the index to use (some databases may ignore this) * @param matchType * ANY or ALL * @param terms * the list of search terms */ public ERXFullTextQualifier(String keyPath, String indexName, MatchType matchType, String... terms) { this(keyPath, indexName, matchType, new NSArray<String>(terms)); } /** * Constructs an ERXFullTextQualifier defaulting indexName to the same as * keyPath. * * @param keyPath * the keypath to qualify on (some databases may ignore this) * @param matchType * ANY or ALL * @param terms * the array of search terms */ public ERXFullTextQualifier(String keyPath, MatchType matchType, NSArray<String> terms) { this(keyPath, keyPath, matchType, terms); } /** * Constructs an ERXFullTextQualifier defaulting indexName to the same as * keyPath. * * @param keyPath * the keypath to qualify on (some databases may ignore this) * @param matchType * ANY or ALL * @param terms * the list of search terms */ public ERXFullTextQualifier(String keyPath, MatchType matchType, String... terms) { this(keyPath, keyPath, matchType, terms); } /** * Returns the keypath for this qualifier. * * @return the keypath for this qualifier */ public String keyPath() { return _keyPath; } /** * Returns the index name for this qualifier. * * @return the index name for this qualifier */ public String indexName() { return _indexName; } /** * Returns the match type (ANY or ALL) for this qualifier. * * @return the match type (ANY or ALL) for this qualifier */ public MatchType matchType() { return _matchType; } /** * Returns the array of search terms for this qualifier. * * @return the array of search terms for this qualifier */ public NSArray<String> terms() { return _terms; } @Override public ERXFullTextQualifier clone() { return new ERXFullTextQualifier(_keyPath, _indexName, _matchType, _terms); } @Override @SuppressWarnings("unchecked") public void addQualifierKeysToSet(NSMutableSet keys) { keys.addObject(_keyPath); } @Override public EOQualifier qualifierWithBindings(NSDictionary bindings, boolean requiresAll) { return clone(); } @Override public void validateKeysWithRootClassDescription(EOClassDescription classDescription) { StringTokenizer keyPathTokenizer = new StringTokenizer(_keyPath, "."); while (keyPathTokenizer.hasMoreElements()) { String key = keyPathTokenizer.nextToken(); if (keyPathTokenizer.hasMoreElements()) { EOClassDescription sourceClassDescription = classDescription; classDescription = sourceClassDescription.classDescriptionForDestinationKey(key); if (classDescription == null) { throw new IllegalStateException("Invalid key '" + key + "' for entity '" + sourceClassDescription.entityName() + "'."); } } else { if (!classDescription.attributeKeys().containsObject(key)) { throw new IllegalStateException("Invalid key '" + key + "' for entity '" + classDescription.entityName() + "'."); } } } } @Override public boolean evaluateWithObject(Object object) { boolean matches; String value = (String) NSKeyValueCodingAdditions.Utility.valueForKeyPath(object, _keyPath); if (value != null) { String lowercaseValue = value.toLowerCase(); if (_matchType == MatchType.ANY) { matches = false; for (String term : _terms) { if (lowercaseValue.contains(term.toLowerCase())) { matches = true; break; } } } else if (_matchType == MatchType.ALL) { matches = true; for (String term : _terms) { if (!lowercaseValue.contains(term.toLowerCase())) { matches = false; break; } } } else { throw new IllegalArgumentException("Unknown match type '" + _matchType + "'."); } } else { matches = false; } return matches; } }