From 7d5626f01a5ff72a8b9bbd803f6a3f4a0926a5da Mon Sep 17 00:00:00 2001
From: Nicolas Capponi <nicolas.capponi@forgerock.com>
Date: Tue, 07 Oct 2014 10:23:41 +0000
Subject: [PATCH] OPENDJ-1590 Migrate collation matching rules
---
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractOrderingMatchingRuleImpl.java | 52 ++
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationLessThanMatchingRuleTest.java | 119 +++++
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractMatchingRuleImpl.java | 10
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationGreaterThanMatchingRuleTest.java | 119 +++++
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractEqualityMatchingRuleImpl.java | 3
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationSubstringMatchingRuleTest.java | 187 ++++++++
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationGreaterThanOrEqualMatchingRuleTest.java | 119 +++++
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/AbstractSubstringMatchingRuleImplTest.java | 29 +
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractSubstringMatchingRuleImpl.java | 57 +-
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationEqualityMatchingRuleTest.java | 120 +++++
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationLessThanOrEqualMatchingRuleTest.java | 119 +++++
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CollationMatchingRulesImpl.java | 365 ++++++++++++++++
12 files changed, 1,256 insertions(+), 43 deletions(-)
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractEqualityMatchingRuleImpl.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractEqualityMatchingRuleImpl.java
index 1e69e23..0ccc622 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractEqualityMatchingRuleImpl.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractEqualityMatchingRuleImpl.java
@@ -39,8 +39,7 @@
*/
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.
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractMatchingRuleImpl.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractMatchingRuleImpl.java
index a34d99d..5a159f3 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractMatchingRuleImpl.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractMatchingRuleImpl.java
@@ -51,11 +51,15 @@
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) {
@@ -92,7 +96,7 @@
public String getIndexID() {
return indexID;
}
- };
+ }
private static final Assertion UNDEFINED_ASSERTION = new Assertion() {
@Override
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractOrderingMatchingRuleImpl.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractOrderingMatchingRuleImpl.java
index c069988..0648348 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractOrderingMatchingRuleImpl.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractOrderingMatchingRuleImpl.java
@@ -47,11 +47,19 @@
*/
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} */
@@ -67,15 +75,14 @@
@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
@@ -85,7 +92,36 @@
@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);
}
};
}
@@ -103,7 +139,7 @@
@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);
}
};
}
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractSubstringMatchingRuleImpl.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractSubstringMatchingRuleImpl.java
index 5d84db3..0ffb344 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractSubstringMatchingRuleImpl.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AbstractSubstringMatchingRuleImpl.java
@@ -34,7 +34,6 @@
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;
@@ -66,7 +65,7 @@
* <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. */
@@ -74,8 +73,9 @@
/** 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;
@@ -149,7 +149,7 @@
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) {
@@ -164,6 +164,11 @@
// (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);
}
@@ -197,7 +202,7 @@
// 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.
@@ -213,7 +218,7 @@
}
for (ByteSequence key : substringKeys) {
- subqueries.add(factory.createExactMatchQuery("substring", key));
+ subqueries.add(factory.createExactMatchQuery(substringIndexId, key));
}
}
}
@@ -243,15 +248,27 @@
/** {@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} */
@@ -276,7 +293,7 @@
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));
@@ -292,10 +309,10 @@
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;
}
@@ -399,17 +416,13 @@
}
}
}
- 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();
@@ -469,9 +482,7 @@
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;
}
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CollationMatchingRulesImpl.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CollationMatchingRulesImpl.java
new file mode 100644
index 0000000..f8afddb
--- /dev/null
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CollationMatchingRulesImpl.java
@@ -0,0 +1,365 @@
+/*
+ * 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);
+ }
+ }
+}
diff --git a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/AbstractSubstringMatchingRuleImplTest.java b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/AbstractSubstringMatchingRuleImplTest.java
index 492a3e8..47f5600 100644
--- a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/AbstractSubstringMatchingRuleImplTest.java
+++ b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/AbstractSubstringMatchingRuleImplTest.java
@@ -62,17 +62,24 @@
}
- 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
@@ -81,13 +88,17 @@
}
@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("=");
@@ -97,7 +108,11 @@
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();
}
@@ -123,7 +138,7 @@
return new FakeSubstringMatchingRuleImpl();
}
- private IndexingOptions newIndexingOptions() {
+ static IndexingOptions newIndexingOptions() {
final IndexingOptions options = mock(IndexingOptions.class);
when(options.substringKeySize()).thenReturn(3);
return options;
diff --git a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationEqualityMatchingRuleTest.java b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationEqualityMatchingRuleTest.java
new file mode 100644
index 0000000..37abf89
--- /dev/null
+++ b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationEqualityMatchingRuleTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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() + "')");
+ }
+
+}
diff --git a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationGreaterThanMatchingRuleTest.java b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationGreaterThanMatchingRuleTest.java
new file mode 100644
index 0000000..f9abe1f
--- /dev/null
+++ b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationGreaterThanMatchingRuleTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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 < '')");
+ }
+}
diff --git a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationGreaterThanOrEqualMatchingRuleTest.java b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationGreaterThanOrEqualMatchingRuleTest.java
new file mode 100644
index 0000000..c70d0ce
--- /dev/null
+++ b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationGreaterThanOrEqualMatchingRuleTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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 < '')");
+ }
+}
diff --git a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationLessThanMatchingRuleTest.java b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationLessThanMatchingRuleTest.java
new file mode 100644
index 0000000..1d9dc7d
--- /dev/null
+++ b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationLessThanMatchingRuleTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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() + "')");
+ }
+}
diff --git a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationLessThanOrEqualMatchingRuleTest.java b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationLessThanOrEqualMatchingRuleTest.java
new file mode 100644
index 0000000..9dde742
--- /dev/null
+++ b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationLessThanOrEqualMatchingRuleTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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() + "')");
+ }
+}
diff --git a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationSubstringMatchingRuleTest.java b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationSubstringMatchingRuleTest.java
new file mode 100644
index 0000000..3244d22
--- /dev/null
+++ b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CollationSubstringMatchingRuleTest.java
@@ -0,0 +1,187 @@
+/*
+ * 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')]"
+ );
+ }
+}
--
Gitblit v1.10.0