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