OPENDJ-1629 CR-5279: fix integer matching rules so that they handle negative values
| | |
| | | * this assumption does not hold true. |
| | | */ |
| | | abstract class AbstractOrderingMatchingRuleImpl extends AbstractMatchingRuleImpl { |
| | | |
| | | private final Collection<? extends Indexer> indexers; |
| | | |
| | | private final String indexId; |
| | | |
| | | /** Constructor for default matching rules. */ |
| | |
| | | }; |
| | | } |
| | | |
| | | /** |
| | | * 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); |
| | | } |
| | | }; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public Assertion getLessOrEqualAssertion(final Schema schema, final ByteSequence value) |
| | |
| | | public Collection<? extends Indexer> getIndexers() { |
| | | return indexers; |
| | | } |
| | | |
| | | final String getIndexId() { |
| | | return indexId; |
| | | } |
| | | } |
| | |
| | | import org.forgerock.opendj.ldap.Assertion; |
| | | import org.forgerock.opendj.ldap.ByteSequence; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.ConditionResult; |
| | | import org.forgerock.opendj.ldap.DecodeException; |
| | | import org.forgerock.opendj.ldap.spi.IndexQueryFactory; |
| | | import org.forgerock.opendj.ldap.spi.Indexer; |
| | | |
| | | /** |
| | |
| | | * Defines the base for collation matching rules. |
| | | */ |
| | | private static abstract class AbstractCollationMatchingRuleImpl extends AbstractMatchingRuleImpl { |
| | | |
| | | private final Locale locale; |
| | | final Collator collator; |
| | | final String indexName; |
| | | final Indexer indexer; |
| | | |
| | | /** |
| | |
| | | AbstractCollationMatchingRuleImpl(Locale locale) { |
| | | this.locale = locale; |
| | | this.collator = createCollator(locale); |
| | | this.indexer = new DefaultIndexer(getSharedIndexName()); |
| | | this.indexName = getPrefixIndexName() + "." + INDEX_ID_SHARED; |
| | | this.indexer = new DefaultIndexer(indexName); |
| | | } |
| | | |
| | | private Collator createCollator(Locale locale) { |
| | |
| | | return builder.toString(); |
| | | } |
| | | |
| | | String getSharedIndexName() { |
| | | return getPrefixIndexName() + "." + INDEX_ID_SHARED; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public Collection<? extends Indexer> getIndexers() { |
| | |
| | | @Override |
| | | public Assertion getAssertion(final Schema schema, final ByteSequence assertionValue) |
| | | throws DecodeException { |
| | | return DefaultAssertion.named(getSharedIndexName(), normalizeAttributeValue(schema, assertionValue)); |
| | | return DefaultAssertion.named(indexName, normalizeAttributeValue(schema, assertionValue)); |
| | | } |
| | | |
| | | } |
| | |
| | | CollationSubstringMatchingRuleImpl(Locale locale) { |
| | | super(locale); |
| | | substringMatchingRule = new AbstractSubstringMatchingRuleImpl( |
| | | getPrefixIndexName() + "." + INDEX_ID_SUBSTRING, getSharedIndexName()) { |
| | | getPrefixIndexName() + "." + INDEX_ID_SUBSTRING, indexName) { |
| | | @Override |
| | | public ByteString normalizeAttributeValue(Schema schema, ByteSequence value) |
| | | throws DecodeException { |
| | |
| | | CollationOrderingMatchingRuleImpl(Locale locale) { |
| | | super(locale); |
| | | |
| | | orderingMatchingRule = new AbstractOrderingMatchingRuleImpl(getSharedIndexName()) { |
| | | orderingMatchingRule = new AbstractOrderingMatchingRuleImpl(indexName) { |
| | | @Override |
| | | public ByteString normalizeAttributeValue(Schema schema, ByteSequence value) throws DecodeException { |
| | | return CollationOrderingMatchingRuleImpl.this.normalizeAttributeValue(schema, value); |
| | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public Assertion getAssertion(Schema schema, ByteSequence assertionValue) throws DecodeException { |
| | | return orderingMatchingRule.getGreaterThanAssertion(schema, assertionValue); |
| | | public Assertion getAssertion(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(indexName, normAssertion, |
| | | ByteString.empty(), false, false); |
| | | } |
| | | }; |
| | | } |
| | | } |
| | | |
| | |
| | | */ |
| | | package org.forgerock.opendj.ldap.schema; |
| | | |
| | | import static com.forgerock.opendj.ldap.CoreMessages.*; |
| | | import static org.forgerock.opendj.ldap.schema.IntegerOrderingMatchingRuleImpl.*; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.i18n.slf4j.LocalizedLogger; |
| | | import java.util.Comparator; |
| | | |
| | | import org.forgerock.opendj.ldap.ByteSequence; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.DecodeException; |
| | | |
| | | /** |
| | | * This class defines the integerMatch matching rule defined in X.520 and |
| | | * referenced in RFC 2252. |
| | | * referenced in RFC 2252. The implementation of this matching rule is |
| | | * intentionally aligned with the ordering matching rule so that they could |
| | | * potentially share the same index. |
| | | */ |
| | | final class IntegerEqualityMatchingRuleImpl extends AbstractEqualityMatchingRuleImpl { |
| | | |
| | | private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); |
| | | @Override |
| | | public Comparator<ByteSequence> comparator(final Schema schema) { |
| | | return COMPARATOR; |
| | | } |
| | | |
| | | @Override |
| | | public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) |
| | | throws DecodeException { |
| | | try { |
| | | return ByteString.valueOf(Integer.parseInt(value.toString())); |
| | | } catch (final Exception e) { |
| | | logger.debug(LocalizableMessage.raw("%s", e)); |
| | | throw DecodeException.error(WARN_ATTR_SYNTAX_ILLEGAL_INTEGER.get(value)); |
| | | } |
| | | return normalizeValueAndEncode(value); |
| | | } |
| | | } |
| | |
| | | */ |
| | | package org.forgerock.opendj.ldap.schema; |
| | | |
| | | import static com.forgerock.opendj.ldap.CoreMessages.*; |
| | | import static com.forgerock.opendj.ldap.CoreMessages.WARN_ATTR_SYNTAX_ILLEGAL_INTEGER; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.i18n.slf4j.LocalizedLogger; |
| | | import java.math.BigInteger; |
| | | import java.util.Comparator; |
| | | |
| | | import org.forgerock.opendj.ldap.Assertion; |
| | | import org.forgerock.opendj.ldap.ByteSequence; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.ConditionResult; |
| | | import org.forgerock.opendj.ldap.DecodeException; |
| | | import org.forgerock.opendj.ldap.spi.IndexQueryFactory; |
| | | |
| | | /** |
| | | * This class defines the integerOrderingMatch matching rule defined in X.520 |
| | | * and referenced in RFC 4519. |
| | | * and referenced in RFC 4519. The implementation of this matching rule is |
| | | * intentionally aligned with the ordering matching rule so that they could |
| | | * potentially share the same index. |
| | | */ |
| | | final class IntegerOrderingMatchingRuleImpl extends AbstractOrderingMatchingRuleImpl { |
| | | static final Comparator<ByteSequence> COMPARATOR = new Comparator<ByteSequence>() { |
| | | @Override |
| | | public int compare(final ByteSequence o1, final ByteSequence o2) { |
| | | final BigInteger i1 = decodeNormalizedValue(o1); |
| | | final BigInteger i2 = decodeNormalizedValue(o2); |
| | | return i1.compareTo(i2); |
| | | } |
| | | }; |
| | | |
| | | private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); |
| | | static ByteString normalizeValueAndEncode(final ByteSequence value) throws DecodeException { |
| | | return encodeNormalizedValue(normalizeValue(value)); |
| | | } |
| | | |
| | | private static BigInteger normalizeValue(final ByteSequence value) throws DecodeException { |
| | | try { |
| | | return new BigInteger(value.toString()); |
| | | } catch (final Exception e) { |
| | | throw DecodeException.error(WARN_ATTR_SYNTAX_ILLEGAL_INTEGER.get(value)); |
| | | } |
| | | } |
| | | |
| | | private static BigInteger decodeNormalizedValue(final ByteSequence normalizedValue) { |
| | | return new BigInteger(normalizedValue.toByteArray()); |
| | | } |
| | | |
| | | private static ByteString encodeNormalizedValue(final BigInteger normalizedValue) { |
| | | return ByteString.wrap(normalizedValue.toByteArray()); |
| | | } |
| | | |
| | | @Override |
| | | public Comparator<ByteSequence> comparator(final Schema schema) { |
| | | return COMPARATOR; |
| | | } |
| | | |
| | | @Override |
| | | public Assertion getAssertion(final Schema schema, final ByteSequence value) |
| | | throws DecodeException { |
| | | final BigInteger normAssertion = normalizeValue(value); |
| | | return new Assertion() { |
| | | @Override |
| | | public <T> T createIndexQuery(final IndexQueryFactory<T> factory) |
| | | throws DecodeException { |
| | | return factory.createRangeMatchQuery(getIndexId(), ByteString.empty(), |
| | | encodeNormalizedValue(normAssertion), false, false); |
| | | } |
| | | |
| | | @Override |
| | | public ConditionResult matches(final ByteSequence normValue) { |
| | | return ConditionResult.valueOf(decodeNormalizedValue(normValue).compareTo( |
| | | normAssertion) < 0); |
| | | } |
| | | }; |
| | | } |
| | | |
| | | @Override |
| | | public Assertion getGreaterOrEqualAssertion(final Schema schema, final ByteSequence value) |
| | | throws DecodeException { |
| | | final BigInteger normAssertion = normalizeValue(value); |
| | | return new Assertion() { |
| | | @Override |
| | | public <T> T createIndexQuery(final IndexQueryFactory<T> factory) |
| | | throws DecodeException { |
| | | return factory.createRangeMatchQuery(getIndexId(), encodeNormalizedValue(normAssertion), ByteString |
| | | .empty(), true, false); |
| | | } |
| | | |
| | | @Override |
| | | public ConditionResult matches(final ByteSequence normValue) { |
| | | return ConditionResult.valueOf(decodeNormalizedValue(normValue).compareTo( |
| | | normAssertion) >= 0); |
| | | } |
| | | }; |
| | | } |
| | | |
| | | @Override |
| | | public Assertion getLessOrEqualAssertion(final Schema schema, final ByteSequence value) |
| | | throws DecodeException { |
| | | final BigInteger normAssertion = normalizeValue(value); |
| | | return new Assertion() { |
| | | @Override |
| | | public <T> T createIndexQuery(final IndexQueryFactory<T> factory) |
| | | throws DecodeException { |
| | | return factory.createRangeMatchQuery(getIndexId(), ByteString.empty(), |
| | | encodeNormalizedValue(normAssertion), false, true); |
| | | } |
| | | |
| | | @Override |
| | | public ConditionResult matches(final ByteSequence normValue) { |
| | | return ConditionResult.valueOf(decodeNormalizedValue(normValue).compareTo( |
| | | normAssertion) <= 0); |
| | | } |
| | | }; |
| | | } |
| | | |
| | | @Override |
| | | public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) |
| | | throws DecodeException { |
| | | try { |
| | | return ByteString.valueOf(Integer.parseInt(value.toString())); |
| | | } catch (final Exception e) { |
| | | logger.debug(LocalizableMessage.raw("%s", e)); |
| | | throw DecodeException.error(WARN_ATTR_SYNTAX_ILLEGAL_INTEGER.get(value)); |
| | | } |
| | | return normalizeValueAndEncode(value); |
| | | } |
| | | } |
| | |
| | | /** |
| | | * Test the IntegerEqualityMatchingRule. |
| | | */ |
| | | //TODO: fix matching rule so that commented data in data providers pass |
| | | public class IntegerEqualityMatchingRuleTest extends MatchingRuleTest { |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | @DataProvider(name = "matchingRuleInvalidAttributeValues") |
| | | public Object[][] createMatchingRuleInvalidAttributeValues() { |
| | | /* |
| | | * The JDK8 BigInteger parser is quite tolerant and allows leading zeros |
| | | * and + characters. It's ok if the matching rule is more tolerant than |
| | | * the syntax itself (see commented data). |
| | | */ |
| | | return new Object[][] { |
| | | //{"01"}, |
| | | //{"00"}, |
| | |
| | | {"b2"}, |
| | | {"-"}, |
| | | {""}, |
| | | {" 63 "}, |
| | | {"- 63"}, |
| | | //{"+63" }, |
| | | {"AB" }, |
| | | {"0xAB"}, |
| | | }; |
| | | } |
| | | |
| | |
| | | return new Object[][] { |
| | | {"1234567890", "1234567890", ConditionResult.TRUE}, |
| | | {"-1", "-1", ConditionResult.TRUE}, |
| | | //{"-9876543210", "-9876543210", ConditionResult.TRUE}, |
| | | {"-9876543210", "-9876543210", ConditionResult.TRUE}, |
| | | {"1", "-1", ConditionResult.FALSE}, |
| | | // Values which are greater than 64 bits. |
| | | { "-987654321987654321987654321", "-987654321987654321987654322", ConditionResult.FALSE }, |
| | | {"987654321987654321987654321", "987654321987654321987654322", ConditionResult.FALSE }, |
| | | { "987654321987654321987654321", "987654321987654321987654321", ConditionResult.TRUE }, |
| | | }; |
| | | } |
| | | |
| | |
| | | * |
| | | * |
| | | * Copyright 2009 Sun Microsystems, Inc. |
| | | * Portions copyright 2014 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.ldap.schema; |
| | | |
| | |
| | | /** |
| | | * Test the IntegerOrderingMatchingRule. |
| | | */ |
| | | //TODO: fix matching rule so that commented data in data providers pass |
| | | public class IntegerOrderingMatchingRuleTest extends OrderingMatchingRuleTest { |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | @DataProvider(name = "OrderingMatchingRuleInvalidValues") |
| | | public Object[][] createOrderingMatchingRuleInvalidValues() { |
| | | /* |
| | | * The JDK8 BigInteger parser is quite tolerant and allows leading zeros |
| | | * and + characters. It's ok if the matching rule is more tolerant than |
| | | * the syntax itself (see commented data). |
| | | */ |
| | | return new Object[][] { |
| | | //{"01"}, |
| | | //{"00"}, |
| | | //{"-01"}, |
| | | { "1-2" }, |
| | | { "b2" }, |
| | | { "-" }, |
| | | { "" }, |
| | | {" 63 "}, |
| | | {"- 63"}, |
| | | //{"+63" }, |
| | |
| | | {"1", "0", 1}, |
| | | {"1", "1", 0}, |
| | | {"45", "54", -1}, |
| | | //{"-63", "63", -1}, |
| | | //{"-63", "0", -1}, |
| | | {"-63", "63", -1}, |
| | | {"-63", "0", -1}, |
| | | {"63", "0", 1}, |
| | | //{"0", "-63", 1}, |
| | | //{"987654321987654321987654321", "987654321987654321987654322", -1}, |
| | | {"0", "-63", 1}, |
| | | // Values which are greater than 64 bits. |
| | | { "-987654321987654321987654321", "-987654321987654321987654322", 1 }, |
| | | {"987654321987654321987654321", "987654321987654321987654322", -1}, |
| | | { "987654321987654321987654321", "987654321987654321987654321", 0 }, |
| | | }; |
| | | } |
| | | |