/* * 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 2009 Sun Microsystems, Inc. * Portions copyright 2011 ForgeRock AS */ package org.forgerock.opendj.ldap.schema; import static org.forgerock.opendj.ldap.CoreMessages.*; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.forgerock.i18n.LocalizableMessage; import com.forgerock.opendj.util.Validator; /** * This class defines a DIT content rule, which defines the set of allowed, * required, and prohibited attributes for entries with a given structural * objectclass, and also indicates which auxiliary classes may be included in * the entry. */ public final class DITContentRule extends SchemaElement { // The structural objectclass for this DIT content rule. private final String structuralClassOID; // The set of user defined names for this definition. private final List names; // Indicates whether this definition is declared "obsolete". private final boolean isObsolete; // The set of auxiliary objectclasses that entries with this content // rule may contain, in a mapping between the objectclass and the // user-defined name for that class. private final Set auxiliaryClassOIDs; // The set of optional attribute types for this DIT content rule. private final Set optionalAttributeOIDs; // The set of prohibited attribute types for this DIT content rule. private final Set prohibitedAttributeOIDs; // The set of required attribute types for this DIT content rule. private final Set requiredAttributeOIDs; // The definition string used to create this objectclass. private final String definition; private ObjectClass structuralClass; private Set auxiliaryClasses = Collections.emptySet(); private Set optionalAttributes = Collections.emptySet(); private Set prohibitedAttributes = Collections.emptySet(); private Set requiredAttributes = Collections.emptySet(); DITContentRule(final String structuralClassOID, final List names, final String description, final boolean obsolete, final Set auxiliaryClassOIDs, final Set optionalAttributeOIDs, final Set prohibitedAttributeOIDs, final Set requiredAttributeOIDs, final Map> extraProperties, final String definition) { super(description, extraProperties); Validator.ensureNotNull(structuralClassOID, names); Validator.ensureNotNull(auxiliaryClassOIDs, optionalAttributeOIDs, prohibitedAttributeOIDs, requiredAttributeOIDs); this.names = names; this.isObsolete = obsolete; this.structuralClassOID = structuralClassOID; this.auxiliaryClassOIDs = auxiliaryClassOIDs; this.optionalAttributeOIDs = optionalAttributeOIDs; this.prohibitedAttributeOIDs = prohibitedAttributeOIDs; this.requiredAttributeOIDs = requiredAttributeOIDs; if (definition != null) { this.definition = definition; } else { this.definition = buildDefinition(); } } /** * Returns {@code true} if the provided object is a DIT content rule having * the same structural object class OID as this DIT content rule. * * @param o * The object to be compared. * @return {@code true} if the provided object is a DIT content rule having * the same numeric OID as this DIT content rule. */ @Override public boolean equals(final Object o) { if (this == o) { return true; } else if (o instanceof DITContentRule) { final DITContentRule other = (DITContentRule) o; return structuralClassOID.equals(other.structuralClassOID); } else { return false; } } /** * Returns an unmodifiable set containing the auxiliary objectclasses that * may be used for entries associated with this DIT content rule. * * @return An unmodifiable set containing the auxiliary objectclasses that * may be used for entries associated with this DIT content rule. */ public Set getAuxiliaryClasses() { return auxiliaryClasses; } /** * Returns the name or structural class OID for this schema definition. If * it has one or more names, then the primary name will be returned. If it * does not have any names, then the OID will be returned. * * @return The name or OID for this schema definition. */ public String getNameOrOID() { if (names.isEmpty()) { return structuralClassOID; } return names.get(0); } /** * Returns an unmodifiable list containing the user-defined names that may * be used to reference this schema definition. * * @return Returns an unmodifiable list containing the user-defined names * that may be used to reference this schema definition. */ public List getNames() { return names; } /** * Returns an unmodifiable set containing the optional attributes for this * DIT content rule. * * @return An unmodifiable set containing the optional attributes for this * DIT content rule. */ public Set getOptionalAttributes() { return optionalAttributes; } /** * Returns an unmodifiable set containing the prohibited attributes for this * DIT content rule. * * @return An unmodifiable set containing the prohibited attributes for this * DIT content rule. */ public Set getProhibitedAttributes() { return prohibitedAttributes; } /** * Returns an unmodifiable set containing the required attributes for this * DIT content rule. * * @return An unmodifiable set containing the required attributes for this * DIT content rule. */ public Set getRequiredAttributes() { return requiredAttributes; } /** * Returns the structural objectclass for this DIT content rule. * * @return The structural objectclass for this DIT content rule. */ public ObjectClass getStructuralClass() { return structuralClass; } /** * Returns the structural class OID for this schema definition. * * @return The structural class OID for this schema definition. */ public String getStructuralClassOID() { return structuralClassOID; } /** * Returns the hash code for this DIT content rule. It will be calculated as * the hash code of the structural object class OID. * * @return The hash code for this DIT content rule. */ @Override public int hashCode() { return structuralClassOID.hashCode(); } /** * Indicates whether this schema definition has the specified name. * * @param name * The name for which to make the determination. * @return true if the specified name is assigned to this * schema definition, or false if not. */ public boolean hasName(final String name) { for (final String n : names) { if (n.equalsIgnoreCase(name)) { return true; } } return false; } /** * Indicates whether this schema definition has the specified name or * structural class OID. * * @param value * The value for which to make the determination. * @return true if the provided value matches the OID or one of * the names assigned to this schema definition, or * false if not. */ public boolean hasNameOrOID(final String value) { return hasName(value) || structuralClassOID.equals(value); } /** * Indicates whether this schema definition is declared "obsolete". * * @return true if this schema definition is declared * "obsolete", or false if not. */ public boolean isObsolete() { return isObsolete; } /** * Indicates whether the provided attribute type is included in the optional * attribute list for this DIT content rule. * * @param attributeType * The attribute type for which to make the determination. * @return true if the provided attribute type is optional for * this DIT content rule, or false if not. */ public boolean isOptional(final AttributeType attributeType) { return optionalAttributes.contains(attributeType); } /** * Indicates whether the provided attribute type is included in the required * attribute list for this DIT content rule. * * @param attributeType * The attribute type for which to make the determination. * @return true if the provided attribute type is required by * this DIT content rule, or false if not. */ public boolean isRequired(final AttributeType attributeType) { return requiredAttributes.contains(attributeType); } /** * Indicates whether the provided attribute type is in the list of required * or optional attributes for this DIT content rule. * * @param attributeType * The attribute type for which to make the determination. * @return true if the provided attribute type is required or * allowed for this DIT content rule, or false if it is * not. */ public boolean isRequiredOrOptional(final AttributeType attributeType) { return isRequired(attributeType) || isOptional(attributeType); } /** * Returns the string representation of this schema definition in the form * specified in RFC 2252. * * @return The string representation of this schema definition in the form * specified in RFC 2252. */ @Override public String toString() { return definition; } DITContentRule duplicate() { return new DITContentRule(structuralClassOID, names, description, isObsolete, auxiliaryClassOIDs, optionalAttributeOIDs, prohibitedAttributeOIDs, requiredAttributeOIDs, extraProperties, definition); } @Override void toStringContent(final StringBuilder buffer) { buffer.append(structuralClassOID); if (!names.isEmpty()) { final Iterator iterator = names.iterator(); final String firstName = iterator.next(); if (iterator.hasNext()) { buffer.append(" NAME ( '"); buffer.append(firstName); while (iterator.hasNext()) { buffer.append("' '"); buffer.append(iterator.next()); } buffer.append("' )"); } else { buffer.append(" NAME '"); buffer.append(firstName); buffer.append("'"); } } if (description != null && description.length() > 0) { buffer.append(" DESC '"); buffer.append(description); buffer.append("'"); } if (isObsolete) { buffer.append(" OBSOLETE"); } if (!auxiliaryClassOIDs.isEmpty()) { final Iterator iterator = auxiliaryClassOIDs.iterator(); final String firstClass = iterator.next(); if (iterator.hasNext()) { buffer.append(" AUX ("); buffer.append(firstClass); while (iterator.hasNext()) { buffer.append(" $ "); buffer.append(iterator.next()); } buffer.append(" )"); } else { buffer.append(" AUX "); buffer.append(firstClass); } } if (!requiredAttributeOIDs.isEmpty()) { final Iterator iterator = requiredAttributeOIDs.iterator(); final String firstName = iterator.next(); if (iterator.hasNext()) { buffer.append(" MUST ( "); buffer.append(firstName); while (iterator.hasNext()) { buffer.append(" $ "); buffer.append(iterator.next()); } buffer.append(" )"); } else { buffer.append(" MUST "); buffer.append(firstName); } } if (!optionalAttributeOIDs.isEmpty()) { final Iterator iterator = optionalAttributeOIDs.iterator(); final String firstName = iterator.next(); if (iterator.hasNext()) { buffer.append(" MAY ( "); buffer.append(firstName); while (iterator.hasNext()) { buffer.append(" $ "); buffer.append(iterator.next()); } buffer.append(" )"); } else { buffer.append(" MAY "); buffer.append(firstName); } } if (!prohibitedAttributeOIDs.isEmpty()) { final Iterator iterator = prohibitedAttributeOIDs.iterator(); final String firstName = iterator.next(); if (iterator.hasNext()) { buffer.append(" NOT ( "); buffer.append(firstName); while (iterator.hasNext()) { buffer.append(" $ "); buffer.append(iterator.next()); } buffer.append(" )"); } else { buffer.append(" NOT "); buffer.append(firstName); } } } void validate(final Schema schema, final List warnings) throws SchemaException { // Get the objectclass with the specified OID. If it does not exist // or is not structural, then fail. if (structuralClassOID != null) { try { structuralClass = schema.getObjectClass(structuralClassOID); } catch (final UnknownSchemaElementException e) { final LocalizableMessage message = ERR_ATTR_SYNTAX_DCR_UNKNOWN_STRUCTURAL_CLASS1.get(getNameOrOID(), structuralClassOID); throw new SchemaException(message, e); } if (structuralClass.getObjectClassType() != ObjectClassType.STRUCTURAL) { final LocalizableMessage message = ERR_ATTR_SYNTAX_DCR_STRUCTURAL_CLASS_NOT_STRUCTURAL1.get(getNameOrOID(), structuralClass.getNameOrOID(), structuralClass .getObjectClassType().toString()); warnings.add(message); } } if (!auxiliaryClassOIDs.isEmpty()) { auxiliaryClasses = new HashSet(auxiliaryClassOIDs.size()); ObjectClass objectClass; for (final String oid : auxiliaryClassOIDs) { try { objectClass = schema.getObjectClass(oid); } catch (final UnknownSchemaElementException e) { // This isn't good because it is an unknown auxiliary class. final LocalizableMessage message = ERR_ATTR_SYNTAX_DCR_UNKNOWN_AUXILIARY_CLASS1.get(getNameOrOID(), oid); throw new SchemaException(message, e); } if (objectClass.getObjectClassType() != ObjectClassType.AUXILIARY) { // This isn't good because it isn't an auxiliary class. final LocalizableMessage message = ERR_ATTR_SYNTAX_DCR_AUXILIARY_CLASS_NOT_AUXILIARY1.get(getNameOrOID(), structuralClass.getOID(), structuralClass.getObjectClassType() .toString()); throw new SchemaException(message); } auxiliaryClasses.add(objectClass); } } if (!requiredAttributeOIDs.isEmpty()) { requiredAttributes = new HashSet(requiredAttributeOIDs.size()); AttributeType attributeType; for (final String oid : requiredAttributeOIDs) { try { attributeType = schema.getAttributeType(oid); } catch (final UnknownSchemaElementException e) { // This isn't good because it means that the DIT content // rule // requires an attribute type that we don't know anything // about. final LocalizableMessage message = ERR_ATTR_SYNTAX_DCR_UNKNOWN_REQUIRED_ATTR1.get(getNameOrOID(), oid); throw new SchemaException(message, e); } requiredAttributes.add(attributeType); } } if (!optionalAttributeOIDs.isEmpty()) { optionalAttributes = new HashSet(optionalAttributeOIDs.size()); AttributeType attributeType; for (final String oid : optionalAttributeOIDs) { try { attributeType = schema.getAttributeType(oid); } catch (final UnknownSchemaElementException e) { // This isn't good because it means that the DIT content // rule // requires an attribute type that we don't know anything // about. final LocalizableMessage message = ERR_ATTR_SYNTAX_DCR_UNKNOWN_OPTIONAL_ATTR1.get(getNameOrOID(), oid); throw new SchemaException(message, e); } optionalAttributes.add(attributeType); } } if (!prohibitedAttributeOIDs.isEmpty()) { prohibitedAttributes = new HashSet(prohibitedAttributeOIDs.size()); AttributeType attributeType; for (final String oid : prohibitedAttributeOIDs) { try { attributeType = schema.getAttributeType(oid); } catch (final UnknownSchemaElementException e) { // This isn't good because it means that the DIT content // rule // requires an attribute type that we don't know anything // about. final LocalizableMessage message = ERR_ATTR_SYNTAX_DCR_UNKNOWN_PROHIBITED_ATTR1.get(getNameOrOID(), oid); throw new SchemaException(message, e); } prohibitedAttributes.add(attributeType); } } // Make sure that none of the prohibited attributes is required by // the structural or any of the auxiliary classes. for (final AttributeType t : prohibitedAttributes) { if (structuralClass.isRequired(t)) { final LocalizableMessage message = ERR_ATTR_SYNTAX_DCR_PROHIBITED_REQUIRED_BY_STRUCTURAL.get(getNameOrOID(), t .getNameOrOID(), structuralClass.getNameOrOID()); throw new SchemaException(message); } for (final ObjectClass oc : auxiliaryClasses) { if (oc.isRequired(t)) { final LocalizableMessage message = ERR_ATTR_SYNTAX_DCR_PROHIBITED_REQUIRED_BY_AUXILIARY.get( getNameOrOID(), t.getNameOrOID(), oc.getNameOrOID()); throw new SchemaException(message); } } } auxiliaryClasses = Collections.unmodifiableSet(auxiliaryClasses); optionalAttributes = Collections.unmodifiableSet(optionalAttributes); prohibitedAttributes = Collections.unmodifiableSet(prohibitedAttributes); requiredAttributes = Collections.unmodifiableSet(requiredAttributes); } }