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.shared.ldap.model.schema;
021
022
023import java.util.ArrayList;
024import java.util.List;
025
026import org.apache.directory.shared.i18n.I18n;
027import org.apache.directory.shared.ldap.model.exception.LdapException;
028import org.apache.directory.shared.ldap.model.exception.LdapSchemaException;
029import org.apache.directory.shared.ldap.model.exception.LdapSchemaExceptionCodes;
030import org.apache.directory.shared.ldap.model.schema.registries.AttributeTypeRegistry;
031import org.apache.directory.shared.ldap.model.schema.registries.ObjectClassRegistry;
032import org.apache.directory.shared.ldap.model.schema.registries.Registries;
033
034
035/**
036 * An objectClass definition.
037 * <p>
038 * According to ldapbis [MODELS]:
039 * </p>
040 *
041 * <pre>
042 *  Object Class definitions are written according to the ABNF:
043 *
044 *    ObjectClassDescription = LPAREN WSP
045 *        numericoid                ; object identifier
046 *        [ SP &quot;NAME&quot; SP qdescrs ]  ; short names (descriptors)
047 *        [ SP &quot;DESC&quot; SP qdstring ] ; description
048 *        [ SP &quot;OBSOLETE&quot; ]         ; not active
049 *        [ SP &quot;SUP&quot; SP oids ]      ; superior object classes
050 *        [ SP kind ]               ; kind of class
051 *        [ SP &quot;MUST&quot; SP oids ]     ; attribute types
052 *        [ SP &quot;MAY&quot; SP oids ]      ; attribute types
053 *        extensions WSP RPAREN
054 *
055 *     kind = &quot;ABSTRACT&quot; / &quot;STRUCTURAL&quot; / &quot;AUXILIARY&quot;
056 *
057 *   where:
058 *     [numericoid] is object identifier assigned to this object class;
059 *     NAME [qdescrs] are short names (descriptors) identifying this object
060 *         class;
061 *     DESC [qdstring] is a short descriptive string;
062 *     OBSOLETE indicates this object class is not active;
063 *     SUP [oids] specifies the direct superclasses of this object class;
064 *     the kind of object class is indicated by one of ABSTRACT,
065 *         STRUCTURAL, or AUXILIARY, default is STRUCTURAL;
066 *     MUST and MAY specify the sets of required and allowed attribute
067 *         types, respectively; and
068 *    [extensions] describe extensions.
069 * </pre>
070 *
071 * @see <a href="http://www.faqs.org/rfcs/rfc2252.html">RFC2252 Section 4.4</a>
072 * @see <a
073 *      href="http://www.ietf.org/internet-drafts/draft-ietf-ldapbis-models-11.txt">ldapbis
074 *      [MODELS]</a>
075 * @see DescriptionUtils#getDescription(ObjectClass)
076 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
077 */
078// super.hashCode is final
079@SuppressWarnings("PMD.OverrideBothEqualsAndHashcode")
080public class ObjectClass extends AbstractSchemaObject
081{
082    /** The ObjectClass type : ABSTRACT, AUXILIARY or STRUCTURAL */
083    private ObjectClassTypeEnum objectClassType = ObjectClassTypeEnum.STRUCTURAL;
084
085    /** The ObjectClass superior OIDs */
086    private List<String> superiorOids;
087
088    /** The ObjectClass superiors */
089    private List<ObjectClass> superiors;
090
091    /** The list of allowed AttributeType OIDs */
092    private List<String> mayAttributeTypeOids;
093
094    /** The list of allowed AttributeTypes */
095    private List<AttributeType> mayAttributeTypes;
096
097    /** The list of required AttributeType OIDs */
098    private List<String> mustAttributeTypeOids;
099
100    /** The list of required AttributeTypes */
101    private List<AttributeType> mustAttributeTypes;
102
103
104    /**
105     * Creates a new instance of MatchingRuleUseDescription
106     * @param oid the OID for this objectClass
107     */
108    public ObjectClass( String oid )
109    {
110        super( SchemaObjectType.OBJECT_CLASS, oid );
111
112        mayAttributeTypeOids = new ArrayList<String>();
113        mustAttributeTypeOids = new ArrayList<String>();
114        superiorOids = new ArrayList<String>();
115
116        mayAttributeTypes = new ArrayList<AttributeType>();
117        mustAttributeTypes = new ArrayList<AttributeType>();
118        superiors = new ArrayList<ObjectClass>();
119        objectClassType = ObjectClassTypeEnum.STRUCTURAL;
120    }
121
122
123    private void buildSuperiors( List<Throwable> errors, Registries registries )
124    {
125        ObjectClassRegistry ocRegistry = registries.getObjectClassRegistry();
126
127        if ( superiorOids != null )
128        {
129            superiors = new ArrayList<ObjectClass>( superiorOids.size() );
130
131            for ( String superiorName : superiorOids )
132            {
133                try
134                {
135                    ObjectClass superior = ocRegistry.lookup( ocRegistry.getOidByName( superiorName ) );
136
137                    // Before adding the superior, check that the ObjectClass type is consistent
138                    switch ( objectClassType )
139                    {
140                        case ABSTRACT:
141                            if ( superior.objectClassType != ObjectClassTypeEnum.ABSTRACT )
142                            {
143                                // An ABSTRACT OC can only inherit from ABSTRACT OCs
144                                String msg = I18n.err( I18n.ERR_04318, oid, superior.getObjectType(), superior );
145
146                                LdapSchemaException ldapSchemaException = new LdapSchemaException(
147                                    LdapSchemaExceptionCodes.OC_ABSTRACT_MUST_INHERIT_FROM_ABSTRACT_OC, msg );
148                                ldapSchemaException.setSourceObject( this );
149                                errors.add( ldapSchemaException );
150                                return;
151                            }
152
153                            break;
154
155                        case AUXILIARY:
156                            if ( superior.objectClassType == ObjectClassTypeEnum.STRUCTURAL )
157                            {
158                                // An AUXILIARY OC cannot inherit from STRUCTURAL OCs
159                                String msg = I18n.err( I18n.ERR_04319, oid, superior );
160
161                                LdapSchemaException ldapSchemaException = new LdapSchemaException(
162                                    LdapSchemaExceptionCodes.OC_AUXILIARY_CANNOT_INHERIT_FROM_STRUCTURAL_OC, msg );
163                                ldapSchemaException.setSourceObject( this );
164                                errors.add( ldapSchemaException );
165                                return;
166                            }
167
168                            break;
169
170                        case STRUCTURAL:
171                            if ( superior.objectClassType == ObjectClassTypeEnum.AUXILIARY )
172                            {
173                                // A STRUCTURAL OC cannot inherit from AUXILIARY OCs
174                                String msg = I18n.err( I18n.ERR_04320, oid, superior );
175
176                                LdapSchemaException ldapSchemaException = new LdapSchemaException(
177                                    LdapSchemaExceptionCodes.OC_STRUCTURAL_CANNOT_INHERIT_FROM_AUXILIARY_OC, msg );
178                                ldapSchemaException.setSourceObject( this );
179                                errors.add( ldapSchemaException );
180                                return;
181                            }
182
183                            break;
184                    }
185
186                    superiors.add( superior );
187                }
188                catch ( LdapException ne )
189                {
190                    // Cannot find the OC
191                    String msg = I18n.err( I18n.ERR_04321, oid, superiorName );
192
193                    LdapSchemaException ldapSchemaException = new LdapSchemaException(
194                        LdapSchemaExceptionCodes.OC_NONEXISTENT_SUPERIOR, msg, ne );
195                    ldapSchemaException.setSourceObject( this );
196                    ldapSchemaException.setRelatedId( superiorName );
197                    errors.add( ldapSchemaException );
198                    return;
199                }
200            }
201        }
202    }
203
204
205    private void buildMay( List<Throwable> errors, Registries registries )
206    {
207        AttributeTypeRegistry atRegistry = registries.getAttributeTypeRegistry();
208
209        if ( mayAttributeTypeOids != null )
210        {
211            mayAttributeTypes = new ArrayList<AttributeType>( mayAttributeTypeOids.size() );
212
213            for ( String mayAttributeTypeName : mayAttributeTypeOids )
214            {
215                try
216                {
217                    AttributeType attributeType = atRegistry.lookup( mayAttributeTypeName );
218
219                    if ( attributeType.isCollective() )
220                    {
221                        // Collective Attributes are not allowed in MAY or MUST
222                        String msg = I18n.err( I18n.ERR_04485_COLLECTIVE_NOT_ALLOWED_IN_MAY, mayAttributeTypeName, oid );
223
224                        LdapSchemaException ldapSchemaException = new LdapSchemaException(
225                            LdapSchemaExceptionCodes.OC_COLLECTIVE_NOT_ALLOWED_IN_MAY, msg );
226                        ldapSchemaException.setSourceObject( this );
227                        ldapSchemaException.setRelatedId( mayAttributeTypeName );
228                        errors.add( ldapSchemaException );
229                        break;
230                    }
231
232                    if ( mayAttributeTypes.contains( attributeType ) )
233                    {
234                        // Already registered : this is an error
235                        String msg = I18n.err( I18n.ERR_04322, oid, mayAttributeTypeName );
236
237                        LdapSchemaException ldapSchemaException = new LdapSchemaException(
238                            LdapSchemaExceptionCodes.OC_DUPLICATE_AT_IN_MAY, msg );
239                        ldapSchemaException.setSourceObject( this );
240                        ldapSchemaException.setRelatedId( mayAttributeTypeName );
241                        errors.add( ldapSchemaException );
242                        break;
243                    }
244
245                    mayAttributeTypes.add( attributeType );
246                }
247                catch ( LdapException ne )
248                {
249                    // Cannot find the AT
250                    String msg = I18n.err( I18n.ERR_04323, oid, mayAttributeTypeName );
251
252                    LdapSchemaException ldapSchemaException = new LdapSchemaException(
253                        LdapSchemaExceptionCodes.OC_NONEXISTENT_MAY_AT, msg, ne );
254                    ldapSchemaException.setSourceObject( this );
255                    ldapSchemaException.setRelatedId( mayAttributeTypeName );
256                    errors.add( ldapSchemaException );
257                    break;
258                }
259            }
260        }
261    }
262
263
264    private void buildMust( List<Throwable> errors, Registries registries )
265    {
266        AttributeTypeRegistry atRegistry = registries.getAttributeTypeRegistry();
267
268        if ( mustAttributeTypeOids != null )
269        {
270            mustAttributeTypes = new ArrayList<AttributeType>( mustAttributeTypeOids.size() );
271
272            for ( String mustAttributeTypeName : mustAttributeTypeOids )
273            {
274                try
275                {
276                    AttributeType attributeType = atRegistry.lookup( mustAttributeTypeName );
277
278                    if ( attributeType.isCollective() )
279                    {
280                        // Collective Attributes are not allowed in MAY or MUST
281                        String msg = I18n.err( I18n.ERR_04484_COLLECTIVE_NOT_ALLOWED_IN_MUST, mustAttributeTypeName,
282                            oid );
283
284                        LdapSchemaException ldapSchemaException = new LdapSchemaException(
285                            LdapSchemaExceptionCodes.OC_COLLECTIVE_NOT_ALLOWED_IN_MUST, msg );
286                        ldapSchemaException.setSourceObject( this );
287                        ldapSchemaException.setRelatedId( mustAttributeTypeName );
288                        errors.add( ldapSchemaException );
289                        break;
290                    }
291
292                    if ( mustAttributeTypes.contains( attributeType ) )
293                    {
294                        // Already registered : this is an error
295                        String msg = I18n.err( I18n.ERR_04324, oid, mustAttributeTypeName );
296
297                        LdapSchemaException ldapSchemaException = new LdapSchemaException(
298                            LdapSchemaExceptionCodes.OC_DUPLICATE_AT_IN_MUST, msg );
299                        ldapSchemaException.setSourceObject( this );
300                        ldapSchemaException.setRelatedId( mustAttributeTypeName );
301                        errors.add( ldapSchemaException );
302                        break;
303                    }
304
305                    // Check that the MUST AT is not also present in the MAY AT
306                    if ( mayAttributeTypes.contains( attributeType ) )
307                    {
308                        // Already registered : this is an error
309                        String msg = I18n.err( I18n.ERR_04325, oid, mustAttributeTypeName );
310
311                        LdapSchemaException ldapSchemaException = new LdapSchemaException(
312                            LdapSchemaExceptionCodes.OC_DUPLICATE_AT_IN_MAY_AND_MUST,
313                            msg );
314                        ldapSchemaException.setSourceObject( this );
315                        ldapSchemaException.setRelatedId( mustAttributeTypeName );
316                        errors.add( ldapSchemaException );
317                        break;
318                    }
319
320                    mustAttributeTypes.add( attributeType );
321                }
322                catch ( LdapException ne )
323                {
324                    // Cannot find the AT
325                    String msg = I18n.err( I18n.ERR_04326, oid, mustAttributeTypeName );
326
327                    LdapSchemaException ldapSchemaException = new LdapSchemaException(
328                        LdapSchemaExceptionCodes.OC_NONEXISTENT_MUST_AT, msg, ne );
329                    ldapSchemaException.setSourceObject( this );
330                    ldapSchemaException.setRelatedId( mustAttributeTypeName );
331                    errors.add( ldapSchemaException );
332                    break;
333                }
334            }
335        }
336    }
337
338
339    /**
340     * Inject the ObjectClass into the registries, updating the references to
341     * other SchemaObject
342     *
343     * @param errors The errors we got while adding the ObjectClass to the registries
344     * @param registries The Registries
345     * @throws Exception on failure
346     *
347     */
348    public void addToRegistries( List<Throwable> errors, Registries registries ) throws LdapException
349    {
350        if ( registries != null )
351        {
352            // The superiors
353            buildSuperiors( errors, registries );
354
355            // The MAY AttributeTypes
356            buildMay( errors, registries );
357
358            // The MUST AttributeTypes
359            buildMust( errors, registries );
360
361            /**
362             * Add the OC references (using and usedBy) :
363             * OC -> AT (MAY and MUST)
364             * OC -> OC (SUPERIORS)
365             */
366            for ( AttributeType mayAttributeType : mayAttributeTypes )
367            {
368                registries.addReference( this, mayAttributeType );
369            }
370
371            for ( AttributeType mustAttributeType : mustAttributeTypes )
372            {
373                registries.addReference( this, mustAttributeType );
374            }
375
376            for ( ObjectClass superiorObjectClass : superiors )
377            {
378                registries.addReference( this, superiorObjectClass );
379            }
380        }
381    }
382
383
384    /**
385     * Remove the ObjectClass from the registries, updating the references to
386     * other SchemaObject.
387     *
388     * If one of the referenced SchemaObject does not exist (SUPERIORS, MAY, MUST),
389     * an exception is thrown.
390     *
391     * @param errors The errors we got while removing the ObjectClass from the registries
392     * @param registries The Registries
393     * @exception If the ObjectClass is not valid
394     */
395    public void removeFromRegistries( List<Throwable> errors, Registries registries ) throws LdapException
396    {
397        if ( registries != null )
398        {
399            ObjectClassRegistry objectClassRegistry = registries.getObjectClassRegistry();
400
401            // Unregister this ObjectClass into the Descendant map
402            objectClassRegistry.unregisterDescendants( this, superiors );
403
404            /**
405             * Remove the OC references (using and usedBy) :
406             * OC -> AT (for MAY and MUST)
407             * OC -> OC
408             */
409            if ( mayAttributeTypes != null )
410            {
411                for ( AttributeType may : mayAttributeTypes )
412                {
413                    registries.delReference( this, may );
414                }
415            }
416
417            if ( mustAttributeTypes != null )
418            {
419                for ( AttributeType must : mustAttributeTypes )
420                {
421                    registries.delReference( this, must );
422                }
423            }
424
425            if ( superiors != null )
426            {
427                for ( ObjectClass superior : superiors )
428                {
429                    registries.delReference( this, superior );
430                }
431            }
432        }
433    }
434
435
436    /**
437     * @return the mayAttributeTypeOids
438     */
439    public List<String> getMayAttributeTypeOids()
440    {
441        return mayAttributeTypeOids;
442    }
443
444
445    /**
446     * @return the mayAttributeTypes
447     */
448    public List<AttributeType> getMayAttributeTypes()
449    {
450        return mayAttributeTypes;
451    }
452
453
454    /**
455     * Add some allowed AttributeType
456     *
457     * @param oids The attributeType oids
458     */
459    public void addMayAttributeTypeOids( String... oids )
460    {
461        if ( locked )
462        {
463            throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
464        }
465
466        if ( !isReadOnly )
467        {
468            for ( String oid : oids )
469            {
470                mayAttributeTypeOids.add( oid );
471            }
472        }
473    }
474
475
476    /**
477     * Add some allowed AttributeTypes
478     *
479     * @param attributeTypes The attributeTypes
480     */
481    public void addMayAttributeTypes( AttributeType... attributeTypes )
482    {
483        if ( locked )
484        {
485            throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
486        }
487
488        if ( !isReadOnly )
489        {
490            for ( AttributeType attributeType : attributeTypes )
491            {
492                if ( !mayAttributeTypeOids.contains( attributeType.getOid() ) )
493                {
494                    mayAttributeTypes.add( attributeType );
495                    mayAttributeTypeOids.add( attributeType.getOid() );
496                }
497            }
498        }
499    }
500
501
502    /**
503     * @param mayAttributeTypeOids the mayAttributeTypeOids to set
504     */
505    public void setMayAttributeTypeOids( List<String> mayAttributeTypeOids )
506    {
507        if ( locked )
508        {
509            throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
510        }
511
512        if ( !isReadOnly )
513        {
514            this.mayAttributeTypeOids = mayAttributeTypeOids;
515        }
516    }
517
518
519    /**
520     * Sets the list of allowed AttributeTypes
521     *
522     * @param mayAttributeTypes the list of allowed AttributeTypes
523     */
524    public void setMayAttributeTypes( List<AttributeType> mayAttributeTypes )
525    {
526        if ( locked )
527        {
528            throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
529        }
530
531        if ( !isReadOnly )
532        {
533            this.mayAttributeTypes = mayAttributeTypes;
534
535            // update the OIDS now
536            mayAttributeTypeOids.clear();
537
538            for ( AttributeType may : mayAttributeTypes )
539            {
540                mayAttributeTypeOids.add( may.getOid() );
541            }
542        }
543    }
544
545
546    /**
547     * Update the associated MAY AttributeType, even if the SchemaObject is readOnly
548     *
549     * @param mayAttributeTypes the list of allowed AttributeTypes
550     */
551    public void updateMayAttributeTypes( List<AttributeType> mayAttributeTypes )
552    {
553        this.mayAttributeTypes.clear();
554        this.mayAttributeTypes.addAll( mayAttributeTypes );
555
556        // update the OIDS now
557        mayAttributeTypeOids.clear();
558
559        for ( AttributeType may : mayAttributeTypes )
560        {
561            mayAttributeTypeOids.add( may.getOid() );
562        }
563    }
564
565
566    /**
567     * @return the mustAttributeTypeOids
568     */
569    public List<String> getMustAttributeTypeOids()
570    {
571        return mustAttributeTypeOids;
572    }
573
574
575    /**
576     * @return the mustAttributeTypes
577     */
578    public List<AttributeType> getMustAttributeTypes()
579    {
580        return mustAttributeTypes;
581    }
582
583
584    /**
585     * Add some required AttributeType OIDs
586     *
587     * @param oids The attributeType OIDs
588     */
589    public void addMustAttributeTypeOids( String... oids )
590    {
591        if ( locked )
592        {
593            throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
594        }
595
596        if ( !isReadOnly )
597        {
598            for ( String oid : oids )
599            {
600                mustAttributeTypeOids.add( oid );
601            }
602        }
603    }
604
605
606    /**
607     * Add some required AttributeTypes
608     *
609     * @param attributeTypes The attributeTypse
610     */
611    public void addMustAttributeTypes( AttributeType... attributeTypes )
612    {
613        if ( locked )
614        {
615            throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
616        }
617
618        if ( !isReadOnly )
619        {
620            for ( AttributeType attributeType : attributeTypes )
621            {
622                if ( !mustAttributeTypeOids.contains( attributeType.getOid() ) )
623                {
624                    mustAttributeTypes.add( attributeType );
625                    mustAttributeTypeOids.add( attributeType.getOid() );
626                }
627            }
628        }
629    }
630
631
632    /**
633     * @param mustAttributeTypeOids the mustAttributeTypeOids to set
634     */
635    public void setMustAttributeTypeOids( List<String> mustAttributeTypeOids )
636    {
637        if ( locked )
638        {
639            throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
640        }
641
642        if ( !isReadOnly )
643        {
644            this.mustAttributeTypeOids = mustAttributeTypeOids;
645        }
646    }
647
648
649    /**
650     * Sets the list of required AttributeTypes
651     *
652     * @param mustAttributeTypes the list of required AttributeTypes
653     */
654    public void setMustAttributeTypes( List<AttributeType> mustAttributeTypes )
655    {
656        if ( locked )
657        {
658            throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
659        }
660
661        if ( !isReadOnly )
662        {
663            this.mustAttributeTypes = mustAttributeTypes;
664
665            // update the OIDS now
666            mustAttributeTypeOids.clear();
667
668            for ( AttributeType may : mustAttributeTypes )
669            {
670                mustAttributeTypeOids.add( may.getOid() );
671            }
672        }
673    }
674
675
676    /**
677     * Update the associated MUST AttributeType, even if the SchemaObject is readOnly
678     *
679     * @param mustAttributeTypes the list of allowed AttributeTypes
680     */
681    public void updateMustAttributeTypes( List<AttributeType> mustAttributeTypes )
682    {
683        this.mustAttributeTypes.clear();
684        this.mustAttributeTypes.addAll( mustAttributeTypes );
685
686        // update the OIDS now
687        mustAttributeTypeOids.clear();
688
689        for ( AttributeType must : mustAttributeTypes )
690        {
691            mustAttributeTypeOids.add( must.getOid() );
692        }
693    }
694
695
696    /**
697     * Gets the superclasses of this ObjectClass.
698     *
699     * @return the superclasses
700     */
701    public List<ObjectClass> getSuperiors()
702    {
703        return superiors;
704    }
705
706
707    /**
708     * Gets the superclasses OIDsof this ObjectClass.
709     *
710     * @return the superclasses OIDs
711     */
712    public List<String> getSuperiorOids()
713    {
714        return superiorOids;
715    }
716
717
718    /**
719     * Add some superior ObjectClass OIDs
720     *
721     * @param oids The superior ObjectClass OIDs
722     */
723    public void addSuperiorOids( String... oids )
724    {
725        if ( locked )
726        {
727            throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
728        }
729
730        if ( !isReadOnly )
731        {
732            for ( String oid : oids )
733            {
734                if ( !superiorOids.contains( oid ) )
735                {
736                    superiorOids.add( oid );
737                }
738            }
739        }
740    }
741
742
743    /**
744     * Add some superior ObjectClasses
745     *
746     * @param objectClasses The superior ObjectClasses
747     */
748    public void addSuperior( ObjectClass... objectClasses )
749    {
750        if ( locked )
751        {
752            throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
753        }
754
755        if ( !isReadOnly )
756        {
757            for ( ObjectClass objectClass : objectClasses )
758            {
759                if ( !superiorOids.contains( objectClass.getOid() ) )
760                {
761                    superiorOids.add( objectClass.getOid() );
762                    superiors.add( objectClass );
763                }
764            }
765        }
766    }
767
768
769    /**
770     * Sets the superior object classes
771     *
772     * @param superiors the object classes to set
773     */
774    public void setSuperiors( List<ObjectClass> superiors )
775    {
776        if ( locked )
777        {
778            throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
779        }
780
781        if ( !isReadOnly )
782        {
783            this.superiors = superiors;
784
785            // update the OIDS now
786            superiorOids.clear();
787
788            for ( ObjectClass oc : superiors )
789            {
790                superiorOids.add( oc.getOid() );
791            }
792        }
793    }
794
795
796    /**
797     * Update the associated SUPERIORS ObjectClasses, even if the SchemaObject is readOnly
798     *
799     * @param superiors the object classes to set
800     */
801    public void updateSuperiors( List<ObjectClass> superiors )
802    {
803        this.superiors.clear();
804        this.superiors.addAll( superiors );
805
806        // update the OIDS now
807        superiorOids.clear();
808
809        for ( ObjectClass oc : superiors )
810        {
811            superiorOids.add( oc.getOid() );
812        }
813    }
814
815
816    /**
817     * Sets the superior object class OIDs
818     *
819     * @param superiorOids the object class OIDs to set
820     */
821    public void setSuperiorOids( List<String> superiorOids )
822    {
823        if ( locked )
824        {
825            throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
826        }
827
828        if ( !isReadOnly )
829        {
830            this.superiorOids = superiorOids;
831        }
832    }
833
834
835    /**
836     * Gets the type of this ObjectClass as a type safe enum.
837     *
838     * @return the ObjectClass type as an enum
839     */
840    public ObjectClassTypeEnum getType()
841    {
842        return objectClassType;
843    }
844
845
846    /**
847     * Set the ObjectClass type, one of ABSTRACT, AUXILIARY or STRUCTURAL.
848     *
849     * @param objectClassType The ObjectClassType value
850     */
851    public void setType( ObjectClassTypeEnum objectClassType )
852    {
853        if ( locked )
854        {
855            throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
856        }
857
858        if ( !isReadOnly )
859        {
860            this.objectClassType = objectClassType;
861        }
862    }
863
864
865    /**
866     * Tells if the current ObjectClass is STRUCTURAL
867     *
868     * @return <code>true</code> if the ObjectClass is STRUCTURAL
869     */
870    public boolean isStructural()
871    {
872        return objectClassType == ObjectClassTypeEnum.STRUCTURAL;
873    }
874
875
876    /**
877     * Tells if the current ObjectClass is ABSTRACT
878     *
879     * @return <code>true</code> if the ObjectClass is ABSTRACT
880     */
881    public boolean isAbstract()
882    {
883        return objectClassType == ObjectClassTypeEnum.ABSTRACT;
884    }
885
886
887    /**
888     * Tells if the current ObjectClass is AUXILIARY
889     *
890     * @return <code>true</code> if the ObjectClass is AUXILIARY
891     */
892    public boolean isAuxiliary()
893    {
894        return objectClassType == ObjectClassTypeEnum.AUXILIARY;
895    }
896
897
898    /**
899     * @see Object#toString()
900     */
901    public String toString()
902    {
903        return objectType + " " + DescriptionUtils.getDescription( this );
904    }
905
906
907    /**
908     * Copy an ObjectClass
909     */
910    public ObjectClass copy()
911    {
912        ObjectClass copy = new ObjectClass( oid );
913
914        // Copy the SchemaObject common data
915        copy.copy( this );
916
917        // Copy the ObjectClass type
918        copy.objectClassType = objectClassType;
919
920        // Copy the Superiors ObjectClasses OIDs
921        copy.superiorOids = new ArrayList<String>();
922
923        for ( String oid : superiorOids )
924        {
925            copy.superiorOids.add( oid );
926        }
927
928        // Copy the Superiors ObjectClasses ( will be empty )
929        copy.superiors = new ArrayList<ObjectClass>();
930
931        // Copy the MAY AttributeTypes OIDs
932        copy.mayAttributeTypeOids = new ArrayList<String>();
933
934        for ( String oid : mayAttributeTypeOids )
935        {
936            copy.mayAttributeTypeOids.add( oid );
937        }
938
939        // Copy the MAY AttributeTypes ( will be empty )
940        copy.mayAttributeTypes = new ArrayList<AttributeType>();
941
942        // Copy the MUST AttributeTypes OIDs
943        copy.mustAttributeTypeOids = new ArrayList<String>();
944
945        for ( String oid : mustAttributeTypeOids )
946        {
947            copy.mustAttributeTypeOids.add( oid );
948        }
949
950        // Copy the MUST AttributeTypes ( will be empty )
951        copy.mustAttributeTypes = new ArrayList<AttributeType>();
952
953        return copy;
954    }
955
956
957    /**
958     * @see Object#equals(Object)
959     */
960    @Override
961    public boolean equals( Object o )
962    {
963        if ( !super.equals( o ) )
964        {
965            return false;
966        }
967
968        if ( !( o instanceof ObjectClass ) )
969        {
970            return false;
971        }
972
973        ObjectClass that = ( ObjectClass ) o;
974
975        // The ObjectClassType
976        if ( objectClassType != that.objectClassType )
977        {
978            return false;
979        }
980
981        // The Superiors OIDs
982        if ( superiorOids.size() != that.superiorOids.size() )
983        {
984            return false;
985        }
986
987        // One way
988        for ( String oid : superiorOids )
989        {
990            if ( !that.superiorOids.contains( oid ) )
991            {
992                return false;
993            }
994        }
995
996        // The other way
997        for ( String oid : that.superiorOids )
998        {
999            if ( !superiorOids.contains( oid ) )
1000            {
1001                return false;
1002            }
1003        }
1004
1005        // The Superiors
1006        if ( superiors.size() != that.superiors.size() )
1007        {
1008            return false;
1009        }
1010
1011        // One way
1012        for ( ObjectClass oid : superiors )
1013        {
1014            if ( !that.superiors.contains( oid ) )
1015            {
1016                return false;
1017            }
1018        }
1019
1020        // The other way
1021        for ( ObjectClass oid : that.superiors )
1022        {
1023            if ( !superiors.contains( oid ) )
1024            {
1025                return false;
1026            }
1027        }
1028
1029        // The MAY OIDs
1030        if ( mayAttributeTypeOids.size() != that.mayAttributeTypeOids.size() )
1031        {
1032            return false;
1033        }
1034
1035        // One way
1036        for ( String oid : mayAttributeTypeOids )
1037        {
1038            if ( !that.mayAttributeTypeOids.contains( oid ) )
1039            {
1040                return false;
1041            }
1042        }
1043
1044        // The other way
1045        for ( String oid : that.mayAttributeTypeOids )
1046        {
1047            if ( !mayAttributeTypeOids.contains( oid ) )
1048            {
1049                return false;
1050            }
1051        }
1052
1053        // The MAY
1054        if ( mayAttributeTypes.size() != that.mayAttributeTypes.size() )
1055        {
1056            return false;
1057        }
1058
1059        // One way
1060        for ( AttributeType oid : mayAttributeTypes )
1061        {
1062            if ( !that.mayAttributeTypes.contains( oid ) )
1063            {
1064                return false;
1065            }
1066        }
1067
1068        // The other way
1069        for ( AttributeType oid : that.mayAttributeTypes )
1070        {
1071            if ( !mayAttributeTypes.contains( oid ) )
1072            {
1073                return false;
1074            }
1075        }
1076
1077        // The MUST OIDs
1078        if ( mustAttributeTypeOids.size() != that.mustAttributeTypeOids.size() )
1079        {
1080            return false;
1081        }
1082
1083        // One way
1084        for ( String oid : mustAttributeTypeOids )
1085        {
1086            if ( !that.mustAttributeTypeOids.contains( oid ) )
1087            {
1088                return false;
1089            }
1090        }
1091
1092        // The other way
1093        for ( String oid : that.mustAttributeTypeOids )
1094        {
1095            if ( !mustAttributeTypeOids.contains( oid ) )
1096            {
1097                return false;
1098            }
1099        }
1100
1101        // The MUST
1102        if ( mustAttributeTypes.size() != that.mustAttributeTypes.size() )
1103        {
1104            return false;
1105        }
1106
1107        // One way
1108        for ( AttributeType oid : mustAttributeTypes )
1109        {
1110            if ( !that.mustAttributeTypes.contains( oid ) )
1111            {
1112                return false;
1113            }
1114        }
1115
1116        // The other way
1117        for ( AttributeType oid : that.mustAttributeTypes )
1118        {
1119            if ( !mustAttributeTypes.contains( oid ) )
1120            {
1121                return false;
1122            }
1123        }
1124
1125        return true;
1126    }
1127
1128
1129    /**
1130     * {@inheritDoc}
1131     */
1132    public void clear()
1133    {
1134        // Clear the common elements
1135        super.clear();
1136
1137        // Clear the references
1138        mayAttributeTypes.clear();
1139        mayAttributeTypeOids.clear();
1140        mustAttributeTypes.clear();
1141        mustAttributeTypeOids.clear();
1142        superiors.clear();
1143        superiorOids.clear();
1144    }
1145}