From 5759eb1a5d28d5b106e04657906ba6a1b20618fc Mon Sep 17 00:00:00 2001
From: Gaetan Boismal <gaetan.boismal@forgerock.com>
Date: Wed, 28 Jan 2015 08:41:37 +0000
Subject: [PATCH] OPENDJ-1731 CR-5871 Builder for Matching rule use
---
opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleUse.java | 228 +++++++++++++++++++++++-
opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/MatchingRuleUseBuilderTestCase.java | 157 +++++++++++++++++
opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java | 101 ++++------
3 files changed, 411 insertions(+), 75 deletions(-)
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleUse.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleUse.java
index 4bf12d4..3e2d34a 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleUse.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleUse.java
@@ -22,16 +22,23 @@
*
*
* Copyright 2009 Sun Microsystems, Inc.
+ * Portions copyright 2015 ForgeRock AS
*/
package org.forgerock.opendj.ldap.schema;
-import static com.forgerock.opendj.ldap.CoreMessages.ERR_ATTR_SYNTAX_MRUSE_UNKNOWN_ATTR1;
-import static com.forgerock.opendj.ldap.CoreMessages.ERR_ATTR_SYNTAX_MRUSE_UNKNOWN_MATCHING_RULE1;
+import static java.util.Arrays.*;
+import static org.forgerock.opendj.ldap.schema.SchemaUtils.*;
+
+import static com.forgerock.opendj.ldap.CoreMessages.*;
+
+import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -45,6 +52,202 @@
* attribute types that may be used for a given matching rule.
*/
public final class MatchingRuleUse extends SchemaElement {
+
+ /** A fluent API for incrementally constructing matching rule uses. */
+ public static final class Builder extends SchemaElementBuilder<Builder> {
+ private String oid;
+ private final List<String> names = new LinkedList<String>();
+ private boolean isObsolete;
+ private final Set<String> attributeOIDs = new LinkedHashSet<String>();
+
+ Builder(MatchingRuleUse mru, SchemaBuilder builder) {
+ super(builder, mru);
+ this.oid = mru.oid;
+ this.names.addAll(mru.names);
+ this.isObsolete = mru.isObsolete;
+ this.attributeOIDs.addAll(mru.attributeOIDs);
+ }
+
+ Builder(final String oid, final SchemaBuilder builder) {
+ super(builder);
+ this.oid = oid;
+ }
+
+ /**
+ * Adds this matching rule use definition to the schema, throwing a
+ * {@code ConflictingSchemaElementException} if there is an existing
+ * matching rule definition with the same numeric OID.
+ *
+ * @return The parent schema builder.
+ * @throws ConflictingSchemaElementException
+ * If there is an existing matching rule use definition with
+ * the same numeric OID.
+ */
+ public SchemaBuilder addToSchema() {
+ return getSchemaBuilder().addMatchingRuleUse(new MatchingRuleUse(this), false);
+ }
+
+ /**
+ * Adds this matching rule use definition to the schema overwriting any
+ * existing matching rule use definition with the same numeric OID.
+ *
+ * @return The parent schema builder.
+ */
+ public SchemaBuilder addToSchemaOverwrite() {
+ return getSchemaBuilder().addMatchingRuleUse(new MatchingRuleUse(this), true);
+ }
+
+ /**
+ * Adds the provided list of attribute types to the list of attribute
+ * type the matching rule applies to.
+ *
+ * @param attributeOIDs
+ * The list of attribute type numeric OIDs.
+ * @return This builder.
+ */
+ public Builder attributes(Collection<String> attributeOIDs) {
+ this.attributeOIDs.addAll(attributeOIDs);
+ return this;
+ }
+
+ /**
+ * Adds the provided list of attribute types to the list of attribute
+ * type the matching rule applies to.
+ *
+ * @param attributeOIDs
+ * The list of attribute type numeric OIDs.
+ * @return This builder.
+ */
+ public Builder attributes(String... attributeOIDs) {
+ this.attributeOIDs.addAll(asList(attributeOIDs));
+ return this;
+ }
+
+ @Override
+ public Builder description(final String description) {
+ return description0(description);
+ }
+
+ @Override
+ public Builder extraProperties(final Map<String, List<String>> extraProperties) {
+ return extraProperties0(extraProperties);
+ }
+
+ @Override
+ public Builder extraProperties(final String extensionName, final String... extensionValues) {
+ return extraProperties0(extensionName, extensionValues);
+ }
+
+ @Override
+ Builder getThis() {
+ return this;
+ }
+
+ /**
+ * Adds the provided user friendly names.
+ *
+ * @param names
+ * The user friendly names.
+ * @return This builder.
+ */
+ public Builder names(final Collection<String> names) {
+ this.names.addAll(names);
+ return this;
+ }
+
+ /**
+ * Adds the provided user friendly names.
+ *
+ * @param names
+ * The user friendly names.
+ * @return This builder.
+ */
+ public Builder names(final String... names) {
+ return names(asList(names));
+ }
+
+ /**
+ * Specifies whether this schema element is obsolete.
+ *
+ * @param isObsolete
+ * {@code true} if this schema element is obsolete
+ * (default is {@code false}).
+ * @return This builder.
+ */
+ public Builder obsolete(final boolean isObsolete) {
+ this.isObsolete = isObsolete;
+ return this;
+ }
+
+ /**
+ * Sets the numeric OID which uniquely identifies this matching rule use
+ * definition.
+ *
+ * @param oid
+ * The numeric OID.
+ * @return This builder.
+ */
+ public Builder oid(final String oid) {
+ this.oid = oid;
+ return this;
+ }
+
+ /**
+ * Removes all attribute types the matching rule applies to.
+ *
+ * @return This builder.
+ */
+ public Builder removeAllAttributes() {
+ this.attributeOIDs.clear();
+ return this;
+ }
+
+ @Override
+ public Builder removeAllExtraProperties() {
+ return removeAllExtraProperties0();
+ }
+
+ /**
+ * Removes all user defined names.
+ *
+ * @return This builder.
+ */
+ public Builder removeAllNames() {
+ this.names.clear();
+ return this;
+ }
+
+ /**
+ * Removes the provided attribute type.
+ *
+ * @param attributeOID
+ * The attribute type OID to be removed.
+ * @return This builder.
+ */
+ public Builder removeAttribute(String attributeOID) {
+ this.attributeOIDs.remove(attributeOID);
+ return this;
+ }
+
+ @Override
+ public Builder removeExtraProperty(String extensionName, String... extensionValues) {
+ return removeExtraProperty0(extensionName, extensionValues);
+ }
+
+ /**
+ * Removes the provided user defined name.
+ *
+ * @param name
+ * The user defined name to be removed.
+ * @return This builder.
+ */
+ public Builder removeName(String name) {
+ this.names.remove(name);
+ return this;
+ }
+
+ }
+
/**
* The OID of the matching rule associated with this matching rule
* use definition.
@@ -66,16 +269,14 @@
private MatchingRule matchingRule;
private Set<AttributeType> attributes = Collections.emptySet();
- MatchingRuleUse(final String oid, final List<String> names, final String description,
- final boolean obsolete, final Set<String> attributeOIDs,
- final Map<String, List<String>> extraProperties, final String definition) {
- super(description, extraProperties, definition);
+ private MatchingRuleUse(final Builder builder) {
+ super(builder);
+ Reject.ifNull(builder.oid);
- Reject.ifNull(oid, names, attributeOIDs);
- this.oid = oid;
- this.names = names;
- this.isObsolete = obsolete;
- this.attributeOIDs = attributeOIDs;
+ this.oid = builder.oid;
+ this.names = unmodifiableCopyOfList(builder.names);
+ this.isObsolete = builder.isObsolete;
+ this.attributeOIDs = unmodifiableCopyOfSet(builder.attributeOIDs);
}
/**
@@ -218,11 +419,6 @@
return isObsolete;
}
- MatchingRuleUse duplicate() {
- return new MatchingRuleUse(oid, names, getDescription(), isObsolete, attributeOIDs,
- getExtraProperties(), toString());
- }
-
@Override
void toStringContent(final StringBuilder buffer) {
buffer.append(oid);
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
index e4299a9..bdd5521 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
@@ -820,13 +820,9 @@
reader.skipWhitespaces();
// The next set of characters must be the OID.
- final String oid = readOID(reader, allowsMalformedNamesAndOptions());
-
- List<String> names = Collections.emptyList();
- String description = "".intern();
- boolean isObsolete = false;
+ final MatchingRuleUse.Builder useBuilder =
+ buildMatchingRuleUse(readOID(reader, allowsMalformedNamesAndOptions()));
Set<String> attributes = null;
- Map<String, List<String>> extraProperties = Collections.emptyMap();
// At this point, we should have a pretty specific syntax that
// describes what may come next, but some of the components are
@@ -843,17 +839,16 @@
// No more tokens.
break;
} else if ("name".equalsIgnoreCase(tokenName)) {
- names = readNameDescriptors(reader, allowsMalformedNamesAndOptions());
+ useBuilder.names(readNameDescriptors(reader, allowsMalformedNamesAndOptions()));
} else if ("desc".equalsIgnoreCase(tokenName)) {
// This specifies the description for the attribute type. It
// is an arbitrary string of characters enclosed in single
// quotes.
- description = readQuotedString(reader);
+ useBuilder.description(readQuotedString(reader));
} else if ("obsolete".equalsIgnoreCase(tokenName)) {
// This indicates whether the attribute type should be
- // considered obsolete. We do not need to do any more
- // parsing for this token.
- isObsolete = true;
+ // considered obsolete.
+ useBuilder.obsolete(true);
} else if ("applies".equalsIgnoreCase(tokenName)) {
attributes = readOIDs(reader, allowsMalformedNamesAndOptions());
} else if (tokenName.matches("^X-[A-Za-z_-]+$")) {
@@ -862,10 +857,7 @@
// or an open parenthesis followed by one or more values in
// single quotes separated by spaces followed by a close
// parenthesis.
- if (extraProperties.isEmpty()) {
- extraProperties = new HashMap<String, List<String>>();
- }
- extraProperties.put(tokenName, readExtensions(reader));
+ useBuilder.extraProperties(tokenName, readExtensions(reader));
} else {
throw new LocalizedIllegalArgumentException(
ERR_ATTR_SYNTAX_MRUSE_ILLEGAL_TOKEN1.get(definition, tokenName));
@@ -876,21 +868,13 @@
if (attributes == null || attributes.size() == 0) {
throw new LocalizedIllegalArgumentException(ERR_ATTR_SYNTAX_MRUSE_NO_ATTR.get(definition));
}
+ useBuilder.attributes(attributes);
- if (!extraProperties.isEmpty()) {
- extraProperties = Collections.unmodifiableMap(extraProperties);
- }
-
- final MatchingRuleUse use =
- new MatchingRuleUse(oid, names, description, isObsolete, attributes,
- extraProperties, definition);
- addMatchingRuleUse(use, overwrite);
+ return overwrite ? useBuilder.addToSchemaOverwrite() : useBuilder.addToSchema();
} catch (final DecodeException e) {
- final LocalizableMessage msg =
- ERR_ATTR_SYNTAX_MRUSE_INVALID1.get(definition, e.getMessageObject());
+ final LocalizableMessage msg = ERR_ATTR_SYNTAX_MRUSE_INVALID1.get(definition, e.getMessageObject());
throw new LocalizedIllegalArgumentException(msg, e.getCause());
}
- return this;
}
/**
@@ -949,41 +933,23 @@
}
/**
- * Adds the provided matching rule use definition to this schema builder.
+ * Returns a builder which can be used for incrementally constructing a new
+ * matching rule use before adding it to the schema. Example usage:
+ *
+ * <pre>
+ * SchemaBuilder builder = ...;
+ * builder.buildMatchingRuleUse("matchingrule-oid")
+ * .name("matching rule use name")
+ * .addToSchema();
+ * </pre>
*
* @param oid
- * The OID of the matching rule use definition.
- * @param names
- * The user-friendly names of the matching rule use definition.
- * @param description
- * The description of the matching rule use definition.
- * @param obsolete
- * {@code true} if the matching rule use definition is obsolete,
- * otherwise {@code false}.
- * @param attributeOIDs
- * The list of attribute types the matching rule applies to.
- * @param extraProperties
- * A map containing additional properties associated with the
- * matching rule use definition.
- * @param overwrite
- * {@code true} if any existing matching rule use with the same
- * OID should be overwritten.
- * @return A reference to this schema builder.
- * @throws ConflictingSchemaElementException
- * If {@code overwrite} was {@code false} and a conflicting
- * schema element was found.
+ * The OID of the matching rule definition.
+ * @return A builder to continue building the MatchingRuleUse.
*/
- SchemaBuilder addMatchingRuleUse(final String oid, final List<String> names,
- final String description, final boolean obsolete, final Set<String> attributeOIDs,
- final Map<String, List<String>> extraProperties, final boolean overwrite) {
+ public MatchingRuleUse.Builder buildMatchingRuleUse(final String oid) {
lazyInitBuilder();
-
- final MatchingRuleUse use =
- new MatchingRuleUse(oid, unmodifiableCopyOfList(names), description, obsolete,
- unmodifiableCopyOfSet(attributeOIDs),
- unmodifiableCopyOfExtraProperties(extraProperties), null);
- addMatchingRuleUse(use, overwrite);
- return this;
+ return new MatchingRuleUse.Builder(oid, this);
}
/**
@@ -1254,6 +1220,21 @@
}
/**
+ * Returns a matching rule use builder whose fields are initialized to the
+ * values of the provided matching rule use object. This method should be used when
+ * duplicating matching rule uses from external schemas or when modifying
+ * existing matching rule uses.
+ *
+ * @param matchingRuleUse
+ * The matching rule use source.
+ * @return A builder to continue building the MatchingRuleUse.
+ */
+ public MatchingRuleUse.Builder buildMatchingRuleUse(final MatchingRuleUse matchingRuleUse) {
+ lazyInitBuilder();
+ return new MatchingRuleUse.Builder(matchingRuleUse, this);
+ }
+
+ /**
* Returns a name form builder whose fields are initialized to the
* values of the provided name form. This method should be used when
* duplicating name forms from external schemas or when modifying
@@ -2258,7 +2239,7 @@
return this;
}
- private void addMatchingRuleUse(final MatchingRuleUse use, final boolean overwrite) {
+ SchemaBuilder addMatchingRuleUse(final MatchingRuleUse use, final boolean overwrite) {
MatchingRuleUse conflictingUse;
if (numericOID2MatchingRuleUses.containsKey(use.getMatchingRuleOID())) {
conflictingUse = numericOID2MatchingRuleUses.get(use.getMatchingRuleOID());
@@ -2285,6 +2266,8 @@
uses.add(use);
}
}
+
+ return this;
}
SchemaBuilder addMatchingRule(final MatchingRule rule, final boolean overwrite) {
@@ -2402,7 +2385,7 @@
}
for (final MatchingRuleUse matchingRuleUse : schema.getMatchingRuleUses()) {
- addMatchingRuleUse(matchingRuleUse.duplicate(), overwrite);
+ addMatchingRuleUse(matchingRuleUse, overwrite);
}
for (final AttributeType attributeType : schema.getAttributeTypes()) {
diff --git a/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/MatchingRuleUseBuilderTestCase.java b/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/MatchingRuleUseBuilderTestCase.java
new file mode 100644
index 0000000..cf88dec
--- /dev/null
+++ b/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/MatchingRuleUseBuilderTestCase.java
@@ -0,0 +1,157 @@
+/*
+ * 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 2015 ForgeRock AS.
+ */
+package org.forgerock.opendj.ldap.schema;
+
+import static java.util.Collections.*;
+
+import static org.fest.assertions.Assertions.*;
+import static org.fest.assertions.MapAssert.*;
+import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
+
+import org.testng.annotations.Test;
+
+public class MatchingRuleUseBuilderTestCase extends AbstractSchemaTestCase {
+
+ @Test
+ public void testValidMatchingRuleUse() {
+ final Schema schema = new SchemaBuilder(Schema.getCoreSchema())
+ .buildMatchingRuleUse(EMR_CASE_EXACT_OID)
+ .names("Matching rule use test")
+ .description("Matching rule use description")
+ .attributes("2.5.4.40", "2.5.4.52", "2.5.4.53")
+ .extraProperties("property name", "property value")
+ .addToSchema()
+ .toSchema();
+
+ assertThat(schema.getWarnings()).isEmpty();
+ final MatchingRuleUse mru = schema.getMatchingRuleUse(EMR_CASE_EXACT_OID);
+ assertThat(mru).isNotNull();
+ assertThat(mru.getMatchingRuleOID()).isEqualTo(EMR_CASE_EXACT_OID);
+ assertThat(mru.getNames()).containsOnly("Matching rule use test");
+ assertThat(mru.getDescription()).isEqualTo("Matching rule use description");
+ assertThat(mru.getAttributes()).containsOnly(schema.getAttributeType("2.5.4.40"),
+ schema.getAttributeType("2.5.4.52"),
+ schema.getAttributeType("2.5.4.53"));
+ assertThat(mru.getExtraProperties()).includes(entry("property name", singletonList("property value")));
+ assertThat(mru.isObsolete()).isFalse();
+ }
+
+ @Test
+ public void testCopyConstructor() {
+ final SchemaBuilder builder = new SchemaBuilder(Schema.getCoreSchema())
+ .buildMatchingRuleUse(EMR_BIT_STRING_OID)
+ .description("Matching rule use description")
+ .names("Matching rule use test")
+ .attributes("2.5.4.40")
+ .extraProperties("property name", "property value")
+ .addToSchema();
+ final Schema schema = builder.toSchema();
+ assertThat(schema.getWarnings()).isEmpty();
+
+ final Schema schemaCopy = builder.buildMatchingRuleUse(schema.getMatchingRuleUse(EMR_BIT_STRING_OID))
+ .oid(EMR_OCTET_STRING_OID)
+ .names("Matching rule use test copy")
+ .attributes("2.5.4.53")
+ .addToSchema()
+ .toSchema();
+ assertThat(schemaCopy.getWarnings()).isEmpty();
+
+ final MatchingRuleUse mru = schemaCopy.getMatchingRuleUse(EMR_OCTET_STRING_OID);
+ assertThat(mru).isNotNull();
+ assertThat(mru.getMatchingRuleOID()).isEqualTo(EMR_OCTET_STRING_OID);
+ assertThat(mru.getNames()).containsOnly("Matching rule use test", "Matching rule use test copy");
+ assertThat(mru.getDescription()).isEqualTo("Matching rule use description");
+ assertThat(mru.getAttributes()).containsOnly(schema.getAttributeType("2.5.4.40"),
+ schema.getAttributeType("2.5.4.53"));
+ assertThat(mru.getExtraProperties()).includes(entry("property name", singletonList("property value")));
+ assertThat(mru.isObsolete()).isFalse();
+ }
+
+ @Test(expectedExceptions = ConflictingSchemaElementException.class)
+ public void testBuilderDoesNotAllowOverwrite() throws Exception {
+ final SchemaBuilder builder = new SchemaBuilder(Schema.getCoreSchema())
+ .buildMatchingRuleUse(EMR_BIT_STRING_OID)
+ .names("Matching rule use test")
+ .attributes("2.5.4.40")
+ .addToSchema();
+
+ builder.buildMatchingRuleUse(EMR_BIT_STRING_OID)
+ .addToSchema()
+ .toSchema();
+ }
+
+ @Test(expectedExceptions = NullPointerException.class)
+ public void testBuilderDoesNotAllowNullMatchingRuleOID() throws Exception {
+ new SchemaBuilder(Schema.getCoreSchema())
+ .buildMatchingRuleUse((String) null)
+ .addToSchema();
+ }
+
+ @Test
+ public void testBuilderRemoveAll() throws Exception {
+ final MatchingRuleUse.Builder builder = new SchemaBuilder(Schema.getCoreSchema())
+ .buildMatchingRuleUse(EMR_BIT_STRING_OID)
+ .description("Matching rule use description")
+ .names("Matching rule use test")
+ .attributes("2.5.4.40", "2.5.4.52")
+ .extraProperties("property name", "property value");
+
+ final Schema schema = builder.removeAllNames()
+ .removeAllAttributes()
+ .removeAllExtraProperties()
+ .addToSchema()
+ .toSchema();
+ assertThat(schema.getWarnings()).isEmpty();
+
+ final MatchingRuleUse mru = schema.getMatchingRuleUse(EMR_BIT_STRING_OID);
+ assertThat(mru.getNames()).isEmpty();
+ assertThat(mru.getAttributes()).isEmpty();
+ assertThat(mru.getExtraProperties()).isEmpty();
+ }
+
+ @Test
+ public void testBuilderRemove() throws Exception {
+ final MatchingRuleUse.Builder builder = new SchemaBuilder(Schema.getCoreSchema())
+ .buildMatchingRuleUse(EMR_OCTET_STRING_OID)
+ .description("Matching rule use description")
+ .names("Matching rule use test", "I should not be in the schema")
+ .attributes("2.5.4.52", "I should not be in the schema")
+ .extraProperties("property name", "property value");
+
+ final Schema schema = builder.removeName("I should not be in the schema")
+ .removeAttribute("I should not be in the schema")
+ .removeExtraProperty("property name")
+ .addToSchema()
+ .toSchema();
+ assertThat(schema.getWarnings()).isEmpty();
+
+ final MatchingRuleUse mru = schema.getMatchingRuleUse(EMR_OCTET_STRING_OID);
+ assertThat(mru.getNames()).containsOnly("Matching rule use test");
+ assertThat(mru.getAttributes()).containsOnly(schema.getAttributeType("2.5.4.52"));
+ assertThat(mru.getExtraProperties()).isEmpty();
+ }
+
+}
--
Gitblit v1.10.0