attributes =
name2AttributeTypes.get(StaticUtils.toLowerCase(name));
if (attributes != null) {
if (attributes.size() == 1) {
return attributes.get(0);
}
throw new UnknownSchemaElementException(WARN_ATTR_TYPE_AMBIGIOUS.get(name));
}
return null;
}
}
/*
* WARNING: do not reference the core schema in the following declarations.
*/
private static final Schema EMPTY_STRICT_SCHEMA = new Schema(new EmptyImpl(true));
private static final Schema EMPTY_NON_STRICT_SCHEMA = new Schema(new EmptyImpl(false));
static final String ATTR_ATTRIBUTE_TYPES = "attributeTypes";
static final String ATTR_DIT_CONTENT_RULES = "dITContentRules";
static final String ATTR_DIT_STRUCTURE_RULES = "dITStructureRules";
static final String ATTR_LDAP_SYNTAXES = "ldapSyntaxes";
static final String ATTR_MATCHING_RULE_USE = "matchingRuleUse";
static final String ATTR_MATCHING_RULES = "matchingRules";
static final String ATTR_NAME_FORMS = "nameForms";
static final String ATTR_OBJECT_CLASSES = "objectClasses";
/**
* Returns the core schema. The core schema is non-strict and contains the
* following standard LDAP schema elements:
*
*
* @return The core schema.
*/
public static Schema getCoreSchema() {
return CoreSchemaImpl.getInstance();
}
/**
* Returns the default schema which should be used by this application. The
* default schema is initially set to the core schema.
*
* @return The default schema which should be used by this application.
*/
public static Schema getDefaultSchema() {
return DefaultSchema.schema;
}
/**
* Returns the empty schema. The empty schema is non-strict and does not
* contain any schema elements.
*
* @return The empty schema.
*/
public static Schema getEmptySchema() {
return EMPTY_NON_STRICT_SCHEMA;
}
/**
* Reads the schema contained in the named subschema sub-entry.
*
* If the requested schema is not returned by the Directory Server then the
* request will fail with an {@link EntryNotFoundException}. More
* specifically, this method will never return {@code null}.
*
* @param connection
* A connection to the Directory Server whose schema is to be
* read.
* @param name
* The distinguished name of the subschema sub-entry.
* @return The schema from the Directory Server.
* @throws ErrorResultException
* If the result code indicates that the request failed for some
* reason.
* @throws UnsupportedOperationException
* If the connection does not support search operations.
* @throws IllegalStateException
* If the connection has already been closed, i.e. if
* {@code connection.isClosed() == true}.
* @throws NullPointerException
* If the {@code connection} or {@code name} was {@code null}.
*/
public static Schema readSchema(final Connection connection, final DN name)
throws ErrorResultException {
return new SchemaBuilder().addSchema(connection, name, true).toSchema();
}
/**
* Asynchronously reads the schema contained in the named subschema
* sub-entry.
*
* If the requested schema is not returned by the Directory Server then the
* request will fail with an {@link EntryNotFoundException}. More
* specifically, the returned future will never return {@code null}.
*
* @param connection
* A connection to the Directory Server whose schema is to be
* read.
* @param name
* The distinguished name of the subschema sub-entry.
* @param handler
* A result handler which can be used to asynchronously process
* the operation result when it is received, may be {@code null}.
* @return A future representing the retrieved schema.
* @throws UnsupportedOperationException
* If the connection does not support search operations.
* @throws IllegalStateException
* If the connection has already been closed, i.e. if
* {@code connection.isClosed() == true}.
* @throws NullPointerException
* If the {@code connection} or {@code name} was {@code null}.
*/
public static FutureResult readSchemaAsync(final Connection connection, final DN name,
final ResultHandler super Schema> handler) {
final FutureResultTransformer future =
new FutureResultTransformer(handler) {
@Override
protected Schema transformResult(final SchemaBuilder builder)
throws ErrorResultException {
return builder.toSchema();
}
};
final SchemaBuilder builder = new SchemaBuilder();
final FutureResult innerFuture =
builder.addSchemaAsync(connection, name, future, true);
future.setFutureResult(innerFuture);
return future;
}
/**
* Reads the schema contained in the subschema sub-entry which applies to
* the named entry.
*
* If the requested entry or its associated schema are not returned by the
* Directory Server then the request will fail with an
* {@link EntryNotFoundException}. More specifically, this method will never
* return {@code null}.
*
* This implementation first reads the {@code subschemaSubentry} attribute
* of the entry in order to identify the schema and then invokes
* {@link #readSchema(Connection, DN)} to read the schema.
*
* @param connection
* A connection to the Directory Server whose schema is to be
* read.
* @param name
* The distinguished name of the entry whose schema is to be
* located.
* @return The schema from the Directory Server which applies to the named
* entry.
* @throws ErrorResultException
* If the result code indicates that the request failed for some
* reason.
* @throws UnsupportedOperationException
* If the connection does not support search operations.
* @throws IllegalStateException
* If the connection has already been closed, i.e. if
* {@code connection.isClosed() == true}.
* @throws NullPointerException
* If the {@code connection} or {@code name} was {@code null}.
*/
public static Schema readSchemaForEntry(final Connection connection, final DN name)
throws ErrorResultException {
return new SchemaBuilder().addSchemaForEntry(connection, name, true).toSchema();
}
/**
* Asynchronously reads the schema contained in the subschema sub-entry
* which applies to the named entry.
*
* If the requested entry or its associated schema are not returned by the
* Directory Server then the request will fail with an
* {@link EntryNotFoundException}. More specifically, the returned future
* will never return {@code null}.
*
* This implementation first reads the {@code subschemaSubentry} attribute
* of the entry in order to identify the schema and then invokes
* {@link #readSchemaAsync(Connection, DN, ResultHandler)} to read the
* schema.
*
* @param connection
* A connection to the Directory Server whose schema is to be
* read.
* @param name
* The distinguished name of the entry whose schema is to be
* located.
* @param handler
* A result handler which can be used to asynchronously process
* the operation result when it is received, may be {@code null}.
* @return A future representing the retrieved schema.
* @throws UnsupportedOperationException
* If the connection does not support search operations.
* @throws IllegalStateException
* If the connection has already been closed, i.e. if
* {@code connection.isClosed() == true}.
* @throws NullPointerException
* If the {@code connection} or {@code name} was {@code null}.
*/
public static FutureResult readSchemaForEntryAsync(final Connection connection,
final DN name, final ResultHandler super Schema> handler) {
final FutureResultTransformer future =
new FutureResultTransformer(handler) {
@Override
protected Schema transformResult(final SchemaBuilder builder)
throws ErrorResultException {
return builder.toSchema();
}
};
final SchemaBuilder builder = new SchemaBuilder();
final FutureResult innerFuture =
builder.addSchemaForEntryAsync(connection, name, future, true);
future.setFutureResult(innerFuture);
return future;
}
/**
* Sets the default schema which should be used by this application. The
* default schema is initially set to the core schema.
*
* @param schema
* The default schema which should be used by this application.
*/
public static void setDefaultSchema(final Schema schema) {
Validator.ensureNotNull(schema);
DefaultSchema.schema = schema;
}
/**
* Parses the provided entry as a subschema subentry. Any problems
* encountered while parsing the entry can be retrieved using the returned
* schema's {@link #getWarnings()} method.
*
* @param entry
* The subschema subentry to be parsed.
* @return The parsed schema.
*/
public static Schema valueOf(final Entry entry) {
return new SchemaBuilder(entry).toSchema();
}
static MatchingRule getDefaultMatchingRule() {
return CoreSchema.getCaseIgnoreMatchingRule();
}
static Syntax getDefaultSyntax() {
return CoreSchema.getDirectoryStringSyntax();
}
private final Impl impl;
Schema(final String schemaName, final boolean allowMalformedNamesAndOptions,
final boolean allowMalformedJPEGPhotos,
final boolean allowNonStandardTelephoneNumbers,
final boolean allowZeroLengthDirectoryStrings,
final Map numericOID2Syntaxes,
final Map numericOID2MatchingRules,
final Map numericOID2MatchingRuleUses,
final Map numericOID2AttributeTypes,
final Map numericOID2ObjectClasses,
final Map numericOID2NameForms,
final Map numericOID2ContentRules,
final Map id2StructureRules,
final Map> name2MatchingRules,
final Map> name2MatchingRuleUses,
final Map> name2AttributeTypes,
final Map> name2ObjectClasses,
final Map> name2NameForms,
final Map> name2ContentRules,
final Map> name2StructureRules,
final Map> objectClass2NameForms,
final Map> nameForm2StructureRules,
final List warnings) {
impl =
new StrictImpl(schemaName, allowMalformedNamesAndOptions,
allowMalformedJPEGPhotos, allowNonStandardTelephoneNumbers,
allowZeroLengthDirectoryStrings, numericOID2Syntaxes,
numericOID2MatchingRules, numericOID2MatchingRuleUses,
numericOID2AttributeTypes, numericOID2ObjectClasses, numericOID2NameForms,
numericOID2ContentRules, id2StructureRules, name2MatchingRules,
name2MatchingRuleUses, name2AttributeTypes, name2ObjectClasses,
name2NameForms, name2ContentRules, name2StructureRules,
objectClass2NameForms, nameForm2StructureRules, warnings);
}
private Schema(final Impl impl) {
this.impl = impl;
}
/**
* Returns {@code true} if this schema allows certain illegal characters in
* OIDs and attribute options. When this compatibility option is set to
* {@code true} the following illegal characters will be permitted in
* addition to those permitted in section 1.4 of RFC 4512:
*
*
* USCORE = %x5F ; underscore ("_")
* DOT = %x2E ; period (".")
*
*
* By default this compatibility option is set to {@code true} because these
* characters are often used for naming purposes (such as collation rules).
*
* @return {@code true} if this schema allows certain illegal characters in
* OIDs and attribute options.
* @see RFC 4512 - Lightweight
* Directory Access Protocol (LDAP): Directory Information Models
*/
public boolean allowMalformedNamesAndOptions() {
return impl.allowMalformedNamesAndOptions();
}
/**
* Returns {@code true} if the JPEG Photo syntax defined for this
* schema allows values which do not conform to the JFIF or Exif
* specifications.
*
* By default this compatibility option is set to {@code true}.
*
* @return {@code true} if the JPEG Photo syntax defined for this
* schema allows values which do not conform to the JFIF
* of Exit specifications.
*/
public boolean allowMalformedJPEGPhotos() {
return impl.allowMalformedJPEGPhotos();
}
/**
* Returns {@code true} if the Telephone Number syntax defined for this
* schema allows values which do not conform to the E.123 international
* telephone number format.
*
* By default this compatibility option is set to {@code true}.
*
* @return {@code true} if the Telephone Number syntax defined for this
* schema allows values which do not conform to the E.123
* international telephone number format.
*/
public boolean allowNonStandardTelephoneNumbers() {
return impl.allowNonStandardTelephoneNumbers();
}
/**
* Returns {@code true} if zero-length values will be allowed by the
* Directory String syntax defined for this schema. This is technically
* forbidden by the LDAP specification, but it was allowed in earlier
* versions of the server, and the discussion of the directory string syntax
* in RFC 2252 does not explicitly state that they are not allowed.
*
* By default this compatibility option is set to {@code false}.
*
* @return {@code true} if zero-length values will be allowed by the
* Directory String syntax defined for this schema, or {@code false}
* if not.
*/
public boolean allowZeroLengthDirectoryStrings() {
return impl.allowZeroLengthDirectoryStrings();
}
/**
* Returns a non-strict view of this schema.
*
* See the description of {@link #isStrict()} for more details.
*
* @return A non-strict view of this schema.
* @see Schema#isStrict()
*/
public Schema asNonStrictSchema() {
if (!impl.isStrict()) {
return this;
} else if (impl instanceof StrictImpl) {
return new Schema(new NonStrictImpl((StrictImpl) impl));
} else {
// EmptyImpl
return EMPTY_NON_STRICT_SCHEMA;
}
}
/**
* Returns a strict view of this schema.
*
* See the description of {@link #isStrict()} for more details.
*
* @return A strict view of this schema.
* @see Schema#isStrict()
*/
public Schema asStrictSchema() {
if (impl.isStrict()) {
return this;
} else if (impl instanceof NonStrictImpl) {
return new Schema(((NonStrictImpl) impl).strictImpl);
} else {
// EmptyImpl
return EMPTY_STRICT_SCHEMA;
}
}
/**
* Returns the attribute type with the specified name or numeric OID.
*
* If the requested attribute type is not registered in this schema and this
* schema is non-strict then a temporary "place-holder" attribute type will
* be created and returned. Place holder attribute types have an OID which
* is the normalized attribute name with the string {@code -oid} appended.
* In addition, they will use the directory string syntax and case ignore
* matching rule.
*
* @param name
* The name or OID of the attribute type to retrieve.
* @return The requested attribute type.
* @throws UnknownSchemaElementException
* If this is a strict schema and the requested attribute type
* was not found or if the provided name is ambiguous.
* @see AttributeType#isPlaceHolder()
*/
public AttributeType getAttributeType(final String name) {
return impl.getAttributeType(name);
}
/**
* Returns an unmodifiable collection containing all of the attribute types
* contained in this schema.
*
* @return An unmodifiable collection containing all of the attribute types
* contained in this schema.
*/
public Collection getAttributeTypes() {
return impl.getAttributeTypes();
}
/**
* Returns an unmodifiable collection containing all of the attribute types
* having the specified name or numeric OID.
*
* @param name
* The name or OID of the attribute types to retrieve.
* @return An unmodifiable collection containing all of the attribute types
* having the specified name or numeric OID.
*/
public List getAttributeTypesWithName(final String name) {
return impl.getAttributeTypesWithName(name);
}
/**
* Returns the DIT content rule associated with the provided structural
* object class, or {@code null} if no rule is defined.
*
* @param structuralClass
* The structural object class .
* @return The DIT content rule associated with the provided structural
* object class, or {@code null} if no rule is defined.
*/
public DITContentRule getDITContentRule(final ObjectClass structuralClass) {
return impl.getDITContentRule(structuralClass);
}
/**
* Returns the DIT content rule with the specified name or numeric OID.
*
* @param name
* The name or OID of the DIT content rule to retrieve.
* @return The requested DIT content rule.
* @throws UnknownSchemaElementException
* If this is a strict schema and the requested DIT content rule
* was not found or if the provided name is ambiguous.
*/
public DITContentRule getDITContentRule(final String name) {
return impl.getDITContentRule(name);
}
/**
* Returns an unmodifiable collection containing all of the DIT content
* rules contained in this schema.
*
* @return An unmodifiable collection containing all of the DIT content
* rules contained in this schema.
*/
public Collection getDITContentRules() {
return impl.getDITContentRules();
}
/**
* Returns an unmodifiable collection containing all of the DIT content
* rules having the specified name or numeric OID.
*
* @param name
* The name or OID of the DIT content rules to retrieve.
* @return An unmodifiable collection containing all of the DIT content
* rules having the specified name or numeric OID.
*/
public Collection getDITContentRulesWithName(final String name) {
return impl.getDITContentRulesWithName(name);
}
/**
* Returns the DIT structure rule with the specified name or numeric OID.
*
* @param ruleID
* The ID of the DIT structure rule to retrieve.
* @return The requested DIT structure rule.
* @throws UnknownSchemaElementException
* If this is a strict schema and the requested DIT structure
* rule was not found.
*/
public DITStructureRule getDITStructureRule(final int ruleID) {
return impl.getDITStructureRule(ruleID);
}
/**
* Returns an unmodifiable collection containing all of the DIT structure
* rules associated with the provided name form.
*
* @param nameForm
* The name form.
* @return An unmodifiable collection containing all of the DIT structure
* rules associated with the provided name form.
*/
public Collection getDITStructureRules(final NameForm nameForm) {
return impl.getDITStructureRules(nameForm);
}
/**
* Returns an unmodifiable collection containing all of the DIT structure
* rules having the specified name or numeric OID.
*
* @param name
* The name or OID of the DIT structure rules to retrieve.
* @return An unmodifiable collection containing all of the DIT structure
* rules having the specified name or numeric OID.
*/
public Collection getDITStructureRulesWithName(final String name) {
return impl.getDITStructureRulesWithName(name);
}
/**
* Returns an unmodifiable collection containing all of the DIT structure
* rules contained in this schema.
*
* @return An unmodifiable collection containing all of the DIT structure
* rules contained in this schema.
*/
public Collection getDITStuctureRules() {
return impl.getDITStuctureRules();
}
/**
* Returns the matching rule with the specified name or numeric OID.
*
* @param name
* The name or OID of the matching rule to retrieve.
* @return The requested matching rule.
* @throws UnknownSchemaElementException
* If this is a strict schema and the requested matching rule
* was not found or if the provided name is ambiguous.
*/
public MatchingRule getMatchingRule(final String name) {
return impl.getMatchingRule(name);
}
/**
* Returns an unmodifiable collection containing all of the matching rules
* contained in this schema.
*
* @return An unmodifiable collection containing all of the matching rules
* contained in this schema.
*/
public Collection getMatchingRules() {
return impl.getMatchingRules();
}
/**
* Returns an unmodifiable collection containing all of the matching rules
* having the specified name or numeric OID.
*
* @param name
* The name or OID of the matching rules to retrieve.
* @return An unmodifiable collection containing all of the matching rules
* having the specified name or numeric OID.
*/
public Collection getMatchingRulesWithName(final String name) {
return impl.getMatchingRulesWithName(name);
}
/**
* Returns the matching rule use associated with the provided matching rule,
* or {@code null} if no use is defined.
*
* @param matchingRule
* The matching rule whose matching rule use is to be retrieved.
* @return The matching rule use associated with the provided matching rule,
* or {@code null} if no use is defined.
*/
public MatchingRuleUse getMatchingRuleUse(final MatchingRule matchingRule) {
return getMatchingRuleUse(matchingRule.getOID());
}
/**
* Returns the matching rule use with the specified name or numeric OID.
*
* @param name
* The name or OID of the matching rule use to retrieve.
* @return The requested matching rule use.
* @throws UnknownSchemaElementException
* If this is a strict schema and the requested matching rule
* use was not found or if the provided name is ambiguous.
*/
public MatchingRuleUse getMatchingRuleUse(final String name) {
return impl.getMatchingRuleUse(name);
}
/**
* Returns an unmodifiable collection containing all of the matching rule
* uses contained in this schema.
*
* @return An unmodifiable collection containing all of the matching rule
* uses contained in this schema.
*/
public Collection getMatchingRuleUses() {
return impl.getMatchingRuleUses();
}
/**
* Returns an unmodifiable collection containing all of the matching rule
* uses having the specified name or numeric OID.
*
* @param name
* The name or OID of the matching rule uses to retrieve.
* @return An unmodifiable collection containing all of the matching rule
* uses having the specified name or numeric OID.
*/
public Collection getMatchingRuleUsesWithName(final String name) {
return impl.getMatchingRuleUsesWithName(name);
}
/**
* Returns the name form with the specified name or numeric OID.
*
* @param name
* The name or OID of the name form to retrieve.
* @return The requested name form.
* @throws UnknownSchemaElementException
* If this is a strict schema and the requested name form was
* not found or if the provided name is ambiguous.
*/
public NameForm getNameForm(final String name) {
return impl.getNameForm(name);
}
/**
* Returns an unmodifiable collection containing all of the name forms
* contained in this schema.
*
* @return An unmodifiable collection containing all of the name forms
* contained in this schema.
*/
public Collection getNameForms() {
return impl.getNameForms();
}
/**
* Returns an unmodifiable collection containing all of the name forms
* associated with the provided structural object class.
*
* @param structuralClass
* The structural object class whose name forms are to be
* retrieved.
* @return An unmodifiable collection containing all of the name forms
* associated with the provided structural object class.
*/
public Collection getNameForms(final ObjectClass structuralClass) {
return impl.getNameForms(structuralClass);
}
/**
* Returns an unmodifiable collection containing all of the name forms
* having the specified name or numeric OID.
*
* @param name
* The name or OID of the name forms to retrieve.
* @return An unmodifiable collection containing all of the name forms
* having the specified name or numeric OID.
*/
public Collection getNameFormsWithName(final String name) {
return impl.getNameFormsWithName(name);
}
/**
* Returns the object class with the specified name or numeric OID.
*
* @param name
* The name or OID of the object class to retrieve.
* @return The requested object class.
* @throws UnknownSchemaElementException
* If this is a strict schema and the requested object class was
* not found or if the provided name is ambiguous.
*/
public ObjectClass getObjectClass(final String name) {
return impl.getObjectClass(name);
}
/**
* Returns an unmodifiable collection containing all of the object classes
* contained in this schema.
*
* @return An unmodifiable collection containing all of the object classes
* contained in this schema.
*/
public Collection getObjectClasses() {
return impl.getObjectClasses();
}
/**
* Returns an unmodifiable collection containing all of the object classes
* having the specified name or numeric OID.
*
* @param name
* The name or OID of the object classes to retrieve.
* @return An unmodifiable collection containing all of the object classes
* having the specified name or numeric OID.
*/
public Collection getObjectClassesWithName(final String name) {
return impl.getObjectClassesWithName(name);
}
/**
* Returns the user-friendly name of this schema which may be used for
* debugging purposes. The format of the schema name is not defined but
* should contain the distinguished name of the subschema sub-entry for
* those schemas retrieved from a Directory Server.
*
* @return The user-friendly name of this schema which may be used for
* debugging purposes.
*/
public String getSchemaName() {
return impl.getSchemaName();
}
/**
* Returns the syntax with the specified numeric OID.
*
* @param numericOID
* The OID of the syntax to retrieve.
* @return The requested syntax.
* @throws UnknownSchemaElementException
* If this is a strict schema and the requested syntax was not
* found or if the provided name is ambiguous.
*/
public Syntax getSyntax(final String numericOID) {
return impl.getSyntax(numericOID);
}
/**
* Returns an unmodifiable collection containing all of the syntaxes
* contained in this schema.
*
* @return An unmodifiable collection containing all of the syntaxes
* contained in this schema.
*/
public Collection getSyntaxes() {
return impl.getSyntaxes();
}
/**
* Returns an unmodifiable collection containing all of the warnings that
* were detected when this schema was constructed.
*
* @return An unmodifiable collection containing all of the warnings that
* were detected when this schema was constructed.
*/
public Collection getWarnings() {
return impl.getWarnings();
}
/**
* Indicates whether or not this schema contains an attribute type with the
* specified name or numeric OID.
*
* @param name
* The name or OID of the attribute type.
* @return {@code true} if this schema contains an attribute type with the
* specified name or numeric OID, otherwise {@code false}.
*/
public boolean hasAttributeType(final String name) {
return impl.hasAttributeType(name);
}
/**
* Indicates whether or not this schema contains a DIT content rule with the
* specified name or numeric OID.
*
* @param name
* The name or OID of the DIT content rule.
* @return {@code true} if this schema contains a DIT content rule with the
* specified name or numeric OID, otherwise {@code false}.
*/
public boolean hasDITContentRule(final String name) {
return impl.hasDITContentRule(name);
}
/**
* Indicates whether or not this schema contains a DIT structure rule with
* the specified rule ID.
*
* @param ruleID
* The ID of the DIT structure rule.
* @return {@code true} if this schema contains a DIT structure rule with
* the specified rule ID, otherwise {@code false}.
*/
public boolean hasDITStructureRule(final int ruleID) {
return impl.hasDITStructureRule(ruleID);
}
/**
* Indicates whether or not this schema contains a matching rule with the
* specified name or numeric OID.
*
* @param name
* The name or OID of the matching rule.
* @return {@code true} if this schema contains a matching rule with the
* specified name or numeric OID, otherwise {@code false}.
*/
public boolean hasMatchingRule(final String name) {
return impl.hasMatchingRule(name);
}
/**
* Indicates whether or not this schema contains a matching rule use with
* the specified name or numeric OID.
*
* @param name
* The name or OID of the matching rule use.
* @return {@code true} if this schema contains a matching rule use with the
* specified name or numeric OID, otherwise {@code false}.
*/
public boolean hasMatchingRuleUse(final String name) {
return impl.hasMatchingRuleUse(name);
}
/**
* Indicates whether or not this schema contains a name form with the
* specified name or numeric OID.
*
* @param name
* The name or OID of the name form.
* @return {@code true} if this schema contains a name form with the
* specified name or numeric OID, otherwise {@code false}.
*/
public boolean hasNameForm(final String name) {
return impl.hasNameForm(name);
}
/**
* Indicates whether or not this schema contains an object class with the
* specified name or numeric OID.
*
* @param name
* The name or OID of the object class.
* @return {@code true} if this schema contains an object class with the
* specified name or numeric OID, otherwise {@code false}.
*/
public boolean hasObjectClass(final String name) {
return impl.hasObjectClass(name);
}
/**
* Indicates whether or not this schema contains a syntax with the specified
* numeric OID.
*
* @param numericOID
* The OID of the syntax.
* @return {@code true} if this schema contains a syntax with the specified
* numeric OID, otherwise {@code false}.
*/
public boolean hasSyntax(final String numericOID) {
return impl.hasSyntax(numericOID);
}
/**
* Indicates whether or not this schema is strict.
*
* Attribute type queries against non-strict schema always succeed: if the
* requested attribute type is not found then a temporary attribute type is
* created automatically having the Octet String syntax and associated
* matching rules.
*
* Strict schema, on the other hand, throw an
* {@link UnknownSchemaElementException} whenever an attempt is made to
* retrieve a non-existent attribute type.
*
* @return {@code true} if this schema is strict.
*/
public boolean isStrict() {
return impl.isStrict();
}
/**
* Adds the definitions of all the schema elements contained in this schema
* to the provided subschema subentry. Any existing attributes (including
* schema definitions) contained in the provided entry will be preserved.
*
* @param entry
* The subschema subentry to which all schema definitions should
* be added.
* @return The updated subschema subentry.
* @throws NullPointerException
* If {@code entry} was {@code null}.
*/
public Entry toEntry(final Entry entry) {
Attribute attr = new LinkedAttribute(Schema.ATTR_LDAP_SYNTAXES);
for (final Syntax syntax : getSyntaxes()) {
attr.add(syntax.toString());
}
if (!attr.isEmpty()) {
entry.addAttribute(attr);
}
attr = new LinkedAttribute(Schema.ATTR_ATTRIBUTE_TYPES);
for (final AttributeType attributeType : getAttributeTypes()) {
attr.add(attributeType.toString());
}
if (!attr.isEmpty()) {
entry.addAttribute(attr);
}
attr = new LinkedAttribute(Schema.ATTR_OBJECT_CLASSES);
for (final ObjectClass objectClass : getObjectClasses()) {
attr.add(objectClass.toString());
}
if (!attr.isEmpty()) {
entry.addAttribute(attr);
}
attr = new LinkedAttribute(Schema.ATTR_MATCHING_RULE_USE);
for (final MatchingRuleUse matchingRuleUse : getMatchingRuleUses()) {
attr.add(matchingRuleUse.toString());
}
if (!attr.isEmpty()) {
entry.addAttribute(attr);
}
attr = new LinkedAttribute(Schema.ATTR_MATCHING_RULES);
for (final MatchingRule matchingRule : getMatchingRules()) {
attr.add(matchingRule.toString());
}
if (!attr.isEmpty()) {
entry.addAttribute(attr);
}
attr = new LinkedAttribute(Schema.ATTR_DIT_CONTENT_RULES);
for (final DITContentRule ditContentRule : getDITContentRules()) {
attr.add(ditContentRule.toString());
}
if (!attr.isEmpty()) {
entry.addAttribute(attr);
}
attr = new LinkedAttribute(Schema.ATTR_DIT_STRUCTURE_RULES);
for (final DITStructureRule ditStructureRule : getDITStuctureRules()) {
attr.add(ditStructureRule.toString());
}
if (!attr.isEmpty()) {
entry.addAttribute(attr);
}
attr = new LinkedAttribute(Schema.ATTR_NAME_FORMS);
for (final NameForm nameForm : getNameForms()) {
attr.add(nameForm.toString());
}
if (!attr.isEmpty()) {
entry.addAttribute(attr);
}
return entry;
}
/**
* Returns {@code true} if the provided entry is valid according to this
* schema and the specified schema validation policy.
*
* If attribute value validation is enabled then following checks will be
* performed:
*
* - checking that there is at least one value
*
- checking that single-valued attributes contain only a single value
*
* In particular, attribute values will not be checked for conformance to
* their syntax since this is expected to have already been performed while
* adding the values to the entry.
*
* @param entry
* The entry to be validated.
* @param policy
* The schema validation policy.
* @param errorMessages
* A collection into which any schema validation warnings or
* error messages can be placed, or {@code null} if they should
* not be saved.
* @return {@code true} if an entry conforms to this schema based on the
* provided schema validation policy.
*/
public boolean validateEntry(final Entry entry, final SchemaValidationPolicy policy,
final Collection errorMessages) {
// First check that the object classes are recognized and that there is
// one
// structural object class.
ObjectClass structuralObjectClass = null;
final Attribute objectClassAttribute = entry.getAttribute(objectClass());
final List objectClasses = new LinkedList();
if (objectClassAttribute != null) {
for (final ByteString v : objectClassAttribute) {
final String objectClassName = v.toString();
final ObjectClass objectClass;
try {
objectClass = getObjectClass(objectClassName);
objectClasses.add(objectClass);
} catch (final UnknownSchemaElementException e) {
if (policy.checkAttributesAndObjectClasses().needsChecking()) {
if (errorMessages != null) {
final LocalizableMessage message =
ERR_ENTRY_SCHEMA_UNKNOWN_OBJECT_CLASS.get(entry.getName()
.toString(), objectClassName);
errorMessages.add(message);
}
if (policy.checkAttributesAndObjectClasses().isReject()) {
return false;
}
}
continue;
}
if (objectClass.getObjectClassType() == ObjectClassType.STRUCTURAL) {
if (structuralObjectClass == null
|| objectClass.isDescendantOf(structuralObjectClass)) {
structuralObjectClass = objectClass;
} else if (!structuralObjectClass.isDescendantOf(objectClass)) {
if (policy.requireSingleStructuralObjectClass().needsChecking()) {
if (errorMessages != null) {
final LocalizableMessage message =
ERR_ENTRY_SCHEMA_MULTIPLE_STRUCTURAL_CLASSES.get(entry
.getName().toString(), structuralObjectClass
.getNameOrOID(), objectClassName);
errorMessages.add(message);
}
if (policy.requireSingleStructuralObjectClass().isReject()) {
return false;
}
}
}
}
}
}
Collection ditStructureRules = Collections.emptyList();
DITContentRule ditContentRule = null;
if (structuralObjectClass == null) {
if (policy.requireSingleStructuralObjectClass().needsChecking()) {
if (errorMessages != null) {
final LocalizableMessage message =
ERR_ENTRY_SCHEMA_NO_STRUCTURAL_CLASS.get(entry.getName().toString());
errorMessages.add(message);
}
if (policy.requireSingleStructuralObjectClass().isReject()) {
return false;
}
}
} else {
ditContentRule = getDITContentRule(structuralObjectClass);
if (ditContentRule != null && ditContentRule.isObsolete()) {
ditContentRule = null;
}
}
// Check entry conforms to object classes and optional content rule.
if (!checkAttributesAndObjectClasses(entry, policy, errorMessages, objectClasses,
ditContentRule)) {
return false;
}
// Check that the name of the entry conforms to at least one applicable
// name
// form.
if (policy.checkNameForms().needsChecking() && structuralObjectClass != null) {
/**
* There may be multiple name forms registered with this structural
* object class. However, we need to select only one of the name
* forms and its corresponding DIT structure rule(s). We will
* iterate over all the name forms and see if at least one is
* acceptable before rejecting the entry. DIT structure rules
* corresponding to other non-acceptable name forms are not applied.
*/
boolean foundMatchingNameForms = false;
NameForm nameForm = null;
final List nameFormWarnings =
(errorMessages != null) ? new LinkedList() : null;
for (final NameForm nf : getNameForms(structuralObjectClass)) {
if (nf.isObsolete()) {
continue;
}
// If there are any candidate name forms then at least one
// should be
// valid.
foundMatchingNameForms = true;
if (checkNameForm(entry, policy, nameFormWarnings, nf)) {
nameForm = nf;
break;
}
}
if (foundMatchingNameForms) {
if (nameForm != null) {
ditStructureRules = getDITStructureRules(nameForm);
} else {
// We couldn't match this entry against any of the name
// forms, so
// append the reasons why they didn't match and reject if
// required.
if (errorMessages != null) {
errorMessages.addAll(nameFormWarnings);
}
if (policy.checkNameForms().isReject()) {
return false;
}
}
}
}
// Check DIT structure rules - this needs the parent entry.
if (policy.checkDITStructureRules().needsChecking() && !entry.getName().isRootDN()) {
boolean foundMatchingRules = false;
boolean foundValidRule = false;
final List ruleWarnings =
(errorMessages != null) ? new LinkedList() : null;
ObjectClass parentStructuralObjectClass = null;
boolean parentEntryHasBeenRead = false;
for (final DITStructureRule rule : ditStructureRules) {
if (rule.isObsolete()) {
continue;
}
foundMatchingRules = true;
// A DIT structure rule with no superiors is automatically
// valid, so
// avoid reading the parent.
if (rule.getSuperiorRules().isEmpty()) {
foundValidRule = true;
break;
}
if (!parentEntryHasBeenRead) {
// Don't drop out immediately on failure because there may
// be some
// applicable rules which do not require the parent entry.
parentStructuralObjectClass =
getParentStructuralObjectClass(entry, policy, ruleWarnings);
parentEntryHasBeenRead = true;
}
if (parentStructuralObjectClass != null) {
if (checkDITStructureRule(entry, policy, ruleWarnings, rule,
structuralObjectClass, parentStructuralObjectClass)) {
foundValidRule = true;
break;
}
}
}
if (foundMatchingRules) {
if (!foundValidRule) {
// We couldn't match this entry against any of the rules, so
// append the reasons why they didn't match and reject if
// required.
if (errorMessages != null) {
errorMessages.addAll(ruleWarnings);
}
if (policy.checkDITStructureRules().isReject()) {
return false;
}
}
} else {
// There is no DIT structure rule for this entry, but there may
// be one for the parent entry. If there is such a rule for the
// parent entry, then this entry will not be valid.
// The parent won't have been read yet.
parentStructuralObjectClass =
getParentStructuralObjectClass(entry, policy, ruleWarnings);
if (parentStructuralObjectClass == null) {
if (errorMessages != null) {
errorMessages.addAll(ruleWarnings);
}
if (policy.checkDITStructureRules().isReject()) {
return false;
}
} else {
for (final NameForm nf : getNameForms(parentStructuralObjectClass)) {
if (!nf.isObsolete()) {
for (final DITStructureRule rule : getDITStructureRules(nf)) {
if (!rule.isObsolete()) {
if (errorMessages != null) {
final LocalizableMessage message =
ERR_ENTRY_SCHEMA_DSR_MISSING_DSR.get(entry
.getName().toString(), rule
.getNameOrRuleID());
errorMessages.add(message);
}
if (policy.checkDITStructureRules().isReject()) {
return false;
}
// We could break out of the loop here in
// warn mode but
// continuing allows us to collect all
// conflicts.
}
}
}
}
}
}
}
// If we've gotten here, then the entry is acceptable.
return true;
}
private boolean checkAttributesAndObjectClasses(final Entry entry,
final SchemaValidationPolicy policy,
final Collection errorMessages,
final List objectClasses, final DITContentRule ditContentRule) {
// Check object classes.
final boolean checkDITContentRule =
policy.checkDITContentRules().needsChecking() && ditContentRule != null;
final boolean checkObjectClasses = policy.checkAttributesAndObjectClasses().needsChecking();
final boolean checkAttributeValues = policy.checkAttributeValues().needsChecking();
if (checkObjectClasses || checkDITContentRule) {
for (final ObjectClass objectClass : objectClasses) {
// Make sure that any auxiliary object classes are permitted by
// the
// content rule.
if (checkDITContentRule) {
if (objectClass.getObjectClassType() == ObjectClassType.AUXILIARY
&& !ditContentRule.getAuxiliaryClasses().contains(objectClass)) {
if (errorMessages != null) {
final LocalizableMessage message =
ERR_ENTRY_SCHEMA_DCR_PROHIBITED_AUXILIARY_OC.get(entry
.getName().toString(), objectClass.getNameOrOID(),
ditContentRule.getNameOrOID());
errorMessages.add(message);
}
if (policy.checkDITContentRules().isReject()) {
return false;
}
}
}
// Make sure that all of the attributes required by the object
// class are
// present.
if (checkObjectClasses) {
for (final AttributeType t : objectClass.getDeclaredRequiredAttributes()) {
final Attribute a =
Attributes.emptyAttribute(AttributeDescription.create(t));
if (!entry.containsAttribute(a, null)) {
if (errorMessages != null) {
final LocalizableMessage message =
ERR_ENTRY_SCHEMA_OC_MISSING_MUST_ATTRIBUTES.get(entry
.getName().toString(), t.getNameOrOID(),
objectClass.getNameOrOID());
errorMessages.add(message);
}
if (policy.checkAttributesAndObjectClasses().isReject()) {
return false;
}
}
}
}
}
// Make sure that all of the attributes required by the content rule
// are
// present.
if (checkDITContentRule) {
for (final AttributeType t : ditContentRule.getRequiredAttributes()) {
final Attribute a = Attributes.emptyAttribute(AttributeDescription.create(t));
if (!entry.containsAttribute(a, null)) {
if (errorMessages != null) {
final LocalizableMessage message =
ERR_ENTRY_SCHEMA_DCR_MISSING_MUST_ATTRIBUTES.get(entry
.getName().toString(), t.getNameOrOID(), ditContentRule
.getNameOrOID());
errorMessages.add(message);
}
if (policy.checkDITContentRules().isReject()) {
return false;
}
}
}
// Make sure that attributes prohibited by the content rule are
// not
// present.
for (final AttributeType t : ditContentRule.getProhibitedAttributes()) {
final Attribute a = Attributes.emptyAttribute(AttributeDescription.create(t));
if (entry.containsAttribute(a, null)) {
if (errorMessages != null) {
final LocalizableMessage message =
ERR_ENTRY_SCHEMA_DCR_PROHIBITED_ATTRIBUTES.get(entry.getName()
.toString(), t.getNameOrOID(), ditContentRule
.getNameOrOID());
errorMessages.add(message);
}
if (policy.checkDITContentRules().isReject()) {
return false;
}
}
}
}
}
// Check attributes.
if (checkObjectClasses || checkDITContentRule || checkAttributeValues) {
for (final Attribute attribute : entry.getAllAttributes()) {
final AttributeType t = attribute.getAttributeDescription().getAttributeType();
if (!t.isOperational()) {
if (checkObjectClasses || checkDITContentRule) {
boolean isAllowed = false;
for (final ObjectClass objectClass : objectClasses) {
if (objectClass.isRequiredOrOptional(t)) {
isAllowed = true;
break;
}
}
if (!isAllowed && ditContentRule != null) {
if (ditContentRule.isRequiredOrOptional(t)) {
isAllowed = true;
}
}
if (!isAllowed) {
if (errorMessages != null) {
final LocalizableMessage message;
if (ditContentRule == null) {
message =
ERR_ENTRY_SCHEMA_OC_DISALLOWED_ATTRIBUTES.get(entry
.getName().toString(), t.getNameOrOID());
} else {
message =
ERR_ENTRY_SCHEMA_DCR_DISALLOWED_ATTRIBUTES.get(entry
.getName().toString(), t.getNameOrOID(),
ditContentRule.getNameOrOID());
}
errorMessages.add(message);
}
if (policy.checkAttributesAndObjectClasses().isReject()
|| policy.checkDITContentRules().isReject()) {
return false;
}
}
}
}
// Check all attributes contain an appropriate number of values.
if (checkAttributeValues) {
final int sz = attribute.size();
if (sz == 0) {
if (errorMessages != null) {
final LocalizableMessage message =
ERR_ENTRY_SCHEMA_AT_EMPTY_ATTRIBUTE.get(entry.getName()
.toString(), t.getNameOrOID());
errorMessages.add(message);
}
if (policy.checkAttributeValues().isReject()) {
return false;
}
} else if (sz > 1 && t.isSingleValue()) {
if (errorMessages != null) {
final LocalizableMessage message =
ERR_ENTRY_SCHEMA_AT_SINGLE_VALUED_ATTRIBUTE.get(entry.getName()
.toString(), t.getNameOrOID());
errorMessages.add(message);
}
if (policy.checkAttributeValues().isReject()) {
return false;
}
}
}
}
}
// If we've gotten here, then things are OK.
return true;
}
private boolean checkDITStructureRule(final Entry entry, final SchemaValidationPolicy policy,
final List ruleWarnings, final DITStructureRule rule,
final ObjectClass structuralObjectClass, final ObjectClass parentStructuralObjectClass) {
boolean matchFound = false;
for (final DITStructureRule parentRule : rule.getSuperiorRules()) {
if (parentRule.getNameForm().getStructuralClass().equals(parentStructuralObjectClass)) {
matchFound = true;
}
}
if (!matchFound) {
if (ruleWarnings != null) {
final LocalizableMessage message =
ERR_ENTRY_SCHEMA_DSR_ILLEGAL_OC.get(entry.getName().toString(), rule
.getNameOrRuleID(), structuralObjectClass.getNameOrOID(),
parentStructuralObjectClass.getNameOrOID());
ruleWarnings.add(message);
}
return false;
}
return true;
}
private boolean checkNameForm(final Entry entry, final SchemaValidationPolicy policy,
final List nameFormWarnings, final NameForm nameForm) {
final RDN rdn = entry.getName().rdn();
if (rdn != null) {
// Make sure that all the required AVAs are present.
for (final AttributeType t : nameForm.getRequiredAttributes()) {
if (rdn.getAttributeValue(t) == null) {
if (nameFormWarnings != null) {
final LocalizableMessage message =
ERR_ENTRY_SCHEMA_NF_MISSING_MUST_ATTRIBUTES.get(entry.getName()
.toString(), t.getNameOrOID(), nameForm.getNameOrOID());
nameFormWarnings.add(message);
}
return false;
}
}
// Make sure that all AVAs in the RDN are allowed.
for (final AVA ava : rdn) {
final AttributeType t = ava.getAttributeType();
if (!nameForm.isRequiredOrOptional(t)) {
if (nameFormWarnings != null) {
final LocalizableMessage message =
ERR_ENTRY_SCHEMA_NF_DISALLOWED_ATTRIBUTES.get(entry.getName()
.toString(), t.getNameOrOID(), nameForm.getNameOrOID());
nameFormWarnings.add(message);
}
return false;
}
}
}
// If we've gotten here, then things are OK.
return true;
}
private ObjectClass getParentStructuralObjectClass(final Entry entry,
final SchemaValidationPolicy policy, final List ruleWarnings) {
final Entry parentEntry;
try {
parentEntry =
policy.checkDITStructureRulesEntryResolver().getEntry(entry.getName().parent());
} catch (final ErrorResultException e) {
if (ruleWarnings != null) {
final LocalizableMessage message =
ERR_ENTRY_SCHEMA_DSR_PARENT_NOT_FOUND.get(entry.getName().toString(), e
.getResult().getDiagnosticMessage());
ruleWarnings.add(message);
}
return null;
}
final ObjectClass parentStructuralObjectClass =
Entries.getStructuralObjectClass(parentEntry, this);
if (parentStructuralObjectClass == null) {
if (ruleWarnings != null) {
final LocalizableMessage message =
ERR_ENTRY_SCHEMA_DSR_NO_PARENT_OC.get(entry.getName().toString());
ruleWarnings.add(message);
}
return null;
}
return parentStructuralObjectClass;
}
}