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.registries.AttributeTypeRegistry;
032import org.apache.directory.api.ldap.model.schema.registries.ObjectClassRegistry;
033import org.apache.directory.api.ldap.model.schema.registries.Registries;
034import org.slf4j.Logger;
035import org.slf4j.LoggerFactory;
036
037/**
038 * An helper class used to store all the methods associated with an ObjectClass
039 * in relation with the Registries and SchemaManager.
040 * 
041 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
042 */
043public class ObjectClassHelper
044{
045    /** A logger for this class */
046    private static final Logger LOG = LoggerFactory.getLogger( ObjectClassHelper.class );
047
048    /**
049     * Inject the ObjectClass into the registries, updating the references to
050     * other SchemaObject
051     *
052     * @param objectClass The ObjectClass to add to the Registries
053     * @param errors The errors we got while adding the ObjectClass to the Registries
054     * @param registries The Registries
055     * @throws Exception on failure
056     */
057    public static void addToRegistries( ObjectClass objectClass, List<Throwable> errors, Registries registries ) throws LdapException
058    {
059        if ( registries != null )
060        {
061            try
062            {
063                objectClass.unlock();
064                
065                // The superiors
066                buildSuperiors( objectClass, errors, registries );
067    
068                // The MAY AttributeTypes
069                buildMay( objectClass, errors, registries );
070    
071                // The MUST AttributeTypes
072                buildMust( objectClass, errors, registries );
073    
074                /**
075                 * Add the OC references (using and usedBy) :
076                 * OC -> AT (MAY and MUST)
077                 * OC -> OC (SUPERIORS)
078                 */
079                for ( AttributeType mayAttributeType : objectClass.getMayAttributeTypes() )
080                {
081                    registries.addReference( objectClass, mayAttributeType );
082                }
083    
084                for ( AttributeType mustAttributeType : objectClass.getMustAttributeTypes() )
085                {
086                    registries.addReference( objectClass, mustAttributeType );
087                }
088    
089                for ( ObjectClass superiorObjectClass : objectClass.getSuperiors() )
090                {
091                    registries.addReference( objectClass, superiorObjectClass );
092                }
093            }
094            finally
095            {
096                objectClass.lock();
097            }
098        }
099    }
100
101
102    /**
103     * Build the references to this ObjectClass SUPERIORS, checking that the type
104     * hierarchy is correct.
105     */
106    private static void buildSuperiors( ObjectClass objectClass, List<Throwable> errors, Registries registries )
107    {
108        ObjectClassRegistry ocRegistry = registries.getObjectClassRegistry();
109        List<String> superiorOids = objectClass.getSuperiorOids();
110
111        if ( superiorOids != null )
112        {
113            objectClass.getSuperiors().clear();
114
115            for ( String superiorName : superiorOids )
116            {
117                try
118                {
119                    ObjectClass superior = ocRegistry.lookup( ocRegistry.getOidByName( superiorName ) );
120
121                    // Before adding the superior, check that the ObjectClass type is consistent
122                    switch ( objectClass.getType() )
123                    {
124                        case ABSTRACT:
125                            if ( superior.getType() != ObjectClassTypeEnum.ABSTRACT )
126                            {
127                                // An ABSTRACT OC can only inherit from ABSTRACT OCs
128                                String msg = I18n.err( I18n.ERR_04318, objectClass.getOid(), superior.getObjectType(), superior );
129
130                                LdapSchemaException ldapSchemaException = new LdapSchemaException(
131                                    LdapSchemaExceptionCodes.OC_ABSTRACT_MUST_INHERIT_FROM_ABSTRACT_OC, msg );
132                                ldapSchemaException.setSourceObject( objectClass );
133                                errors.add( ldapSchemaException );
134                                LOG.info( msg );
135
136                                continue;
137                            }
138
139                            break;
140
141                        case AUXILIARY:
142                            if ( superior.getType() == ObjectClassTypeEnum.STRUCTURAL )
143                            {
144                                // An AUXILIARY OC cannot inherit from STRUCTURAL OCs
145                                String msg = I18n.err( I18n.ERR_04319, objectClass.getOid(), superior );
146
147                                LdapSchemaException ldapSchemaException = new LdapSchemaException(
148                                    LdapSchemaExceptionCodes.OC_AUXILIARY_CANNOT_INHERIT_FROM_STRUCTURAL_OC, msg );
149                                ldapSchemaException.setSourceObject( objectClass );
150                                errors.add( ldapSchemaException );
151                                LOG.info( msg );
152
153                                continue;
154                            }
155
156                            break;
157
158                        case STRUCTURAL:
159                            if ( superior.getType() == ObjectClassTypeEnum.AUXILIARY )
160                            {
161                                // A STRUCTURAL OC cannot inherit from AUXILIARY OCs
162                                String msg = I18n.err( I18n.ERR_04320, objectClass.getOid(), superior );
163
164                                LdapSchemaException ldapSchemaException = new LdapSchemaException(
165                                    LdapSchemaExceptionCodes.OC_STRUCTURAL_CANNOT_INHERIT_FROM_AUXILIARY_OC, msg );
166                                ldapSchemaException.setSourceObject( objectClass );
167                                errors.add( ldapSchemaException );
168                                LOG.info( msg );
169
170                                continue;
171                            }
172
173                            break;
174                    }
175
176                    objectClass.getSuperiors().add( superior );
177                }
178                catch ( LdapException ne )
179                {
180                    // Cannot find the OC
181                    String msg = I18n.err( I18n.ERR_04321, objectClass.getOid(), superiorName );
182
183                    LdapSchemaException ldapSchemaException = new LdapSchemaException(
184                        LdapSchemaExceptionCodes.OC_NONEXISTENT_SUPERIOR, msg, ne );
185                    ldapSchemaException.setSourceObject( objectClass );
186                    ldapSchemaException.setRelatedId( superiorName );
187                    errors.add( ldapSchemaException );
188                    LOG.info( msg );
189
190                    return;
191                }
192            }
193        }
194    }
195
196
197    /**
198     * Build and check the MUST AT for this ObjectClass.
199     */
200    private static void buildMust( ObjectClass objectClass, List<Throwable> errors, Registries registries )
201    {
202        AttributeTypeRegistry atRegistry = registries.getAttributeTypeRegistry();
203        List<String> mustAttributeTypeOids = objectClass.getMustAttributeTypeOids();
204
205        if ( mustAttributeTypeOids != null )
206        {
207            objectClass.getMustAttributeTypes().clear();
208
209            for ( String mustAttributeTypeName : mustAttributeTypeOids )
210            {
211                try
212                {
213                    AttributeType attributeType = atRegistry.lookup( mustAttributeTypeName );
214
215                    if ( attributeType.isCollective() )
216                    {
217                        // Collective Attributes are not allowed in MAY or MUST
218                        String msg = I18n.err( I18n.ERR_04484_COLLECTIVE_NOT_ALLOWED_IN_MUST, mustAttributeTypeName,
219                            objectClass.getOid() );
220
221                        LdapSchemaException ldapSchemaException = new LdapSchemaException(
222                            LdapSchemaExceptionCodes.OC_COLLECTIVE_NOT_ALLOWED_IN_MUST, msg );
223                        ldapSchemaException.setSourceObject( objectClass );
224                        ldapSchemaException.setRelatedId( mustAttributeTypeName );
225                        errors.add( ldapSchemaException );
226                        LOG.info( msg );
227
228                        break;
229                    }
230
231                    if ( objectClass.getMustAttributeTypes().contains( attributeType ) )
232                    {
233                        // Already registered : this is an error
234                        String msg = I18n.err( I18n.ERR_04324, objectClass.getOid(), mustAttributeTypeName );
235
236                        LdapSchemaException ldapSchemaException = new LdapSchemaException(
237                            LdapSchemaExceptionCodes.OC_DUPLICATE_AT_IN_MUST, msg );
238                        ldapSchemaException.setSourceObject( objectClass );
239                        ldapSchemaException.setRelatedId( mustAttributeTypeName );
240                        errors.add( ldapSchemaException );
241                        LOG.info( msg );
242
243                        break;
244                    }
245
246                    // Check that the MUST AT is not also present in the MAY AT
247                    if ( objectClass.getMayAttributeTypes().contains( attributeType ) )
248                    {
249                        // Already registered : this is an error
250                        String msg = I18n.err( I18n.ERR_04325, objectClass.getOid(), mustAttributeTypeName );
251
252                        LdapSchemaException ldapSchemaException = new LdapSchemaException(
253                            LdapSchemaExceptionCodes.OC_DUPLICATE_AT_IN_MAY_AND_MUST,
254                            msg );
255                        ldapSchemaException.setSourceObject( objectClass );
256                        ldapSchemaException.setRelatedId( mustAttributeTypeName );
257                        errors.add( ldapSchemaException );
258                        LOG.info( msg );
259
260                        break;
261                    }
262
263                    objectClass.getMustAttributeTypes().add( attributeType );
264                }
265                catch ( LdapException ne )
266                {
267                    // Cannot find the AT
268                    String msg = I18n.err( I18n.ERR_04326, objectClass.getOid(), mustAttributeTypeName );
269
270                    LdapSchemaException ldapSchemaException = new LdapSchemaException(
271                        LdapSchemaExceptionCodes.OC_NONEXISTENT_MUST_AT, msg, ne );
272                    ldapSchemaException.setSourceObject( objectClass );
273                    ldapSchemaException.setRelatedId( mustAttributeTypeName );
274                    errors.add( ldapSchemaException );
275                    LOG.info( msg );
276
277                    break;
278                }
279            }
280        }
281    }
282    
283    
284    /**
285     * Build and check the MAY AT for this ObjectClass
286     */
287    private static void buildMay( ObjectClass objectClass, List<Throwable> errors, Registries registries )
288    {
289        AttributeTypeRegistry atRegistry = registries.getAttributeTypeRegistry();
290        List<String> mayAttributeTypeOids = objectClass.getMayAttributeTypeOids();
291
292        if ( mayAttributeTypeOids != null )
293        {
294            objectClass.getMayAttributeTypes().clear();
295
296            for ( String mayAttributeTypeName : mayAttributeTypeOids )
297            {
298                try
299                {
300                    AttributeType attributeType = atRegistry.lookup( mayAttributeTypeName );
301
302                    if ( attributeType.isCollective() )
303                    {
304                        // Collective Attributes are not allowed in MAY or MUST
305                        String msg = I18n.err( I18n.ERR_04485_COLLECTIVE_NOT_ALLOWED_IN_MAY, mayAttributeTypeName, objectClass.getOid() );
306
307                        LdapSchemaException ldapSchemaException = new LdapSchemaException(
308                            LdapSchemaExceptionCodes.OC_COLLECTIVE_NOT_ALLOWED_IN_MAY, msg );
309                        ldapSchemaException.setSourceObject( objectClass );
310                        ldapSchemaException.setRelatedId( mayAttributeTypeName );
311                        errors.add( ldapSchemaException );
312                        LOG.info( msg );
313
314                        break;
315                    }
316
317                    if ( objectClass.getMayAttributeTypes().contains( attributeType ) )
318                    {
319                        // Already registered : this is an error
320                        String msg = I18n.err( I18n.ERR_04322, objectClass.getOid(), mayAttributeTypeName );
321
322                        LdapSchemaException ldapSchemaException = new LdapSchemaException(
323                            LdapSchemaExceptionCodes.OC_DUPLICATE_AT_IN_MAY, msg );
324                        ldapSchemaException.setSourceObject( objectClass );
325                        ldapSchemaException.setRelatedId( mayAttributeTypeName );
326                        errors.add( ldapSchemaException );
327                        LOG.info( msg );
328
329                        break;
330                    }
331
332                    objectClass.getMayAttributeTypes().add( attributeType );
333                }
334                catch ( LdapException ne )
335                {
336                    // Cannot find the AT
337                    String msg = I18n.err( I18n.ERR_04323, objectClass.getOid(), mayAttributeTypeName );
338
339                    LdapSchemaException ldapSchemaException = new LdapSchemaException(
340                        LdapSchemaExceptionCodes.OC_NONEXISTENT_MAY_AT, msg, ne );
341                    ldapSchemaException.setSourceObject( objectClass );
342                    ldapSchemaException.setRelatedId( mayAttributeTypeName );
343                    errors.add( ldapSchemaException );
344                    LOG.info( msg );
345
346                    break;
347                }
348            }
349        }
350    }
351    
352    
353    /**
354     * Remove the ObjectClass from the registries, updating the references to
355     * other SchemaObject.
356     *
357     * If one of the referenced SchemaObject does not exist (SUPERIORS, MAY, MUST),
358     * an exception is thrown.
359     *
360     * @param objectClass The ObjectClass to remove fro the registries
361     * @param errors The errors we got while removing the ObjectClass from the registries
362     * @param registries The Registries
363     * @exception If the ObjectClass is not valid
364     */
365    public static void removeFromRegistries( ObjectClass objectClass, List<Throwable> errors, Registries registries ) throws LdapException
366    {
367        if ( registries != null )
368        {
369            ObjectClassRegistry objectClassRegistry = registries.getObjectClassRegistry();
370
371            // Unregister this ObjectClass into the Descendant map
372            objectClassRegistry.unregisterDescendants( objectClass, objectClass.getSuperiors() );
373
374            /**
375             * Remove the OC references (using and usedBy) :
376             * OC -> AT (for MAY and MUST)
377             * OC -> OC
378             */
379            if ( objectClass.getMayAttributeTypes() != null )
380            {
381                for ( AttributeType may : objectClass.getMayAttributeTypes() )
382                {
383                    registries.delReference( objectClass, may );
384                }
385            }
386
387            if ( objectClass.getMustAttributeTypes() != null )
388            {
389                for ( AttributeType must : objectClass.getMustAttributeTypes() )
390                {
391                    registries.delReference( objectClass, must );
392                }
393            }
394
395            if ( objectClass.getSuperiors() != null )
396            {
397                for ( ObjectClass superior : objectClass.getSuperiors() )
398                {
399                    registries.delReference( objectClass, superior );
400                }
401            }
402        }
403    }
404}