001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.directory.api.ldap.model.schema.registries.helper;
021
022import java.util.List;
023
024import org.apache.directory.api.i18n.I18n;
025import org.apache.directory.api.ldap.model.exception.LdapException;
026import org.apache.directory.api.ldap.model.exception.LdapSchemaException;
027import org.apache.directory.api.ldap.model.exception.LdapSchemaExceptionCodes;
028import org.apache.directory.api.ldap.model.schema.AttributeType;
029import org.apache.directory.api.ldap.model.schema.ObjectClass;
030import org.apache.directory.api.ldap.model.schema.ObjectClassTypeEnum;
031import org.apache.directory.api.ldap.model.schema.SchemaErrorHandler;
032import org.apache.directory.api.ldap.model.schema.registries.AttributeTypeRegistry;
033import org.apache.directory.api.ldap.model.schema.registries.ObjectClassRegistry;
034import org.apache.directory.api.ldap.model.schema.registries.Registries;
035import org.slf4j.Logger;
036import org.slf4j.LoggerFactory;
037
038/**
039 * An helper class used to store all the methods associated with an ObjectClass
040 * in relation with the Registries and SchemaManager.
041 * 
042 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
043 */
044public final class ObjectClassHelper
045{
046    /** A logger for this class */
047    private static final Logger LOG = LoggerFactory.getLogger( ObjectClassHelper.class );
048
049    private ObjectClassHelper()
050    {
051    }
052
053
054    /**
055     * Inject the ObjectClass into the registries, updating the references to
056     * other SchemaObject
057     *
058     * @param objectClass The ObjectClass to add to the Registries
059     * @param errorHandler Error handler
060     * @param registries The Registries
061     */
062    public static void addToRegistries( ObjectClass objectClass, SchemaErrorHandler errorHandler, Registries registries )
063    {
064        if ( registries != null )
065        {
066            try
067            {
068                objectClass.unlock();
069                
070                // The superiors
071                buildSuperiors( objectClass, errorHandler, registries );
072    
073                // The MAY AttributeTypes
074                buildMay( objectClass, errorHandler, registries );
075    
076                // The MUST AttributeTypes
077                buildMust( objectClass, errorHandler, registries );
078    
079                /**
080                 * Add the OC references (using and usedBy) :
081                 * OC -> AT (MAY and MUST)
082                 * OC -> OC (SUPERIORS)
083                 */
084                for ( AttributeType mayAttributeType : objectClass.getMayAttributeTypes() )
085                {
086                    registries.addReference( objectClass, mayAttributeType );
087                }
088    
089                for ( AttributeType mustAttributeType : objectClass.getMustAttributeTypes() )
090                {
091                    registries.addReference( objectClass, mustAttributeType );
092                }
093    
094                for ( ObjectClass superiorObjectClass : objectClass.getSuperiors() )
095                {
096                    registries.addReference( objectClass, superiorObjectClass );
097                }
098            }
099            finally
100            {
101                objectClass.lock();
102            }
103        }
104    }
105
106
107    /**
108     * Build the references to this ObjectClass SUPERIORS, checking that the type
109     * hierarchy is correct.
110     * 
111     * @param objectClass The oOjectClass to process
112     * @param errorHandler The error handler
113     * @param registries The Registries instance
114     */
115    private static void buildSuperiors( ObjectClass objectClass, SchemaErrorHandler errorHandler, Registries registries )
116    {
117        ObjectClassRegistry ocRegistry = registries.getObjectClassRegistry();
118        List<String> superiorOids = objectClass.getSuperiorOids();
119
120        if ( superiorOids != null )
121        {
122            objectClass.getSuperiors().clear();
123
124            for ( String superiorName : superiorOids )
125            {
126                try
127                {
128                    ObjectClass superior = ocRegistry.lookup( ocRegistry.getOidByName( superiorName ) );
129
130                    // Before adding the superior, check that the ObjectClass type is consistent
131                    switch ( objectClass.getType() )
132                    {
133                        case ABSTRACT:
134                            if ( superior.getType() != ObjectClassTypeEnum.ABSTRACT )
135                            {
136                                // An ABSTRACT OC can only inherit from ABSTRACT OCs
137                                String msg = I18n.err( I18n.ERR_13766_ABSTRACT_OC_CANNOT_INHERIT_FROM_OC, 
138                                    objectClass.getOid(), superior.getObjectType(), superior );
139
140                                LdapSchemaException ldapSchemaException = new LdapSchemaException(
141                                    LdapSchemaExceptionCodes.OC_ABSTRACT_MUST_INHERIT_FROM_ABSTRACT_OC, msg );
142                                ldapSchemaException.setSourceObject( objectClass );
143                                errorHandler.handle( LOG, msg, ldapSchemaException );
144
145                                continue;
146                            }
147
148                            break;
149
150                        case AUXILIARY:
151                            if ( superior.getType() == ObjectClassTypeEnum.STRUCTURAL )
152                            {
153                                // An AUXILIARY OC cannot inherit from STRUCTURAL OCs
154                                String msg = I18n.err( I18n.ERR_13767_AUX_OC_CANNOT_INHERIT_FROM_STRUCT_OC, objectClass.getOid(), superior );
155
156                                LdapSchemaException ldapSchemaException = new LdapSchemaException(
157                                    LdapSchemaExceptionCodes.OC_AUXILIARY_CANNOT_INHERIT_FROM_STRUCTURAL_OC, msg );
158                                ldapSchemaException.setSourceObject( objectClass );
159                                errorHandler.handle( LOG, msg, ldapSchemaException );
160
161                                continue;
162                            }
163
164                            break;
165
166                        case STRUCTURAL:
167                            if ( superior.getType() == ObjectClassTypeEnum.AUXILIARY )
168                            {
169                                // A STRUCTURAL OC cannot inherit from AUXILIARY OCs
170                                String msg = I18n.err( I18n.ERR_13768_STRUCT_OC_CANNOT_INHERIT_FROM_AUX_OC, objectClass.getOid(), superior );
171
172                                LdapSchemaException ldapSchemaException = new LdapSchemaException(
173                                    LdapSchemaExceptionCodes.OC_STRUCTURAL_CANNOT_INHERIT_FROM_AUXILIARY_OC, msg );
174                                ldapSchemaException.setSourceObject( objectClass );
175                                errorHandler.handle( LOG, msg, ldapSchemaException );
176
177                                continue;
178                            }
179
180                            break;
181
182                        default:
183                            throw new IllegalArgumentException( I18n.err( I18n.ERR_13717_UNEXPECTED_OBJECT_CLASS_TYPE_ENUM, 
184                                objectClass.getType() ) );
185                    }
186
187                    objectClass.getSuperiors().add( superior );
188                }
189                catch ( LdapException ne )
190                {
191                    // Cannot find the OC
192                    String msg = I18n.err( I18n.ERR_13769_CANNOT_REGISTER_SUPERIOR_MISSING, 
193                        objectClass.getOid(), superiorName );
194
195                    LdapSchemaException ldapSchemaException = new LdapSchemaException(
196                        LdapSchemaExceptionCodes.OC_NONEXISTENT_SUPERIOR, msg, ne );
197                    ldapSchemaException.setSourceObject( objectClass );
198                    ldapSchemaException.setRelatedId( superiorName );
199                    errorHandler.handle( LOG, msg, ldapSchemaException );
200
201                    return;
202                }
203            }
204        }
205    }
206
207
208    /**
209     * Build and check the MUST AT for this ObjectClass.
210     * 
211     * @param objectClass The oOjectClass to process
212     * @param errorHandler The error handler
213     * @param registries The Registries instance
214     */
215    private static void buildMust( ObjectClass objectClass, SchemaErrorHandler errorHandler, Registries registries )
216    {
217        AttributeTypeRegistry atRegistry = registries.getAttributeTypeRegistry();
218        List<String> mustAttributeTypeOids = objectClass.getMustAttributeTypeOids();
219
220        if ( mustAttributeTypeOids != null )
221        {
222            objectClass.getMustAttributeTypes().clear();
223
224            for ( String mustAttributeTypeName : mustAttributeTypeOids )
225            {
226                try
227                {
228                    AttributeType attributeType = atRegistry.lookup( mustAttributeTypeName );
229
230                    if ( attributeType.isCollective() )
231                    {
232                        // Collective Attributes are not allowed in MAY or MUST
233                        String msg = I18n.err( I18n.ERR_13778_COLLECTIVE_NOT_ALLOWED_IN_MUST, mustAttributeTypeName,
234                            objectClass.getOid() );
235
236                        LdapSchemaException ldapSchemaException = new LdapSchemaException(
237                            LdapSchemaExceptionCodes.OC_COLLECTIVE_NOT_ALLOWED_IN_MUST, msg );
238                        ldapSchemaException.setSourceObject( objectClass );
239                        ldapSchemaException.setRelatedId( mustAttributeTypeName );
240                        errorHandler.handle( LOG, msg, ldapSchemaException );
241
242                        continue;
243                    }
244
245                    if ( objectClass.getMustAttributeTypes().contains( attributeType ) )
246                    {
247                        // Already registered : this is an error
248                        String msg = I18n.err( I18n.ERR_13772_CANNOT_REGISTER_DUPLICATE_AT_IN_MUST, 
249                            objectClass.getOid(), mustAttributeTypeName );
250
251                        LdapSchemaException ldapSchemaException = new LdapSchemaException(
252                            LdapSchemaExceptionCodes.OC_DUPLICATE_AT_IN_MUST, msg );
253                        ldapSchemaException.setSourceObject( objectClass );
254                        ldapSchemaException.setRelatedId( mustAttributeTypeName );
255                        errorHandler.handle( LOG, msg, ldapSchemaException );
256
257                        continue;
258                    }
259
260                    // Check that the MUST AT is not also present in the MAY AT
261                    if ( objectClass.getMayAttributeTypes().contains( attributeType ) )
262                    {
263                        // Already registered : this is an error
264                        String msg = I18n.err( I18n.ERR_13773_CANNOT_REGISTER_DUPLICATE_AT_IN_MAY_AND_MUST, 
265                            objectClass.getOid(), mustAttributeTypeName );
266
267                        LdapSchemaException ldapSchemaException = new LdapSchemaException(
268                            LdapSchemaExceptionCodes.OC_DUPLICATE_AT_IN_MAY_AND_MUST,
269                            msg );
270                        ldapSchemaException.setSourceObject( objectClass );
271                        ldapSchemaException.setRelatedId( mustAttributeTypeName );
272                        errorHandler.handle( LOG, msg, ldapSchemaException );
273
274                        continue;
275                    }
276
277                    objectClass.getMustAttributeTypes().add( attributeType );
278                }
279                catch ( LdapException ne )
280                {
281                    // Cannot find the AT
282                    String msg = I18n.err( I18n.ERR_13774_CANNOT_REGISTER_AT_IN_MUST_DOES_NOT_EXIST, 
283                        objectClass.getOid(), mustAttributeTypeName );
284
285                    LdapSchemaException ldapSchemaException = new LdapSchemaException(
286                        LdapSchemaExceptionCodes.OC_NONEXISTENT_MUST_AT, msg, ne );
287                    ldapSchemaException.setSourceObject( objectClass );
288                    ldapSchemaException.setRelatedId( mustAttributeTypeName );
289                    errorHandler.handle( LOG, msg, ldapSchemaException );
290                }
291            }
292        }
293    }
294    
295    
296    /**
297     * Build and check the MAY AT for this ObjectClass
298     * 
299     * @param objectClass The oOjectClass to process
300     * @param errorHandler The error handler
301     * @param registries The Registries instance
302     */
303    private static void buildMay( ObjectClass objectClass, SchemaErrorHandler errorHandler, Registries registries )
304    {
305        AttributeTypeRegistry atRegistry = registries.getAttributeTypeRegistry();
306        List<String> mayAttributeTypeOids = objectClass.getMayAttributeTypeOids();
307
308        if ( mayAttributeTypeOids != null )
309        {
310            objectClass.getMayAttributeTypes().clear();
311
312            for ( String mayAttributeTypeName : mayAttributeTypeOids )
313            {
314                try
315                {
316                    AttributeType attributeType = atRegistry.lookup( mayAttributeTypeName );
317
318                    if ( attributeType.isCollective() )
319                    {
320                        // Collective Attributes are not allowed in MAY or MUST
321                        String msg = I18n.err( I18n.ERR_13779_COLLECTIVE_NOT_ALLOWED_IN_MAY, mayAttributeTypeName, objectClass.getOid() );
322
323                        LdapSchemaException ldapSchemaException = new LdapSchemaException(
324                            LdapSchemaExceptionCodes.OC_COLLECTIVE_NOT_ALLOWED_IN_MAY, msg );
325                        ldapSchemaException.setSourceObject( objectClass );
326                        ldapSchemaException.setRelatedId( mayAttributeTypeName );
327                        errorHandler.handle( LOG, msg, ldapSchemaException );
328
329                        continue;
330                    }
331
332                    if ( objectClass.getMayAttributeTypes().contains( attributeType ) )
333                    {
334                        // Already registered : this is an error
335                        String msg = I18n.err( 
336                            I18n.ERR_13770_CANNOT_REGISTER_DUPLICATE_AT_IN_MAY, objectClass.getOid(), mayAttributeTypeName );
337
338                        LdapSchemaException ldapSchemaException = new LdapSchemaException(
339                            LdapSchemaExceptionCodes.OC_DUPLICATE_AT_IN_MAY, msg );
340                        ldapSchemaException.setSourceObject( objectClass );
341                        ldapSchemaException.setRelatedId( mayAttributeTypeName );
342                        errorHandler.handle( LOG, msg, ldapSchemaException );
343
344                        continue;
345                    }
346
347                    objectClass.getMayAttributeTypes().add( attributeType );
348                }
349                catch ( LdapException ne )
350                {
351                    // Cannot find the AT
352                    String msg = I18n.err( I18n.ERR_13771_CANNOT_REGISTER_AT_IN_MAY_DOES_NOT_EXIST, objectClass.getOid(), mayAttributeTypeName );
353
354                    LdapSchemaException ldapSchemaException = new LdapSchemaException(
355                        LdapSchemaExceptionCodes.OC_NONEXISTENT_MAY_AT, msg, ne );
356                    ldapSchemaException.setSourceObject( objectClass );
357                    ldapSchemaException.setRelatedId( mayAttributeTypeName );
358                    errorHandler.handle( LOG, msg, ldapSchemaException );
359                }
360            }
361        }
362    }
363    
364    
365    /**
366     * Remove the ObjectClass from the registries, updating the references to
367     * other SchemaObject.
368     *
369     * If one of the referenced SchemaObject does not exist (SUPERIORS, MAY, MUST),
370     * an exception is thrown.
371     *
372     * @param objectClass The ObjectClass to remove fro the registries
373     * @param errorHandler Error handler
374     * @param registries The Registries
375     * @throws LdapException If the ObjectClass is not valid
376     */
377    public static void removeFromRegistries( ObjectClass objectClass, SchemaErrorHandler errorHandler, Registries registries ) throws LdapException
378    {
379        if ( registries != null )
380        {
381            ObjectClassRegistry objectClassRegistry = registries.getObjectClassRegistry();
382
383            // Unregister this ObjectClass into the Descendant map
384            objectClassRegistry.unregisterDescendants( objectClass, objectClass.getSuperiors() );
385
386            /**
387             * Remove the OC references (using and usedBy) :
388             * OC -> AT (for MAY and MUST)
389             * OC -> OC
390             */
391            if ( objectClass.getMayAttributeTypes() != null )
392            {
393                for ( AttributeType may : objectClass.getMayAttributeTypes() )
394                {
395                    registries.delReference( objectClass, may );
396                }
397            }
398
399            if ( objectClass.getMustAttributeTypes() != null )
400            {
401                for ( AttributeType must : objectClass.getMustAttributeTypes() )
402                {
403                    registries.delReference( objectClass, must );
404                }
405            }
406
407            if ( objectClass.getSuperiors() != null )
408            {
409                for ( ObjectClass superior : objectClass.getSuperiors() )
410                {
411                    registries.delReference( objectClass, superior );
412                }
413            }
414        }
415    }
416}