| | |
| | | */ |
| | | package org.opends.server.schema; |
| | | |
| | | import java.nio.CharBuffer; |
| | | import java.text.CollationKey; |
| | | import java.text.Collator; |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.HashMap; |
| | | import java.util.HashSet; |
| | | import java.util.LinkedList; |
| | | import java.util.List; |
| | | import java.util.Locale; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.SortedSet; |
| | | import java.util.TreeSet; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.i18n.slf4j.LocalizedLogger; |
| | | import org.forgerock.opendj.config.server.ConfigException; |
| | | import org.forgerock.opendj.ldap.ByteSequence; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.ByteStringBuilder; |
| | | import org.forgerock.opendj.ldap.ConditionResult; |
| | | import org.forgerock.opendj.ldap.DecodeException; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.schema.CoreSchema; |
| | | import org.forgerock.opendj.ldap.schema.MatchingRule; |
| | | import org.forgerock.opendj.ldap.schema.Schema; |
| | | import org.forgerock.opendj.ldap.spi.IndexQueryFactory; |
| | | import org.forgerock.opendj.ldap.spi.IndexingOptions; |
| | | import org.opends.server.admin.server.ConfigurationChangeListener; |
| | | import org.opends.server.admin.std.meta.CollationMatchingRuleCfgDefn.MatchingRuleType; |
| | | import org.opends.server.admin.std.server.CollationMatchingRuleCfg; |
| | | import org.opends.server.api.AbstractMatchingRule; |
| | | import org.opends.server.api.ExtensibleIndexer; |
| | | import org.opends.server.api.ExtensibleMatchingRule; |
| | | import org.opends.server.api.MatchingRule; |
| | | import org.opends.server.api.MatchingRuleFactory; |
| | | import org.opends.server.api.OrderingMatchingRule; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.types.ConfigChangeResult; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.InitializationException; |
| | | import org.opends.server.util.StaticUtils; |
| | | |
| | | import static org.opends.messages.ConfigMessages.*; |
| | | import static org.opends.messages.CoreMessages.*; |
| | | import static org.opends.messages.SchemaMessages.*; |
| | | import static org.opends.server.schema.SchemaConstants.*; |
| | | import static org.opends.server.util.ServerConstants.*; |
| | | |
| | | /** |
| | | * This class is a factory class for Collation matching rules. It |
| | |
| | | |
| | | private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); |
| | | |
| | | |
| | | // Whether equality matching rules are enabled. |
| | | private boolean equalityMatchingRuleType; |
| | | |
| | | // Whether less-than matching rules are enabled. |
| | | private boolean lessThanMatchingRuleType; |
| | | |
| | | // Whether less-than-equal-to matching rules are enabled. |
| | | private boolean lessThanEqualToMatchingRuleType; |
| | | |
| | | // Whether less-than-equal-to matching rules are enabled. |
| | | private boolean greaterThanMatchingRuleType; |
| | | |
| | | // Whether greater-than matching rules are enabled. |
| | | private boolean greaterThanEqualToMatchingRuleType; |
| | | |
| | | // Whether greater-than-equal-to matching rules are enabled. |
| | | private boolean substringMatchingRuleType; |
| | | |
| | | // Stores the list of available locales on this JVM. |
| | | private static final Set<Locale> supportedLocales = new HashSet<Locale>( |
| | | Arrays.asList(Locale.getAvailableLocales())); |
| | |
| | | new HashMap<String, MatchingRule>(); |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new instance of CollationMatchingRuleFactory. |
| | | */ |
| | |
| | | super(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public final Collection<MatchingRule> getMatchingRules() |
| | | public final Collection<org.forgerock.opendj.ldap.schema.MatchingRule> getMatchingRules() |
| | | { |
| | | return Collections.unmodifiableCollection(matchingRules.values()); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Adds a new mapping of OID and MatchingRule. |
| | | * |
| | |
| | | matchingRules.put(oid, matchingRule); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Returns the Matching rule for the specified OID. |
| | | * |
| | | * @param oid |
| | | * OID of the matching rule to be searched. |
| | | * @return MatchingRule corresponding to an OID. |
| | | */ |
| | | private MatchingRule getMatchingRule(String oid) |
| | | { |
| | | return matchingRules.get(oid); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Clears the Map containing matching Rules. |
| | | */ |
| | |
| | | matchingRules.clear(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Reads the configuration and initializes matching rule types. |
| | | * |
| | | * @param ruleTypes |
| | | * The Set containing allowed matching rule types. |
| | | */ |
| | | private void initializeMatchingRuleTypes(SortedSet<MatchingRuleType> ruleTypes) |
| | | { |
| | | for (MatchingRuleType type : ruleTypes) |
| | | { |
| | | switch (type) |
| | | { |
| | | case EQUALITY: |
| | | equalityMatchingRuleType = true; |
| | | break; |
| | | case LESS_THAN: |
| | | lessThanMatchingRuleType = true; |
| | | break; |
| | | case LESS_THAN_OR_EQUAL_TO: |
| | | lessThanEqualToMatchingRuleType = true; |
| | | break; |
| | | case GREATER_THAN: |
| | | greaterThanMatchingRuleType = true; |
| | | break; |
| | | case GREATER_THAN_OR_EQUAL_TO: |
| | | greaterThanEqualToMatchingRuleType = true; |
| | | break; |
| | | case SUBSTRING: |
| | | substringMatchingRuleType = true; |
| | | break; |
| | | default: |
| | | // No default values allowed. |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new Collator instance. |
| | | * |
| | | * @param locale |
| | | * Locale for the collator |
| | | * @return Returns a new Collator instance |
| | | */ |
| | | private Collator createCollator(Locale locale) |
| | | { |
| | | Collator collator = Collator.getInstance(locale); |
| | | collator.setStrength(Collator.PRIMARY); |
| | | collator.setDecomposition(Collator.FULL_DECOMPOSITION); |
| | | return collator; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | |
| | | public void initializeMatchingRule(CollationMatchingRuleCfg configuration) |
| | | throws ConfigException, InitializationException |
| | | { |
| | | initializeMatchingRuleTypes(configuration.getMatchingRuleType()); |
| | | final Schema coreSchema = CoreSchema.getInstance(); |
| | | for (String collation : configuration.getCollation()) |
| | | { |
| | | CollationMapper mapper = new CollationMapper(collation); |
| | |
| | | Locale locale = getLocale(languageTag); |
| | | if (locale != null) |
| | | { |
| | | createLessThanMatchingRule(mapper, locale); |
| | | createLessThanOrEqualToMatchingRule(mapper, locale); |
| | | createEqualityMatchingRule(mapper, locale); |
| | | createGreaterThanOrEqualToMatchingRule(mapper, locale); |
| | | createGreaterThanMatchingRule(mapper, locale); |
| | | createSubstringMatchingRule(mapper, locale); |
| | | try |
| | | { |
| | | final int[] numericSuffixes = { 1, 2, 3, 4, 5, 6 }; |
| | | for (int suffix : numericSuffixes) |
| | | { |
| | | final String oid = nOID + "." + suffix; |
| | | addMatchingRule(oid, coreSchema.getMatchingRule(oid)); |
| | | } |
| | | // the default (equality) matching rule |
| | | addMatchingRule(nOID, coreSchema.getMatchingRule(nOID)); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.error(LocalizableMessage.raw("Error when adding a collation matching rule with oid %s, tag %s: %s", |
| | | nOID, languageTag, e.getMessage())); |
| | | } |
| | | } |
| | | else |
| | | { |
| | |
| | | // Clear the associated matching rules. |
| | | resetRules(); |
| | | |
| | | initializeMatchingRuleTypes(configuration.getMatchingRuleType()); |
| | | final Schema coreSchema = CoreSchema.getInstance(); |
| | | for (String collation : configuration.getCollation()) |
| | | { |
| | | // validation has already been performed in isConfigurationChangeAcceptable() |
| | | CollationMapper mapper = new CollationMapper(collation); |
| | | String languageTag = mapper.getLanguageTag(); |
| | | Locale locale = getLocale(languageTag); |
| | | createLessThanMatchingRule(mapper, locale); |
| | | createLessThanOrEqualToMatchingRule(mapper, locale); |
| | | createEqualityMatchingRule(mapper, locale); |
| | | createGreaterThanOrEqualToMatchingRule(mapper, locale); |
| | | createGreaterThanMatchingRule(mapper, locale); |
| | | createSubstringMatchingRule(mapper, locale); |
| | | String nOID = mapper.getNumericOID(); |
| | | addMatchingRule(nOID, coreSchema.getMatchingRule(nOID)); |
| | | } |
| | | |
| | | try |
| | |
| | | return new ConfigChangeResult(resultCode, adminActionRequired, messages); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | |
| | | } |
| | | |
| | | |
| | | |
| | | private Collection<String> copyNames(MatchingRule matchingRule) |
| | | { |
| | | Collection<String> defaultNames = new HashSet<String>(); |
| | | if (matchingRule != null) |
| | | { |
| | | defaultNames.addAll(matchingRule.getNames()); |
| | | } |
| | | return defaultNames; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates Less-than Matching Rule. |
| | | * |
| | | * @param mapper |
| | | * CollationMapper containing OID and the language Tag. |
| | | * @param locale |
| | | * Locale value |
| | | */ |
| | | private void createLessThanMatchingRule(CollationMapper mapper, Locale locale) |
| | | { |
| | | if (!lessThanMatchingRuleType) return; |
| | | |
| | | String oid = mapper.getNumericOID() + ".1"; |
| | | String lTag = mapper.getLanguageTag(); |
| | | |
| | | Collection<String> names = copyNames(getMatchingRule(oid)); |
| | | names.add(lTag + ".lt"); |
| | | names.add(lTag + ".1"); |
| | | |
| | | MatchingRule matchingRule = |
| | | new CollationLessThanMatchingRule(oid, names, locale); |
| | | addMatchingRule(oid, matchingRule); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates Less-Than-Equal-To Matching Rule. |
| | | * |
| | | * @param mapper |
| | | * CollationMapper containing OID and the language Tag. |
| | | * @param locale |
| | | * Locale value |
| | | */ |
| | | private void createLessThanOrEqualToMatchingRule( |
| | | CollationMapper mapper, Locale locale) |
| | | { |
| | | if (!lessThanEqualToMatchingRuleType) return; |
| | | |
| | | String oid = mapper.getNumericOID() + ".2"; |
| | | String lTag = mapper.getLanguageTag(); |
| | | |
| | | Collection<String> names = copyNames(getMatchingRule(oid)); |
| | | names.add(lTag + ".lte"); |
| | | names.add(lTag + ".2"); |
| | | |
| | | MatchingRule matchingRule = |
| | | new CollationLessThanOrEqualToMatchingRule(oid, names, locale); |
| | | addMatchingRule(oid, matchingRule); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates Equality Matching Rule. |
| | | * |
| | | * @param mapper |
| | | * CollationMapper containing OID and the language Tag. |
| | | * @param locale |
| | | * Locale value |
| | | */ |
| | | private void createEqualityMatchingRule(CollationMapper mapper, Locale locale) |
| | | { |
| | | if (!equalityMatchingRuleType) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | // Register the default OID as equality matching rule. |
| | | String lTag = mapper.getLanguageTag(); |
| | | String nOID = mapper.getNumericOID(); |
| | | |
| | | Collection<String> defaultNames = copyNames(getMatchingRule(nOID)); |
| | | defaultNames.add(lTag); |
| | | MatchingRule matchingRule = |
| | | new CollationEqualityMatchingRule(nOID, defaultNames, locale); |
| | | addMatchingRule(nOID, matchingRule); |
| | | |
| | | // Register OID.3 as the equality matching rule. |
| | | String OID = mapper.getNumericOID() + ".3"; |
| | | Collection<String> names = copyNames(getMatchingRule(OID)); |
| | | names.add(lTag + ".eq"); |
| | | names.add(lTag + ".3"); |
| | | |
| | | MatchingRule equalityMatchingRule = |
| | | new CollationEqualityMatchingRule(OID, names, locale); |
| | | addMatchingRule(OID, equalityMatchingRule); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates Greater-than-equal-to Matching Rule. |
| | | * |
| | | * @param mapper |
| | | * CollationMapper containing OID and the language Tag. |
| | | * @param locale |
| | | * Locale value |
| | | */ |
| | | private void createGreaterThanOrEqualToMatchingRule( |
| | | CollationMapper mapper, Locale locale) |
| | | { |
| | | if (!greaterThanEqualToMatchingRuleType) return; |
| | | |
| | | String oid = mapper.getNumericOID() + ".4"; |
| | | String lTag = mapper.getLanguageTag(); |
| | | |
| | | Collection<String> names = copyNames(getMatchingRule(oid)); |
| | | names.add(lTag + ".gte"); |
| | | names.add(lTag + ".4"); |
| | | |
| | | MatchingRule matchingRule = |
| | | new CollationGreaterThanOrEqualToMatchingRule(oid, names, locale); |
| | | addMatchingRule(oid, matchingRule); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates Greater-than Matching Rule. |
| | | * |
| | | * @param mapper |
| | | * CollationMapper containing OID and the language Tag. |
| | | * @param locale |
| | | * Locale value |
| | | */ |
| | | private void createGreaterThanMatchingRule(CollationMapper mapper, |
| | | Locale locale) |
| | | { |
| | | if (!greaterThanMatchingRuleType) return; |
| | | |
| | | String oid = mapper.getNumericOID() + ".5"; |
| | | String lTag = mapper.getLanguageTag(); |
| | | |
| | | Collection<String> names = copyNames(getMatchingRule(oid)); |
| | | names.add(lTag + ".gt"); |
| | | names.add(lTag + ".5"); |
| | | |
| | | MatchingRule matchingRule = |
| | | new CollationGreaterThanMatchingRule(oid, names, locale); |
| | | addMatchingRule(oid, matchingRule); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates substring Matching Rule. |
| | | * |
| | | * @param mapper |
| | | * CollationMapper containing OID and the language Tag. |
| | | * @param locale |
| | | * Locale value |
| | | */ |
| | | private void createSubstringMatchingRule(CollationMapper mapper, |
| | | Locale locale) |
| | | { |
| | | if (!substringMatchingRuleType) return; |
| | | |
| | | String oid = mapper.getNumericOID() + ".6"; |
| | | String lTag = mapper.getLanguageTag(); |
| | | |
| | | Collection<String> names = copyNames(getMatchingRule(oid)); |
| | | names.add(lTag + ".sub"); |
| | | names.add(lTag + ".6"); |
| | | |
| | | MatchingRule matchingRule = |
| | | new CollationSubstringMatchingRule(oid, names, locale); |
| | | addMatchingRule(oid, matchingRule); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Verifies if the locale is supported by the JVM. |
| | | * |
| | |
| | | |
| | | |
| | | /** |
| | | * Evaluates and converts 2 consecutive characters of the provided string |
| | | * starting at startPos and converts them into a single escaped char. |
| | | * |
| | | * @param hexString |
| | | * The hexadecimal string containing the escape sequence. |
| | | * @param startPos |
| | | * The starting position of the hexadecimal escape sequence. |
| | | * @return The escaped character |
| | | * @throws DecodeException |
| | | * If the provided string contains invalid hexadecimal digits . |
| | | */ |
| | | private static char hexToEscapedChar(String hexString, int startPos) |
| | | throws DecodeException |
| | | { |
| | | // The two positions must be the hex characters that |
| | | // comprise the escaped value. |
| | | if ((startPos + 1) >= hexString.length()) |
| | | { |
| | | LocalizableMessage message = |
| | | ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE.get(hexString, |
| | | startPos + 1); |
| | | throw DecodeException.error(message); |
| | | } |
| | | byte byteValue = 0; |
| | | switch (hexString.charAt(startPos)) |
| | | { |
| | | case 0x30: // '0' |
| | | break; |
| | | case 0x31: // '1' |
| | | byteValue = (byte) 0x10; |
| | | break; |
| | | case 0x32: // '2' |
| | | byteValue = (byte) 0x20; |
| | | break; |
| | | case 0x33: // '3' |
| | | byteValue = (byte) 0x30; |
| | | break; |
| | | case 0x34: // '4' |
| | | byteValue = (byte) 0x40; |
| | | break; |
| | | case 0x35: // '5' |
| | | byteValue = (byte) 0x50; |
| | | break; |
| | | case 0x36: // '6' |
| | | byteValue = (byte) 0x60; |
| | | break; |
| | | case 0x37: // '7' |
| | | byteValue = (byte) 0x70; |
| | | break; |
| | | case 0x38: // '8' |
| | | byteValue = (byte) 0x80; |
| | | break; |
| | | case 0x39: // '9' |
| | | byteValue = (byte) 0x90; |
| | | break; |
| | | case 0x41: // 'A' |
| | | case 0x61: // 'a' |
| | | byteValue = (byte) 0xA0; |
| | | break; |
| | | case 0x42: // 'B' |
| | | case 0x62: // 'b' |
| | | byteValue = (byte) 0xB0; |
| | | break; |
| | | case 0x43: // 'C' |
| | | case 0x63: // 'c' |
| | | byteValue = (byte) 0xC0; |
| | | break; |
| | | case 0x44: // 'D' |
| | | case 0x64: // 'd' |
| | | byteValue = (byte) 0xD0; |
| | | break; |
| | | case 0x45: // 'E' |
| | | case 0x65: // 'e' |
| | | byteValue = (byte) 0xE0; |
| | | break; |
| | | case 0x46: // 'F' |
| | | case 0x66: // 'f' |
| | | byteValue = (byte) 0xF0; |
| | | break; |
| | | default: |
| | | LocalizableMessage message = |
| | | ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE.get(hexString, startPos); |
| | | throw DecodeException.error(message); |
| | | } |
| | | |
| | | switch (hexString.charAt(++startPos)) |
| | | { |
| | | case 0x30: // '0' |
| | | break; |
| | | case 0x31: // '1' |
| | | byteValue |= (byte) 0x01; |
| | | break; |
| | | case 0x32: // '2' |
| | | byteValue |= (byte) 0x02; |
| | | break; |
| | | case 0x33: // '3' |
| | | byteValue |= (byte) 0x03; |
| | | break; |
| | | case 0x34: // '4' |
| | | byteValue |= (byte) 0x04; |
| | | break; |
| | | case 0x35: // '5' |
| | | byteValue |= (byte) 0x05; |
| | | break; |
| | | case 0x36: // '6' |
| | | byteValue |= (byte) 0x06; |
| | | break; |
| | | case 0x37: // '7' |
| | | byteValue |= (byte) 0x07; |
| | | break; |
| | | case 0x38: // '8' |
| | | byteValue |= (byte) 0x08; |
| | | break; |
| | | case 0x39: // '9' |
| | | byteValue |= (byte) 0x09; |
| | | break; |
| | | case 0x41: // 'A' |
| | | case 0x61: // 'a' |
| | | byteValue |= (byte) 0x0A; |
| | | break; |
| | | case 0x42: // 'B' |
| | | case 0x62: // 'b' |
| | | byteValue |= (byte) 0x0B; |
| | | break; |
| | | case 0x43: // 'C' |
| | | case 0x63: // 'c' |
| | | byteValue |= (byte) 0x0C; |
| | | break; |
| | | case 0x44: // 'D' |
| | | case 0x64: // 'd' |
| | | byteValue |= (byte) 0x0D; |
| | | break; |
| | | case 0x45: // 'E' |
| | | case 0x65: // 'e' |
| | | byteValue |= (byte) 0x0E; |
| | | break; |
| | | case 0x46: // 'F' |
| | | case 0x66: // 'f' |
| | | byteValue |= (byte) 0x0F; |
| | | break; |
| | | default: |
| | | LocalizableMessage message = |
| | | ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE.get(hexString, startPos); |
| | | throw DecodeException.error(message); |
| | | } |
| | | return (char) byteValue; |
| | | } |
| | | |
| | | /** |
| | | * Collation Extensible matching rule. |
| | | */ |
| | | private abstract class CollationMatchingRule |
| | | extends AbstractMatchingRule |
| | | implements ExtensibleMatchingRule |
| | | { |
| | | // Names for this class. |
| | | private final Collection<String> names; |
| | | |
| | | // Collator for performing equality match. |
| | | protected final Collator collator; |
| | | |
| | | // Numeric OID of the rule. |
| | | private final String nOID; |
| | | |
| | | // Locale associated with this rule. |
| | | private final Locale locale; |
| | | |
| | | // Indexer of this rule. |
| | | protected ExtensibleIndexer indexer; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Constructs a new CollationMatchingRule. |
| | | * |
| | | * @param nOID |
| | | * OID of the collation matching rule |
| | | * @param names |
| | | * names of this matching rule |
| | | * @param locale |
| | | * Locale of the collation matching rule |
| | | */ |
| | | private CollationMatchingRule(String nOID, |
| | | Collection<String> names, Locale locale) |
| | | { |
| | | this.names = names; |
| | | this.collator = createCollator(locale); |
| | | this.locale = locale; |
| | | this.nOID = nOID; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public Collection<String> getNames() |
| | | { |
| | | return Collections.unmodifiableCollection(names); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public String getOID() |
| | | { |
| | | return nOID; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public String getDescription() |
| | | { |
| | | // There is no standard description for this matching rule. |
| | | return null; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public String getSyntaxOID() |
| | | { |
| | | return SYNTAX_DIRECTORY_STRING_OID; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Returns the name of the index database for this matching rule. An |
| | | * index name for this rule will be based upon the Locale. This will |
| | | * ensure that multiple collation matching rules corresponding to |
| | | * the same Locale can share the same index database. |
| | | * |
| | | * @return The name of the index for this matching rule. |
| | | */ |
| | | public String getIndexName() |
| | | { |
| | | String language = locale.getLanguage(); |
| | | String country = locale.getCountry(); |
| | | String variant = locale.getVariant(); |
| | | StringBuilder builder = new StringBuilder(language); |
| | | if (country != null && country.length() > 0) |
| | | { |
| | | builder.append("_"); |
| | | builder.append(locale.getCountry()); |
| | | } |
| | | if (variant != null && variant.length() > 0) |
| | | { |
| | | builder.append("_"); |
| | | builder.append(locale.getVariant()); |
| | | } |
| | | return builder.toString(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public Collection<ExtensibleIndexer> getIndexers() |
| | | { |
| | | if (indexer == null) |
| | | { |
| | | // The default implementation contains shared indexer and |
| | | // doesn't use the config. |
| | | indexer = new CollationSharedExtensibleIndexer(this); |
| | | } |
| | | return Collections.singletonList(indexer); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Collation rule for Equality matching rule. |
| | | */ |
| | | private final class CollationEqualityMatchingRule |
| | | extends CollationMatchingRule |
| | | implements OrderingMatchingRule |
| | | { |
| | | |
| | | /** |
| | | * The serial version identifier required to satisfy the compiler because |
| | | * this class implements the <CODE>java.io.Serializable</CODE> interface. |
| | | * This value was generated using the <CODE>serialver</CODE> command-line |
| | | * utility included with the Java SDK. |
| | | */ |
| | | private static final long serialVersionUID = 3990778178484159862L; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Constructs a new CollationEqualityMatchingRule. |
| | | * |
| | | * @param nOID |
| | | * OID of the collation matching rule |
| | | * @param names |
| | | * names of this matching rule |
| | | * @param locale |
| | | * Locale of the collation matching rule |
| | | */ |
| | | private CollationEqualityMatchingRule(String nOID, |
| | | Collection<String> names, Locale locale) |
| | | { |
| | | super(nOID, names, locale); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public ByteString normalizeAttributeValue(ByteSequence value) |
| | | throws DecodeException |
| | | { |
| | | CollationKey key = collator.getCollationKey(value.toString()); |
| | | return ByteString.wrap(key.toByteArray()); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public ConditionResult valuesMatch(ByteSequence attributeValue, |
| | | ByteSequence assertionValue) |
| | | { |
| | | return ConditionResult.valueOf(assertionValue.equals(attributeValue)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public <T> T createIndexQuery(ByteSequence assertionValue, |
| | | IndexQueryFactory<T> factory) throws DecodeException |
| | | { |
| | | // Normalize the assertion value. |
| | | return factory.createExactMatchQuery(indexer |
| | | .getExtensibleIndexID(), normalizeAttributeValue(assertionValue)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public int compare(byte[] arg0, byte[] arg1) |
| | | { |
| | | return StaticUtils.compare(arg0, arg1); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public int compareValues(ByteSequence value1, ByteSequence value2) |
| | | { |
| | | return value1.compareTo(value2); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Collation rule for Substring matching rule. |
| | | */ |
| | | private final class CollationSubstringMatchingRule extends |
| | | CollationMatchingRule |
| | | { |
| | | // Substring Indexer associated with this instance. |
| | | private CollationSubstringExtensibleIndexer subIndexer; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Constructs a new CollationSubstringMatchingRule. |
| | | * |
| | | * @param nOID |
| | | * OID of the collation matching rule |
| | | * @param names |
| | | * names of this matching rule |
| | | * @param locale |
| | | * Locale of the collation matching rule |
| | | */ |
| | | private CollationSubstringMatchingRule(String nOID, |
| | | Collection<String> names, Locale locale) |
| | | { |
| | | super(nOID, names, locale); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public ByteString normalizeAttributeValue(ByteSequence value) |
| | | throws DecodeException |
| | | { |
| | | CollationKey key = collator.getCollationKey(value.toString()); |
| | | return ByteString.wrap(key.toByteArray()); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Utility class which abstracts a substring assertion value. |
| | | */ |
| | | private final class Assertion |
| | | { |
| | | // Initial part of the substring filter. |
| | | private String subInitial; |
| | | |
| | | // any parts of the substring filter. |
| | | private List<String> subAny; |
| | | |
| | | // Final part of the substring filter. |
| | | private String subFinal; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new instance of Assertion. |
| | | * |
| | | * @param subInitial |
| | | * Initial part of the filter. |
| | | * @param subAny |
| | | * Any part of the filter. |
| | | * @param subFinal |
| | | * Final part of the filter. |
| | | */ |
| | | private Assertion(String subInitial, List<String> subAny, |
| | | String subFinal) |
| | | { |
| | | this.subInitial = subInitial; |
| | | this.subAny = subAny; |
| | | this.subFinal = subFinal; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Returns the Initial part of the assertion. |
| | | * |
| | | * @return Initial part of assertion. |
| | | */ |
| | | private String getInitial() |
| | | { |
| | | return subInitial; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Returns the any part of the assertion. |
| | | * |
| | | * @return Any part of the assertion. |
| | | */ |
| | | private List<String> getAny() |
| | | { |
| | | return subAny; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Returns the final part of the assertion. |
| | | * |
| | | * @return Final part of the assertion. |
| | | */ |
| | | private String getFinal() |
| | | { |
| | | return subFinal; |
| | | } |
| | | } |
| | | |
| | | private Assertion parseAssertion(ByteSequence value) throws DecodeException |
| | | { |
| | | // Get a string representation of the value. |
| | | String filterString = value.toString(); |
| | | int endPos = filterString.length(); |
| | | |
| | | // Find the locations of all the asterisks in the value. Also, |
| | | // check to see if there are any escaped values, since they will |
| | | // need special treatment. |
| | | boolean hasEscape = false; |
| | | LinkedList<Integer> asteriskPositions = new LinkedList<Integer>(); |
| | | for (int i = 0; i < endPos; i++) |
| | | { |
| | | if (filterString.charAt(i) == 0x2A) // The asterisk. |
| | | { |
| | | asteriskPositions.add(i); |
| | | } |
| | | else if (filterString.charAt(i) == 0x5C) // The backslash. |
| | | { |
| | | hasEscape = true; |
| | | } |
| | | } |
| | | |
| | | // If there were no asterisks, then this isn't a substring filter. |
| | | if (asteriskPositions.isEmpty()) |
| | | { |
| | | throw DecodeException.error( |
| | | ERR_SEARCH_FILTER_SUBSTRING_NO_ASTERISKS.get(filterString, 0, endPos)); |
| | | } |
| | | |
| | | // If the value starts with an asterisk, then there is no |
| | | // subInitial component. Otherwise, parse out the subInitial. |
| | | String subInitial; |
| | | int firstPos = asteriskPositions.removeFirst(); |
| | | if (firstPos == 0) |
| | | { |
| | | subInitial = null; |
| | | } |
| | | else |
| | | { |
| | | if (hasEscape) |
| | | { |
| | | CharBuffer buffer = CharBuffer.allocate(firstPos); |
| | | for (int i = 0; i < firstPos; i++) |
| | | { |
| | | if (filterString.charAt(i) == 0x5C) |
| | | { |
| | | char escapeValue = hexToEscapedChar(filterString, i + 1); |
| | | i += 2; // Move to the next sequence. |
| | | buffer.put(escapeValue); |
| | | } |
| | | else |
| | | { |
| | | buffer.put(filterString.charAt(i)); |
| | | } |
| | | } |
| | | |
| | | char[] subInitialChars = new char[buffer.position()]; |
| | | buffer.flip(); |
| | | buffer.get(subInitialChars); |
| | | subInitial = new String(subInitialChars); |
| | | } |
| | | else |
| | | { |
| | | subInitial = filterString.substring(0, firstPos); |
| | | } |
| | | } |
| | | |
| | | // Next, process through the rest of the asterisks to get the |
| | | // subAny values. |
| | | List<String> subAny = new ArrayList<String>(); |
| | | for (int asteriskPos : asteriskPositions) |
| | | { |
| | | int length = asteriskPos - firstPos - 1; |
| | | |
| | | if (hasEscape) |
| | | { |
| | | CharBuffer buffer = CharBuffer.allocate(length); |
| | | for (int i = firstPos + 1; i < asteriskPos; i++) |
| | | { |
| | | if (filterString.charAt(i) == 0x5C) |
| | | { |
| | | char escapeValue = hexToEscapedChar(filterString, i + 1); |
| | | i += 2; // Move to the next sequence. |
| | | buffer.put(escapeValue); |
| | | } |
| | | else |
| | | { |
| | | buffer.put(filterString.charAt(i)); |
| | | } |
| | | } |
| | | |
| | | char[] subAnyChars = new char[buffer.position()]; |
| | | buffer.flip(); |
| | | buffer.get(subAnyChars); |
| | | subAny.add(new String(subAnyChars)); |
| | | } |
| | | else |
| | | { |
| | | subAny.add(filterString.substring(firstPos + 1, firstPos |
| | | + length + 1)); |
| | | } |
| | | |
| | | firstPos = asteriskPos; |
| | | } |
| | | |
| | | // Finally, see if there is anything after the last asterisk, |
| | | // which would be the subFinal value. |
| | | String subFinal; |
| | | if (firstPos == (endPos - 1)) |
| | | { |
| | | subFinal = null; |
| | | } |
| | | else |
| | | { |
| | | int length = endPos - firstPos - 1; |
| | | |
| | | if (hasEscape) |
| | | { |
| | | CharBuffer buffer = CharBuffer.allocate(length); |
| | | for (int i = firstPos + 1; i < endPos; i++) |
| | | { |
| | | if (filterString.charAt(i) == 0x5C) |
| | | { |
| | | char escapeValue = hexToEscapedChar(filterString, i + 1); |
| | | i += 2; // Move to the next sequence. |
| | | buffer.put(escapeValue); |
| | | } |
| | | else |
| | | { |
| | | buffer.put(filterString.charAt(i)); |
| | | } |
| | | } |
| | | |
| | | char[] subFinalChars = new char[buffer.position()]; |
| | | buffer.flip(); |
| | | buffer.get(subFinalChars); |
| | | subFinal = new String(subFinalChars); |
| | | } |
| | | else |
| | | { |
| | | subFinal = |
| | | filterString.substring(firstPos + 1, length + firstPos |
| | | + 1); |
| | | } |
| | | } |
| | | |
| | | return new Assertion(subInitial, subAny, subFinal); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public ByteString normalizeAssertionValue(ByteSequence value) |
| | | throws DecodeException |
| | | { |
| | | Assertion assertion = parseAssertion(value); |
| | | String subInitial = assertion.getInitial(); |
| | | |
| | | // Normalize the Values in the following format: |
| | | // initialLength, initial, numberofany, anyLength1, any1, |
| | | // anyLength2, any2, ..., anyLengthn, anyn, finalLength, final |
| | | List<Integer> normalizedList = new ArrayList<Integer>(); |
| | | |
| | | if (subInitial == null) |
| | | { |
| | | normalizedList.add(0); |
| | | } |
| | | else |
| | | { |
| | | addLengthAndBytes(subInitial, normalizedList); |
| | | } |
| | | |
| | | List<String> subAny = assertion.getAny(); |
| | | if (subAny.isEmpty()) |
| | | { |
| | | normalizedList.add(0); |
| | | } |
| | | else |
| | | { |
| | | normalizedList.add(subAny.size()); |
| | | for (String any : subAny) |
| | | { |
| | | addLengthAndBytes(any, normalizedList); |
| | | } |
| | | } |
| | | |
| | | String subFinal = assertion.getFinal(); |
| | | if (subFinal == null) |
| | | { |
| | | normalizedList.add(0); |
| | | } |
| | | else |
| | | { |
| | | addLengthAndBytes(subFinal, normalizedList); |
| | | } |
| | | |
| | | byte[] normalizedBytes = new byte[normalizedList.size()]; |
| | | for (int i = 0; i < normalizedList.size(); i++) |
| | | { |
| | | normalizedBytes[i] = normalizedList.get(i).byteValue(); |
| | | } |
| | | return ByteString.wrap(normalizedBytes); |
| | | } |
| | | |
| | | |
| | | |
| | | private void addLengthAndBytes(String substring, |
| | | List<Integer> normalizedList) |
| | | { |
| | | CollationKey key = collator.getCollationKey(substring); |
| | | byte[] substrBytes = key.toByteArray(); |
| | | |
| | | // Last 4 bytes are 0s with PRIMARY strength. |
| | | int length = substrBytes.length - 4; |
| | | normalizedList.add(length); |
| | | for (int i = 0; i < length; i++) |
| | | { |
| | | normalizedList.add((int) substrBytes[i]); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public ConditionResult valuesMatch(ByteSequence attributeValue, |
| | | ByteSequence assertionValue) |
| | | { // FIXME Code similar to |
| | | // AbstractSubstringMatchingRuleImpl.DefaultSubstringAssertion.matches() |
| | | int valueLength = attributeValue.length() - 4; |
| | | int valuePos = 0; // position in the value bytes array. |
| | | |
| | | // First byte is the length of subInitial. |
| | | int subInitialLength = 0xFF & assertionValue.byteAt(0); |
| | | |
| | | if (subInitialLength != 0) |
| | | { |
| | | if (subInitialLength > valueLength) |
| | | { |
| | | return ConditionResult.FALSE; |
| | | } |
| | | |
| | | for (; valuePos < subInitialLength; valuePos++) |
| | | { |
| | | if (attributeValue.byteAt(valuePos) != assertionValue |
| | | .byteAt(valuePos + 1)) |
| | | { |
| | | return ConditionResult.FALSE; |
| | | } |
| | | } |
| | | } |
| | | |
| | | int assertPos = subInitialLength + 1; |
| | | int anySize = 0xFF & assertionValue.byteAt(assertPos++); |
| | | if (anySize != 0) |
| | | { |
| | | while (anySize-- > 0) |
| | | { |
| | | int anyLength = 0xFF & assertionValue.byteAt(assertPos++); |
| | | int end = valueLength - anyLength; |
| | | boolean match = false; |
| | | |
| | | for (; valuePos <= end; valuePos++) |
| | | { |
| | | if (assertionValue.byteAt(assertPos) == attributeValue |
| | | .byteAt(valuePos)) |
| | | { |
| | | boolean subMatch = true; |
| | | for (int i = 1; i < anyLength; i++) |
| | | { |
| | | if (assertionValue.byteAt(assertPos + i) != attributeValue |
| | | .byteAt(valuePos + i)) |
| | | { |
| | | subMatch = false; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (subMatch) |
| | | { |
| | | match = subMatch; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (match) |
| | | { |
| | | valuePos += anyLength; |
| | | } |
| | | else |
| | | { |
| | | return ConditionResult.FALSE; |
| | | } |
| | | |
| | | assertPos = assertPos + anyLength; |
| | | } |
| | | } |
| | | |
| | | int finalLength = 0xFF & assertionValue.byteAt(assertPos++); |
| | | if (finalLength != 0) |
| | | { |
| | | if ((valueLength - finalLength) < valuePos) |
| | | { |
| | | return ConditionResult.FALSE; |
| | | } |
| | | |
| | | if (finalLength != assertionValue.length() - assertPos) |
| | | { |
| | | // Some issue with the encoding. |
| | | return ConditionResult.FALSE; |
| | | } |
| | | |
| | | valuePos = valueLength - finalLength; |
| | | for (int i = 0; i < finalLength; i++, valuePos++) |
| | | { |
| | | if (assertionValue.byteAt(assertPos + i) != attributeValue |
| | | .byteAt(valuePos)) |
| | | { |
| | | return ConditionResult.FALSE; |
| | | } |
| | | } |
| | | } |
| | | |
| | | return ConditionResult.TRUE; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public final Collection<ExtensibleIndexer> getIndexers() |
| | | { |
| | | List<ExtensibleIndexer> indexers = new ArrayList<ExtensibleIndexer>(); |
| | | if (subIndexer == null) |
| | | { |
| | | subIndexer = new CollationSubstringExtensibleIndexer(this); |
| | | } |
| | | if (indexer == null) |
| | | { |
| | | indexer = new CollationSharedExtensibleIndexer(this); |
| | | } |
| | | |
| | | indexers.add(subIndexer); |
| | | indexers.add(indexer); |
| | | |
| | | return indexers; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Makes a byte array representing a substring index key for one |
| | | * substring of a value. |
| | | * |
| | | * @param value |
| | | * The String containing the value. |
| | | * @param pos |
| | | * The starting position of the substring. |
| | | * @param len |
| | | * The length of the substring. |
| | | * @return A byte string containing a substring key. |
| | | */ |
| | | private ByteString makeSubstringKey(String value, int pos, int len) |
| | | { |
| | | String sub = value.substring(pos, pos + len); |
| | | CollationKey col = collator.getCollationKey(sub); |
| | | byte[] key = col.toByteArray(); |
| | | // truncate the key |
| | | return ByteString.wrap(key).subSequence(0, key.length - 4); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Uses an equality index to retrieve the entry IDs that might |
| | | * contain a given initial substring. |
| | | * |
| | | * @param bytes |
| | | * A normalized initial substring of an attribute value. |
| | | * @return The candidate entry IDs. |
| | | */ |
| | | private <T> T matchInitialSubstring(String value, |
| | | IndexQueryFactory<T> factory) |
| | | { |
| | | // Use the shared equality indexer. |
| | | return createRangeMatchQuery(value, factory, this.indexer); |
| | | } |
| | | |
| | | private <T> T createRangeMatchQuery(String value, |
| | | IndexQueryFactory<T> factory, ExtensibleIndexer indexer) |
| | | { // FIXME Code similar to |
| | | // AbstractSubstringMatchingRuleImpl.DefaultSubstringAssertion.rangeMatch() |
| | | ByteString lower = makeSubstringKey(value, 0, value.length()); |
| | | ByteStringBuilder upper = new ByteStringBuilder(lower); |
| | | for (int i = upper.length() - 1; i >= 0; i--) |
| | | { |
| | | if (upper.byteAt(i) == 0xFF) |
| | | { |
| | | // We have to carry the overflow to the more significant byte. |
| | | upper.setByte(i, (byte) 0); |
| | | } |
| | | else |
| | | { |
| | | // No overflow, we can stop. |
| | | upper.setByte(i, (byte) (upper.byteAt(i) + 1)); |
| | | break; |
| | | } |
| | | } |
| | | // Read the range: lower <= keys < upper. |
| | | return factory.createRangeMatchQuery( |
| | | indexer.getExtensibleIndexID(), lower, upper, true, false); |
| | | } |
| | | |
| | | /** |
| | | * Retrieves the Index Records that might contain a given substring. |
| | | * |
| | | * @param value |
| | | * A String representing the attribute value. |
| | | * @param factory |
| | | * An IndexQueryFactory which issues calls to the backend. |
| | | * @param substrLength |
| | | * The length of the substring. |
| | | * @return The candidate entry IDs. |
| | | */ |
| | | private <T> T matchSubstring(String value, IndexQueryFactory<T> factory) |
| | | { // FIXME Code similar to |
| | | // AbstractSubstringMatchingRuleImpl.DefaultSubstringAssertion.substringMatch() |
| | | int substrLength = factory.getIndexingOptions().substringKeySize(); |
| | | if (value.length() < substrLength) |
| | | { |
| | | return createRangeMatchQuery(value, factory, subIndexer); |
| | | } |
| | | |
| | | List<T> queryList = new ArrayList<T>(); |
| | | Set<ByteString> set = new TreeSet<ByteString>(); |
| | | for (int first = 0, last = substrLength; |
| | | last <= value.length(); |
| | | first++, last++) |
| | | { |
| | | set.add(makeSubstringKey(value, first, substrLength)); |
| | | } |
| | | |
| | | for (ByteString keyBytes : set) |
| | | { |
| | | queryList.add(factory.createExactMatchQuery( |
| | | subIndexer.getExtensibleIndexID(), keyBytes)); |
| | | } |
| | | return factory.createIntersectionQuery(queryList); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public <T> T createIndexQuery(ByteSequence assertionValue, |
| | | IndexQueryFactory<T> factory) throws DecodeException |
| | | { // FIXME Code similar to |
| | | // AbstractSubstringMatchingRuleImpl.DefaultSubstringAssertion.createIndexQuery()? |
| | | Assertion assertion = parseAssertion(assertionValue); |
| | | String subInitial = assertion.getInitial(); |
| | | List<String> subAny = assertion.getAny(); |
| | | String subFinal = assertion.getFinal(); |
| | | List<T> queries = new ArrayList<T>(); |
| | | |
| | | if (subInitial == null && subAny.isEmpty() && subFinal == null) |
| | | { |
| | | // Can happen with a filter like "cn:en.6:=*". |
| | | // Just return an empty record. |
| | | return factory.createMatchAllQuery(); |
| | | } |
| | | List<String> elements = new ArrayList<String>(); |
| | | if (subInitial != null) |
| | | { |
| | | // Always use the shared indexer for initial match. |
| | | queries.add(matchInitialSubstring(subInitial, factory)); |
| | | } |
| | | |
| | | if (subAny != null && subAny.size() > 0) |
| | | { |
| | | elements.addAll(subAny); |
| | | } |
| | | |
| | | if (subFinal != null) |
| | | { |
| | | elements.add(subFinal); |
| | | } |
| | | |
| | | for (String element : elements) |
| | | { |
| | | queries.add(matchSubstring(element, factory)); |
| | | } |
| | | return factory.createIntersectionQuery(queries); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * An abstract Collation rule for Ordering matching rule. |
| | | */ |
| | | private abstract class CollationOrderingMatchingRule |
| | | extends CollationMatchingRule |
| | | implements OrderingMatchingRule |
| | | { |
| | | |
| | | /** |
| | | * The serial version identifier required to satisfy the compiler because |
| | | * this class implements the <CODE>java.io.Serializable</CODE> interface. |
| | | * This value was generated using the <CODE>serialver</CODE> command-line |
| | | * utility included with the Java SDK. |
| | | */ |
| | | private static final long serialVersionUID = 7354051060508436941L; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Constructs a new CollationOrderingMatchingRule. |
| | | * |
| | | * @param nOID |
| | | * OID of the collation matching rule |
| | | * @param names |
| | | * names of this matching rule |
| | | * @param locale |
| | | * Locale of the collation matching rule |
| | | */ |
| | | private CollationOrderingMatchingRule(String nOID, |
| | | Collection<String> names, Locale locale) |
| | | { |
| | | super(nOID, names, locale); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public ByteString normalizeAttributeValue(ByteSequence value) |
| | | throws DecodeException |
| | | { |
| | | CollationKey key = collator.getCollationKey(value.toString()); |
| | | return ByteString.wrap(key.toByteArray()); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public int compare(byte[] arg0, byte[] arg1) |
| | | { |
| | | return StaticUtils.compare(arg0, arg1); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public int compareValues(ByteSequence value1, ByteSequence value2) |
| | | { |
| | | return value1.compareTo(value2); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Collation matching rule for Less-than matching rule. |
| | | */ |
| | | private final class CollationLessThanMatchingRule extends |
| | | CollationOrderingMatchingRule |
| | | { |
| | | /** |
| | | * The serial version identifier required to satisfy the compiler because |
| | | * this class implements the <CODE>java.io.Serializable</CODE> interface. |
| | | * This value was generated using the <CODE>serialver</CODE> command-line |
| | | * utility included with the Java SDK. |
| | | */ |
| | | private static final long serialVersionUID = -7578406829946732713L; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Constructs a new CollationLessThanMatchingRule. |
| | | * |
| | | * @param nOID |
| | | * OID of the collation matching rule |
| | | * @param names |
| | | * names of this matching rule |
| | | * @param locale |
| | | * Locale of the collation matching rule |
| | | */ |
| | | private CollationLessThanMatchingRule(String nOID, |
| | | Collection<String> names, Locale locale) |
| | | { |
| | | super(nOID, names, locale); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public ConditionResult valuesMatch(ByteSequence attributeValue, |
| | | ByteSequence assertionValue) |
| | | { |
| | | int ret = attributeValue.compareTo(assertionValue); |
| | | return ConditionResult.valueOf(ret < 0); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public <T> T createIndexQuery(ByteSequence assertionValue, |
| | | IndexQueryFactory<T> factory) throws DecodeException |
| | | { |
| | | return factory.createRangeMatchQuery(indexer |
| | | .getExtensibleIndexID(), ByteString.empty(), |
| | | normalizeAttributeValue(assertionValue), false, false); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Collation rule for less-than-equal-to matching rule. |
| | | */ |
| | | private final class CollationLessThanOrEqualToMatchingRule extends |
| | | CollationOrderingMatchingRule |
| | | { |
| | | /** |
| | | * The serial version identifier required to satisfy the compiler because |
| | | * this class implements the <CODE>java.io.Serializable</CODE> interface. |
| | | * This value was generated using the <CODE>serialver</CODE> command-line |
| | | * utility included with the Java SDK. |
| | | */ |
| | | private static final long serialVersionUID = 7222067708233629974L; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Constructs a new CollationLessThanOrEqualToMatchingRule. |
| | | * |
| | | * @param nOID |
| | | * OID of the collation matching rule |
| | | * @param names |
| | | * names of this matching rule |
| | | * @param locale |
| | | * Locale of the collation matching rule |
| | | */ |
| | | private CollationLessThanOrEqualToMatchingRule(String nOID, |
| | | Collection<String> names, Locale locale) |
| | | { |
| | | super(nOID, names, locale); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public ConditionResult valuesMatch(ByteSequence attributeValue, |
| | | ByteSequence assertionValue) |
| | | { |
| | | int ret = attributeValue.compareTo(assertionValue); |
| | | return ConditionResult.valueOf(ret <= 0); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public <T> T createIndexQuery(ByteSequence assertionValue, |
| | | IndexQueryFactory<T> factory) throws DecodeException |
| | | { |
| | | // Read the range: lower < keys <= upper. |
| | | return factory.createRangeMatchQuery(indexer |
| | | .getExtensibleIndexID(), ByteString.empty(), |
| | | normalizeAttributeValue(assertionValue), false, true); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Collation rule for greater-than matching rule. |
| | | */ |
| | | private final class CollationGreaterThanMatchingRule extends |
| | | CollationOrderingMatchingRule |
| | | { |
| | | /** |
| | | * The serial version identifier required to satisfy the compiler because |
| | | * this class implements the <CODE>java.io.Serializable</CODE> interface. |
| | | * This value was generated using the <CODE>serialver</CODE> command-line |
| | | * utility included with the Java SDK. |
| | | */ |
| | | private static final long serialVersionUID = 1204368277332957024L; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Constructs a new CollationGreaterThanMatchingRule. |
| | | * |
| | | * @param nOID |
| | | * OID of the collation matching rule |
| | | * @param names |
| | | * names of this matching rule |
| | | * @param locale |
| | | * Locale of the collation matching rule |
| | | */ |
| | | private CollationGreaterThanMatchingRule(String nOID, |
| | | Collection<String> names, Locale locale) |
| | | { |
| | | super(nOID, names, locale); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public ConditionResult valuesMatch(ByteSequence attributeValue, |
| | | ByteSequence assertionValue) |
| | | { |
| | | int ret = attributeValue.compareTo(assertionValue); |
| | | return ConditionResult.valueOf(ret > 0); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public <T> T createIndexQuery(ByteSequence assertionValue, |
| | | IndexQueryFactory<T> factory) throws DecodeException |
| | | { |
| | | return factory.createRangeMatchQuery(indexer |
| | | .getExtensibleIndexID(), normalizeAttributeValue(assertionValue), |
| | | ByteString.empty(), false, false); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Collation rule for greater-than-equal-to matching rule. |
| | | */ |
| | | private final class CollationGreaterThanOrEqualToMatchingRule extends |
| | | CollationOrderingMatchingRule |
| | | { |
| | | /** |
| | | * The serial version identifier required to satisfy the compiler because |
| | | * this class implements the <CODE>java.io.Serializable</CODE> interface. |
| | | * This value was generated using the <CODE>serialver</CODE> command-line |
| | | * utility included with the Java SDK. |
| | | */ |
| | | private static final long serialVersionUID = -5212358378014047933L; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Constructs a new CollationGreaterThanOrEqualToMatchingRule. |
| | | * |
| | | * @param nOID |
| | | * OID of the collation matching rule |
| | | * @param names |
| | | * names of this matching rule |
| | | * @param locale |
| | | * Locale of the collation matching rule |
| | | */ |
| | | private CollationGreaterThanOrEqualToMatchingRule(String nOID, |
| | | Collection<String> names, Locale locale) |
| | | { |
| | | super(nOID, names, locale); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public ConditionResult valuesMatch(ByteSequence attributeValue, |
| | | ByteSequence assertionValue) |
| | | { |
| | | int ret = attributeValue.compareTo(assertionValue); |
| | | return ConditionResult.valueOf(ret >= 0); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public <T> T createIndexQuery(ByteSequence assertionValue, |
| | | IndexQueryFactory<T> factory) throws DecodeException |
| | | { |
| | | // Read the range: lower <= keys < upper. |
| | | return factory.createRangeMatchQuery(indexer |
| | | .getExtensibleIndexID(), normalizeAttributeValue(assertionValue), |
| | | ByteString.empty(), true, false); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Extensible Indexer class for Collation Matching rules which share |
| | | * the same index. This Indexer is shared by Equality and Ordering |
| | | * Collation Matching Rules. |
| | | */ |
| | | private final class CollationSharedExtensibleIndexer extends |
| | | ExtensibleIndexer |
| | | { |
| | | |
| | | /** |
| | | * The Extensible Matching Rule. |
| | | */ |
| | | private final CollationMatchingRule matchingRule; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new instance of CollationSharedExtensibleIndexer. |
| | | * |
| | | * @param matchingRule |
| | | * The Collation Matching Rule. |
| | | */ |
| | | private CollationSharedExtensibleIndexer( |
| | | CollationMatchingRule matchingRule) |
| | | { |
| | | this.matchingRule = matchingRule; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public String getExtensibleIndexID() |
| | | { |
| | | return EXTENSIBLE_INDEXER_ID_SHARED; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public final void createKeys(Schema schema, ByteSequence value, |
| | | IndexingOptions options, Collection<ByteString> keys) |
| | | throws DecodeException |
| | | { |
| | | keys.add(matchingRule.normalizeAttributeValue(value)); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public String getIndexID() |
| | | { |
| | | return matchingRule.getIndexName() + "." + getExtensibleIndexID(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Extensible Indexer class for Collation Substring Matching rules. |
| | | * This Indexer is used by Substring Collation Matching Rules. |
| | | */ |
| | | private final class CollationSubstringExtensibleIndexer extends |
| | | ExtensibleIndexer |
| | | { |
| | | private final CollationSubstringMatchingRule matchingRule; |
| | | |
| | | /** |
| | | * Creates a new instance of CollationSubstringExtensibleIndexer. |
| | | * |
| | | * @param matchingRule |
| | | * The CollationSubstringMatching Rule. |
| | | */ |
| | | private CollationSubstringExtensibleIndexer( |
| | | CollationSubstringMatchingRule matchingRule) |
| | | { |
| | | this.matchingRule = matchingRule; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public void createKeys(Schema schema, ByteSequence value, |
| | | IndexingOptions options, Collection<ByteString> keys) |
| | | { // TODO merge with AbstractSubstringMatchingRuleImpl.SubstringIndexer.createKeys(); |
| | | String normValue = value.toString(); |
| | | int keyLength = options.substringKeySize(); |
| | | for (int i = 0, remain = normValue.length(); remain > 0; i++, remain--) |
| | | { |
| | | int len = Math.min(keyLength, remain); |
| | | keys.add(matchingRule.makeSubstringKey(normValue, i, len)); |
| | | } |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public String getIndexID() |
| | | { |
| | | return matchingRule.getIndexName() + "." + getExtensibleIndexID(); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public String getExtensibleIndexID() |
| | | { |
| | | return EXTENSIBLE_INDEXER_ID_SUBSTRING; |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * A utility class for extracting the OID and Language Tag from the |
| | | * configuration entry. |
| | | */ |