| | |
| | | */ |
| | | abstract class AbstractEqualityMatchingRuleImpl extends AbstractMatchingRuleImpl { |
| | | |
| | | private final Collection<? extends Indexer> indexers = |
| | | Collections.singleton(new DefaultIndexer("equality")); |
| | | private final Collection<? extends Indexer> indexers = Collections.singleton(new DefaultIndexer("equality")); |
| | | |
| | | AbstractEqualityMatchingRuleImpl() { |
| | | // Nothing to do. |
| | |
| | | private final ByteSequence normalizedAssertionValue; |
| | | |
| | | static DefaultAssertion equality(final ByteSequence normalizedAssertionValue) { |
| | | return new DefaultAssertion("equality", normalizedAssertionValue); |
| | | return named("equality", normalizedAssertionValue); |
| | | } |
| | | |
| | | static DefaultAssertion approximate(final ByteSequence normalizedAssertionValue) { |
| | | return new DefaultAssertion("approximate", normalizedAssertionValue); |
| | | return named("approximate", normalizedAssertionValue); |
| | | } |
| | | |
| | | static DefaultAssertion named(final String indexID, final ByteSequence normalizedAssertionValue) { |
| | | return new DefaultAssertion(indexID, normalizedAssertionValue); |
| | | } |
| | | |
| | | private DefaultAssertion(final String indexID, final ByteSequence normalizedAssertionValue) { |
| | |
| | | public String getIndexID() { |
| | | return indexID; |
| | | } |
| | | }; |
| | | } |
| | | |
| | | private static final Assertion UNDEFINED_ASSERTION = new Assertion() { |
| | | @Override |
| | |
| | | */ |
| | | abstract class AbstractOrderingMatchingRuleImpl extends AbstractMatchingRuleImpl { |
| | | |
| | | private final Collection<? extends Indexer> indexers = |
| | | Collections.singleton(new DefaultIndexer("ordering")); |
| | | private final Collection<? extends Indexer> indexers; |
| | | |
| | | private final String indexId; |
| | | |
| | | /** Constructor for default matching rules. */ |
| | | AbstractOrderingMatchingRuleImpl() { |
| | | // Nothing to do. |
| | | this("ordering"); |
| | | } |
| | | |
| | | /** Constructor for non-default matching rules. */ |
| | | AbstractOrderingMatchingRuleImpl(String indexId) { |
| | | this.indexId = indexId; |
| | | this.indexers = Collections.singleton(new DefaultIndexer(indexId)); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | |
| | | |
| | | @Override |
| | | public <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException { |
| | | return factory.createRangeMatchQuery("ordering", ByteString.empty(), normAssertion, false, false); |
| | | return factory.createRangeMatchQuery(indexId, ByteString.empty(), normAssertion, false, false); |
| | | } |
| | | }; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public Assertion getGreaterOrEqualAssertion(final Schema schema, final ByteSequence value) |
| | | throws DecodeException { |
| | | public Assertion getGreaterOrEqualAssertion(final Schema schema, final ByteSequence value) throws DecodeException { |
| | | final ByteString normAssertion = normalizeAttributeValue(schema, value); |
| | | return new Assertion() { |
| | | @Override |
| | |
| | | |
| | | @Override |
| | | public <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException { |
| | | return factory.createRangeMatchQuery("ordering", normAssertion, ByteString.empty(), true, false); |
| | | return factory.createRangeMatchQuery(indexId, normAssertion, ByteString.empty(), true, false); |
| | | } |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * Retrieves the normalized form of the provided assertion value, which is |
| | | * best suited for efficiently performing greater than matching |
| | | * operations on that value. The assertion value is guaranteed to be valid |
| | | * against this matching rule's assertion syntax. |
| | | * |
| | | * @param schema |
| | | * The schema in which this matching rule is defined. |
| | | * @param assertionValue |
| | | * The syntax checked assertion value to be normalized. |
| | | * @return The normalized version of the provided assertion value. |
| | | * @throws DecodeException |
| | | * if an syntax error occurred while parsing the value. |
| | | */ |
| | | public Assertion getGreaterThanAssertion(Schema schema, ByteSequence assertionValue) throws DecodeException { |
| | | final ByteString normAssertion = normalizeAttributeValue(schema, assertionValue); |
| | | return new Assertion() { |
| | | @Override |
| | | public ConditionResult matches(final ByteSequence attributeValue) { |
| | | return ConditionResult.valueOf(attributeValue.compareTo(normAssertion) > 0); |
| | | } |
| | | |
| | | @Override |
| | | public <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException { |
| | | return factory.createRangeMatchQuery(indexId, normAssertion, ByteString.empty(), false, false); |
| | | } |
| | | }; |
| | | } |
| | |
| | | |
| | | @Override |
| | | public <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException { |
| | | return factory.createRangeMatchQuery("ordering", ByteString.empty(), normAssertion, false, true); |
| | | return factory.createRangeMatchQuery(indexId, ByteString.empty(), normAssertion, false, true); |
| | | } |
| | | }; |
| | | } |
| | |
| | | import java.util.List; |
| | | import java.util.TreeSet; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.opendj.ldap.Assertion; |
| | | import org.forgerock.opendj.ldap.ByteSequence; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | |
| | | * <li>normFinal will contain "final"</li> |
| | | * </ul> |
| | | */ |
| | | static final class DefaultSubstringAssertion implements Assertion { |
| | | final class DefaultSubstringAssertion implements Assertion { |
| | | /** Normalized substring for the text before the first '*' character. */ |
| | | private final ByteString normInitial; |
| | | /** Normalized substrings for all text chunks in between '*' characters. */ |
| | |
| | | /** Normalized substring for the text after the last '*' character. */ |
| | | private final ByteString normFinal; |
| | | |
| | | private DefaultSubstringAssertion(final ByteString normInitial, |
| | | final ByteString[] normAnys, final ByteString normFinal) { |
| | | |
| | | private DefaultSubstringAssertion(final ByteString normInitial, final ByteString[] normAnys, |
| | | final ByteString normFinal) { |
| | | this.normInitial = normInitial; |
| | | this.normAnys = normAnys; |
| | | this.normFinal = normFinal; |
| | |
| | | final Collection<T> subqueries = new LinkedList<T>(); |
| | | if (normInitial != null) { |
| | | // relies on the fact that equality indexes are also ordered |
| | | subqueries.add(rangeMatch(factory, "equality", normInitial)); |
| | | subqueries.add(rangeMatch(factory, equalityIndexId, normInitial)); |
| | | } |
| | | if (normAnys != null) { |
| | | for (ByteString normAny : normAnys) { |
| | |
| | | // (possible overlapping with the use of equality index at the start of this method) |
| | | substringMatch(factory, normInitial, subqueries); |
| | | } |
| | | |
| | | if (normInitial == null && (normAnys == null || normAnys.length == 0) && normFinal == null) { |
| | | // Can happen with a filter like "cn:en.6:=*", just return an empty record |
| | | return factory.createMatchAllQuery(); |
| | | } |
| | | return factory.createIntersectionQuery(subqueries); |
| | | } |
| | | |
| | |
| | | // There are two cases, depending on whether the user-provided |
| | | // substring is smaller than the configured index substring length or not. |
| | | if (normSubstring.length() < substrLength) { |
| | | subqueries.add(rangeMatch(factory, "substring", normSubstring)); |
| | | subqueries.add(rangeMatch(factory, substringIndexId, normSubstring)); |
| | | } else { |
| | | // Break the value up into fragments of length equal to the |
| | | // index substring length, and read those keys. |
| | |
| | | } |
| | | |
| | | for (ByteSequence key : substringKeys) { |
| | | subqueries.add(factory.createExactMatchQuery("substring", key)); |
| | | subqueries.add(factory.createExactMatchQuery(substringIndexId, key)); |
| | | } |
| | | } |
| | | } |
| | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public String getIndexID() { |
| | | return "substring"; |
| | | return substringIndexId; |
| | | } |
| | | } |
| | | |
| | | private final Collection<? extends Indexer> indexers = |
| | | Collections.singleton(new SubstringIndexer()); |
| | | private final Collection<? extends Indexer> indexers = Collections.singleton(new SubstringIndexer()); |
| | | |
| | | /** Identifier of the substring index. */ |
| | | private final String substringIndexId; |
| | | |
| | | /** Identifier of the equality index. */ |
| | | private final String equalityIndexId; |
| | | |
| | | /** Constructor for default matching rules. */ |
| | | AbstractSubstringMatchingRuleImpl() { |
| | | // Nothing to do. |
| | | this("substring", "equality"); |
| | | } |
| | | |
| | | /** Constructor for non-default matching rules. */ |
| | | AbstractSubstringMatchingRuleImpl(String substringIndexId, String equalityIndexId) { |
| | | this.substringIndexId = substringIndexId; |
| | | this.equalityIndexId = equalityIndexId; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | |
| | | |
| | | ByteString bytes = evaluateEscapes(reader, escapeChars, false); |
| | | if (bytes.length() > 0) { |
| | | initialString = normalizeSubString(schema, bytes); |
| | | initialString = bytes; |
| | | } |
| | | if (reader.remaining() == 0) { |
| | | throw DecodeException.error(WARN_ATTR_SYNTAX_SUBSTRING_NO_WILDCARDS.get(assertionValue)); |
| | |
| | | if (anyStrings == null) { |
| | | anyStrings = new LinkedList<ByteSequence>(); |
| | | } |
| | | anyStrings.add(normalizeSubString(schema, bytes)); |
| | | anyStrings.add(bytes); |
| | | } else { |
| | | if (bytes.length() > 0) { |
| | | finalString = normalizeSubString(schema, bytes); |
| | | finalString = bytes; |
| | | } |
| | | break; |
| | | } |
| | |
| | | } |
| | | } |
| | | } |
| | | final LocalizableMessage message = ERR_INVALID_ESCAPE_CHAR.get(reader.getString(), c1); |
| | | throw DecodeException.error(message); |
| | | throw DecodeException.error(ERR_INVALID_ESCAPE_CHAR.get(reader.getString(), c1)); |
| | | } |
| | | |
| | | // The two positions must be the hex characters that |
| | | // comprise the escaped value. |
| | | if (reader.remaining() == 0) { |
| | | final LocalizableMessage message = |
| | | ERR_HEX_DECODE_INVALID_LENGTH.get(reader.getString()); |
| | | |
| | | throw DecodeException.error(message); |
| | | throw DecodeException.error(ERR_HEX_DECODE_INVALID_LENGTH.get(reader.getString())); |
| | | } |
| | | |
| | | final char c2 = reader.read(); |
| | |
| | | b |= 0x0F; |
| | | break; |
| | | default: |
| | | final LocalizableMessage message = |
| | | ERR_HEX_DECODE_INVALID_CHARACTER.get(new String(new char[] { c1, c2 }), c1); |
| | | throw DecodeException.error(message); |
| | | throw DecodeException.error(ERR_HEX_DECODE_INVALID_CHARACTER.get(new String(new char[] { c1, c2 }), c1)); |
| | | } |
| | | return (char) b; |
| | | } |
| New file |
| | |
| | | /* |
| | | * CDDL HEADER START |
| | | * |
| | | * The contents of this file are subject to the terms of the |
| | | * Common Development and Distribution License, Version 1.0 only |
| | | * (the "License"). You may not use this file except in compliance |
| | | * with the License. |
| | | * |
| | | * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt |
| | | * or http://forgerock.org/license/CDDLv1.0.html. |
| | | * See the License for the specific language governing permissions |
| | | * and limitations under the License. |
| | | * |
| | | * When distributing Covered Code, include this CDDL HEADER in each |
| | | * file and include the License file at legal-notices/CDDLv1_0.txt. |
| | | * If applicable, add the following below this CDDL HEADER, with the |
| | | * fields enclosed by brackets "[]" replaced with your own identifying |
| | | * information: |
| | | * Portions Copyright [yyyy] [name of copyright owner] |
| | | * |
| | | * CDDL HEADER END |
| | | * |
| | | * |
| | | * Copyright 2014 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.ldap.schema; |
| | | |
| | | import java.text.Collator; |
| | | import java.util.Arrays; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.Locale; |
| | | |
| | | import org.forgerock.i18n.LocalizedIllegalArgumentException; |
| | | import org.forgerock.opendj.ldap.Assertion; |
| | | import org.forgerock.opendj.ldap.ByteSequence; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.DecodeException; |
| | | import org.forgerock.opendj.ldap.spi.Indexer; |
| | | |
| | | /** |
| | | * Implementations of collation matching rules. Each matching rule is created |
| | | * from a locale (eg, "en-US" or "en-GB"). |
| | | * <p> |
| | | * The PRIMARY strength is used for collation, which means that only primary |
| | | * differences are considered significant for comparison. It usually means that |
| | | * spaces, case, and accent are not significant, although it is language dependant. |
| | | * <p> |
| | | * For a given locale, two indexes are used: a shared one (for equality and |
| | | * ordering rules) and a substring one (for substring rule). |
| | | */ |
| | | public final class CollationMatchingRulesImpl { |
| | | |
| | | private static final String INDEX_ID_SHARED = "shared"; |
| | | private static final String INDEX_ID_SUBSTRING = "substring"; |
| | | |
| | | private CollationMatchingRulesImpl() { |
| | | // This class is not instanciable |
| | | } |
| | | |
| | | /** |
| | | * Creates a collation equality matching Rule. |
| | | * |
| | | * @param locale |
| | | * the locale to use for this rule |
| | | * @return the matching rule implementation |
| | | */ |
| | | public static CollationEqualityMatchingRuleImpl equalityMatchingRule(Locale locale) { |
| | | return new CollationEqualityMatchingRuleImpl(locale); |
| | | } |
| | | |
| | | /** |
| | | * Creates a collation substring matching Rule. |
| | | * |
| | | * @param locale |
| | | * the locale to use for this rule |
| | | * @return the matching rule implementation |
| | | */ |
| | | public static CollationSubstringMatchingRuleImpl substringMatchingRule(Locale locale) { |
| | | return new CollationSubstringMatchingRuleImpl(locale); |
| | | } |
| | | |
| | | /** |
| | | * Creates a collation less than matching Rule. |
| | | * |
| | | * @param locale |
| | | * the locale to use for this rule |
| | | * @return the matching rule implementation |
| | | */ |
| | | public static CollationLessThanMatchingRuleImpl lessThanMatchingRule(Locale locale) { |
| | | return new CollationLessThanMatchingRuleImpl(locale); |
| | | } |
| | | |
| | | /** |
| | | * Creates a collation less than or equal matching Rule. |
| | | * |
| | | * @param locale |
| | | * the locale to use for this rule |
| | | * @return the matching rule implementation |
| | | */ |
| | | public static CollationLessThanOrEqualToMatchingRuleImpl lessThanOrEqualMatchingRule(Locale locale) { |
| | | return new CollationLessThanOrEqualToMatchingRuleImpl(locale); |
| | | } |
| | | |
| | | /** |
| | | * Creates a collation greater than matching Rule. |
| | | * |
| | | * @param locale |
| | | * the locale to use for this rule |
| | | * @return the matching rule implementation |
| | | */ |
| | | public static CollationGreaterThanMatchingRuleImpl greaterThanMatchingRule(Locale locale) { |
| | | return new CollationGreaterThanMatchingRuleImpl(locale); |
| | | } |
| | | |
| | | /** |
| | | * Creates a collation greater than or equal matching Rule. |
| | | * |
| | | * @param locale |
| | | * the locale to use for this rule |
| | | * @return the matching rule implementation |
| | | */ |
| | | public static CollationGreaterThanOrEqualToMatchingRuleImpl greaterThanOrEqualToMatchingRule(Locale locale) { |
| | | return new CollationGreaterThanOrEqualToMatchingRuleImpl(locale); |
| | | } |
| | | |
| | | /** |
| | | * Defines the base for collation matching rules. |
| | | */ |
| | | static abstract class AbstractCollationMatchingRuleImpl extends AbstractMatchingRuleImpl { |
| | | |
| | | private final Locale locale; |
| | | final Collator collator; |
| | | final Indexer indexer; |
| | | |
| | | /** |
| | | * Creates the collation matching rule with the provided locale. |
| | | * |
| | | * @param locale |
| | | * Locale associated with this rule. |
| | | */ |
| | | public AbstractCollationMatchingRuleImpl(Locale locale) { |
| | | this.locale = locale; |
| | | this.collator = createCollator(locale); |
| | | this.indexer = new DefaultIndexer(getSharedIndexName()); |
| | | } |
| | | |
| | | private Collator createCollator(Locale locale) { |
| | | Collator collator = Collator.getInstance(locale); |
| | | collator.setStrength(Collator.PRIMARY); |
| | | collator.setDecomposition(Collator.FULL_DECOMPOSITION); |
| | | return collator; |
| | | } |
| | | |
| | | /** |
| | | * Returns the prefix 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 prefix name of the index for this matching rule. |
| | | */ |
| | | String getPrefixIndexName() { |
| | | 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(country); |
| | | } |
| | | if (variant != null && variant.length() > 0) { |
| | | builder.append("_"); |
| | | builder.append(variant); |
| | | } |
| | | return builder.toString(); |
| | | } |
| | | |
| | | String getSharedIndexName() { |
| | | return getPrefixIndexName() + "." + INDEX_ID_SHARED; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public Collection<? extends Indexer> getIndexers() { |
| | | return Collections.singletonList(indexer); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) |
| | | throws DecodeException { |
| | | try { |
| | | final byte[] byteArray = collator.getCollationKey(value.toString()).toByteArray(); |
| | | // Last 4 bytes are 0s when collator strength is set to PRIMARY, so skip them |
| | | return ByteString.wrap(byteArray).subSequence(0, byteArray.length - 4); |
| | | } catch (final LocalizedIllegalArgumentException e) { |
| | | throw DecodeException.error(e.getMessageObject()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Defines the collation equality matching rule. |
| | | */ |
| | | static final class CollationEqualityMatchingRuleImpl extends AbstractCollationMatchingRuleImpl { |
| | | |
| | | /** |
| | | * Creates the matching rule with the provided locale. |
| | | * |
| | | * @param locale |
| | | * Locale associated with this rule. |
| | | */ |
| | | CollationEqualityMatchingRuleImpl(Locale locale) { |
| | | super(locale); |
| | | } |
| | | |
| | | @Override |
| | | public Assertion getAssertion(final Schema schema, final ByteSequence assertionValue) |
| | | throws DecodeException { |
| | | return DefaultAssertion.named(getSharedIndexName(), normalizeAttributeValue(schema, assertionValue)); |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * Defines the collation substring matching rule. |
| | | */ |
| | | static final class CollationSubstringMatchingRuleImpl extends AbstractCollationMatchingRuleImpl { |
| | | |
| | | private final AbstractSubstringMatchingRuleImpl substringMatchingRule; |
| | | private final Indexer subIndexer; |
| | | |
| | | /** |
| | | * Creates the matching rule with the provided locale. |
| | | * |
| | | * @param locale |
| | | * Locale associated with this rule. |
| | | */ |
| | | CollationSubstringMatchingRuleImpl(Locale locale) { |
| | | super(locale); |
| | | substringMatchingRule = new AbstractSubstringMatchingRuleImpl( |
| | | getPrefixIndexName() + "." + INDEX_ID_SUBSTRING, getSharedIndexName()) { |
| | | @Override |
| | | public ByteString normalizeAttributeValue(Schema schema, ByteSequence value) |
| | | throws DecodeException { |
| | | return CollationSubstringMatchingRuleImpl.this.normalizeAttributeValue(schema, value); |
| | | } |
| | | }; |
| | | // default substring matching rule has exactly one indexer |
| | | subIndexer = substringMatchingRule.getIndexers().iterator().next(); |
| | | } |
| | | |
| | | @Override |
| | | public Assertion getAssertion(final Schema schema, final ByteSequence assertionValue) throws DecodeException { |
| | | return substringMatchingRule.getAssertion(schema, assertionValue); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public Assertion getSubstringAssertion(Schema schema, ByteSequence subInitial, |
| | | List<? extends ByteSequence> subAnyElements, ByteSequence subFinal) throws DecodeException { |
| | | return substringMatchingRule.getSubstringAssertion(schema, subInitial, subAnyElements, subFinal); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public final Collection<? extends Indexer> getIndexers() { |
| | | return Arrays.asList(subIndexer, indexer); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Defines the collation ordering matching rule. |
| | | */ |
| | | static abstract class CollationOrderingMatchingRuleImpl extends AbstractCollationMatchingRuleImpl { |
| | | |
| | | final AbstractOrderingMatchingRuleImpl orderingMatchingRule; |
| | | |
| | | /** |
| | | * Creates the matching rule with the provided locale. |
| | | * |
| | | * @param locale |
| | | * Locale associated with this rule. |
| | | */ |
| | | CollationOrderingMatchingRuleImpl(Locale locale) { |
| | | super(locale); |
| | | |
| | | orderingMatchingRule = new AbstractOrderingMatchingRuleImpl(getSharedIndexName()) { |
| | | @Override |
| | | public ByteString normalizeAttributeValue(Schema schema, ByteSequence value) throws DecodeException { |
| | | return CollationOrderingMatchingRuleImpl.this.normalizeAttributeValue(schema, value); |
| | | } |
| | | }; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Defines the collation less than matching rule. |
| | | */ |
| | | static final class CollationLessThanMatchingRuleImpl extends CollationOrderingMatchingRuleImpl { |
| | | |
| | | CollationLessThanMatchingRuleImpl(Locale locale) { |
| | | super(locale); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public Assertion getAssertion(Schema schema, ByteSequence assertionValue) throws DecodeException { |
| | | return orderingMatchingRule.getAssertion(schema, assertionValue); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Defines the collation less than or equal matching rule. |
| | | */ |
| | | static final class CollationLessThanOrEqualToMatchingRuleImpl extends CollationOrderingMatchingRuleImpl { |
| | | |
| | | CollationLessThanOrEqualToMatchingRuleImpl(Locale locale) { |
| | | super(locale); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public Assertion getAssertion(Schema schema, ByteSequence assertionValue) throws DecodeException { |
| | | return orderingMatchingRule.getLessOrEqualAssertion(schema, assertionValue); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Defines the collation greater than matching rule. |
| | | */ |
| | | static final class CollationGreaterThanMatchingRuleImpl extends CollationOrderingMatchingRuleImpl { |
| | | |
| | | CollationGreaterThanMatchingRuleImpl(Locale locale) { |
| | | super(locale); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public Assertion getAssertion(Schema schema, ByteSequence assertionValue) throws DecodeException { |
| | | return orderingMatchingRule.getGreaterThanAssertion(schema, assertionValue); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Defines the collation greater than or equal matching rule. |
| | | */ |
| | | static final class CollationGreaterThanOrEqualToMatchingRuleImpl extends CollationOrderingMatchingRuleImpl { |
| | | |
| | | CollationGreaterThanOrEqualToMatchingRuleImpl(Locale locale) { |
| | | super(locale); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public Assertion getAssertion(Schema schema, ByteSequence assertionValue) throws DecodeException { |
| | | return orderingMatchingRule.getGreaterOrEqualAssertion(schema, assertionValue); |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | } |
| | | |
| | | private static class FakeIndexQueryFactory implements IndexQueryFactory<String> { |
| | | static class FakeIndexQueryFactory implements IndexQueryFactory<String> { |
| | | |
| | | private final IndexingOptions options; |
| | | private final boolean normalizedValuesAreReadable; |
| | | |
| | | public FakeIndexQueryFactory(IndexingOptions options) { |
| | | this(options, true); |
| | | } |
| | | |
| | | public FakeIndexQueryFactory(IndexingOptions options, boolean normalizedValuesAreReadable) { |
| | | this.options = options; |
| | | this.normalizedValuesAreReadable = normalizedValuesAreReadable; |
| | | } |
| | | |
| | | @Override |
| | | public String createExactMatchQuery(String indexID, ByteSequence key) { |
| | | return "exactMatch(" + indexID + ", value=='" + key + "')"; |
| | | String keyValue = normalizedValuesAreReadable ? key.toString() : key.toByteString().toHexString(); |
| | | return "exactMatch(" + indexID + ", value=='" + keyValue + "')"; |
| | | } |
| | | |
| | | @Override |
| | |
| | | } |
| | | |
| | | @Override |
| | | public String createRangeMatchQuery(String indexID, ByteSequence lower, |
| | | ByteSequence upper, boolean lowerIncluded, boolean upperIncluded) { |
| | | public String createRangeMatchQuery(String indexID, ByteSequence lower, ByteSequence upper, |
| | | boolean lowerIncluded, boolean upperIncluded) { |
| | | final StringBuilder sb = new StringBuilder("rangeMatch"); |
| | | sb.append("("); |
| | | sb.append(indexID); |
| | | sb.append(", '"); |
| | | sb.append(lower); |
| | | if (normalizedValuesAreReadable) { |
| | | sb.append(lower); |
| | | } else if (!lower.isEmpty()) { |
| | | sb.append(lower.toByteString().toHexString()); |
| | | } |
| | | sb.append("' <"); |
| | | if (lowerIncluded) { |
| | | sb.append("="); |
| | |
| | | sb.append("="); |
| | | } |
| | | sb.append(" '"); |
| | | sb.append(upper); |
| | | if (normalizedValuesAreReadable) { |
| | | sb.append(upper); |
| | | } else if (!upper.isEmpty()) { |
| | | sb.append(upper.toByteString().toHexString()); |
| | | } |
| | | sb.append("')"); |
| | | return sb.toString(); |
| | | } |
| | |
| | | return new FakeSubstringMatchingRuleImpl(); |
| | | } |
| | | |
| | | private IndexingOptions newIndexingOptions() { |
| | | static IndexingOptions newIndexingOptions() { |
| | | final IndexingOptions options = mock(IndexingOptions.class); |
| | | when(options.substringKeySize()).thenReturn(3); |
| | | return options; |
| New file |
| | |
| | | /* |
| | | * CDDL HEADER START |
| | | * |
| | | * The contents of this file are subject to the terms of the |
| | | * Common Development and Distribution License, Version 1.0 only |
| | | * (the "License"). You may not use this file except in compliance |
| | | * with the License. |
| | | * |
| | | * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt |
| | | * or http://forgerock.org/license/CDDLv1.0.html. |
| | | * See the License for the specific language governing permissions |
| | | * and limitations under the License. |
| | | * |
| | | * When distributing Covered Code, include this CDDL HEADER in each |
| | | * file and include the License file at legal-notices/CDDLv1_0.txt. |
| | | * If applicable, add the following below this CDDL HEADER, with the |
| | | * fields enclosed by brackets "[]" replaced with your own identifying |
| | | * information: |
| | | * Portions Copyright [yyyy] [name of copyright owner] |
| | | * |
| | | * CDDL HEADER END |
| | | * |
| | | * |
| | | * Copyright 2014 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.ldap.schema; |
| | | |
| | | import java.util.Locale; |
| | | |
| | | import org.forgerock.opendj.ldap.Assertion; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.ConditionResult; |
| | | import org.forgerock.opendj.ldap.schema.AbstractSubstringMatchingRuleImplTest.FakeIndexQueryFactory; |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.forgerock.opendj.ldap.schema.AbstractSubstringMatchingRuleImplTest.*; |
| | | import static org.testng.Assert.*; |
| | | |
| | | @SuppressWarnings("javadoc") |
| | | @Test |
| | | public class CollationEqualityMatchingRuleTest extends MatchingRuleTest { |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | @DataProvider(name = "matchingRuleInvalidAttributeValues") |
| | | public Object[][] createMatchingRuleInvalidAttributeValues() { |
| | | return new Object[][] { }; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | @DataProvider(name = "matchingrules") |
| | | public Object[][] createMatchingRuleTest() { |
| | | return new Object[][] { |
| | | // \u00E9 = LATIN SMALL LETTER E WITH ACUTE |
| | | // \u00C9 = LATIN CAPITAL LETTER E WITH ACUTE |
| | | { "passe", "passe", ConditionResult.TRUE }, |
| | | { "passe ", "passe", ConditionResult.TRUE }, |
| | | { "passE", "passe", ConditionResult.TRUE }, |
| | | { "pass\u00E9", "passe", ConditionResult.TRUE }, |
| | | { "pass\u00E9", "passE", ConditionResult.TRUE }, |
| | | { "pass\u00E9", "pass\u00C9", ConditionResult.TRUE }, |
| | | { "passe", "pass\u00E9", ConditionResult.TRUE }, |
| | | { "passE", "pass\u00E9", ConditionResult.TRUE }, |
| | | { "pass\u00C9", "pass\u00E9", ConditionResult.TRUE }, |
| | | { "abc", "abd", ConditionResult.FALSE }, |
| | | { "abc", "acc", ConditionResult.FALSE }, |
| | | { "abc", "bbc", ConditionResult.FALSE }, |
| | | { "abc", "abD", ConditionResult.FALSE }, |
| | | { "def", "d\u00E9g", ConditionResult.FALSE }, |
| | | { "def", "dEg", ConditionResult.FALSE }, |
| | | { "dEf", "deg", ConditionResult.FALSE }, |
| | | { "d\u00E9f", "dEg", ConditionResult.FALSE }, |
| | | { "abd", "abc", ConditionResult.FALSE }, |
| | | { "acc", "abc", ConditionResult.FALSE }, |
| | | { "bbc", "abc", ConditionResult.FALSE }, |
| | | { "abD", "abc", ConditionResult.FALSE }, |
| | | { "d\u00E9g", "def", ConditionResult.FALSE }, |
| | | { "dEg", "def", ConditionResult.FALSE }, |
| | | { "deg", "dEf", ConditionResult.FALSE }, |
| | | { "dEg", "d\u00E9f", ConditionResult.FALSE }, |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | protected MatchingRule getRule() { |
| | | // Note that oid and names are not used by the test (ie, they could be any value, test should pass anyway) |
| | | // Only the implementation class and the provided locale are really tested here. |
| | | String oid = "1.3.6.1.4.1.42.2.27.9.4.76.1.3"; |
| | | Schema schema = new SchemaBuilder(Schema.getCoreSchema()). |
| | | buildMatchingRule(oid). |
| | | syntaxOID(SchemaConstants.SYNTAX_DIRECTORY_STRING_OID). |
| | | names("fr.eq"). |
| | | implementation(CollationMatchingRulesImpl.equalityMatchingRule(new Locale("fr"))). |
| | | addToSchema(). |
| | | toSchema(); |
| | | return schema.getMatchingRule(oid); |
| | | } |
| | | |
| | | @Test |
| | | public void testCreateIndexQuery() throws Exception { |
| | | ByteString value = ByteString.valueOf("abc"); |
| | | MatchingRule matchingRule = getRule(); |
| | | Assertion assertion = matchingRule.getAssertion(value); |
| | | |
| | | String indexQuery = assertion.createIndexQuery(new FakeIndexQueryFactory(newIndexingOptions(), false)); |
| | | |
| | | ByteString normalizedValue = matchingRule.normalizeAttributeValue(value); |
| | | assertEquals(indexQuery, "exactMatch(fr.shared, value=='" + normalizedValue.toHexString() + "')"); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | /* |
| | | * CDDL HEADER START |
| | | * |
| | | * The contents of this file are subject to the terms of the |
| | | * Common Development and Distribution License, Version 1.0 only |
| | | * (the "License"). You may not use this file except in compliance |
| | | * with the License. |
| | | * |
| | | * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt |
| | | * or http://forgerock.org/license/CDDLv1.0.html. |
| | | * See the License for the specific language governing permissions |
| | | * and limitations under the License. |
| | | * |
| | | * When distributing Covered Code, include this CDDL HEADER in each |
| | | * file and include the License file at legal-notices/CDDLv1_0.txt. |
| | | * If applicable, add the following below this CDDL HEADER, with the |
| | | * fields enclosed by brackets "[]" replaced with your own identifying |
| | | * information: |
| | | * Portions Copyright [yyyy] [name of copyright owner] |
| | | * |
| | | * CDDL HEADER END |
| | | * |
| | | * |
| | | * Copyright 2014 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.ldap.schema; |
| | | |
| | | import java.util.Locale; |
| | | |
| | | import org.forgerock.opendj.ldap.Assertion; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.ConditionResult; |
| | | import org.forgerock.opendj.ldap.schema.AbstractSubstringMatchingRuleImplTest.FakeIndexQueryFactory; |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.forgerock.opendj.ldap.schema.AbstractSubstringMatchingRuleImplTest.*; |
| | | import static org.testng.Assert.*; |
| | | |
| | | @SuppressWarnings("javadoc") |
| | | @Test |
| | | public class CollationGreaterThanMatchingRuleTest extends MatchingRuleTest { |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | @DataProvider(name = "matchingRuleInvalidAttributeValues") |
| | | public Object[][] createMatchingRuleInvalidAttributeValues() { |
| | | return new Object[][] { }; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | @DataProvider(name = "matchingrules") |
| | | public Object[][] createMatchingRuleTest() { |
| | | return new Object[][] { |
| | | // \u00E9 = LATIN SMALL LETTER E WITH ACUTE |
| | | // \u00C9 = LATIN CAPITAL LETTER E WITH ACUTE |
| | | { "abc", "abd", ConditionResult.FALSE }, |
| | | { "abc", "acc", ConditionResult.FALSE }, |
| | | { "abc", "bbc", ConditionResult.FALSE }, |
| | | { "abc", "abD", ConditionResult.FALSE }, |
| | | { "def", "d\u00E9g", ConditionResult.FALSE }, |
| | | { "def", "dEg", ConditionResult.FALSE }, |
| | | { "dEf", "deg", ConditionResult.FALSE }, |
| | | { "d\u00E9f", "dEg", ConditionResult.FALSE }, |
| | | { "passe", "passe", ConditionResult.FALSE }, |
| | | { "passe ", "passe", ConditionResult.FALSE }, |
| | | { "passE", "passe", ConditionResult.FALSE }, |
| | | { "pass\u00E9", "passe", ConditionResult.FALSE }, |
| | | { "pass\u00E9", "passE", ConditionResult.FALSE }, |
| | | { "pass\u00E9", "pass\u00C9", ConditionResult.FALSE }, |
| | | { "passe", "pass\u00E9", ConditionResult.FALSE }, |
| | | { "passE", "pass\u00E9", ConditionResult.FALSE }, |
| | | { "pass\u00C9", "pass\u00E9", ConditionResult.FALSE }, |
| | | { "abd", "abc", ConditionResult.TRUE }, |
| | | { "acc", "abc", ConditionResult.TRUE }, |
| | | { "bbc", "abc", ConditionResult.TRUE }, |
| | | { "abD", "abc", ConditionResult.TRUE }, |
| | | { "d\u00E9g", "def", ConditionResult.TRUE }, |
| | | { "dEg", "def", ConditionResult.TRUE }, |
| | | { "deg", "dEf", ConditionResult.TRUE }, |
| | | { "dEg", "d\u00E9f", ConditionResult.TRUE }, |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | protected MatchingRule getRule() { |
| | | // Note that oid and names are not used by the test (ie, they could be any value, test should pass anyway) |
| | | // Only the implementation class and the provided locale are really tested here. |
| | | String oid = "1.3.6.1.4.1.42.2.27.9.4.76.1.5"; |
| | | Schema schema = new SchemaBuilder(Schema.getCoreSchema()). |
| | | buildMatchingRule(oid). |
| | | syntaxOID(SchemaConstants.SYNTAX_DIRECTORY_STRING_OID). |
| | | names("fr.gt"). |
| | | implementation(CollationMatchingRulesImpl.greaterThanMatchingRule(new Locale("fr"))). |
| | | addToSchema(). |
| | | toSchema(); |
| | | return schema.getMatchingRule(oid); |
| | | } |
| | | |
| | | @Test |
| | | public void testCreateIndexQuery() throws Exception { |
| | | ByteString value = ByteString.valueOf("abc"); |
| | | MatchingRule matchingRule = getRule(); |
| | | Assertion assertion = matchingRule.getAssertion(value); |
| | | |
| | | String indexQuery = assertion.createIndexQuery(new FakeIndexQueryFactory(newIndexingOptions(), false)); |
| | | |
| | | ByteString normalizedValue = matchingRule.normalizeAttributeValue(value); |
| | | assertEquals(indexQuery, "rangeMatch(fr.shared, '" + normalizedValue.toHexString() + "' < value < '')"); |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * CDDL HEADER START |
| | | * |
| | | * The contents of this file are subject to the terms of the |
| | | * Common Development and Distribution License, Version 1.0 only |
| | | * (the "License"). You may not use this file except in compliance |
| | | * with the License. |
| | | * |
| | | * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt |
| | | * or http://forgerock.org/license/CDDLv1.0.html. |
| | | * See the License for the specific language governing permissions |
| | | * and limitations under the License. |
| | | * |
| | | * When distributing Covered Code, include this CDDL HEADER in each |
| | | * file and include the License file at legal-notices/CDDLv1_0.txt. |
| | | * If applicable, add the following below this CDDL HEADER, with the |
| | | * fields enclosed by brackets "[]" replaced with your own identifying |
| | | * information: |
| | | * Portions Copyright [yyyy] [name of copyright owner] |
| | | * |
| | | * CDDL HEADER END |
| | | * |
| | | * |
| | | * Copyright 2014 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.ldap.schema; |
| | | |
| | | import java.util.Locale; |
| | | |
| | | import org.forgerock.opendj.ldap.Assertion; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.ConditionResult; |
| | | import org.forgerock.opendj.ldap.schema.AbstractSubstringMatchingRuleImplTest.FakeIndexQueryFactory; |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.forgerock.opendj.ldap.schema.AbstractSubstringMatchingRuleImplTest.*; |
| | | import static org.testng.Assert.*; |
| | | |
| | | @SuppressWarnings("javadoc") |
| | | @Test |
| | | public class CollationGreaterThanOrEqualMatchingRuleTest extends MatchingRuleTest { |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | @DataProvider(name = "matchingRuleInvalidAttributeValues") |
| | | public Object[][] createMatchingRuleInvalidAttributeValues() { |
| | | return new Object[][] { }; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | @DataProvider(name = "matchingrules") |
| | | public Object[][] createMatchingRuleTest() { |
| | | return new Object[][] { |
| | | // \u00E9 = LATIN SMALL LETTER E WITH ACUTE |
| | | // \u00C9 = LATIN CAPITAL LETTER E WITH ACUTE |
| | | { "abc", "abd", ConditionResult.FALSE }, |
| | | { "abc", "acc", ConditionResult.FALSE }, |
| | | { "abc", "bbc", ConditionResult.FALSE }, |
| | | { "abc", "abD", ConditionResult.FALSE }, |
| | | { "def", "d\u00E9g", ConditionResult.FALSE }, |
| | | { "def", "dEg", ConditionResult.FALSE }, |
| | | { "dEf", "deg", ConditionResult.FALSE }, |
| | | { "d\u00E9f", "dEg", ConditionResult.FALSE }, |
| | | { "passe", "passe", ConditionResult.TRUE }, |
| | | { "passe ", "passe", ConditionResult.TRUE }, |
| | | { "passE", "passe", ConditionResult.TRUE }, |
| | | { "pass\u00E9", "passe", ConditionResult.TRUE }, |
| | | { "pass\u00E9", "passE", ConditionResult.TRUE }, |
| | | { "pass\u00E9", "pass\u00C9", ConditionResult.TRUE }, |
| | | { "passe", "pass\u00E9", ConditionResult.TRUE }, |
| | | { "passE", "pass\u00E9", ConditionResult.TRUE }, |
| | | { "pass\u00C9", "pass\u00E9", ConditionResult.TRUE }, |
| | | { "abd", "abc", ConditionResult.TRUE }, |
| | | { "acc", "abc", ConditionResult.TRUE }, |
| | | { "bbc", "abc", ConditionResult.TRUE }, |
| | | { "abD", "abc", ConditionResult.TRUE }, |
| | | { "d\u00E9g", "def", ConditionResult.TRUE }, |
| | | { "dEg", "def", ConditionResult.TRUE }, |
| | | { "deg", "dEf", ConditionResult.TRUE }, |
| | | { "dEg", "d\u00E9f", ConditionResult.TRUE }, |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | protected MatchingRule getRule() { |
| | | // Note that oid and names are not used by the test (ie, they could be any value, test should pass anyway) |
| | | // Only the implementation class and the provided locale are really tested here. |
| | | String oid = "1.3.6.1.4.1.42.2.27.9.4.76.1.4"; |
| | | Schema schema = new SchemaBuilder(Schema.getCoreSchema()). |
| | | buildMatchingRule(oid). |
| | | syntaxOID(SchemaConstants.SYNTAX_DIRECTORY_STRING_OID). |
| | | names("fr.gt2"). |
| | | implementation(CollationMatchingRulesImpl.greaterThanOrEqualToMatchingRule(new Locale("fr"))). |
| | | addToSchema(). |
| | | toSchema(); |
| | | return schema.getMatchingRule(oid); |
| | | } |
| | | |
| | | @Test |
| | | public void testCreateIndexQuery() throws Exception { |
| | | ByteString value = ByteString.valueOf("abc"); |
| | | MatchingRule matchingRule = getRule(); |
| | | Assertion assertion = matchingRule.getAssertion(value); |
| | | |
| | | String indexQuery = assertion.createIndexQuery(new FakeIndexQueryFactory(newIndexingOptions(), false)); |
| | | |
| | | ByteString normalizedValue = matchingRule.normalizeAttributeValue(value); |
| | | assertEquals(indexQuery, "rangeMatch(fr.shared, '" + normalizedValue.toHexString() + "' <= value < '')"); |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * CDDL HEADER START |
| | | * |
| | | * The contents of this file are subject to the terms of the |
| | | * Common Development and Distribution License, Version 1.0 only |
| | | * (the "License"). You may not use this file except in compliance |
| | | * with the License. |
| | | * |
| | | * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt |
| | | * or http://forgerock.org/license/CDDLv1.0.html. |
| | | * See the License for the specific language governing permissions |
| | | * and limitations under the License. |
| | | * |
| | | * When distributing Covered Code, include this CDDL HEADER in each |
| | | * file and include the License file at legal-notices/CDDLv1_0.txt. |
| | | * If applicable, add the following below this CDDL HEADER, with the |
| | | * fields enclosed by brackets "[]" replaced with your own identifying |
| | | * information: |
| | | * Portions Copyright [yyyy] [name of copyright owner] |
| | | * |
| | | * CDDL HEADER END |
| | | * |
| | | * |
| | | * Copyright 2014 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.ldap.schema; |
| | | |
| | | import java.util.Locale; |
| | | |
| | | import org.forgerock.opendj.ldap.Assertion; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.ConditionResult; |
| | | import org.forgerock.opendj.ldap.schema.AbstractSubstringMatchingRuleImplTest.FakeIndexQueryFactory; |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.forgerock.opendj.ldap.schema.AbstractSubstringMatchingRuleImplTest.*; |
| | | import static org.testng.Assert.*; |
| | | |
| | | @SuppressWarnings("javadoc") |
| | | @Test |
| | | public class CollationLessThanMatchingRuleTest extends MatchingRuleTest { |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | @DataProvider(name = "matchingRuleInvalidAttributeValues") |
| | | public Object[][] createMatchingRuleInvalidAttributeValues() { |
| | | return new Object[][] { }; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | @DataProvider(name = "matchingrules") |
| | | public Object[][] createMatchingRuleTest() { |
| | | return new Object[][] { |
| | | // \u00E9 = LATIN SMALL LETTER E WITH ACUTE |
| | | // \u00C9 = LATIN CAPITAL LETTER E WITH ACUTE |
| | | { "abc", "abd", ConditionResult.TRUE }, |
| | | { "abc", "acc", ConditionResult.TRUE }, |
| | | { "abc", "bbc", ConditionResult.TRUE }, |
| | | { "abc", "abD", ConditionResult.TRUE }, |
| | | { "def", "d\u00E9g", ConditionResult.TRUE }, |
| | | { "def", "dEg", ConditionResult.TRUE }, |
| | | { "dEf", "deg", ConditionResult.TRUE }, |
| | | { "d\u00E9f", "dEg", ConditionResult.TRUE }, |
| | | { "passe", "passe", ConditionResult.FALSE }, |
| | | { "passe ", "passe", ConditionResult.FALSE }, |
| | | { "passE", "passe", ConditionResult.FALSE }, |
| | | { "pass\u00E9", "passe", ConditionResult.FALSE }, |
| | | { "pass\u00E9", "passE", ConditionResult.FALSE }, |
| | | { "pass\u00E9", "pass\u00C9", ConditionResult.FALSE }, |
| | | { "passe", "pass\u00E9", ConditionResult.FALSE }, |
| | | { "passE", "pass\u00E9", ConditionResult.FALSE }, |
| | | { "pass\u00C9", "pass\u00E9", ConditionResult.FALSE }, |
| | | { "abd", "abc", ConditionResult.FALSE }, |
| | | { "acc", "abc", ConditionResult.FALSE }, |
| | | { "bbc", "abc", ConditionResult.FALSE }, |
| | | { "abD", "abc", ConditionResult.FALSE }, |
| | | { "d\u00E9g", "def", ConditionResult.FALSE }, |
| | | { "dEg", "def", ConditionResult.FALSE }, |
| | | { "deg", "dEf", ConditionResult.FALSE }, |
| | | { "dEg", "d\u00E9f", ConditionResult.FALSE }, |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | protected MatchingRule getRule() { |
| | | // Note that oid and names are not used by the test (ie, they could be any value, test should pass anyway) |
| | | // Only the implementation class and the provided locale are really tested here. |
| | | String oid = "1.3.6.1.4.1.42.2.27.9.4.76.1.1"; |
| | | Schema schema = new SchemaBuilder(Schema.getCoreSchema()). |
| | | buildMatchingRule(oid). |
| | | syntaxOID(SchemaConstants.SYNTAX_DIRECTORY_STRING_OID). |
| | | names("fr.lt"). |
| | | implementation(CollationMatchingRulesImpl.lessThanMatchingRule(new Locale("fr"))). |
| | | addToSchema(). |
| | | toSchema(); |
| | | return schema.getMatchingRule(oid); |
| | | } |
| | | |
| | | @Test |
| | | public void testCreateIndexQuery() throws Exception { |
| | | ByteString value = ByteString.valueOf("abc"); |
| | | MatchingRule matchingRule = getRule(); |
| | | Assertion assertion = matchingRule.getAssertion(value); |
| | | |
| | | String indexQuery = assertion.createIndexQuery(new FakeIndexQueryFactory(newIndexingOptions(), false)); |
| | | |
| | | ByteString normalizedValue = matchingRule.normalizeAttributeValue(value); |
| | | assertEquals(indexQuery, "rangeMatch(fr.shared, '' < value < '" + normalizedValue.toHexString() + "')"); |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * CDDL HEADER START |
| | | * |
| | | * The contents of this file are subject to the terms of the |
| | | * Common Development and Distribution License, Version 1.0 only |
| | | * (the "License"). You may not use this file except in compliance |
| | | * with the License. |
| | | * |
| | | * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt |
| | | * or http://forgerock.org/license/CDDLv1.0.html. |
| | | * See the License for the specific language governing permissions |
| | | * and limitations under the License. |
| | | * |
| | | * When distributing Covered Code, include this CDDL HEADER in each |
| | | * file and include the License file at legal-notices/CDDLv1_0.txt. |
| | | * If applicable, add the following below this CDDL HEADER, with the |
| | | * fields enclosed by brackets "[]" replaced with your own identifying |
| | | * information: |
| | | * Portions Copyright [yyyy] [name of copyright owner] |
| | | * |
| | | * CDDL HEADER END |
| | | * |
| | | * |
| | | * Copyright 2014 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.ldap.schema; |
| | | |
| | | import java.util.Locale; |
| | | |
| | | import org.forgerock.opendj.ldap.Assertion; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.ConditionResult; |
| | | import org.forgerock.opendj.ldap.schema.AbstractSubstringMatchingRuleImplTest.FakeIndexQueryFactory; |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.forgerock.opendj.ldap.schema.AbstractSubstringMatchingRuleImplTest.*; |
| | | import static org.testng.Assert.*; |
| | | |
| | | @SuppressWarnings("javadoc") |
| | | @Test |
| | | public class CollationLessThanOrEqualMatchingRuleTest extends MatchingRuleTest { |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | @DataProvider(name = "matchingRuleInvalidAttributeValues") |
| | | public Object[][] createMatchingRuleInvalidAttributeValues() { |
| | | return new Object[][] { }; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | @DataProvider(name = "matchingrules") |
| | | public Object[][] createMatchingRuleTest() { |
| | | return new Object[][] { |
| | | // \u00E9 = LATIN SMALL LETTER E WITH ACUTE |
| | | // \u00C9 = LATIN CAPITAL LETTER E WITH ACUTE |
| | | { "abc", "abd", ConditionResult.TRUE }, |
| | | { "abc", "acc", ConditionResult.TRUE }, |
| | | { "abc", "bbc", ConditionResult.TRUE }, |
| | | { "abc", "abD", ConditionResult.TRUE }, |
| | | { "def", "d\u00E9g", ConditionResult.TRUE }, |
| | | { "def", "dEg", ConditionResult.TRUE }, |
| | | { "dEf", "deg", ConditionResult.TRUE }, |
| | | { "d\u00E9f", "dEg", ConditionResult.TRUE }, |
| | | { "passe", "passe", ConditionResult.TRUE }, |
| | | { "passe ", "passe", ConditionResult.TRUE }, |
| | | { "passE", "passe", ConditionResult.TRUE }, |
| | | { "pass\u00E9", "passe", ConditionResult.TRUE }, |
| | | { "pass\u00E9", "passE", ConditionResult.TRUE }, |
| | | { "pass\u00E9", "pass\u00C9", ConditionResult.TRUE }, |
| | | { "passe", "pass\u00E9", ConditionResult.TRUE }, |
| | | { "passE", "pass\u00E9", ConditionResult.TRUE }, |
| | | { "pass\u00C9", "pass\u00E9", ConditionResult.TRUE }, |
| | | { "abd", "abc", ConditionResult.FALSE }, |
| | | { "acc", "abc", ConditionResult.FALSE }, |
| | | { "bbc", "abc", ConditionResult.FALSE }, |
| | | { "abD", "abc", ConditionResult.FALSE }, |
| | | { "d\u00E9g", "def", ConditionResult.FALSE }, |
| | | { "dEg", "def", ConditionResult.FALSE }, |
| | | { "deg", "dEf", ConditionResult.FALSE }, |
| | | { "dEg", "d\u00E9f", ConditionResult.FALSE }, |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | protected MatchingRule getRule() { |
| | | // Note that oid and names are not used by the test (ie, they could be any value, test should pass anyway) |
| | | // Only the implementation class and the provided locale are really tested here. |
| | | String oid = "1.3.6.1.4.1.42.2.27.9.4.76.1.2"; |
| | | Schema schema = new SchemaBuilder(Schema.getCoreSchema()). |
| | | buildMatchingRule(oid). |
| | | syntaxOID(SchemaConstants.SYNTAX_DIRECTORY_STRING_OID). |
| | | names("fr.lte"). |
| | | implementation(CollationMatchingRulesImpl.lessThanOrEqualMatchingRule(new Locale("fr"))). |
| | | addToSchema(). |
| | | toSchema(); |
| | | return schema.getMatchingRule(oid); |
| | | } |
| | | |
| | | @Test |
| | | public void testCreateIndexQuery() throws Exception { |
| | | ByteString value = ByteString.valueOf("abc"); |
| | | MatchingRule matchingRule = getRule(); |
| | | Assertion assertion = matchingRule.getAssertion(value); |
| | | |
| | | String indexQuery = assertion.createIndexQuery(new FakeIndexQueryFactory(newIndexingOptions(), false)); |
| | | |
| | | ByteString normalizedValue = matchingRule.normalizeAttributeValue(value); |
| | | assertEquals(indexQuery, "rangeMatch(fr.shared, '' < value <= '" + normalizedValue.toHexString() + "')"); |
| | | } |
| | | } |
| New file |
| | |
| | | /* |
| | | * CDDL HEADER START |
| | | * |
| | | * The contents of this file are subject to the terms of the |
| | | * Common Development and Distribution License, Version 1.0 only |
| | | * (the "License"). You may not use this file except in compliance |
| | | * with the License. |
| | | * |
| | | * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt |
| | | * or http://forgerock.org/license/CDDLv1.0.html. |
| | | * See the License for the specific language governing permissions |
| | | * and limitations under the License. |
| | | * |
| | | * When distributing Covered Code, include this CDDL HEADER in each |
| | | * file and include the License file at legal-notices/CDDLv1_0.txt. |
| | | * If applicable, add the following below this CDDL HEADER, with the |
| | | * fields enclosed by brackets "[]" replaced with your own identifying |
| | | * information: |
| | | * Portions Copyright [yyyy] [name of copyright owner] |
| | | * |
| | | * CDDL HEADER END |
| | | * |
| | | * |
| | | * Copyright 2014 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.ldap.schema; |
| | | |
| | | import java.util.Locale; |
| | | |
| | | import org.forgerock.opendj.ldap.Assertion; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.ConditionResult; |
| | | import org.forgerock.opendj.ldap.schema.AbstractSubstringMatchingRuleImplTest.FakeIndexQueryFactory; |
| | | import org.testng.annotations.DataProvider; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import static org.forgerock.opendj.ldap.schema.AbstractSubstringMatchingRuleImplTest.*; |
| | | import static org.testng.Assert.*; |
| | | |
| | | @SuppressWarnings("javadoc") |
| | | @Test |
| | | public class CollationSubstringMatchingRuleTest extends SubstringMatchingRuleTest { |
| | | |
| | | @DataProvider(name = "substringInvalidAssertionValues") |
| | | public Object[][] createMatchingRuleInvalidAssertionValues() { |
| | | return new Object[][] { }; |
| | | } |
| | | |
| | | @DataProvider(name = "substringInvalidAttributeValues") |
| | | public Object[][] createMatchingRuleInvalidAttributeValues() { |
| | | return new Object[][] { }; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | @DataProvider(name = "substringFinalMatchData") |
| | | public Object[][] createSubstringFinalMatchData() { |
| | | return new Object[][] { |
| | | { "this is a value", "value", ConditionResult.TRUE }, |
| | | { "this is a value", "alue", ConditionResult.TRUE }, |
| | | { "this is a value", "ue", ConditionResult.TRUE }, |
| | | { "this is a value", "e", ConditionResult.TRUE }, |
| | | { "this is a value", "valu", ConditionResult.FALSE }, |
| | | { "this is a value", "this", ConditionResult.FALSE }, |
| | | { "this is a value", "VALUE", ConditionResult.TRUE }, |
| | | { "this is a value", "AlUe", ConditionResult.TRUE }, |
| | | { "this is a value", "UE", ConditionResult.TRUE }, |
| | | { "this is a value", "E", ConditionResult.TRUE }, |
| | | { "this is a value", "THIS", ConditionResult.FALSE }, |
| | | { "this is a value", " ", ConditionResult.TRUE }, |
| | | { "this is a VALUE", "value", ConditionResult.TRUE }, |
| | | { "end with space ", " ", ConditionResult.TRUE }, |
| | | { "end with space ", "space", ConditionResult.TRUE }, |
| | | { "end with space ", "SPACE", ConditionResult.TRUE }, |
| | | // \u00E9 = LATIN SMALL LETTER E WITH ACUTE |
| | | // \u00C9 = LATIN CAPITAL LETTER E WITH ACUTE |
| | | { "il est passe", "passE", ConditionResult.TRUE }, |
| | | { "il est passe", "pass\u00E9", ConditionResult.TRUE }, |
| | | { "il est passe", "pass\u00C9", ConditionResult.TRUE }, |
| | | { "il est pass\u00C9", "passe", ConditionResult.TRUE }, |
| | | { "il est pass\u00E9", "passE", ConditionResult.TRUE }, |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | @DataProvider(name = "substringInitialMatchData") |
| | | public Object[][] createSubstringInitialMatchData() { |
| | | return new Object[][] { |
| | | { "this is a value", "this", ConditionResult.TRUE }, |
| | | { "this is a value", "th", ConditionResult.TRUE }, |
| | | { "this is a value", "t", ConditionResult.TRUE }, |
| | | { "this is a value", "is", ConditionResult.FALSE }, |
| | | { "this is a value", "a", ConditionResult.FALSE }, |
| | | { "this is a value", "TH", ConditionResult.TRUE }, |
| | | { "this is a value", "T", ConditionResult.TRUE }, |
| | | { "this is a value", "IS", ConditionResult.FALSE }, |
| | | { "this is a value", "A", ConditionResult.FALSE }, |
| | | { "this is a value", "VALUE", ConditionResult.FALSE }, |
| | | { "this is a value", " ", ConditionResult.TRUE }, |
| | | { "this is a value", "NOT", ConditionResult.FALSE }, |
| | | { "this is a value", "THIS", ConditionResult.TRUE }, |
| | | // \u00E9 = LATIN SMALL LETTER E WITH ACUTE |
| | | // \u00C9 = LATIN CAPITAL LETTER E WITH ACUTE |
| | | { "il etait passe", "Il \u00E9", ConditionResult.TRUE }, |
| | | { "il etait passe", "Il \u00C9", ConditionResult.TRUE }, |
| | | { "il etait passe", "Il E", ConditionResult.TRUE }, |
| | | { "il \u00E9tait passe", "IL e", ConditionResult.TRUE }, |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | @DataProvider(name = "substringMiddleMatchData") |
| | | public Object[][] createSubstringMiddleMatchData() { |
| | | return new Object[][] { |
| | | { "this is a value", strings("this"), ConditionResult.TRUE }, |
| | | { "this is a value", strings("is"), ConditionResult.TRUE }, |
| | | { "this is a value", strings("a"), ConditionResult.TRUE }, |
| | | { "this is a value", strings("value"), ConditionResult.TRUE }, |
| | | { "this is a value", strings("THIS"), ConditionResult.TRUE }, |
| | | { "this is a value", strings("IS"), ConditionResult.TRUE }, |
| | | { "this is a value", strings("A"), ConditionResult.TRUE }, |
| | | { "this is a value", strings("VALUE"), ConditionResult.TRUE }, |
| | | { "this is a value", strings(" "), ConditionResult.TRUE }, |
| | | { "this is a value", strings("this", "is", "a", "value"), ConditionResult.TRUE }, |
| | | // The matching rule requires ordered non overlapping substrings. |
| | | // Issue #730 was not valid. |
| | | { "this is a value", strings("value", "this"), ConditionResult.FALSE }, |
| | | { "this is a value", strings("this", "this is"), ConditionResult.FALSE }, |
| | | { "this is a value", strings("this", "IS", "a", "VALue"), ConditionResult.TRUE }, |
| | | { "this is a value", strings("his IS", "A val"), ConditionResult.TRUE }, |
| | | { "this is a value", strings("not"), ConditionResult.FALSE }, |
| | | { "this is a value", strings("this", "not"), ConditionResult.FALSE }, |
| | | { "this is a value", strings(" "), ConditionResult.TRUE }, |
| | | // \u00E9 = LATIN SMALL LETTER E WITH ACUTE |
| | | // \u00C9 = LATIN CAPITAL LETTER E WITH ACUTE |
| | | { "il est passe par la", strings("est", "pass\u00E9"), ConditionResult.TRUE }, |
| | | { "il est passe par la", strings("pass\u00E9", "Par"), ConditionResult.TRUE }, |
| | | { "il est passe par la", strings("est", "pass\u00C9", "PAR", "La"), ConditionResult.TRUE }, |
| | | { "il est pass\u00E9 par la", strings("il", "Est", "pass\u00C9"), ConditionResult.TRUE }, |
| | | { "il est pass\u00C9 par la", strings("est", "passe"), ConditionResult.TRUE }, |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | protected MatchingRule getRule() { |
| | | // Note that oid and names are not used by the test (ie, they could be any value, test should pass anyway) |
| | | // Only the implementation class and the provided locale are really tested here. |
| | | String oid = "1.3.6.1.4.1.42.2.27.9.4.76.1.6"; |
| | | Schema schema = new SchemaBuilder(Schema.getCoreSchema()). |
| | | buildMatchingRule(oid). |
| | | syntaxOID(SchemaConstants.SYNTAX_DIRECTORY_STRING_OID). |
| | | names("fr.sub"). |
| | | implementation(CollationMatchingRulesImpl.substringMatchingRule(new Locale("fr"))). |
| | | addToSchema(). |
| | | toSchema(); |
| | | return schema.getMatchingRule(oid); |
| | | } |
| | | |
| | | @Test |
| | | public void testCreateIndexQuery() throws Exception { |
| | | ByteString value = ByteString.valueOf("a*c"); |
| | | MatchingRule matchingRule = getRule(); |
| | | Assertion assertion = matchingRule.getAssertion(value); |
| | | |
| | | String indexQuery = assertion.createIndexQuery(new FakeIndexQueryFactory(newIndexingOptions(), false)); |
| | | |
| | | ByteString binit = matchingRule.normalizeAttributeValue(ByteString.valueOf("a")); |
| | | ByteString bfinal = matchingRule.normalizeAttributeValue(ByteString.valueOf("c")); |
| | | assertEquals(indexQuery, |
| | | "intersect[" |
| | | + "rangeMatch(fr.shared, '" + binit.toHexString() + "' <= value < '00 54'), " |
| | | + "rangeMatch(fr.substring, '" + bfinal.toHexString() + "' <= value < '00 56'), " |
| | | + "rangeMatch(fr.substring, '" + binit.toHexString() + "' <= value < '00 54')]" |
| | | ); |
| | | } |
| | | } |