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;
021
022
023import java.util.ArrayList;
024import java.util.List;
025
026import org.apache.directory.api.i18n.I18n;
027
028
029/**
030 * An objectClass definition.
031 * <p>
032 * According to ldapbis [MODELS]:
033 * </p>
034 *
035 * <pre>
036 *  Object Class definitions are written according to the ABNF:
037 *
038 *    ObjectClassDescription = LPAREN WSP
039 *        numericoid                ; object identifier
040 *        [ SP &quot;NAME&quot; SP qdescrs ]  ; short names (descriptors)
041 *        [ SP &quot;DESC&quot; SP qdstring ] ; description
042 *        [ SP &quot;OBSOLETE&quot; ]         ; not active
043 *        [ SP &quot;SUP&quot; SP oids ]      ; superior object classes
044 *        [ SP kind ]               ; kind of class
045 *        [ SP &quot;MUST&quot; SP oids ]     ; attribute types
046 *        [ SP &quot;MAY&quot; SP oids ]      ; attribute types
047 *        extensions WSP RPAREN
048 *
049 *     kind = &quot;ABSTRACT&quot; / &quot;STRUCTURAL&quot; / &quot;AUXILIARY&quot;
050 *
051 *   where:
052 *     [numericoid] is object identifier assigned to this object class;
053 *     NAME [qdescrs] are short names (descriptors) identifying this object
054 *         class;
055 *     DESC [qdstring] is a short descriptive string;
056 *     OBSOLETE indicates this object class is not active;
057 *     SUP [oids] specifies the direct superclasses of this object class;
058 *     the kind of object class is indicated by one of ABSTRACT,
059 *         STRUCTURAL, or AUXILIARY, default is STRUCTURAL;
060 *     MUST and MAY specify the sets of required and allowed attribute
061 *         types, respectively; and
062 *    [extensions] describe extensions.
063 * </pre>
064 *
065 * @see <a href="http://www.faqs.org/rfcs/rfc2252.html">RFC2252 Section 4.4</a>
066 * @see <a
067 *      href="http://www.ietf.org/internet-drafts/draft-ietf-ldapbis-models-11.txt">ldapbis
068 *      [MODELS]</a>
069 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
070 */
071public class ObjectClass extends AbstractSchemaObject
072{
073    /** The mandatory serialVersionUID */
074    public static final long serialVersionUID = 1L;
075
076    /** The ObjectClass type : ABSTRACT, AUXILIARY or STRUCTURAL */
077    protected ObjectClassTypeEnum objectClassType = ObjectClassTypeEnum.STRUCTURAL;
078
079    /** The ObjectClass superior OIDs */
080    protected transient List<String> superiorOids;
081
082    /** The ObjectClass superiors */
083    protected transient List<ObjectClass> superiors;
084
085    /** The list of allowed AttributeType OIDs */
086    protected transient List<String> mayAttributeTypeOids;
087
088    /** The list of allowed AttributeTypes */
089    protected transient List<AttributeType> mayAttributeTypes;
090
091    /** The list of required AttributeType OIDs */
092    protected transient List<String> mustAttributeTypeOids;
093
094    /** The list of required AttributeTypes */
095    protected transient List<AttributeType> mustAttributeTypes;
096
097
098    /**
099     * Creates a new instance of MatchingRuleUseDescription
100     * @param oid the OID for this objectClass
101     */
102    public ObjectClass( String oid )
103    {
104        super( SchemaObjectType.OBJECT_CLASS, oid );
105
106        mayAttributeTypeOids = new ArrayList<>();
107        mustAttributeTypeOids = new ArrayList<>();
108        superiorOids = new ArrayList<>();
109
110        mayAttributeTypes = new ArrayList<>();
111        mustAttributeTypes = new ArrayList<>();
112        superiors = new ArrayList<>();
113        objectClassType = ObjectClassTypeEnum.STRUCTURAL;
114    }
115
116
117    /**
118     * @return the mayAttributeTypeOids
119     */
120    public List<String> getMayAttributeTypeOids()
121    {
122        return mayAttributeTypeOids;
123    }
124
125
126    /**
127     * @return the mayAttributeTypes
128     */
129    public List<AttributeType> getMayAttributeTypes()
130    {
131        return mayAttributeTypes;
132    }
133
134
135    /**
136     * Add some allowed AttributeType
137     *
138     * @param oids The attributeType oids
139     */
140    public void addMayAttributeTypeOids( String... oids )
141    {
142        if ( locked )
143        {
144            throw new UnsupportedOperationException( I18n.err( I18n.ERR_13700_CANNOT_MODIFY_LOCKED_SCHEMA_OBJECT, getName() ) );
145        }
146
147        for ( String oid : oids )
148        {
149            mayAttributeTypeOids.add( oid );
150        }
151    }
152
153
154    /**
155     * Add some allowed AttributeTypes
156     *
157     * @param attributeTypes The attributeTypes
158     */
159    public void addMayAttributeTypes( AttributeType... attributeTypes )
160    {
161        if ( locked )
162        {
163            throw new UnsupportedOperationException( I18n.err( I18n.ERR_13700_CANNOT_MODIFY_LOCKED_SCHEMA_OBJECT, getName() ) );
164        }
165
166        for ( AttributeType attributeType : attributeTypes )
167        {
168            if ( !mayAttributeTypeOids.contains( attributeType.getOid() ) )
169            {
170                mayAttributeTypes.add( attributeType );
171                mayAttributeTypeOids.add( attributeType.getOid() );
172            }
173        }
174    }
175
176
177    /**
178     * @param mayAttributeTypeOids the mayAttributeTypeOids to set
179     */
180    public void setMayAttributeTypeOids( List<String> mayAttributeTypeOids )
181    {
182        if ( locked )
183        {
184            throw new UnsupportedOperationException( I18n.err( I18n.ERR_13700_CANNOT_MODIFY_LOCKED_SCHEMA_OBJECT, getName() ) );
185        }
186
187        this.mayAttributeTypeOids = mayAttributeTypeOids;
188    }
189
190
191    /**
192     * Sets the list of allowed AttributeTypes
193     *
194     * @param mayAttributeTypes the list of allowed AttributeTypes
195     */
196    public void setMayAttributeTypes( List<AttributeType> mayAttributeTypes )
197    {
198        if ( locked )
199        {
200            throw new UnsupportedOperationException( I18n.err( I18n.ERR_13700_CANNOT_MODIFY_LOCKED_SCHEMA_OBJECT, getName() ) );
201        }
202
203        this.mayAttributeTypes = mayAttributeTypes;
204
205        // update the OIDS now
206        mayAttributeTypeOids.clear();
207
208        for ( AttributeType may : mayAttributeTypes )
209        {
210            mayAttributeTypeOids.add( may.getOid() );
211        }
212    }
213
214
215    /**
216     * @return the mustAttributeTypeOids
217     */
218    public List<String> getMustAttributeTypeOids()
219    {
220        return mustAttributeTypeOids;
221    }
222
223
224    /**
225     * @return the mustAttributeTypes
226     */
227    public List<AttributeType> getMustAttributeTypes()
228    {
229        return mustAttributeTypes;
230    }
231
232
233    /**
234     * Add some required AttributeType OIDs
235     *
236     * @param oids The attributeType OIDs
237     */
238    public void addMustAttributeTypeOids( String... oids )
239    {
240        if ( locked )
241        {
242            throw new UnsupportedOperationException( I18n.err( I18n.ERR_13700_CANNOT_MODIFY_LOCKED_SCHEMA_OBJECT, getName() ) );
243        }
244
245        for ( String oid : oids )
246        {
247            mustAttributeTypeOids.add( oid );
248        }
249    }
250
251
252    /**
253     * Add some required AttributeTypes
254     *
255     * @param attributeTypes The attributeTypse
256     */
257    public void addMustAttributeTypes( AttributeType... attributeTypes )
258    {
259        if ( locked )
260        {
261            throw new UnsupportedOperationException( I18n.err( I18n.ERR_13700_CANNOT_MODIFY_LOCKED_SCHEMA_OBJECT, getName() ) );
262        }
263
264        for ( AttributeType attributeType : attributeTypes )
265        {
266            if ( !mustAttributeTypeOids.contains( attributeType.getOid() ) )
267            {
268                mustAttributeTypes.add( attributeType );
269                mustAttributeTypeOids.add( attributeType.getOid() );
270            }
271        }
272    }
273
274
275    /**
276     * @param mustAttributeTypeOids the mustAttributeTypeOids to set
277     */
278    public void setMustAttributeTypeOids( List<String> mustAttributeTypeOids )
279    {
280        if ( locked )
281        {
282            throw new UnsupportedOperationException( I18n.err( I18n.ERR_13700_CANNOT_MODIFY_LOCKED_SCHEMA_OBJECT, getName() ) );
283        }
284
285        this.mustAttributeTypeOids = mustAttributeTypeOids;
286    }
287
288
289    /**
290     * Sets the list of required AttributeTypes
291     *
292     * @param mustAttributeTypes the list of required AttributeTypes
293     */
294    public void setMustAttributeTypes( List<AttributeType> mustAttributeTypes )
295    {
296        if ( locked )
297        {
298            throw new UnsupportedOperationException( I18n.err( I18n.ERR_13700_CANNOT_MODIFY_LOCKED_SCHEMA_OBJECT, getName() ) );
299        }
300
301        this.mustAttributeTypes = mustAttributeTypes;
302
303        // update the OIDS now
304        mustAttributeTypeOids.clear();
305
306        for ( AttributeType may : mustAttributeTypes )
307        {
308            mustAttributeTypeOids.add( may.getOid() );
309        }
310    }
311
312
313    /**
314     * Gets the superclasses of this ObjectClass.
315     *
316     * @return the superclasses
317     */
318    public List<ObjectClass> getSuperiors()
319    {
320        return superiors;
321    }
322
323
324    /**
325     * Gets the superclasses OIDsof this ObjectClass.
326     *
327     * @return the superclasses OIDs
328     */
329    public List<String> getSuperiorOids()
330    {
331        return superiorOids;
332    }
333
334
335    /**
336     * Add some superior ObjectClass OIDs
337     *
338     * @param oids The superior ObjectClass OIDs
339     */
340    public void addSuperiorOids( String... oids )
341    {
342        if ( locked )
343        {
344            throw new UnsupportedOperationException( I18n.err( I18n.ERR_13700_CANNOT_MODIFY_LOCKED_SCHEMA_OBJECT, getName() ) );
345        }
346
347        for ( String oid : oids )
348        {
349            if ( !superiorOids.contains( oid ) )
350            {
351                superiorOids.add( oid );
352            }
353        }
354    }
355
356
357    /**
358     * Add some superior ObjectClasses
359     *
360     * @param objectClasses The superior ObjectClasses
361     */
362    public void addSuperior( ObjectClass... objectClasses )
363    {
364        if ( locked )
365        {
366            throw new UnsupportedOperationException( I18n.err( I18n.ERR_13700_CANNOT_MODIFY_LOCKED_SCHEMA_OBJECT, getName() ) );
367        }
368
369        for ( ObjectClass objectClass : objectClasses )
370        {
371            if ( !superiorOids.contains( objectClass.getOid() ) )
372            {
373                superiorOids.add( objectClass.getOid() );
374                superiors.add( objectClass );
375            }
376        }
377    }
378
379
380    /**
381     * Sets the superior object classes
382     *
383     * @param superiors the object classes to set
384     */
385    public void setSuperiors( List<ObjectClass> superiors )
386    {
387        if ( locked )
388        {
389            throw new UnsupportedOperationException( I18n.err( I18n.ERR_13700_CANNOT_MODIFY_LOCKED_SCHEMA_OBJECT, getName() ) );
390        }
391
392        this.superiors = superiors;
393
394        // update the OIDS now
395        superiorOids.clear();
396
397        for ( ObjectClass oc : superiors )
398        {
399            superiorOids.add( oc.getOid() );
400        }
401    }
402
403
404    /**
405     * Sets the superior object class OIDs
406     *
407     * @param superiorOids the object class OIDs to set
408     */
409    public void setSuperiorOids( List<String> superiorOids )
410    {
411        if ( locked )
412        {
413            throw new UnsupportedOperationException( I18n.err( I18n.ERR_13700_CANNOT_MODIFY_LOCKED_SCHEMA_OBJECT, getName() ) );
414        }
415
416        this.superiorOids = superiorOids;
417    }
418
419
420    /**
421     * Gets the type of this ObjectClass as a type safe enum.
422     *
423     * @return the ObjectClass type as an enum
424     */
425    public ObjectClassTypeEnum getType()
426    {
427        return objectClassType;
428    }
429
430
431    /**
432     * Set the ObjectClass type, one of ABSTRACT, AUXILIARY or STRUCTURAL.
433     *
434     * @param objectClassType The ObjectClassType value
435     */
436    public void setType( ObjectClassTypeEnum objectClassType )
437    {
438        if ( locked )
439        {
440            throw new UnsupportedOperationException( I18n.err( I18n.ERR_13700_CANNOT_MODIFY_LOCKED_SCHEMA_OBJECT, getName() ) );
441        }
442
443        this.objectClassType = objectClassType;
444    }
445
446
447    /**
448     * Tells if the current ObjectClass is STRUCTURAL
449     *
450     * @return <code>true</code> if the ObjectClass is STRUCTURAL
451     */
452    public boolean isStructural()
453    {
454        return objectClassType == ObjectClassTypeEnum.STRUCTURAL;
455    }
456
457
458    /**
459     * Tells if the current ObjectClass is ABSTRACT
460     *
461     * @return <code>true</code> if the ObjectClass is ABSTRACT
462     */
463    public boolean isAbstract()
464    {
465        return objectClassType == ObjectClassTypeEnum.ABSTRACT;
466    }
467
468
469    /**
470     * Tells if the current ObjectClass is AUXILIARY
471     *
472     * @return <code>true</code> if the ObjectClass is AUXILIARY
473     */
474    public boolean isAuxiliary()
475    {
476        return objectClassType == ObjectClassTypeEnum.AUXILIARY;
477    }
478
479
480    /**
481     * {@inheritDoc}
482     */
483    @Override
484    public String toString()
485    {
486        return SchemaObjectRenderer.OPEN_LDAP_SCHEMA_RENDERER.render( this );
487    }
488
489
490    /**
491     * Copy an ObjectClass
492     */
493    @Override
494    public ObjectClass copy()
495    {
496        ObjectClass copy = new ObjectClass( oid );
497
498        // Copy the SchemaObject common data
499        copy.copy( this );
500
501        // Copy the ObjectClass type
502        copy.objectClassType = objectClassType;
503
504        // Copy the Superiors ObjectClasses OIDs
505        copy.superiorOids = new ArrayList<>();
506
507        for ( String oid : superiorOids )
508        {
509            copy.superiorOids.add( oid );
510        }
511
512        // Copy the Superiors ObjectClasses ( will be empty )
513        copy.superiors = new ArrayList<>();
514
515        // Copy the MAY AttributeTypes OIDs
516        copy.mayAttributeTypeOids = new ArrayList<>();
517
518        for ( String oid : mayAttributeTypeOids )
519        {
520            copy.mayAttributeTypeOids.add( oid );
521        }
522
523        // Copy the MAY AttributeTypes ( will be empty )
524        copy.mayAttributeTypes = new ArrayList<>();
525
526        // Copy the MUST AttributeTypes OIDs
527        copy.mustAttributeTypeOids = new ArrayList<>();
528
529        for ( String oid : mustAttributeTypeOids )
530        {
531            copy.mustAttributeTypeOids.add( oid );
532        }
533
534        // Copy the MUST AttributeTypes ( will be empty )
535        copy.mustAttributeTypes = new ArrayList<>();
536
537        return copy;
538    }
539
540    
541    /**
542     * @see Object#equals(Object)
543     */
544    @Override
545    public int hashCode()
546    {
547        int hash = h;
548        
549        hash = hash * 17 + objectClassType.getValue();
550        
551        // As the elements aren't sorted, we don't iterate on 
552        // them and add the computed hashcode to the global hashcode
553        int tempHash = 0;
554        
555        for ( String oid : superiorOids )
556        {
557            tempHash += oid.hashCode();
558        }
559        
560        hash = hash * 17 + tempHash;
561        
562        tempHash = 0;
563        
564        for ( String may : mayAttributeTypeOids )
565        {
566            tempHash += may.hashCode();
567        }
568     
569        hash = hash * 17 + tempHash;
570        
571        tempHash = 0;
572
573        for ( String must : mustAttributeTypeOids )
574        {
575            tempHash += must.hashCode();
576        }
577     
578        hash = hash * 17 + tempHash;
579
580        return hash;
581    }
582    
583
584    /**
585     * @see Object#equals(Object)
586     */
587    @Override
588    public boolean equals( Object o )
589    {
590        if ( !super.equals( o ) )
591        {
592            return false;
593        }
594
595        if ( !( o instanceof ObjectClass ) )
596        {
597            return false;
598        }
599
600        ObjectClass that = ( ObjectClass ) o;
601
602        // The ObjectClassType
603        if ( objectClassType != that.objectClassType )
604        {
605            return false;
606        }
607
608        // The Superiors OIDs
609        if ( superiorOids.size() != that.superiorOids.size() )
610        {
611            return false;
612        }
613
614        // One way
615        for ( String oid : superiorOids )
616        {
617            if ( !that.superiorOids.contains( oid ) )
618            {
619                return false;
620            }
621        }
622
623        // The other way
624        for ( String oid : that.superiorOids )
625        {
626            if ( !superiorOids.contains( oid ) )
627            {
628                return false;
629            }
630        }
631
632        // The Superiors
633        if ( superiors.size() != that.superiors.size() )
634        {
635            return false;
636        }
637
638        // One way
639        for ( ObjectClass superior : superiors )
640        {
641            if ( !that.superiors.contains( superior ) )
642            {
643                return false;
644            }
645        }
646
647        // The other way
648        for ( ObjectClass superior : that.superiors )
649        {
650            if ( !superiors.contains( superior ) )
651            {
652                return false;
653            }
654        }
655
656        // The MAY OIDs
657        if ( mayAttributeTypeOids.size() != that.mayAttributeTypeOids.size() )
658        {
659            return false;
660        }
661
662        // One way
663        for ( String oid : mayAttributeTypeOids )
664        {
665            if ( !that.mayAttributeTypeOids.contains( oid ) )
666            {
667                return false;
668            }
669        }
670
671        // The other way
672        for ( String oid : that.mayAttributeTypeOids )
673        {
674            if ( !mayAttributeTypeOids.contains( oid ) )
675            {
676                return false;
677            }
678        }
679
680        // The MAY
681        if ( mayAttributeTypes.size() != that.mayAttributeTypes.size() )
682        {
683            return false;
684        }
685
686        // One way
687        for ( AttributeType oid : mayAttributeTypes )
688        {
689            if ( !that.mayAttributeTypes.contains( oid ) )
690            {
691                return false;
692            }
693        }
694
695        // The other way
696        for ( AttributeType oid : that.mayAttributeTypes )
697        {
698            if ( !mayAttributeTypes.contains( oid ) )
699            {
700                return false;
701            }
702        }
703
704        // The MUST OIDs
705        if ( mustAttributeTypeOids.size() != that.mustAttributeTypeOids.size() )
706        {
707            return false;
708        }
709
710        // One way
711        for ( String oid : mustAttributeTypeOids )
712        {
713            if ( !that.mustAttributeTypeOids.contains( oid ) )
714            {
715                return false;
716            }
717        }
718
719        // The other way
720        for ( String oid : that.mustAttributeTypeOids )
721        {
722            if ( !mustAttributeTypeOids.contains( oid ) )
723            {
724                return false;
725            }
726        }
727
728        // The MUST
729        if ( mustAttributeTypes.size() != that.mustAttributeTypes.size() )
730        {
731            return false;
732        }
733
734        // One way
735        for ( AttributeType oid : mustAttributeTypes )
736        {
737            if ( !that.mustAttributeTypes.contains( oid ) )
738            {
739                return false;
740            }
741        }
742
743        // The other way
744        for ( AttributeType oid : that.mustAttributeTypes )
745        {
746            if ( !mustAttributeTypes.contains( oid ) )
747            {
748                return false;
749            }
750        }
751
752        return true;
753    }
754
755
756    /**
757     * {@inheritDoc}
758     */
759    @Override
760    public void clear()
761    {
762        // Clear the common elements
763        super.clear();
764
765        // Clear the references
766        mayAttributeTypes.clear();
767        mayAttributeTypeOids.clear();
768        mustAttributeTypes.clear();
769        mustAttributeTypeOids.clear();
770        superiors.clear();
771        superiorOids.clear();
772    }
773}