mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Gaetan Boismal
14.33.2015 cc1f99dc6bf00e63e6a1df0c6f39d9e37d176b04
OPENDJ-1652 (CR-5591) Add fluent builder for object class
4 files modified
1 files added
1313 ■■■■■ changed files
opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CoreSchemaImpl.java 502 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectClass.java 403 ●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java 174 ●●●●● patch | view | raw | blame | history
opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/ObjectClassBuilderTestCase.java 208 ●●●●● patch | view | raw | blame | history
opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaBuilderTestCase.java 26 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CoreSchemaImpl.java
@@ -22,12 +22,13 @@
 *
 *
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 *      Portions copyright 2013-2014 ForgeRock AS.
 *      Portions copyright 2013-2015 ForgeRock AS.
 *      Portions copyright 2014 Manuel Gaupp
 */
package org.forgerock.opendj.ldap.schema;
import static org.forgerock.opendj.ldap.schema.CollationMatchingRulesImpl.*;
import static org.forgerock.opendj.ldap.schema.ObjectClassType.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import static org.forgerock.opendj.ldap.schema.TimeBasedMatchingRulesImpl.*;
@@ -35,12 +36,10 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
final class CoreSchemaImpl {
@@ -73,8 +72,6 @@
    private static final String EMPTY_STRING = "".intern();
    private static final Set<String> EMPTY_STRING_SET = Collections.emptySet();
    private static final Schema SINGLETON;
    /**
@@ -337,10 +334,13 @@
                .singletonList("authPassword"), "password authentication information", false, null,
                EMR_AUTH_PASSWORD_EXACT_OID, null, null, null, SYNTAX_AUTH_PASSWORD_OID, false,
                false, false, AttributeUsage.USER_APPLICATIONS, RFC3112_ORIGIN, false);
        builder.addObjectClass("1.3.6.1.4.1.4203.1.4.7", Collections
                .singletonList("authPasswordObject"), "authentication password mix in class",
                false, EMPTY_STRING_SET, EMPTY_STRING_SET, Collections.singleton("authPassword"),
                ObjectClassType.AUXILIARY, RFC3112_ORIGIN, false);
        builder.buildObjectClass("1.3.6.1.4.1.4203.1.4.7")
                .names("authPasswordObject")
                .type(AUXILIARY)
                .description("authentication password mix in class")
                .optionalAttributes("authPassword")
                .extraProperties(RFC3112_ORIGIN)
                .addToSchema();
    }
    private static void addRFC4519(final SchemaBuilder builder) {
@@ -540,229 +540,137 @@
                SYNTAX_BIT_STRING_OID, false, false, false, AttributeUsage.USER_APPLICATIONS,
                RFC4519_ORIGIN, false);
        Set<String> attrs = new HashSet<String>();
        attrs.add("seeAlso");
        attrs.add("ou");
        attrs.add("l");
        attrs.add("description");
        builder.buildObjectClass("2.5.6.11")
                .names("applicationProcess")
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .requiredAttributes("cn")
                .optionalAttributes("seeAlso", "ou", "l", "description")
                .extraProperties(RFC4519_ORIGIN)
                .addToSchema();
        builder.addObjectClass("2.5.6.11", Collections.singletonList("applicationProcess"),
                EMPTY_STRING, false, Collections.singleton(TOP_OBJECTCLASS_NAME), Collections
                        .singleton("cn"), attrs, ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
        builder.buildObjectClass("2.5.6.2")
                .names("country")
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .requiredAttributes("c")
                .optionalAttributes("searchGuide", "description")
                .extraProperties(RFC4519_ORIGIN)
                .addToSchema();
        attrs = new HashSet<String>();
        attrs.add("searchGuide");
        attrs.add("description");
        builder.buildObjectClass("1.3.6.1.4.1.1466.344")
                .names("dcObject")
                .type(AUXILIARY)
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .requiredAttributes("dc")
                .extraProperties(RFC4519_ORIGIN)
                .addToSchema();
        builder.addObjectClass("2.5.6.2", Collections.singletonList("country"), EMPTY_STRING,
                false, Collections.singleton(TOP_OBJECTCLASS_NAME), Collections.singleton("c"),
                attrs, ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
        builder.buildObjectClass("2.5.6.14")
                .names("device")
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .requiredAttributes("cn")
                .optionalAttributes("serialNumber", "seeAlso", "owner", "ou", "o", "l", "description")
                .extraProperties(RFC4519_ORIGIN)
                .addToSchema();
        builder.addObjectClass("1.3.6.1.4.1.1466.344", Collections.singletonList("dcObject"),
                EMPTY_STRING, false, Collections.singleton(TOP_OBJECTCLASS_NAME), Collections
                        .singleton("dc"), EMPTY_STRING_SET, ObjectClassType.AUXILIARY,
                RFC4519_ORIGIN, false);
        builder.buildObjectClass("2.5.6.9")
                .names("groupOfNames")
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .requiredAttributes("member", "cn")
                .optionalAttributes("businessCategory", "seeAlso", "owner", "ou", "o", "description")
                .extraProperties(RFC4519_ORIGIN)
                .addToSchema();
        attrs = new HashSet<String>();
        attrs.add("serialNumber");
        attrs.add("seeAlso");
        attrs.add("owner");
        attrs.add("ou");
        attrs.add("o");
        attrs.add("l");
        attrs.add("description");
        builder.buildObjectClass("2.5.6.17")
                .names("groupOfUniqueNames")
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .requiredAttributes("member", "cn")
                .optionalAttributes("businessCategory", "seeAlso", "owner", "ou", "o", "description")
                .extraProperties(RFC4519_ORIGIN)
                .addToSchema();
        builder.addObjectClass("2.5.6.14", Collections.singletonList("device"), EMPTY_STRING,
                false, Collections.singleton(TOP_OBJECTCLASS_NAME), Collections.singleton("cn"),
                attrs, ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
        builder.buildObjectClass("2.5.6.3")
                .names("locality")
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .optionalAttributes("street", "seeAlso", "searchGuide", "st", "l", "description")
                .extraProperties(RFC4519_ORIGIN)
                .addToSchema();
        Set<String> must = new HashSet<String>();
        must.add("member");
        must.add("cn");
        builder.buildObjectClass("2.5.6.4")
                .names("organization")
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .requiredAttributes("o")
                .optionalAttributes("userPassword", "searchGuide", "seeAlso", "businessCategory", "x121Address",
                        "registeredAddress", "destinationIndicator", "preferredDeliveryMethod", "telexNumber",
                        "teletexTerminalIdentifier", "telephoneNumber", "internationalISDNNumber",
                        "facsimileTelephoneNumber", "street", "postOfficeBox", "postalCode", "postalAddress",
                        "physicalDeliveryOfficeName", "st", "l", "description")
                .extraProperties(RFC4519_ORIGIN)
                .addToSchema();
        attrs = new HashSet<String>();
        attrs.add("businessCategory");
        attrs.add("seeAlso");
        attrs.add("owner");
        attrs.add("ou");
        attrs.add("o");
        attrs.add("description");
        builder.buildObjectClass("2.5.6.7")
                .names("organizationalPerson")
                .superiorObjectClasses("person")
                .optionalAttributes("title", "x121Address", "registeredAddress", "destinationIndicator",
                        "preferredDeliveryMethod", "telexNumber", "teletexTerminalIdentifier", "telephoneNumber",
                        "internationalISDNNumber", "facsimileTelephoneNumber", "street", "postOfficeBox",
                        "postalCode", "postalAddress", "physicalDeliveryOfficeName", "ou", "st", "l")
                .extraProperties(RFC4519_ORIGIN)
                .addToSchema();
        builder.addObjectClass("2.5.6.9", Collections.singletonList("groupOfNames"), EMPTY_STRING,
                false, Collections.singleton(TOP_OBJECTCLASS_NAME), must, attrs,
                ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
        builder.buildObjectClass("2.5.6.8")
                .names("organizationalRole")
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .requiredAttributes("cn")
                .optionalAttributes("x121Address", "registeredAddress", "destinationIndicator",
                        "preferredDeliveryMethod", "telexNumber", "teletexTerminalIdentifier", "telephoneNumber",
                        "internationalISDNNumber", "facsimileTelephoneNumber", "seeAlso", "roleOccupant",
                        "preferredDeliveryMethod", "street", "postOfficeBox", "postalCode", "postalAddress",
                        "physicalDeliveryOfficeName", "ou", "st", "l", "description")
                .extraProperties(RFC4519_ORIGIN)
                .addToSchema();
        attrs = new HashSet<String>();
        attrs.add("businessCategory");
        attrs.add("seeAlso");
        attrs.add("owner");
        attrs.add("ou");
        attrs.add("o");
        attrs.add("description");
        builder.buildObjectClass("2.5.6.5")
                .names("organizationalUnit")
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .requiredAttributes("ou")
                .optionalAttributes("businessCategory", "description", "destinationIndicator",
                        "facsimileTelephoneNumber", "internationalISDNNumber", "l", "physicalDeliveryOfficeName",
                        "postalAddress", "postalCode", "postOfficeBox", "preferredDeliveryMethod",
                        "registeredAddress", "searchGuide", "seeAlso", "st", "street", "telephoneNumber",
                        "teletexTerminalIdentifier", "telexNumber", "userPassword", "x121Address")
                .extraProperties(RFC4519_ORIGIN)
                .addToSchema();
        builder.addObjectClass("2.5.6.17", Collections.singletonList("groupOfUniqueNames"),
                EMPTY_STRING, false, Collections.singleton(TOP_OBJECTCLASS_NAME), must, attrs,
                ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
        builder.buildObjectClass("2.5.6.6")
                .names("person")
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .requiredAttributes("sn", "cn")
                .optionalAttributes("userPassword", "telephoneNumber", "destinationIndicator", "seeAlso", "description")
                .extraProperties(RFC4519_ORIGIN)
                .addToSchema();
        attrs = new HashSet<String>();
        attrs.add("street");
        attrs.add("seeAlso");
        attrs.add("searchGuide");
        attrs.add("st");
        attrs.add("l");
        attrs.add("description");
        builder.buildObjectClass("2.5.6.10")
                .names("residentialPerson")
                .superiorObjectClasses("person")
                .requiredAttributes("l")
                .optionalAttributes("businessCategory", "x121Address", "registeredAddress", "destinationIndicator",
                        "preferredDeliveryMethod", "telexNumber", "teletexTerminalIdentifier", "telephoneNumber",
                        "internationalISDNNumber", "facsimileTelephoneNumber", "preferredDeliveryMethod", "street",
                        "postOfficeBox", "postalCode", "postalAddress", "physicalDeliveryOfficeName", "st", "l")
                .extraProperties(RFC4519_ORIGIN)
                .addToSchema();
        builder.addObjectClass("2.5.6.3", Collections.singletonList("locality"), EMPTY_STRING,
                false, Collections.singleton(TOP_OBJECTCLASS_NAME), EMPTY_STRING_SET, attrs,
                ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
        attrs = new HashSet<String>();
        attrs.add("userPassword");
        attrs.add("searchGuide");
        attrs.add("seeAlso");
        attrs.add("businessCategory");
        attrs.add("x121Address");
        attrs.add("registeredAddress");
        attrs.add("destinationIndicator");
        attrs.add("preferredDeliveryMethod");
        attrs.add("telexNumber");
        attrs.add("teletexTerminalIdentifier");
        attrs.add("telephoneNumber");
        attrs.add("internationalISDNNumber");
        attrs.add("facsimileTelephoneNumber");
        attrs.add("street");
        attrs.add("postOfficeBox");
        attrs.add("postalCode");
        attrs.add("postalAddress");
        attrs.add("physicalDeliveryOfficeName");
        attrs.add("st");
        attrs.add("l");
        attrs.add("description");
        builder.addObjectClass("2.5.6.4", Collections.singletonList("organization"), EMPTY_STRING,
                false, Collections.singleton(TOP_OBJECTCLASS_NAME), Collections.singleton("o"),
                attrs, ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
        attrs = new HashSet<String>();
        attrs.add("title");
        attrs.add("x121Address");
        attrs.add("registeredAddress");
        attrs.add("destinationIndicator");
        attrs.add("preferredDeliveryMethod");
        attrs.add("telexNumber");
        attrs.add("teletexTerminalIdentifier");
        attrs.add("telephoneNumber");
        attrs.add("internationalISDNNumber");
        attrs.add("facsimileTelephoneNumber");
        attrs.add("street");
        attrs.add("postOfficeBox");
        attrs.add("postalCode");
        attrs.add("postalAddress");
        attrs.add("physicalDeliveryOfficeName");
        attrs.add("ou");
        attrs.add("st");
        attrs.add("l");
        builder.addObjectClass("2.5.6.7", Collections.singletonList("organizationalPerson"),
                EMPTY_STRING, false, Collections.singleton("person"), EMPTY_STRING_SET, attrs,
                ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
        attrs = new HashSet<String>();
        attrs.add("x121Address");
        attrs.add("registeredAddress");
        attrs.add("destinationIndicator");
        attrs.add("preferredDeliveryMethod");
        attrs.add("telexNumber");
        attrs.add("teletexTerminalIdentifier");
        attrs.add("telephoneNumber");
        attrs.add("internationalISDNNumber");
        attrs.add("facsimileTelephoneNumber");
        attrs.add("seeAlso");
        attrs.add("roleOccupant");
        attrs.add("preferredDeliveryMethod");
        attrs.add("street");
        attrs.add("postOfficeBox");
        attrs.add("postalCode");
        attrs.add("postalAddress");
        attrs.add("physicalDeliveryOfficeName");
        attrs.add("ou");
        attrs.add("st");
        attrs.add("l");
        attrs.add("description");
        builder.addObjectClass("2.5.6.8", Collections.singletonList("organizationalRole"),
                EMPTY_STRING, false, Collections.singleton(TOP_OBJECTCLASS_NAME), Collections
                        .singleton("cn"), attrs, ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
        attrs = new HashSet<String>();
        attrs.add("businessCategory");
        attrs.add("description");
        attrs.add("destinationIndicator");
        attrs.add("facsimileTelephoneNumber");
        attrs.add("internationalISDNNumber");
        attrs.add("l");
        attrs.add("physicalDeliveryOfficeName");
        attrs.add("postalAddress");
        attrs.add("postalCode");
        attrs.add("postOfficeBox");
        attrs.add("preferredDeliveryMethod");
        attrs.add("registeredAddress");
        attrs.add("searchGuide");
        attrs.add("seeAlso");
        attrs.add("st");
        attrs.add("street");
        attrs.add("telephoneNumber");
        attrs.add("teletexTerminalIdentifier");
        attrs.add("telexNumber");
        attrs.add("userPassword");
        attrs.add("x121Address");
        builder.addObjectClass("2.5.6.5", Collections.singletonList("organizationalUnit"),
                EMPTY_STRING, false, Collections.singleton(TOP_OBJECTCLASS_NAME), Collections
                        .singleton("ou"), attrs, ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
        must = new HashSet<String>();
        must.add("sn");
        must.add("cn");
        attrs = new HashSet<String>();
        attrs.add("userPassword");
        attrs.add("telephoneNumber");
        attrs.add("destinationIndicator");
        attrs.add("seeAlso");
        attrs.add("description");
        builder.addObjectClass("2.5.6.6", Collections.singletonList("person"), EMPTY_STRING, false,
                Collections.singleton(TOP_OBJECTCLASS_NAME), must, attrs,
                ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
        attrs = new HashSet<String>();
        attrs.add("businessCategory");
        attrs.add("x121Address");
        attrs.add("registeredAddress");
        attrs.add("destinationIndicator");
        attrs.add("preferredDeliveryMethod");
        attrs.add("telexNumber");
        attrs.add("teletexTerminalIdentifier");
        attrs.add("telephoneNumber");
        attrs.add("internationalISDNNumber");
        attrs.add("facsimileTelephoneNumber");
        attrs.add("preferredDeliveryMethod");
        attrs.add("street");
        attrs.add("postOfficeBox");
        attrs.add("postalCode");
        attrs.add("postalAddress");
        attrs.add("physicalDeliveryOfficeName");
        attrs.add("st");
        attrs.add("l");
        builder.addObjectClass("2.5.6.10", Collections.singletonList("residentialPerson"),
                EMPTY_STRING, false, Collections.singleton("person"), Collections.singleton("l"),
                attrs, ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
        builder.addObjectClass("1.3.6.1.1.3.1", Collections.singletonList("uidObject"),
                EMPTY_STRING, false, Collections.singleton(TOP_OBJECTCLASS_NAME), Collections
                        .singleton("uid"), attrs, ObjectClassType.AUXILIARY, RFC4519_ORIGIN, false);
        builder.buildObjectClass("1.3.6.1.1.3.1")
                .names("uidObject")
                .type(AUXILIARY)
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .requiredAttributes("uid")
                .optionalAttributes("businessCategory", "x121Address", "registeredAddress", "destinationIndicator",
                        "preferredDeliveryMethod", "telexNumber", "teletexTerminalIdentifier", "telephoneNumber",
                        "internationalISDNNumber", "facsimileTelephoneNumber", "preferredDeliveryMethod", "street",
                        "postOfficeBox", "postalCode", "postalAddress", "physicalDeliveryOfficeName", "st", "l")
                .extraProperties(RFC4519_ORIGIN)
                .addToSchema();
    }
    private static void addRFC4523(final SchemaBuilder builder) {
@@ -811,53 +719,79 @@
                null, null, SYNTAX_CERTLIST_OID, false, false, false,
                AttributeUsage.USER_APPLICATIONS, RFC4523_ORIGIN, false);
        builder.addObjectClass("2.5.6.21", Collections.singletonList("pkiUser"),
                "X.509 PKI User", false, Collections.singleton(TOP_OBJECTCLASS_NAME), EMPTY_STRING_SET,
                Collections.singleton("userCertificate"), ObjectClassType.AUXILIARY, RFC4523_ORIGIN, false);
        builder.buildObjectClass("2.5.6.21")
                .names("pkiUser")
                .type(AUXILIARY)
                .description("X.509 PKI User")
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .optionalAttributes("userCertificate")
                .extraProperties(RFC4523_ORIGIN)
                .addToSchema();
        Set<String> attrs = new HashSet<String>();
        attrs.add("cACertificate");
        attrs.add("certificateRevocationList");
        attrs.add("authorityRevocationList");
        attrs.add("crossCertificatePair");
        builder.buildObjectClass("2.5.6.22")
                .names("pkiCA")
                .type(AUXILIARY)
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .description("X.509 PKI Certificate Authority")
                .optionalAttributes("cACertificate", "certificateRevocationList",
                    "authorityRevocationList", "crossCertificatePair")
                .extraProperties(RFC4523_ORIGIN)
                .addToSchema();
        builder.addObjectClass("2.5.6.22", Collections.singletonList("pkiCA"),
                "X.509 PKI Certificate Authority", false, Collections.singleton(TOP_OBJECTCLASS_NAME),
                EMPTY_STRING_SET, attrs, ObjectClassType.AUXILIARY, RFC4523_ORIGIN, false);
        builder.buildObjectClass("2.5.6.19")
                .names("cRLDistributionPoint")
                .description("X.509 CRL distribution point")
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .requiredAttributes("cn")
                .optionalAttributes("certificateRevocationList", "authorityRevocationList", "deltaRevocationList")
                .extraProperties(RFC4523_ORIGIN)
                .addToSchema();
        attrs = new HashSet<String>();
        attrs.add("certificateRevocationList");
        attrs.add("authorityRevocationList");
        attrs.add("deltaRevocationList");
        builder.buildObjectClass("2.5.6.23")
                .names("deltaCRL")
                .type(AUXILIARY)
                .description("X.509 delta CRL")
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .optionalAttributes("deltaRevocationList")
                .extraProperties(RFC4523_ORIGIN)
                .addToSchema();
        builder.addObjectClass("2.5.6.19", Collections.singletonList("cRLDistributionPoint"),
                "X.509 CRL distribution point", false, Collections.singleton(TOP_OBJECTCLASS_NAME),
                Collections.singleton("cn"), attrs, ObjectClassType.STRUCTURAL, RFC4523_ORIGIN, false);
        builder.buildObjectClass("2.5.6.15")
                .names("strongAuthenticationUser")
                .type(AUXILIARY)
                .description("X.521 strong authentication user")
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .requiredAttributes("userCertificate")
                .extraProperties(RFC4523_ORIGIN)
                .addToSchema();
        builder.addObjectClass("2.5.6.23", Collections.singletonList("deltaCRL"),
                "X.509 delta CRL", false, Collections.singleton(TOP_OBJECTCLASS_NAME), EMPTY_STRING_SET,
                Collections.singleton("deltaRevocationList"), ObjectClassType.AUXILIARY, RFC4523_ORIGIN, false);
        builder.addObjectClass("2.5.6.15", Collections.singletonList("strongAuthenticationUser"),
                "X.521 strong authentication user", false, Collections.singleton(TOP_OBJECTCLASS_NAME),
                Collections.singleton("userCertificate"), EMPTY_STRING_SET, ObjectClassType.AUXILIARY,
                RFC4523_ORIGIN, false);
        builder.addObjectClass("2.5.6.18", Collections.singletonList("userSecurityInformation"),
                "X.521 user security information", false, Collections.singleton(TOP_OBJECTCLASS_NAME), EMPTY_STRING_SET,
                Collections.singleton("supportedAlgorithms"), ObjectClassType.AUXILIARY, RFC4523_ORIGIN, false);
        builder.buildObjectClass("2.5.6.18")
                .names("userSecurityInformation")
                .type(AUXILIARY)
                .description("X.521 user security information")
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .optionalAttributes("supportedAlgorithms")
                .extraProperties(RFC4523_ORIGIN)
                .addToSchema();
        attrs = new HashSet<String>();
        attrs.add("authorityRevocationList");
        attrs.add("certificateRevocationList");
        attrs.add("cACertificate");
        builder.buildObjectClass("2.5.6.16")
                .names("certificationAuthority")
                .type(AUXILIARY)
                .description("X.509 certificate authority")
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .requiredAttributes("authorityRevocationList", "certificateRevocationList", "cACertificate")
                .optionalAttributes("crossCertificatePair")
                .extraProperties(RFC4523_ORIGIN)
                .addToSchema();
        builder.addObjectClass("2.5.6.16", Collections.singletonList("certificationAuthority"),
                "X.509 certificate authority", false, Collections.singleton(TOP_OBJECTCLASS_NAME), attrs,
                Collections.singleton("crossCertificatePair"), ObjectClassType.AUXILIARY, RFC4523_ORIGIN, false);
        builder.addObjectClass("2.5.6.16.2", Collections.singletonList("certificationAuthority-V2"),
                "X.509 certificate authority, version 2", false, Collections.singleton("certificationAuthority"),
                EMPTY_STRING_SET, Collections.singleton("deltaRevocationList"), ObjectClassType.AUXILIARY,
                RFC4523_ORIGIN, false);
        builder.buildObjectClass("2.5.6.16.2")
                .names("certificationAuthority-V2")
                .type(AUXILIARY)
                .description("X.509 certificate authority, version 2")
                .superiorObjectClasses("certificationAuthority")
                .optionalAttributes("deltaRevocationList")
                .extraProperties(RFC4523_ORIGIN)
                .addToSchema();
    }
    private static void addRFC4530(final SchemaBuilder builder) {
@@ -1267,32 +1201,36 @@
    }
    private static void defaultObjectClasses(final SchemaBuilder builder) {
        builder.addObjectClass(TOP_OBJECTCLASS_OID,
                Collections.singletonList(TOP_OBJECTCLASS_NAME), TOP_OBJECTCLASS_DESCRIPTION,
                false, EMPTY_STRING_SET, Collections.singleton("objectClass"), EMPTY_STRING_SET,
                ObjectClassType.ABSTRACT, RFC4512_ORIGIN, false);
        builder.buildObjectClass(TOP_OBJECTCLASS_OID)
                .names(TOP_OBJECTCLASS_NAME)
                .type(ABSTRACT)
                .description(TOP_OBJECTCLASS_DESCRIPTION)
                .requiredAttributes("objectClass")
                .extraProperties(RFC4512_ORIGIN)
                .addToSchema();
        builder.addObjectClass("2.5.6.1", Collections.singletonList("alias"), EMPTY_STRING, false,
                Collections.singleton("top"), Collections.singleton("aliasedObjectName"),
                EMPTY_STRING_SET, ObjectClassType.STRUCTURAL, RFC4512_ORIGIN, false);
        builder.buildObjectClass("2.5.6.1")
                .names("alias")
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .requiredAttributes("aliasedObjectName")
                .extraProperties(RFC4512_ORIGIN)
                .addToSchema();
        builder.addObjectClass(EXTENSIBLE_OBJECT_OBJECTCLASS_OID, Collections
                .singletonList(EXTENSIBLE_OBJECT_OBJECTCLASS_NAME), EMPTY_STRING, false,
                Collections.singleton(TOP_OBJECTCLASS_NAME), EMPTY_STRING_SET, EMPTY_STRING_SET,
                ObjectClassType.AUXILIARY, RFC4512_ORIGIN, false);
        builder.buildObjectClass(EXTENSIBLE_OBJECT_OBJECTCLASS_OID)
                .names(EXTENSIBLE_OBJECT_OBJECTCLASS_NAME)
                .type(AUXILIARY)
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .extraProperties(RFC4512_ORIGIN)
                .addToSchema();
        final Set<String> subschemaAttrs = new HashSet<String>();
        subschemaAttrs.add("dITStructureRules");
        subschemaAttrs.add("nameForms");
        subschemaAttrs.add("ditContentRules");
        subschemaAttrs.add("objectClasses");
        subschemaAttrs.add("attributeTypes");
        subschemaAttrs.add("matchingRules");
        subschemaAttrs.add("matchingRuleUse");
        builder.addObjectClass("2.5.20.1", Collections.singletonList("subschema"), EMPTY_STRING,
                false, Collections.singleton(TOP_OBJECTCLASS_NAME), EMPTY_STRING_SET,
                subschemaAttrs, ObjectClassType.AUXILIARY, RFC4512_ORIGIN, false);
        builder.buildObjectClass("2.5.20.1")
                .names("subschema")
                .type(AUXILIARY)
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .optionalAttributes("dITStructureRules", "nameForms", "ditContentRules", "objectClasses",
                    "attributeTypes", "matchingRules", "matchingRuleUse")
                .extraProperties(RFC4512_ORIGIN)
                .addToSchema();
    }
    private static void defaultSyntaxes(final SchemaBuilder builder) {
opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectClass.java
@@ -22,24 +22,31 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2015 ForgeRock AS.
 */
package org.forgerock.opendj.ldap.schema;
import static com.forgerock.opendj.ldap.CoreMessages.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.EXTENSIBLE_OBJECT_OBJECTCLASS_NAME;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.EXTENSIBLE_OBJECT_OBJECTCLASS_OID;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.TOP_OBJECTCLASS_NAME;
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;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.util.Reject;
import static java.util.Arrays.*;
import static java.util.Collections.*;
import static org.forgerock.opendj.ldap.schema.ObjectClassType.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import static org.forgerock.opendj.ldap.schema.SchemaUtils.*;
import static com.forgerock.opendj.ldap.CoreMessages.*;
/**
 * This class defines a data structure for storing and interacting with an
@@ -51,6 +58,308 @@
 * accessed via their getters or via the {@link #toString()} methods.
 */
public final class ObjectClass extends SchemaElement {
    /** A fluent API for incrementally constructing object classes. */
    public static final class Builder extends SchemaElementBuilder<Builder> {
        private boolean isObsolete;
        private final List<String> names = new LinkedList<String>();
        private String oid;
        private final Set<String> optionalAttributes = new LinkedHashSet<String>();
        private final Set<String> requiredAttributes = new LinkedHashSet<String>();
        private final Set<String> superiorClasses = new LinkedHashSet<String>();
        private ObjectClassType type = null;
        Builder(final ObjectClass oc, final SchemaBuilder builder) {
            super(builder, oc);
            this.oid = oc.oid;
            this.names.addAll(oc.names);
            this.isObsolete = oc.isObsolete;
            this.type = oc.objectClassType;
            this.superiorClasses.addAll(oc.superiorClassOIDs);
            this.requiredAttributes.addAll(oc.requiredAttributeOIDs);
            this.optionalAttributes.addAll(optionalAttributes);
        }
        Builder(final String oid, final SchemaBuilder builder) {
            super(builder);
            oid(oid);
        }
        /**
         * Adds this object class to the schema overwriting any existing object class
         * with the same numeric OID.
         *
         * @return The parent schema builder.
         */
        public SchemaBuilder addToSchemaOverwrite() {
            return getSchemaBuilder().addObjectClass(new ObjectClass(this), true);
        }
        /**
         * Adds this object class to the schema, throwing an
         * {@code  ConflictingSchemaElementException} if there is an existing
         * object class with the same numeric OID.
         *
         * @return The parent schema builder.
         * @throws ConflictingSchemaElementException
         *             If there is an existing object class with the same numeric
         *             OID.
         */
        public SchemaBuilder addToSchema() {
            return getSchemaBuilder().addObjectClass(new ObjectClass(this), false);
        }
        @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 object class.
         *
         * @param oid
         *            The numeric OID.
         * @return This builder.
         */
        public Builder oid(final String oid) {
            this.oid = oid;
            return this;
        }
        /**
         * Adds the provided optional attributes.
         *
         * @param attributeNamesOrOIDs
         *      The list of optional attribute names or OIDs.
         * @return This builder.
         */
        public Builder optionalAttributes(final Collection<String> attributeNamesOrOIDs) {
            this.optionalAttributes.addAll(attributeNamesOrOIDs);
            return this;
        }
        /**
         * Adds the provided optional attributes.
         *
         * @param attributeNamesOrOIDs
         *      The list of optional attribute names or OIDs.
         * @return This builder.
         */
        public Builder optionalAttributes(final String... attributeNamesOrOIDs) {
            this.optionalAttributes.addAll(asList(attributeNamesOrOIDs));
            return this;
        }
        @Override
        public Builder removeAllExtraProperties() {
            return removeAllExtraProperties0();
        }
        @Override
        public Builder removeExtraProperty(final String extensionName, final String... extensionValues) {
            return removeExtraProperty0(extensionName, extensionValues);
        }
        /**
         * Removes all user defined names.
         *
         * @return This builder.
         */
        public Builder removeAllNames() {
            this.names.clear();
            return this;
        }
        /**
         * Removes all optional attributes.
         *
         * @return This builder.
         */
        public Builder removeAllOptionalAttributes() {
            this.optionalAttributes.clear();
            return this;
        }
        /**
         * Removes all required attributes.
         *
         * @return This builder.
         */
        public Builder removeAllRequiredAttributes() {
            this.requiredAttributes.clear();
            return this;
        }
        /**
         * Removes all superior object class.
         *
         * @return This builder.
         */
        public Builder removeAllSuperiorObjectClass() {
            this.superiorClasses.clear();
            return this;
        }
        /**
         * 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;
        }
        /**
         * Removes the provided optional attribute.
         *
         * @param attributeNameOrOID
         *            The optional attribute name or OID to be removed.
         * @return This builder.
         */
        public Builder removeOptionalAttribute(String attributeNameOrOID) {
            this.optionalAttributes.remove(attributeNameOrOID);
            return this;
        }
        /**
         * Removes the provided required attribute.
         *
         * @param attributeNameOrOID
         *            The provided required attribute name or OID to be removed.
         * @return This builder.
         */
        public Builder removeRequiredAttribute(String attributeNameOrOID) {
            this.optionalAttributes.remove(attributeNameOrOID);
            return this;
        }
        /**
         * Removes the provided superior object class.
         *
         * @param objectClassNameOrOID
         *            The superior object class name or OID to be removed.
         * @return This builder.
         */
        public Builder removeSuperiorObjectClass(String objectClassNameOrOID) {
            this.superiorClasses.remove(objectClassNameOrOID);
            return this;
        }
        /**
         * Adds the provided required attributes.
         *
         * @param attributeNamesOrOIDs
         *      The list of required attribute names or OIDs.
         * @return This builder.
         */
        public Builder requiredAttributes(final Collection<String> attributeNamesOrOIDs) {
            this.requiredAttributes.addAll(attributeNamesOrOIDs);
            return this;
        }
        /**
         * Adds the provided required attributes.
         *
         * @param attributeNamesOrOIDs
         *      The list of required attribute names or OIDs.
         * @return This builder.
         */
        public Builder requiredAttributes(final String... attributeNamesOrOIDs) {
            this.requiredAttributes.addAll(asList(attributeNamesOrOIDs));
            return this;
        }
        /**
         * Adds the provided superior object classes.
         *
         * @param objectClassNamesOrOIDs
         *      The list of superior object classes names or OIDs.
         * @return This builder.
         */
        public Builder superiorObjectClasses(final Collection<String> objectClassNamesOrOIDs) {
            this.superiorClasses.addAll(objectClassNamesOrOIDs);
            return this;
        }
        /**
         * Adds the provided superior object classes.
         *
         * @param objectClassNamesOrOIDs
         *      The list of superior object classes names or OIDs.
         * @return This builder.
         */
        public Builder superiorObjectClasses(final String... objectClassNamesOrOIDs) {
            this.superiorClasses.addAll(asList(objectClassNamesOrOIDs));
            return this;
        }
        /**
         * Sets the type of this object class.
         *
         * @param type
         *      The object class type.
         * @return This builder.
         */
        public Builder type(final ObjectClassType type) {
            this.type = type;
            return this;
        }
    }
    /** The OID that may be used to reference this definition. */
    private final String oid;
@@ -72,11 +381,11 @@
    /** The set of optional attribute types for this objectclass. */
    private final Set<String> optionalAttributeOIDs;
    private Set<ObjectClass> superiorClasses = Collections.emptySet();
    private Set<AttributeType> declaredRequiredAttributes = Collections.emptySet();
    private Set<AttributeType> requiredAttributes = Collections.emptySet();
    private Set<AttributeType> declaredOptionalAttributes = Collections.emptySet();
    private Set<AttributeType> optionalAttributes = Collections.emptySet();
    private Set<ObjectClass> superiorClasses = emptySet();
    private Set<AttributeType> declaredRequiredAttributes = emptySet();
    private Set<AttributeType> requiredAttributes = emptySet();
    private Set<AttributeType> declaredOptionalAttributes = emptySet();
    private Set<AttributeType> optionalAttributes = emptySet();
    /** Indicates whether or not validation has been performed. */
    private boolean needsValidating = true;
@@ -84,23 +393,6 @@
    /** The indicates whether or not validation failed. */
    private boolean isValid;
    ObjectClass(final String oid, final List<String> names, final String description,
            final boolean obsolete, final Set<String> superiorClassOIDs,
            final Set<String> requiredAttributeOIDs, final Set<String> optionalAttributeOIDs,
            final ObjectClassType objectClassType, final Map<String, List<String>> extraProperties,
            final String definition) {
        super(description, extraProperties, definition);
        Reject.ifNull(oid, names, superiorClassOIDs, requiredAttributeOIDs, optionalAttributeOIDs, objectClassType);
        this.oid = oid;
        this.names = names;
        this.isObsolete = obsolete;
        this.superiorClassOIDs = superiorClassOIDs;
        this.objectClassType = objectClassType;
        this.requiredAttributeOIDs = requiredAttributeOIDs;
        this.optionalAttributeOIDs = optionalAttributeOIDs;
    }
    /**
     * Construct a extensibleObject object class where the set of allowed
     * attribute types of this object class is implicitly the set of all
@@ -111,15 +403,31 @@
     * @param extraProperties
     *            The map of "extra" properties for this schema definition
     */
    ObjectClass(final String description, final Map<String, List<String>> extraProperties) {
        super(description, extraProperties, null);
        this.oid = EXTENSIBLE_OBJECT_OBJECTCLASS_OID;
        this.names = Collections.singletonList(EXTENSIBLE_OBJECT_OBJECTCLASS_NAME);
        this.isObsolete = false;
        this.superiorClassOIDs = Collections.singleton(TOP_OBJECTCLASS_NAME);
        this.objectClassType = ObjectClassType.AUXILIARY;
        this.requiredAttributeOIDs = Collections.emptySet();
        this.optionalAttributeOIDs = Collections.emptySet();
    static ObjectClass newExtensibleObjectObjectClass(final String description,
        final Map<String, List<String>> extraProperties, final SchemaBuilder builder) {
        return new ObjectClass(new Builder(EXTENSIBLE_OBJECT_OBJECTCLASS_OID, builder)
               .description(description)
               .extraProperties(extraProperties)
               .names(EXTENSIBLE_OBJECT_OBJECTCLASS_NAME)
               .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
               .type(AUXILIARY));
    }
    private ObjectClass(final Builder builder) {
        super(builder);
        if (builder.oid == null || builder.oid.isEmpty()) {
            throw new IllegalArgumentException("An OID must be specified.");
        }
        this.oid = builder.oid;
        this.names = unmodifiableCopyOfList(builder.names);
        this.isObsolete = builder.isObsolete;
        this.superiorClassOIDs = unmodifiableCopyOfSet(builder.superiorClasses);
        this.objectClassType = builder.type;
        this.requiredAttributeOIDs = unmodifiableCopyOfSet(builder.requiredAttributes);
        this.optionalAttributeOIDs = unmodifiableCopyOfSet(builder.optionalAttributes);
    }
    /**
@@ -198,8 +506,7 @@
     * @return The objectclass type for this objectclass.
     */
    public ObjectClassType getObjectClassType() {
        return objectClassType;
        return objectClassType != null ? objectClassType : STRUCTURAL;
    }
    /**
@@ -208,7 +515,6 @@
     * @return The OID for this schema definition.
     */
    public String getOID() {
        return oid;
    }
@@ -356,12 +662,6 @@
        return isRequired(attributeType) || isOptional(attributeType);
    }
    ObjectClass duplicate() {
        return new ObjectClass(oid, names, getDescription(), isObsolete, superiorClassOIDs,
                requiredAttributeOIDs, optionalAttributeOIDs, objectClassType,
                getExtraProperties(), toString());
    }
    @Override
    void toStringContent(final StringBuilder buffer) {
        buffer.append(oid);
@@ -473,7 +773,7 @@
        // Init a flag to check to inheritance from top (only needed for
        // structural object classes) per RFC 4512
        boolean derivesTop = objectClassType != ObjectClassType.STRUCTURAL;
        boolean derivesTop = getObjectClassType() != ObjectClassType.STRUCTURAL;
        if (!superiorClassOIDs.isEmpty()) {
            superiorClasses = new HashSet<ObjectClass>(superiorClassOIDs.size());
@@ -491,14 +791,15 @@
                // Make sure that the inheritance configuration is acceptable.
                final ObjectClassType superiorType = superiorClass.getObjectClassType();
                switch (objectClassType) {
                final ObjectClassType type = getObjectClassType();
                switch (type) {
                case ABSTRACT:
                    // Abstract classes may only inherit from other abstract
                    // classes.
                    if (superiorType != ObjectClassType.ABSTRACT) {
                        final LocalizableMessage message =
                                WARN_ATTR_SYNTAX_OBJECTCLASS_INVALID_SUPERIOR_TYPE1.get(
                                        getNameOrOID(), objectClassType.toString(), superiorType
                                        getNameOrOID(), type.toString(), superiorType
                                                .toString(), superiorClass.getNameOrOID());
                        failValidation(invalidSchemaElements, warnings, message);
                        return false;
@@ -513,7 +814,7 @@
                            && superiorType != ObjectClassType.AUXILIARY) {
                        final LocalizableMessage message =
                                WARN_ATTR_SYNTAX_OBJECTCLASS_INVALID_SUPERIOR_TYPE1.get(
                                        getNameOrOID(), objectClassType.toString(), superiorType
                                        getNameOrOID(), type.toString(), superiorType
                                                .toString(), superiorClass.getNameOrOID());
                        failValidation(invalidSchemaElements, warnings, message);
                        return false;
@@ -527,7 +828,7 @@
                            && superiorType != ObjectClassType.STRUCTURAL) {
                        final LocalizableMessage message =
                                WARN_ATTR_SYNTAX_OBJECTCLASS_INVALID_SUPERIOR_TYPE1.get(
                                        getNameOrOID(), objectClassType.toString(), superiorType
                                        getNameOrOID(), type.toString(), superiorType
                                                .toString(), superiorClass.getNameOrOID());
                        failValidation(invalidSchemaElements, warnings, message);
                        return false;
opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2011-2014 ForgeRock AS
 *      Portions Copyright 2011-2015 ForgeRock AS
 *      Portions Copyright 2014 Manuel Gaupp
 */
package org.forgerock.opendj.ldap.schema;
@@ -66,11 +66,16 @@
import com.forgerock.opendj.util.StaticUtils;
import com.forgerock.opendj.util.SubstringReader;
import static java.util.Collections.*;
import static org.forgerock.opendj.ldap.LdapException.*;
import static org.forgerock.opendj.ldap.schema.ObjectClass.*;
import static org.forgerock.opendj.ldap.schema.ObjectClassType.*;
import static org.forgerock.opendj.ldap.schema.Schema.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
import static org.forgerock.opendj.ldap.schema.SchemaUtils.*;
import static com.forgerock.opendj.ldap.CoreMessages.*;
import static com.forgerock.opendj.util.StaticUtils.*;
@@ -1143,7 +1148,7 @@
     *
     * @param oid
     *            The OID of the matching rule definition.
     * @return A builder to continue building the NameForm.
     * @return A builder to continue building the MatchingRule.
     */
    public MatchingRule.Builder buildMatchingRule(final String oid) {
        lazyInitBuilder();
@@ -1394,6 +1399,39 @@
    }
    /**
     * Returns an object class builder whose fields are initialized to the
     * values of the provided object class. This method should be used when
     * duplicating object classes from external schemas or when modifying
     * existing object classes.
     *
     * @param objectClass
     *            The object class source.
     * @return A builder to continue building the ObjectClass.
     */
    public ObjectClass.Builder buildObjectClass(final ObjectClass objectClass) {
        lazyInitBuilder();
        return new ObjectClass.Builder(objectClass, this);
    }
    /**
     * Returns a builder which can be used for incrementally constructing a new
     * object class before adding it to the schema. Example usage:
     *
     * <pre>
     * SchemaBuilder builder = ...;
     * builder.buildObjectClass("objectclass-oid").name("object class name").addToSchema();
     * </pre>
     *
     * @param oid
     *            The OID of the object class definition.
     * @return A builder to continue building the ObjectClass.
     */
    public ObjectClass.Builder buildObjectClass(final String oid) {
        lazyInitBuilder();
        return new ObjectClass.Builder(oid, this);
    }
    /**
     * Duplicates the syntax.
     *
     * @param syntax
@@ -1470,17 +1508,11 @@
            // parenthesis.
            reader.skipWhitespaces();
            // The next set of characters must be the OID.
            // The next set of characters is the OID.
            final String oid = readOID(reader, allowsMalformedNamesAndOptions());
            List<String> names = Collections.emptyList();
            String description = "".intern();
            boolean isObsolete = false;
            Set<String> superiorClasses = Collections.emptySet();
            Set<String> requiredAttributes = Collections.emptySet();
            Set<String> optionalAttributes = Collections.emptySet();
            ObjectClassType objectClassType = ObjectClassType.STRUCTURAL;
            Map<String, List<String>> extraProperties = Collections.emptyMap();
            Set<String> superiorClasses = emptySet();
            ObjectClassType ocType = null;
            ObjectClass.Builder ocBuilder = new ObjectClass.Builder(oid, this).definition(definition);
            // At this point, we should have a pretty specific syntax that
            // describes what may come next, but some of the components are
@@ -1497,47 +1529,39 @@
                    // No more tokens.
                    break;
                } else if ("name".equalsIgnoreCase(tokenName)) {
                    names = readNameDescriptors(reader, allowsMalformedNamesAndOptions());
                    ocBuilder.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);
                    ocBuilder.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.
                    ocBuilder.obsolete(true);
                } else if ("sup".equalsIgnoreCase(tokenName)) {
                    superiorClasses = readOIDs(reader, allowsMalformedNamesAndOptions());
                } else if ("abstract".equalsIgnoreCase(tokenName)) {
                    // This indicates that entries must not include this
                    // objectclass unless they also include a non-abstract
                    // objectclass that inherits from this class. We do not need
                    // any more parsing for this token.
                    objectClassType = ObjectClassType.ABSTRACT;
                    // objectclass that inherits from this class.
                    ocType = ABSTRACT;
                } else if ("structural".equalsIgnoreCase(tokenName)) {
                    // This indicates that this is a structural objectclass. We
                    // do not need any more parsing for this token.
                    objectClassType = ObjectClassType.STRUCTURAL;
                    ocType = STRUCTURAL;
                } else if ("auxiliary".equalsIgnoreCase(tokenName)) {
                    // This indicates that this is an auxiliary objectclass. We
                    // do not need any more parsing for this token.
                    objectClassType = ObjectClassType.AUXILIARY;
                    ocType = AUXILIARY;
                } else if ("must".equalsIgnoreCase(tokenName)) {
                    requiredAttributes = readOIDs(reader, allowsMalformedNamesAndOptions());
                    ocBuilder.requiredAttributes(readOIDs(reader, allowsMalformedNamesAndOptions()));
                } else if ("may".equalsIgnoreCase(tokenName)) {
                    optionalAttributes = readOIDs(reader, allowsMalformedNamesAndOptions());
                    ocBuilder.optionalAttributes(readOIDs(reader, allowsMalformedNamesAndOptions()));
                } else if (tokenName.matches("^X-[A-Za-z_-]+$")) {
                    // This must be a non-standard property and it must be
                    // followed by either a single definition in single quotes
                    // 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));
                    final List<String> extensions = readExtensions(reader);
                    ocBuilder.extraProperties(tokenName, extensions.toArray(new String[extensions.size()]));
                } else {
                    throw new LocalizedIllegalArgumentException(
                        ERR_ATTR_SYNTAX_OBJECTCLASS_ILLEGAL_TOKEN1.get(definition, tokenName));
@@ -1545,81 +1569,21 @@
            }
            if (EXTENSIBLE_OBJECT_OBJECTCLASS_OID.equals(oid)) {
                addObjectClass(new ObjectClass(description, extraProperties), overwrite);
                addObjectClass(newExtensibleObjectObjectClass(
                    ocBuilder.getDescription(), ocBuilder.getExtraProperties(), this), overwrite);
                return this;
            } else {
                if (objectClassType == ObjectClassType.STRUCTURAL && superiorClasses.isEmpty()) {
                    superiorClasses = Collections.singleton(TOP_OBJECTCLASS_NAME);
                if (ocType == STRUCTURAL && superiorClasses.isEmpty()) {
                    superiorClasses = singleton(TOP_OBJECTCLASS_NAME);
                }
                if (!extraProperties.isEmpty()) {
                    extraProperties = Collections.unmodifiableMap(extraProperties);
                }
                addObjectClass(new ObjectClass(oid, names, description, isObsolete,
                        superiorClasses, requiredAttributes, optionalAttributes, objectClassType,
                        extraProperties, definition), overwrite);
                ocBuilder.superiorObjectClasses(superiorClasses)
                         .type(ocType);
                return overwrite ? ocBuilder.addToSchemaOverwrite() : ocBuilder.addToSchema();
            }
        } catch (final DecodeException e) {
            final LocalizableMessage msg =
                    ERR_ATTR_SYNTAX_OBJECTCLASS_INVALID1.get(definition, e.getMessageObject());
            throw new LocalizedIllegalArgumentException(msg, e.getCause());
            throw new LocalizedIllegalArgumentException(
                ERR_ATTR_SYNTAX_OBJECTCLASS_INVALID1.get(definition, e.getMessageObject()), e.getCause());
        }
        return this;
    }
    /**
     * Adds the provided object class definition to this schema builder.
     *
     * @param oid
     *            The OID of the object class definition.
     * @param names
     *            The user-friendly names of the object class definition.
     * @param description
     *            The description of the object class definition.
     * @param obsolete
     *            {@code true} if the object class definition is obsolete,
     *            otherwise {@code false}.
     * @param superiorClassOIDs
     *            A list of direct superclasses of the object class.
     * @param requiredAttributeOIDs
     *            A list of attribute types that entries must contain.
     * @param optionalAttributeOIDs
     *            A list of attribute types that entries may contain.
     * @param objectClassType
     *            The type of the object class.
     * @param extraProperties
     *            A map containing additional properties associated with the
     *            object class definition.
     * @param overwrite
     *            {@code true} if any existing object class 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.
     */
    SchemaBuilder addObjectClass(final String oid, final List<String> names,
            final String description, final boolean obsolete, Set<String> superiorClassOIDs,
            final Set<String> requiredAttributeOIDs, final Set<String> optionalAttributeOIDs,
            final ObjectClassType objectClassType, final Map<String, List<String>> extraProperties,
            final boolean overwrite) {
        lazyInitBuilder();
        if (EXTENSIBLE_OBJECT_OBJECTCLASS_OID.equals(oid)) {
            addObjectClass(new ObjectClass(description,
                    unmodifiableCopyOfExtraProperties(extraProperties)), overwrite);
        } else {
            if (objectClassType == ObjectClassType.STRUCTURAL && superiorClassOIDs.isEmpty()) {
                superiorClassOIDs = Collections.singleton(TOP_OBJECTCLASS_NAME);
            }
            addObjectClass(new ObjectClass(oid, unmodifiableCopyOfList(names), description,
                    obsolete, unmodifiableCopyOfSet(superiorClassOIDs),
                    unmodifiableCopyOfSet(requiredAttributeOIDs),
                    unmodifiableCopyOfSet(optionalAttributeOIDs), objectClassType,
                    unmodifiableCopyOfExtraProperties(extraProperties), null), overwrite);
        }
        return this;
    }
    /**
@@ -2535,7 +2499,7 @@
        return this;
    }
    private void addObjectClass(final ObjectClass oc, final boolean overwrite) {
    SchemaBuilder addObjectClass(final ObjectClass oc, final boolean overwrite) {
        ObjectClass conflictingOC;
        if (numericOID2ObjectClasses.containsKey(oc.getOID())) {
            conflictingOC = numericOID2ObjectClasses.get(oc.getOID());
@@ -2562,6 +2526,8 @@
                classes.add(oc);
            }
        }
        return this;
    }
    private void addSchema0(final Schema schema, final boolean overwrite) {
@@ -2594,7 +2560,7 @@
        }
        for (final ObjectClass objectClass : schema.getObjectClasses()) {
            addObjectClass(objectClass.duplicate(), overwrite);
            addObjectClass(objectClass, overwrite);
        }
        for (final NameForm nameForm : schema.getNameForms()) {
opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/ObjectClassBuilderTestCase.java
New file
@@ -0,0 +1,208 @@
/*
 * 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 java.util.List;
import java.util.Set;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static java.util.Collections.*;
import static org.fest.assertions.Assertions.*;
import static org.fest.assertions.MapAssert.*;
import static org.forgerock.opendj.ldap.schema.ObjectClassType.*;
import static org.forgerock.opendj.ldap.schema.Schema.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
public class ObjectClassBuilderTestCase extends AbstractSchemaTestCase {
    @DataProvider
    Object[][] validObjectClasses() {
        // OID, obsolete, names, optional attributes, required attributes,
        // superior object classes, type, description,
        // extra property name, extra property values, overwrite
        return new Object[][] {
            // Basic object class
            { "1.2.3.4", false, singletonList("MyObjectClass"), emptySet(), singleton("cn"),
                singleton(TOP_OBJECTCLASS_NAME), STRUCTURAL, "MyObjectClass description.", "New extra property",
                "New extra value", false },
            // Allowed overrides existing core schema object class groupOfNames
            { "2.5.6.9", false, singletonList("groupOfFirstNames"), emptySet(), singleton("cn"),
                singleton(TOP_OBJECTCLASS_NAME), AUXILIARY, "MyObjectClass description.", "New extra property",
                "New extra value", true },
            // No name provided, should be validated
            { "1.2.3.4", false, emptyList(), singleton("name"), singleton("cn"),
                singleton(TOP_OBJECTCLASS_NAME), STRUCTURAL, "MyObjectClass description.", "New extra property",
                "New extra value", false },
            // Empty description, should be validated
            { "1.2.3.4", false, emptyList(), singleton("name"), singleton("cn"),
                singleton(TOP_OBJECTCLASS_NAME), STRUCTURAL, "", "New extra property",
                "New extra value", false },
        };
    }
    @Test(dataProvider = "validObjectClasses")
    public void testValidOCBuilder(final String oid, final boolean isObsolete, final List<String> names,
            final Set<String> optionalAttributeOIDs, final Set<String> requiredAttributesOIDs,
            final Set<String> superiorClassOIDs, final ObjectClassType type, final String description,
            final String extraPropertyName, final String extraPropertyValue, final boolean overwrite)
            throws Exception {
        final ObjectClass.Builder ocBuilder = new SchemaBuilder(getCoreSchema())
            .buildObjectClass(oid)
            .description(description)
            .obsolete(isObsolete)
            .names(names)
            .superiorObjectClasses(superiorClassOIDs)
            .requiredAttributes(requiredAttributesOIDs)
            .optionalAttributes(optionalAttributeOIDs)
            .type(type)
            .extraProperties(extraPropertyName, extraPropertyValue);
        final Schema schema = overwrite ? ocBuilder.addToSchemaOverwrite().toSchema()
                                        : ocBuilder.addToSchema().toSchema();
        assertThat(schema.getWarnings()).isEmpty();
        final ObjectClass oc = schema.getObjectClass(oid);
        assertThat(oc).isNotNull();
        assertThat(oc.getOID()).isEqualTo(oid);
        assertThat(oc.getDescription()).isEqualTo(description);
        assertThat(oc.isObsolete()).isEqualTo(isObsolete);
        assertThat(oc.getNames()).containsOnly(names.toArray());
        assertSchemaElementsContainsAll(oc.getSuperiorClasses(), superiorClassOIDs);
        assertSchemaElementsContainsAll(oc.getRequiredAttributes(), requiredAttributesOIDs);
        assertSchemaElementsContainsAll(oc.getOptionalAttributes(), optionalAttributeOIDs);
        assertThat(oc.getObjectClassType()).isEqualTo(type);
        assertThat(oc.getExtraProperties()).includes(entry(extraPropertyName, singletonList(extraPropertyValue)));
    }
    @Test
    public void testOCBuilderDefaultValues() throws Exception {
        final SchemaBuilder sb = new SchemaBuilder(getCoreSchema());
        final ObjectClass.Builder ocBuilder = sb.buildObjectClass("1.1.1.42")
                .description("Default object class")
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .names("defaultObjectClass");
        final Schema schema = ocBuilder.addToSchema().toSchema();
        assertThat(schema.getWarnings()).isEmpty();
        final ObjectClass oc = schema.getObjectClass("defaultObjectClass");
        assertThat(oc).isNotNull();
        assertThat(oc.getOID()).isEqualTo("1.1.1.42");
        assertThat(oc.getDescription()).isEqualTo("Default object class");
        assertThat(oc.isObsolete()).isFalse();
        assertThat(oc.getNames()).containsOnly("defaultObjectClass");
        assertSchemaElementsContainsAll(oc.getSuperiorClasses(), TOP_OBJECTCLASS_NAME);
        final Set<AttributeType> topReqAttrs = schema.getObjectClass(TOP_OBJECTCLASS_NAME).getRequiredAttributes();
        assertThat(oc.getRequiredAttributes()).containsOnly(topReqAttrs.toArray());
        assertThat(oc.getOptionalAttributes()).isEmpty();
        assertThat(oc.getObjectClassType()).isEqualTo(STRUCTURAL);
        assertThat(oc.getExtraProperties()).isEmpty();
    }
    @Test
    public void testOCBuilderCopyConstructor() throws Exception {
        final SchemaBuilder sb = new SchemaBuilder(getCoreSchema());
        final ObjectClass.Builder ocBuilder = sb.buildObjectClass("1.1.1.42")
                .description("Object class to duplicate")
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .names("ObjectClassToDuplicate")
                .requiredAttributes("name");
        final Schema schema = ocBuilder.addToSchema().toSchema();
        assertThat(schema.getWarnings()).isEmpty();
        sb.buildObjectClass(schema.getObjectClass("ObjectClassToDuplicate"))
                .oid("1.1.1.43")
                .names("Copy")
                .obsolete(true)
                .addToSchemaOverwrite();
        final Schema schemaCopy = sb.toSchema();
        assertThat(schemaCopy.getWarnings()).isEmpty();
        final ObjectClass ocCopy = schemaCopy.getObjectClass("Copy");
        assertThat(ocCopy).isNotNull();
        assertThat(ocCopy.getOID()).isEqualTo("1.1.1.43");
        assertThat(ocCopy.getDescription()).isEqualTo("Object class to duplicate");
        assertThat(ocCopy.isObsolete()).isTrue();
        assertThat(ocCopy.getNames()).containsOnly("ObjectClassToDuplicate", "Copy");
        assertSchemaElementsContainsAll(ocCopy.getSuperiorClasses(), TOP_OBJECTCLASS_NAME);
        assertSchemaElementsContainsAll(ocCopy.getRequiredAttributes(), "name");
        assertThat(ocCopy.getOptionalAttributes()).isEmpty();
        assertThat(ocCopy.getObjectClassType()).isEqualTo(STRUCTURAL);
        assertThat(ocCopy.getExtraProperties()).isEmpty();
    }
    @Test(expectedExceptions = ConflictingSchemaElementException.class)
    public void testOCBuilderDoesNotAllowOverwrite() throws Exception {
        final ObjectClass.Builder ocBuilder = new SchemaBuilder(getCoreSchema())
            .buildObjectClass("2.5.6.9")
            .description("MyObjectClass description")
            .names("groupOfFirstNames")
            .requiredAttributes("cn")
            .type(AUXILIARY)
            .extraProperties("New extra property", "New extra value");
        ocBuilder.addToSchema().toSchema();
    }
    @Test(expectedExceptions = IllegalArgumentException.class)
    public void testOCBuilderDoesNotAllowEmptyOID() throws Exception {
        final ObjectClass.Builder ocBuilder = new SchemaBuilder(getCoreSchema())
            .buildObjectClass("")
            .description("MyObjectClass description")
            .names("MyObjectClass")
            .requiredAttributes("cn")
            .extraProperties("New extra property", "New extra value");
        ocBuilder.addToSchema().toSchema();
    }
    private void assertSchemaElementsContainsAll(final Set<? extends SchemaElement> elements,
            final Set<String> namesOrOIDs) throws Exception {
        assertSchemaElementsContainsAll(elements, namesOrOIDs.toArray(new String[namesOrOIDs.size()]));
    }
    private void assertSchemaElementsContainsAll(final Set<? extends SchemaElement> elements,
            final String... namesOrOIDs) throws Exception {
        for (final String nameOrOID : namesOrOIDs) {
            assertThat(assertSchemaElementsContains(elements, nameOrOID)).isTrue();
        }
    }
    private boolean assertSchemaElementsContains(final Set<? extends SchemaElement> elements, final String nameOrOID) {
        for (final SchemaElement element : elements) {
            final String oid = element instanceof AttributeType ? ((AttributeType) element).getNameOrOID()
                                                            : ((ObjectClass) element).getNameOrOID();
            if (oid.equals(nameOrOID)) {
                return true;
            }
        }
        return false;
    }
}
opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaBuilderTestCase.java
@@ -21,13 +21,11 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2012-2014 ForgeRock AS.
 *      Portions Copyright 2012-2015 ForgeRock AS.
 */
package org.forgerock.opendj.ldap.schema;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.forgerock.i18n.LocalizedIllegalArgumentException;
import org.forgerock.opendj.ldap.ByteString;
@@ -46,6 +44,7 @@
import static org.fest.assertions.Assertions.*;
import static org.fest.assertions.Fail.*;
import static org.forgerock.opendj.ldap.schema.CoreSchema.*;
import static org.forgerock.opendj.ldap.schema.Schema.*;
import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
import static org.forgerock.opendj.ldap.spi.LdapPromises.*;
@@ -1151,18 +1150,15 @@
     */
    @Test
    public final void testSchemaBuilderAddObjectClass() throws Exception {
        final SchemaBuilder scBuild = new SchemaBuilder(Schema.getDefaultSchema());
        Set<String> attrs = new HashSet<String>();
        attrs.add("seeAlso");
        attrs.add("ou");
        attrs.add("l");
        attrs.add("description");
        scBuild.addObjectClass("2.5.6.14", Collections.singletonList("device"),
                "New description for the new existing Object Class", false, Collections
                        .singleton(TOP_OBJECTCLASS_NAME), Collections.singleton("cn"), attrs,
                ObjectClassType.STRUCTURAL, Collections.singletonMap(SCHEMA_PROPERTY_ORIGIN,
                        Collections.singletonList("RFC 4519")), true);
        final SchemaBuilder scBuild = new SchemaBuilder(getDefaultSchema());
        scBuild.buildObjectClass("2.5.6.14")
                .names("device")
                .description("New description for the new existing Object Class")
                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
                .requiredAttributes("cn")
                .optionalAttributes("seeAlso", "ou", "l", "description")
                .extraProperties(SCHEMA_PROPERTY_ORIGIN, "RFC 4519")
                .addToSchemaOverwrite();
        Schema sc = scBuild.toSchema();
        assertThat(sc.getWarnings()).isEmpty();