blob: 9decf316fafb197068e3c43d6950c26d1a535d80 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.directory.api.ldap.model.schema.registries.helper;
import java.util.HashSet;
import java.util.Set;
import org.apache.directory.api.i18n.I18n;
import org.apache.directory.api.ldap.model.exception.LdapException;
import org.apache.directory.api.ldap.model.exception.LdapSchemaException;
import org.apache.directory.api.ldap.model.exception.LdapSchemaExceptionCodes;
import org.apache.directory.api.ldap.model.schema.AttributeType;
import org.apache.directory.api.ldap.model.schema.LdapSyntax;
import org.apache.directory.api.ldap.model.schema.MatchingRule;
import org.apache.directory.api.ldap.model.schema.SchemaErrorHandler;
import org.apache.directory.api.ldap.model.schema.UsageEnum;
import org.apache.directory.api.ldap.model.schema.registries.AttributeTypeRegistry;
import org.apache.directory.api.ldap.model.schema.registries.Registries;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An helper class used to store all the methods associated with an AttributeType
* in relation with the Registries and SchemaManager.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public final class AttributeTypeHelper
{
/** A logger for this class */
private static final Logger LOG = LoggerFactory.getLogger( AttributeTypeHelper.class );
private AttributeTypeHelper()
{
}
/**
* Inject the AttributeType into the Registries, updating the references to
* other SchemaObject
*
* If one of the referenced SchemaObject does not exist (SUP, EQUALITY, ORDERING, SUBSTR, SYNTAX),
* an exception is thrown.
*
* @param attributeType The AttributeType to add to the Registries
* @param errorHandler Error handler
* @param registries The Registries
* @throws LdapException If the AttributeType is not valid
*/
public static void addToRegistries( AttributeType attributeType, SchemaErrorHandler errorHandler, Registries registries ) throws LdapException
{
if ( registries != null )
{
try
{
attributeType.unlock();
AttributeTypeRegistry attributeTypeRegistry = registries.getAttributeTypeRegistry();
// The superior
if ( !buildSuperior( attributeType, errorHandler, registries ) )
{
// We have had errors, let's stop here as we need a correct superior to continue
return;
}
// The Syntax
buildSyntax( attributeType, errorHandler, registries );
// The EQUALITY matching rule
buildEquality( attributeType, errorHandler, registries );
// The ORDERING matching rule
buildOrdering( attributeType, errorHandler, registries );
// The SUBSTR matching rule
buildSubstring( attributeType, errorHandler, registries );
// Check the USAGE
checkUsage( attributeType, errorHandler );
// Check the COLLECTIVE element
checkCollective( attributeType, errorHandler );
// Inject the attributeType into the oid/normalizer map
attributeTypeRegistry.addMappingFor( attributeType );
// Register this AttributeType into the Descendant map
attributeTypeRegistry.registerDescendants( attributeType, attributeType.getSuperior() );
/**
* Add the AT references (using and usedBy) :
* AT -> MR (for EQUALITY, ORDERING and SUBSTR)
* AT -> S
* AT -> AT
*/
if ( attributeType.getEquality() != null )
{
registries.addReference( attributeType, attributeType.getEquality() );
}
if ( attributeType.getOrdering() != null )
{
registries.addReference( attributeType, attributeType.getOrdering() );
}
if ( attributeType.getSubstring() != null )
{
registries.addReference( attributeType, attributeType.getSubstring() );
}
if ( attributeType.getSyntax() != null )
{
registries.addReference( attributeType, attributeType.getSyntax() );
}
if ( attributeType.getSuperior() != null )
{
registries.addReference( attributeType, attributeType.getSuperior() );
}
}
finally
{
attributeType.lock();
}
}
}
/**
* Build the Superior AttributeType reference for an AttributeType
*
* @param attributeType The AttributeType to process
* @param errorHandler The error handler
* @param registries The Registries instance
* @return <tt>true</tt> if the AttributeType superiors hierarchy is correct, or if we don't have any superior
*/
private static boolean buildSuperior( AttributeType attributeType, SchemaErrorHandler errorHandler,
Registries registries )
{
AttributeType currentSuperior;
AttributeTypeRegistry attributeTypeRegistry = registries.getAttributeTypeRegistry();
String superiorOid = attributeType.getSuperiorOid();
if ( superiorOid != null )
{
// This AT has a superior
try
{
currentSuperior = ( AttributeType ) attributeTypeRegistry.lookup( superiorOid );
}
catch ( Exception e )
{
// Not allowed.
String msg = I18n.err( I18n.ERR_13752_CANNOT_FIND_SUPERIOR, superiorOid, attributeType.getName() );
LdapSchemaException ldapSchemaException = new LdapSchemaException(
LdapSchemaExceptionCodes.AT_NONEXISTENT_SUPERIOR, msg, e );
ldapSchemaException.setSourceObject( attributeType );
ldapSchemaException.setRelatedId( superiorOid );
errorHandler.handle( LOG, msg, ldapSchemaException );
// Get out now
return false;
}
if ( currentSuperior != null )
{
// a special case : if the superior is collective, this is an error
if ( currentSuperior.isCollective() )
{
String msg = I18n.err( I18n.ERR_13776_CANNOT_SUBTYPE_COLLECTIVE,
currentSuperior, attributeType.getName() );
LdapSchemaException ldapSchemaException = new LdapSchemaException(
LdapSchemaExceptionCodes.AT_CANNOT_SUBTYPE_COLLECTIVE_AT, msg );
ldapSchemaException.setSourceObject( attributeType );
errorHandler.handle( LOG, msg, ldapSchemaException );
return false;
}
attributeType.setSuperior( currentSuperior );
// Recursively update the superior if not already done. We don't recurse
// if the superior's superior is not null, as it means it has already been
// handled.
if ( currentSuperior.getSuperior() == null )
{
registries.buildReference( currentSuperior );
}
// Update the descendant MAP
try
{
attributeTypeRegistry.registerDescendants( attributeType, currentSuperior );
}
catch ( LdapException ne )
{
errorHandler.handle( LOG, ne.getMessage(), ne );
return false;
}
// Check for cycles now
Set<String> superiors = new HashSet<>();
superiors.add( attributeType.getOid() );
AttributeType tmp = currentSuperior;
boolean isOk = true;
while ( tmp != null )
{
if ( superiors.contains( tmp.getOid() ) )
{
// There is a cycle : bad bad bad !
// Not allowed.
String msg = I18n.err( I18n.ERR_13753_CYCLE_DETECTED, attributeType.getName() );
LdapSchemaException ldapSchemaException = new LdapSchemaException(
LdapSchemaExceptionCodes.AT_CYCLE_TYPE_HIERARCHY, msg );
ldapSchemaException.setSourceObject( attributeType );
errorHandler.handle( LOG, msg, ldapSchemaException );
isOk = false;
break;
}
else
{
superiors.add( tmp.getOid() );
tmp = tmp.getSuperior();
}
}
superiors.clear();
return isOk;
}
else
{
// Not allowed.
String msg = I18n.err( I18n.ERR_13752_CANNOT_FIND_SUPERIOR, superiorOid, attributeType.getName() );
LdapSchemaException ldapSchemaException = new LdapSchemaException(
LdapSchemaExceptionCodes.AT_NONEXISTENT_SUPERIOR, msg );
ldapSchemaException.setSourceObject( attributeType );
ldapSchemaException.setRelatedId( superiorOid );
errorHandler.handle( LOG, msg, ldapSchemaException );
// Get out now
return false;
}
}
else
{
// No superior, just return
return true;
}
}
/**
* Build the SYNTAX reference for an AttributeType
*
* @param attributeType The AttributeType to process
* @param errorHandler The error handler
* @param registries The Registries instance
*/
private static void buildSyntax( AttributeType attributeType, SchemaErrorHandler errorHandler,
Registries registries )
{
String syntaxOid = attributeType.getSyntaxOid();
if ( syntaxOid != null )
{
LdapSyntax currentSyntax = null;
try
{
currentSyntax = registries.getLdapSyntaxRegistry().lookup( syntaxOid );
}
catch ( LdapException ne )
{
// Not allowed.
String msg = I18n.err( I18n.ERR_13754_CANNOT_FIND_SYNTAX, syntaxOid, attributeType.getName() );
LdapSchemaException ldapSchemaException = new LdapSchemaException(
LdapSchemaExceptionCodes.AT_NONEXISTENT_SYNTAX, msg, ne );
ldapSchemaException.setSourceObject( attributeType );
ldapSchemaException.setRelatedId( syntaxOid );
errorHandler.handle( LOG, msg, ldapSchemaException );
return;
}
if ( currentSyntax != null )
{
// Update the Syntax reference
attributeType.setSyntax( currentSyntax );
}
else
{
// Not allowed.
String msg = I18n.err( I18n.ERR_13754_CANNOT_FIND_SYNTAX, syntaxOid, attributeType.getName() );
LdapSchemaException ldapSchemaException = new LdapSchemaException(
LdapSchemaExceptionCodes.AT_NONEXISTENT_SYNTAX, msg );
ldapSchemaException.setSourceObject( attributeType );
ldapSchemaException.setRelatedId( syntaxOid );
errorHandler.handle( LOG, msg, ldapSchemaException );
}
}
else
{
// We inherit from the superior's syntax, if any
if ( attributeType.getSuperior() != null )
{
if ( attributeType.getSuperior().getSyntax() != null )
{
attributeType.setSyntax( attributeType.getSuperior().getSyntax() );
}
else
{
String msg = I18n.err( I18n.ERR_13754_CANNOT_FIND_SYNTAX, syntaxOid, attributeType.getName() );
LdapSchemaException ldapSchemaException = new LdapSchemaException(
LdapSchemaExceptionCodes.AT_NONEXISTENT_SYNTAX, msg );
ldapSchemaException.setSourceObject( attributeType );
ldapSchemaException.setRelatedId( syntaxOid );
errorHandler.handle( LOG, msg, ldapSchemaException );
}
}
else
{
// Not allowed.
String msg = I18n.err( I18n.ERR_13755_AT_MUST_HAVE_A_SYNTAX_OID, attributeType.getName() );
LdapSchemaException ldapSchemaException = new LdapSchemaException(
LdapSchemaExceptionCodes.AT_SYNTAX_OR_SUPERIOR_REQUIRED, msg );
ldapSchemaException.setSourceObject( attributeType );
errorHandler.handle( LOG, msg, ldapSchemaException );
}
}
}
/**
* Build the EQUALITY MR reference for an AttributeType
*
* @param attributeType The AttributeType to process
* @param errorHandler The error handler
* @param registries The Registries instance
*/
private static void buildEquality( AttributeType attributeType, SchemaErrorHandler errorHandler,
Registries registries )
{
String equalityOid = attributeType.getEqualityOid();
// The equality MR. It can be null
if ( equalityOid != null )
{
MatchingRule currentEquality = null;
try
{
currentEquality = registries.getMatchingRuleRegistry().lookup( equalityOid );
}
catch ( LdapException ne )
{
// Not allowed.
String msg = I18n.err( I18n.ERR_13756_CANNOT_FIND_EQUALITY_MR_OBJECT, equalityOid, attributeType.getName() );
LdapSchemaException ldapSchemaException = new LdapSchemaException(
LdapSchemaExceptionCodes.AT_NONEXISTENT_EQUALITY_MATCHING_RULE, msg, ne );
ldapSchemaException.setSourceObject( attributeType );
ldapSchemaException.setRelatedId( equalityOid );
errorHandler.handle( LOG, msg, ldapSchemaException );
return;
}
if ( currentEquality != null )
{
attributeType.setEquality( currentEquality );
// Restore the old equality OID to preserve the user's provided value
attributeType.setEqualityOid( equalityOid );
}
else
{
// Not allowed.
String msg = I18n.err( I18n.ERR_13757_CANNOT_FIND_EQUALITY_MR_INSTANCE, equalityOid, attributeType.getName() );
LdapSchemaException ldapSchemaException = new LdapSchemaException(
LdapSchemaExceptionCodes.AT_NONEXISTENT_EQUALITY_MATCHING_RULE, msg );
ldapSchemaException.setSourceObject( attributeType );
ldapSchemaException.setRelatedId( equalityOid );
errorHandler.handle( LOG, msg, ldapSchemaException );
}
}
else
{
AttributeType superior = attributeType.getSuperior();
// If the AT has a superior, take its Equality MR if any
if ( ( superior != null ) && ( superior.getEquality() != null ) )
{
attributeType.setEquality( superior.getEquality() );
}
}
}
/**
* Build the SUBSTR MR reference for an AttributeType
*
* @param attributeType The AttributeType to process
* @param errorHandler The error handler
* @param registries The Registries instance
*/
private static void buildSubstring( AttributeType attributeType, SchemaErrorHandler errorHandler,
Registries registries )
{
String substringOid = attributeType.getSubstringOid();
// The Substring MR. It can be null
if ( substringOid != null )
{
MatchingRule currentSubstring = null;
try
{
currentSubstring = registries.getMatchingRuleRegistry().lookup( substringOid );
}
catch ( LdapException ne )
{
// Not allowed.
String msg = I18n.err( I18n.ERR_13760_CANNOT_FIND_SUBSTR_MR_OBJECT, substringOid, attributeType.getName() );
LdapSchemaException ldapSchemaException = new LdapSchemaException(
LdapSchemaExceptionCodes.AT_NONEXISTENT_SUBSTRING_MATCHING_RULE, msg, ne );
ldapSchemaException.setSourceObject( attributeType );
ldapSchemaException.setRelatedId( substringOid );
errorHandler.handle( LOG, msg, ldapSchemaException );
return;
}
if ( currentSubstring != null )
{
attributeType.setSubstring( currentSubstring );
}
else
{
// Not allowed.
String msg = I18n.err( I18n.ERR_13761_CANNOT_FIND_SUBSTR_MR_INSTANCE, substringOid, attributeType.getName() );
LdapSchemaException ldapSchemaException = new LdapSchemaException(
LdapSchemaExceptionCodes.AT_NONEXISTENT_SUBSTRING_MATCHING_RULE, msg );
ldapSchemaException.setSourceObject( attributeType );
ldapSchemaException.setRelatedId( substringOid );
errorHandler.handle( LOG, msg, ldapSchemaException );
}
}
else
{
AttributeType superior = attributeType.getSuperior();
// If the AT has a superior, take its Substring MR if any
if ( ( superior != null ) && ( superior.getSubstring() != null ) )
{
attributeType.setSubstring( superior.getSubstring() );
}
}
}
/**
* Build the ORDERING MR reference for an AttributeType
*
* @param attributeType The AttributeType to process
* @param errorHandler The error handler
* @param registries The Registries instance
*/
private static void buildOrdering( AttributeType attributeType, SchemaErrorHandler errorHandler,
Registries registries )
{
String orderingOid = attributeType.getOrderingOid();
if ( orderingOid != null )
{
MatchingRule currentOrdering = null;
try
{
currentOrdering = registries.getMatchingRuleRegistry().lookup( orderingOid );
}
catch ( LdapException ne )
{
// Not allowed.
String msg = I18n.err( I18n.ERR_13758_CANNOT_FIND_ORDERING_MR_OBJECT, orderingOid, attributeType.getName() );
LdapSchemaException ldapSchemaException = new LdapSchemaException(
LdapSchemaExceptionCodes.AT_NONEXISTENT_ORDERING_MATCHING_RULE, msg, ne );
ldapSchemaException.setSourceObject( attributeType );
ldapSchemaException.setRelatedId( orderingOid );
errorHandler.handle( LOG, msg, ldapSchemaException );
return;
}
if ( currentOrdering != null )
{
attributeType.setOrdering( currentOrdering );
}
else
{
// Not allowed.
String msg = I18n.err( I18n.ERR_13759_CANNOT_FIND_ORDERING_MR_INSTANCE, orderingOid, attributeType.getName() );
LdapSchemaException ldapSchemaException = new LdapSchemaException(
LdapSchemaExceptionCodes.AT_NONEXISTENT_ORDERING_MATCHING_RULE, msg );
ldapSchemaException.setSourceObject( attributeType );
ldapSchemaException.setRelatedId( orderingOid );
errorHandler.handle( LOG, msg, ldapSchemaException );
}
}
else
{
AttributeType superior = attributeType.getSuperior();
// If the AT has a superior, take its Ordering MR if any
if ( ( superior != null ) && ( superior.getOrdering() != null ) )
{
attributeType.setOrdering( superior.getOrdering() );
}
}
}
/**
* Check the constraints for the Usage field.
*
* @param attributeType The AttributeType to check
* @param errorHandler The error handler
*/
private static void checkUsage( AttributeType attributeType, SchemaErrorHandler errorHandler )
{
AttributeType superior = attributeType.getSuperior();
// Check that the AT usage is the same that its superior
if ( ( superior != null ) && ( attributeType.getUsage() != superior.getUsage() ) )
{
// This is an error
String msg = I18n.err( I18n.ERR_13762_AT_MUST_HAVE_SUPERIOR_USAGE, attributeType.getName() );
LdapSchemaException ldapSchemaException = new LdapSchemaException(
LdapSchemaExceptionCodes.AT_MUST_HAVE_SAME_USAGE_THAN_SUPERIOR, msg );
ldapSchemaException.setSourceObject( attributeType );
errorHandler.handle( LOG, msg, ldapSchemaException );
return;
}
// Now, check that the AttributeType's USAGE does not conflict
if ( !attributeType.isUserModifiable() && ( attributeType.getUsage() == UsageEnum.USER_APPLICATIONS ) )
{
// Cannot have a not user modifiable AT which is not an operational AT
String msg = I18n.err( I18n.ERR_13763_AT_MUST_BE_USER_MODIFIABLE, attributeType.getName() );
LdapSchemaException ldapSchemaException = new LdapSchemaException(
LdapSchemaExceptionCodes.AT_USER_APPLICATIONS_USAGE_MUST_BE_USER_MODIFIABLE, msg );
ldapSchemaException.setSourceObject( attributeType );
errorHandler.handle( LOG, msg, ldapSchemaException );
}
}
/**
* Check the constraints for the Collective field.
*
* @param attributeType The AttributeType to check
* @param errorHandler The error handler
*/
private static void checkCollective( AttributeType attributeType, SchemaErrorHandler errorHandler )
{
AttributeType superior = attributeType.getSuperior();
if ( ( superior != null ) && superior.isCollective() )
{
// An AttributeType will be collective if its superior is collective
attributeType.setCollective( true );
}
if ( attributeType.isCollective() && ( attributeType.getUsage() != UsageEnum.USER_APPLICATIONS ) )
{
// An AttributeType which is collective must be a USER attributeType
String msg = I18n.err( I18n.ERR_13764_AT_COLLECTIVE_SHOULD_BE_USER_APP, attributeType.getName() );
LdapSchemaException ldapSchemaException = new LdapSchemaException(
LdapSchemaExceptionCodes.AT_COLLECTIVE_MUST_HAVE_USER_APPLICATIONS_USAGE, msg );
ldapSchemaException.setSourceObject( attributeType );
errorHandler.handle( LOG, msg, ldapSchemaException );
}
if ( attributeType.isCollective() && attributeType.isSingleValued() )
{
// A collective attribute must be multi-valued
String msg = I18n.err( I18n.ERR_13777_COLLECTIVE_NOT_MULTI_VALUED, attributeType.getName() );
LdapSchemaException ldapSchemaException = new LdapSchemaException(
LdapSchemaExceptionCodes.AT_COLLECTIVE_CANNOT_BE_SINGLE_VALUED, msg );
ldapSchemaException.setSourceObject( attributeType );
errorHandler.handle( LOG, msg, ldapSchemaException );
}
}
/**
* Remove the AttributeType from the registries, updating the references to
* other SchemaObject.
*
* If one of the referenced SchemaObject does not exist (SUP, EQUALITY, ORDERING, SUBSTR, SYNTAX),
* an exception is thrown.
*
* @param attributeType The AttributeType to remove from the Registries
* @param errorHandler Error handler
* @param registries The Registries
* @throws LdapException If the AttributeType is not valid
*/
public static void removeFromRegistries( AttributeType attributeType, SchemaErrorHandler errorHandler, Registries registries ) throws LdapException
{
if ( registries != null )
{
AttributeTypeRegistry attributeTypeRegistry = registries.getAttributeTypeRegistry();
// Remove the attributeType from the oid/normalizer map
attributeTypeRegistry.removeMappingFor( attributeType );
// Unregister this AttributeType into the Descendant map
attributeTypeRegistry.unregisterDescendants( attributeType, attributeType.getSuperior() );
/**
* Remove the AT references (using and usedBy) :
* AT -> MR (for EQUALITY, ORDERING and SUBSTR)
* AT -> S
* AT -> AT
*/
if ( attributeType.getEquality() != null )
{
registries.delReference( attributeType, attributeType.getEquality() );
}
if ( attributeType.getOrdering() != null )
{
registries.delReference( attributeType, attributeType.getOrdering() );
}
if ( attributeType.getSubstring() != null )
{
registries.delReference( attributeType, attributeType.getSubstring() );
}
if ( attributeType.getSyntax() != null )
{
registries.delReference( attributeType, attributeType.getSyntax() );
}
if ( attributeType.getSuperior() != null )
{
registries.delReference( attributeType, attributeType.getSuperior() );
}
}
}
}