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     */
020    package org.apache.directory.shared.ldap.schema;
021    
022    
023    import java.util.HashSet;
024    import java.util.List;
025    import java.util.Set;
026    
027    import org.apache.directory.shared.i18n.I18n;
028    import org.apache.directory.shared.ldap.exception.LdapException;
029    import org.apache.directory.shared.ldap.exception.LdapProtocolErrorException;
030    import org.apache.directory.shared.ldap.exception.LdapSchemaViolationException;
031    import org.apache.directory.shared.ldap.message.ResultCodeEnum;
032    import org.apache.directory.shared.ldap.schema.registries.AttributeTypeRegistry;
033    import org.apache.directory.shared.ldap.schema.registries.Registries;
034    import org.slf4j.Logger;
035    import org.slf4j.LoggerFactory;
036    
037    
038    /**
039     * An attributeType specification. attributeType specifications describe the
040     * nature of attributes within the directory. The attributeType specification's
041     * properties are accessible through this interface.
042     * <p>
043     * According to ldapbis [MODELS]:
044     * </p>
045     * 
046     * <pre>
047     *  4.1.2. Attribute Types
048     *  
049     *    Attribute Type definitions are written according to the ABNF:
050     *  
051     *      AttributeTypeDescription = LPAREN WSP
052     *          numericoid                   ; object identifier
053     *          [ SP &quot;NAME&quot; SP qdescrs ]     ; short names (descriptors)
054     *          [ SP &quot;DESC&quot; SP qdstring ]    ; description
055     *          [ SP &quot;OBSOLETE&quot; ]            ; not active
056     *          [ SP &quot;SUP&quot; SP oid ]          ; supertype
057     *          [ SP &quot;EQUALITY&quot; SP oid ]     ; equality matching rule
058     *          [ SP &quot;ORDERING&quot; SP oid ]     ; ordering matching rule
059     *          [ SP &quot;SUBSTR&quot; SP oid ]       ; substrings matching rule
060     *          [ SP &quot;SYNTAX&quot; SP noidlen ]   ; value syntax
061     *          [ SP &quot;SINGLE-VALUE&quot; ]        ; single-value
062     *          [ SP &quot;COLLECTIVE&quot; ]          ; collective
063     *          [ SP &quot;NO-USER-MODIFICATION&quot; ]; not user modifiable
064     *          [ SP &quot;USAGE&quot; SP usage ]      ; usage
065     *          extensions WSP RPAREN        ; extensions
066     *  
067     *      usage = &quot;userApplications&quot;     / ; user
068     *              &quot;directoryOperation&quot;   / ; directory operational
069     *              &quot;distributedOperation&quot; / ; DSA-shared operational
070     *              &quot;dSAOperation&quot;           ; DSA-specific operational
071     *  
072     *    where:
073     *      [numericoid] is object identifier assigned to this attribute type;
074     *      NAME [qdescrs] are short names (descriptors) identifying this
075     *          attribute type;
076     *      DESC [qdstring] is a short descriptive string;
077     *      OBSOLETE indicates this attribute type is not active;
078     *      SUP oid specifies the direct supertype of this type;
079     *      EQUALITY, ORDERING, SUBSTRING provide the oid of the equality,
080     *          ordering, and substrings matching rules, respectively;
081     *      SYNTAX identifies value syntax by object identifier and may suggest
082     *          a minimum upper bound;
083     *      COLLECTIVE indicates this attribute type is collective [X.501];
084     *      NO-USER-MODIFICATION indicates this attribute type is not user
085     *          modifiable;
086     *      USAGE indicates the application of this attribute type; and
087     *      [extensions] describe extensions.
088     *  
089     *    Each attribute type description must contain at least one of the SUP
090     *    or SYNTAX fields.
091     *  
092     *    Usage of userApplications, the default, indicates that attributes of
093     *    this type represent user information.  That is, they are user
094     *    attributes.
095     *  
096     *    COLLECTIVE requires usage userApplications.  Use of collective
097     *    attribute types in LDAP is not discussed in this technical
098     *    specification.
099     *  
100     *    A usage of directoryOperation, distributedOperation, or dSAOperation
101     *    indicates that attributes of this type represent operational and/or
102     *    administrative information.  That is, they are operational attributes.
103     *  
104     *    directoryOperation usage indicates that the attribute of this type is
105     *    a directory operational attribute.  distributedOperation usage
106     *    indicates that the attribute of this DSA-shared usage operational
107     *    attribute.  dSAOperation usage indicates that the attribute of this
108     *    type is a DSA-specific operational attribute.
109     *  
110     *    NO-USER-MODIFICATION requires an operational usage.
111     *  
112     *    Note that the [AttributeTypeDescription] does not list the matching
113     *    rules which can be used with that attribute type in an extensibleMatch
114     *    search filter.  This is done using the 'matchingRuleUse' attribute
115     *    described in Section 4.1.4.
116     *  
117     *    This document refines the schema description of X.501 by requiring
118     *    that the SYNTAX field in an [AttributeTypeDescription] be a string
119     *    representation of an object identifier for the LDAP string syntax
120     *    definition with an optional indication of the suggested minimum bound
121     *    of a value of this attribute.
122     *  
123     *    A suggested minimum upper bound on the number of characters in a value
124     *    with a string-based syntax, or the number of bytes in a value for all
125     *    other syntaxes, may be indicated by appending this bound count inside
126     *    of curly braces following the syntax's OBJECT IDENTIFIER in an
127     *  
128     *    Attribute Type Description.  This bound is not part of the syntax name
129     *    itself.  For instance, &quot;1.3.6.4.1.1466.0{64}&quot; suggests that server
130     *    implementations should allow a string to be 64 characters long,
131     *    although they may allow longer strings.  Note that a single character
132     *    of the Directory String syntax may be encoded in more than one octet
133     *    since UTF-8 is a variable-length encoding.
134     * </pre>
135     * 
136     * @see <a href="http://www.faqs.org/rfcs/rfc2252.html">RFC 2252 Section 4.2</a>
137     * @see <a
138     *      href="http://www.ietf.org/internet-drafts/draft-ietf-ldapbis-models-11.txt">
139     *      ldapbis [MODELS]</a>
140     * @see DescriptionUtils#getDescription(AttributeType)
141     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
142     * @version $Rev: 927122 $
143     */
144    public class AttributeType extends AbstractSchemaObject implements Cloneable
145    {
146        /** A logger for this class */
147        private static final Logger LOG = LoggerFactory.getLogger( AttributeType.class );
148    
149        /** The serialVersionUID */
150        public static final long serialVersionUID = 1L;
151    
152        /** The syntax OID associated with this AttributeType */
153        private String syntaxOid;
154    
155        /** The syntax associated with the syntaxID */
156        private LdapSyntax syntax;
157    
158        /** The equality OID associated with this AttributeType */
159        private String equalityOid;
160    
161        /** The equality MatchingRule associated with the equalityID */
162        private MatchingRule equality;
163    
164        /** The substring OID associated with this AttributeType */
165        private String substringOid;
166    
167        /** The substring MatchingRule associated with the substringID */
168        private MatchingRule substring;
169    
170        /** The ordering OID associated with this AttributeType */
171        private String orderingOid;
172    
173        /** The ordering MatchingRule associated with the orderingID */
174        private MatchingRule ordering;
175    
176        /** The superior AttributeType OID */
177        private String superiorOid;
178    
179        /** The superior AttributeType */
180        private AttributeType superior;
181    
182        /** whether or not this type is single valued */
183        private boolean isSingleValued = false;
184    
185        /** whether or not this type is a collective attribute */
186        private boolean isCollective = false;
187    
188        /** whether or not this type can be modified by directory users */
189        private boolean canUserModify = true;
190    
191        /** the usage for this attributeType */
192        private UsageEnum usage = UsageEnum.USER_APPLICATIONS;
193    
194        /** the length of this attribute in bytes */
195        private int syntaxLength = 0;
196    
197    
198        /**
199         * Creates a AttributeType object using a unique OID.
200         * 
201         * @param oid the OID for this AttributeType
202         */
203        public AttributeType( String oid )
204        {
205            super( SchemaObjectType.ATTRIBUTE_TYPE, oid );
206        }
207    
208    
209        /**
210         * Build the Superior AttributeType reference for an AttributeType
211         */
212        private boolean buildSuperior( List<Throwable> errors, Registries registries )
213        {
214            AttributeType superior = null;
215            AttributeTypeRegistry attributeTypeRegistry = registries.getAttributeTypeRegistry();
216    
217            if ( superiorOid != null )
218            {
219                // This AT has a superior
220                try
221                {
222                    superior = attributeTypeRegistry.lookup( superiorOid );
223                }
224                catch ( Exception e )
225                {
226                    // Not allowed.
227                    String msg = I18n.err( I18n.ERR_04303, superiorOid, getName() );
228    
229                    Throwable error = new LdapProtocolErrorException( msg );
230                    errors.add( error );
231                    LOG.info( msg );
232    
233                    // Get out now
234                    return false;
235                }
236    
237                if ( superior != null )
238                {
239                    this.superior = superior;
240    
241                    // Recursively update the superior if not already done. We don't recurse
242                    // if the superior's superior is not null, as it means it has already been
243                    // handled.
244                    if ( superior.getSuperior() == null )
245                    {
246                        registries.buildReference( errors, superior );
247                    }
248    
249                    // Update the descendant MAP
250                    try
251                    {
252                        attributeTypeRegistry.registerDescendants( this, superior );
253                    }
254                    catch ( LdapException ne )
255                    {
256                        errors.add( ne );
257                        LOG.info( ne.getMessage() );
258                        return false;
259                    }
260    
261                    // Check for cycles now
262                    Set<String> superiors = new HashSet<String>();
263                    superiors.add( oid );
264                    AttributeType tmp = superior;
265                    boolean isOk = true;
266    
267                    while ( tmp != null )
268                    {
269                        if ( superiors.contains( tmp.getOid() ) )
270                        {
271                            // There is a cycle : bad bad bad !
272                            // Not allowed.
273                            String msg = I18n.err( I18n.ERR_04304, getName() );
274    
275                            Throwable error = new LdapProtocolErrorException( msg );
276                            errors.add( error );
277                            LOG.info( msg );
278                            isOk = false;
279    
280                            break;
281                        }
282                        else
283                        {
284                            superiors.add( tmp.getOid() );
285                            tmp = tmp.getSuperior();
286                        }
287                    }
288    
289                    superiors.clear();
290    
291                    return isOk;
292                }
293                else
294                {
295                    // Not allowed.
296                    String msg = I18n.err( I18n.ERR_04305, superiorOid, getName() );
297    
298                    Throwable error = new LdapSchemaViolationException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, msg );
299                    errors.add( error );
300                    LOG.info( msg );
301    
302                    // Get out now
303                    return false;
304                }
305            }
306            else
307            {
308                // No superior, just return
309                return true;
310            }
311        }
312    
313    
314        /**
315         * Build the SYNTAX reference for an AttributeType
316         */
317        private void buildSyntax( List<Throwable> errors, Registries registries )
318        {
319            if ( syntaxOid != null )
320            {
321                LdapSyntax syntax = null;
322    
323                try
324                {
325                    syntax = registries.getLdapSyntaxRegistry().lookup( syntaxOid );
326                }
327                catch ( LdapException ne )
328                {
329                    // Not allowed.
330                    String msg = I18n.err( I18n.ERR_04306, syntaxOid, getName() );
331    
332                    Throwable error = new LdapProtocolErrorException( msg );
333                    errors.add( error );
334                    LOG.info( msg );
335                    return;
336                }
337    
338                if ( syntax != null )
339                {
340                    // Update the Syntax reference
341                    this.syntax = syntax;
342                }
343                else
344                {
345                    // Not allowed.
346                    String msg = I18n.err( I18n.ERR_04306, syntaxOid, getName() );
347    
348                    Throwable error = new LdapSchemaViolationException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, msg );
349                    errors.add( error );
350                    LOG.info( msg );
351                    return;
352                }
353            }
354            else
355            {
356                // We inherit from the superior's syntax, if any
357                if ( superior != null )
358                {
359                    this.syntax = superior.getSyntax();
360                    this.syntaxOid = this.syntax.getOid();
361                }
362                else
363                {
364                    // Not allowed.
365                    String msg = I18n.err( I18n.ERR_04307, getName() );
366    
367                    Throwable error = new LdapProtocolErrorException( msg );
368                    errors.add( error );
369                    LOG.info( msg );
370                    return;
371                }
372            }
373        }
374    
375    
376        /**
377         * Build the EQUALITY MR reference for an AttributeType
378         */
379        private void buildEquality( List<Throwable> errors, Registries registries )
380        {
381            // The equality MR. It can be null
382            if ( equalityOid != null )
383            {
384                MatchingRule equality = null;
385    
386                try
387                {
388                    equality = registries.getMatchingRuleRegistry().lookup( equalityOid );
389                }
390                catch ( LdapException ne )
391                {
392                    // Not allowed.
393                    String msg = I18n.err( I18n.ERR_04308, equalityOid, getName() );
394    
395                    Throwable error = new LdapProtocolErrorException( msg );
396                    errors.add( error );
397                    LOG.info( msg );
398                    return;
399                }
400    
401                if ( equality != null )
402                {
403                    this.equality = equality;
404                }
405                else
406                {
407                    // Not allowed.
408                    String msg = I18n.err( I18n.ERR_04309, equalityOid, getName() );
409    
410                    Throwable error = new LdapSchemaViolationException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, msg );
411                    errors.add( error );
412                    LOG.info( msg );
413                }
414            }
415            else
416            {
417                // If the AT has a superior, take its Equality MR if any
418                if ( ( superior != null ) && ( superior.getEquality() != null ) )
419                {
420                    this.equality = superior.getEquality();
421                    this.equalityOid = this.equality.getOid();
422                }
423            }
424        }
425    
426    
427        /**
428         * Build the ORDERING MR reference for an AttributeType
429         */
430        private void buildOrdering( List<Throwable> errors, Registries registries )
431        {
432            if ( orderingOid != null )
433            {
434                MatchingRule ordering = null;
435    
436                try
437                {
438                    ordering = registries.getMatchingRuleRegistry().lookup( orderingOid );
439                }
440                catch ( LdapException ne )
441                {
442                    // Not allowed.
443                    String msg = I18n.err( I18n.ERR_04310, orderingOid, getName() );
444    
445                    Throwable error = new LdapProtocolErrorException( msg );
446                    errors.add( error );
447                    LOG.info( msg );
448                    return;
449                }
450    
451                if ( ordering != null )
452                {
453                    this.ordering = ordering;
454                }
455                else
456                {
457                    // Not allowed.
458                    String msg = I18n.err( I18n.ERR_04311, orderingOid, getName() );
459    
460                    Throwable error = new LdapProtocolErrorException( msg );
461                    errors.add( error );
462                    LOG.info( msg );
463                }
464            }
465            else
466            {
467                // If the AT has a superior, take its Ordering MR if any
468                if ( ( superior != null ) && ( superior.getOrdering() != null ) )
469                {
470                    this.ordering = superior.getOrdering();
471                    this.orderingOid = this.ordering.getOid();
472                }
473            }
474        }
475    
476    
477        /**
478         * Build the SUBSTR MR reference for an AttributeType
479         */
480        private void buildSubstring( List<Throwable> errors, Registries registries )
481        {
482            // The Substring MR. It can be null
483            if ( substringOid != null )
484            {
485                MatchingRule substring = null;
486    
487                try
488                {
489                    substring = registries.getMatchingRuleRegistry().lookup( substringOid );
490                }
491                catch ( LdapException ne )
492                {
493                    // Not allowed.
494                    String msg = I18n.err( I18n.ERR_04312, substringOid, getName() );
495    
496                    Throwable error = new LdapProtocolErrorException( msg );
497                    errors.add( error );
498                    LOG.info( msg );
499                    return;
500                }
501    
502                if ( substring != null )
503                {
504                    this.substring = substring;
505                }
506                else
507                {
508                    // Not allowed.
509                    String msg = I18n.err( I18n.ERR_04313, substringOid, getName() );
510    
511                    Throwable error = new LdapSchemaViolationException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, msg );
512                    errors.add( error );
513                    LOG.info( msg );
514                    return;
515                }
516            }
517            else
518            {
519                // If the AT has a superior, take its Substring MR if any
520                if ( ( superior != null ) && ( superior.getSubstring() != null ) )
521                {
522                    this.substring = superior.getSubstring();
523                    this.substringOid = this.substring.getOid();
524                }
525            }
526        }
527    
528    
529        /**
530         * Check the constraints for the Usage field.
531         */
532        private void checkUsage( List<Throwable> errors, Registries registries )
533        {
534            // Check that the AT usage is the same that its superior
535            if ( ( superior != null ) && ( usage != superior.getUsage() ) )
536            {
537                // This is an error
538                String msg = I18n.err( I18n.ERR_04314, getName() );
539    
540                Throwable error = new LdapProtocolErrorException( msg );
541                errors.add( error );
542                LOG.info( msg );
543                return;
544            }
545    
546            // Now, check that the AttributeType's USAGE does not conflict
547            if ( !isUserModifiable() && ( usage == UsageEnum.USER_APPLICATIONS ) )
548            {
549                // Cannot have a not user modifiable AT which is not an operational AT
550                String msg = I18n.err( I18n.ERR_04315, getName() );
551    
552                Throwable error = new LdapProtocolErrorException( msg );
553                errors.add( error );
554                LOG.info( msg );
555            }
556        }
557    
558    
559        /**
560         * Check the constraints for the Collective field.
561         */
562        private void checkCollective( List<Throwable> errors, Registries registries )
563        {
564            if ( superior != null )
565            {
566                if ( superior.isCollective() )
567                {
568                    // An AttributeType will be collective if its superior is collective
569                    this.isCollective = true;
570                }
571            }
572    
573            if ( isCollective() && ( usage != UsageEnum.USER_APPLICATIONS ) )
574            {
575                // An AttributeType which is collective must be a USER attributeType
576                String msg = I18n.err( I18n.ERR_04316, getName() );
577    
578                Throwable error = new LdapProtocolErrorException( msg );
579                errors.add( error );
580                LOG.info( msg );
581            }
582        }
583    
584    
585        /**
586         * Inject the attributeType into the registries, updating the references to
587         * other SchemaObject.
588         * 
589         * If one of the referenced SchemaObject does not exist (SUP, EQUALITY, ORDERING, SUBSTR, SYNTAX), 
590         * an exception is thrown.
591         *
592         * @param registries The Registries
593         * @exception If the AttributeType is not valid 
594         */
595        public void addToRegistries( List<Throwable> errors, Registries registries ) throws LdapException
596        {
597            if ( registries != null )
598            {
599                AttributeTypeRegistry attributeTypeRegistry = registries.getAttributeTypeRegistry();
600    
601                // The superior
602                if ( !buildSuperior( errors, registries ) )
603                {
604                    // We have had errors, let's stop here as we need a correct superior to continue
605                    return;
606                }
607    
608                // The Syntax
609                buildSyntax( errors, registries );
610    
611                // The EQUALITY matching rule
612                buildEquality( errors, registries );
613    
614                // The ORDERING matching rule
615                buildOrdering( errors, registries );
616    
617                // The SUBSTR matching rule
618                buildSubstring( errors, registries );
619    
620                // Check the USAGE
621                checkUsage( errors, registries );
622    
623                // Check the COLLECTIVE element
624                checkCollective( errors, registries );
625    
626                // Inject the attributeType into the oid/normalizer map
627                attributeTypeRegistry.addMappingFor( this );
628    
629                // Register this AttributeType into the Descendant map
630                attributeTypeRegistry.registerDescendants( this, superior );
631    
632                /**
633                 * Add the AT references (using and usedBy) : 
634                 * AT -> MR (for EQUALITY, ORDERING and SUBSTR)
635                 * AT -> S
636                 * AT -> AT
637                 */
638                if ( equality != null )
639                {
640                    registries.addReference( this, equality );
641                }
642    
643                if ( ordering != null )
644                {
645                    registries.addReference( this, ordering );
646                }
647    
648                if ( substring != null )
649                {
650                    registries.addReference( this, substring );
651                }
652    
653                if ( syntax != null )
654                {
655                    registries.addReference( this, syntax );
656                }
657    
658                if ( superior != null )
659                {
660                    registries.addReference( this, superior );
661                }
662            }
663        }
664    
665    
666        /**
667         * Remove the attributeType from the registries, updating the references to
668         * other SchemaObject.
669         * 
670         * If one of the referenced SchemaObject does not exist (SUP, EQUALITY, ORDERING, SUBSTR, SYNTAX), 
671         * an exception is thrown.
672         *
673         * @param registries The Registries
674         * @exception If the AttributeType is not valid 
675         */
676        public void removeFromRegistries( List<Throwable> errors, Registries registries ) throws LdapException
677        {
678            if ( registries != null )
679            {
680                AttributeTypeRegistry attributeTypeRegistry = registries.getAttributeTypeRegistry();
681    
682                // Remove the attributeType from the oid/normalizer map
683                attributeTypeRegistry.removeMappingFor( this );
684    
685                // Unregister this AttributeType into the Descendant map
686                attributeTypeRegistry.unregisterDescendants( this, superior );
687    
688                /**
689                 * Remove the AT references (using and usedBy) : 
690                 * AT -> MR (for EQUALITY, ORDERING and SUBSTR)
691                 * AT -> S
692                 * AT -> AT
693                 */
694                if ( equality != null )
695                {
696                    registries.delReference( this, equality );
697                }
698    
699                if ( ordering != null )
700                {
701                    registries.delReference( this, ordering );
702                }
703    
704                if ( substring != null )
705                {
706                    registries.delReference( this, substring );
707                }
708    
709                if ( syntax != null )
710                {
711                    registries.delReference( this, syntax );
712                }
713    
714                if ( superior != null )
715                {
716                    registries.delReference( this, superior );
717                }
718            }
719        }
720    
721    
722        /**
723         * Gets whether or not this AttributeType is single-valued.
724         * 
725         * @return true if only one value can exist for this AttributeType, false
726         *         otherwise
727         */
728        public boolean isSingleValued()
729        {
730            return isSingleValued;
731        }
732    
733    
734        /**
735         * Tells if this AttributeType is Single Valued or not
736         *
737         * @param singleValue True if the AttributeType is single-valued
738         */
739        public void setSingleValued( boolean singleValued )
740        {
741            if ( locked )
742            {
743                throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
744            }
745            
746            if ( !isReadOnly )
747            {
748                this.isSingleValued = singleValued;
749            }
750        }
751    
752    
753        /**
754         * Gets whether or not this AttributeType can be modified by a user.
755         * 
756         * @return true if users can modify it, false if only the directory can.
757         */
758        public boolean isUserModifiable()
759        {
760            return canUserModify;
761        }
762    
763    
764        /**
765         * Tells if this AttributeType can be modified by a user or not
766         *
767         * @param canUserModify The flag to set
768         */
769        public void setUserModifiable( boolean canUserModify )
770        {
771            if ( locked )
772            {
773                throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
774            }
775            
776            if ( !isReadOnly )
777            {
778                this.canUserModify = canUserModify;
779            }
780        }
781    
782    
783        /**
784         * Gets whether or not this AttributeType is a collective attribute.
785         * 
786         * @return true if the attribute is collective, false otherwise
787         */
788        public boolean isCollective()
789        {
790            return isCollective;
791        }
792    
793    
794        /**
795         * Updates the collective flag
796         *
797         * @param collective The new value to set
798         */
799        public void updateCollective( boolean collective )
800        {
801            if ( locked )
802            {
803                throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
804            }
805            
806            this.isCollective = collective;
807        }
808    
809    
810        /**
811         * Sets the collective flag
812         *
813         * @param collective The new value to set
814         */
815        public void setCollective( boolean collective )
816        {
817            if ( locked )
818            {
819                throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
820            }
821            
822            if ( !isReadOnly )
823            {
824                this.isCollective = collective;
825            }
826        }
827    
828    
829        /**
830         * Determines the usage for this AttributeType.
831         * 
832         * @return a type safe UsageEnum
833         */
834        public UsageEnum getUsage()
835        {
836            return usage;
837        }
838    
839    
840        /**
841         * Sets the AttributeType usage, one of :<br>
842         * <li>USER_APPLICATIONS
843         * <li>DIRECTORY_OPERATION
844         * <li>DISTRIBUTED_OPERATION
845         * <li>DSA_OPERATION
846         * <br>
847         * @see UsageEnum
848         * @param usage The AttributeType usage
849         */
850        public void setUsage( UsageEnum usage )
851        {
852            if ( locked )
853            {
854                throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
855            }
856            
857            if ( !isReadOnly )
858            {
859                this.usage = usage;
860            }
861        }
862    
863    
864        /**
865         * Updates the AttributeType usage, one of :<br>
866         * <li>USER_APPLICATIONS
867         * <li>DIRECTORY_OPERATION
868         * <li>DISTRIBUTED_OPERATION
869         * <li>DSA_OPERATION
870         * <br>
871         * @see UsageEnum
872         * @param usage The AttributeType usage
873         */
874        public void updateUsage( UsageEnum usage )
875        {
876            if ( locked )
877            {
878                throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
879            }
880            
881            this.usage = usage;
882        }
883    
884    
885        /**
886         * Gets a length limit for this AttributeType.
887         * 
888         * @return the length of the attribute
889         */
890        public int getSyntaxLength()
891        {
892            return syntaxLength;
893        }
894    
895    
896        /**
897         * Sets the length limit of this AttributeType based on its associated
898         * syntax.
899         * 
900         * @param length the new length to set
901         */
902        public void setSyntaxLength( int length )
903        {
904            if ( locked )
905            {
906                throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
907            }
908            
909            if ( !isReadOnly )
910            {
911                this.syntaxLength = length;
912            }
913        }
914    
915    
916        /**
917         * Gets the the superior AttributeType of this AttributeType.
918         * 
919         * @return the superior AttributeType for this AttributeType
920         */
921        public AttributeType getSuperior()
922        {
923            return superior;
924        }
925    
926    
927        /**
928         * Gets the OID of the superior AttributeType for this AttributeType.
929         * 
930         * @return The OID of the superior AttributeType for this AttributeType.
931         */
932        public String getSuperiorOid()
933        {
934            return superiorOid;
935        }
936    
937    
938        /**
939         * Gets the Name of the superior AttributeType for this AttributeType.
940         * 
941         * @return The Name of the superior AttributeType for this AttributeType.
942         */
943        public String getSuperiorName()
944        {
945            if ( superior != null )
946            {
947                return superior.getName();
948            }
949            else
950            {
951                return superiorOid;
952            }
953        }
954    
955    
956        /**
957         * Sets the superior AttributeType OID of this AttributeType
958         *
959         * @param superiorOid The superior AttributeType OID of this AttributeType
960         */
961        public void setSuperiorOid( String superiorOid )
962        {
963            if ( locked )
964            {
965                throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
966            }
967            
968            if ( !isReadOnly )
969            {
970                this.superiorOid = superiorOid;
971            }
972        }
973    
974    
975        /**
976         * Sets the superior for this AttributeType
977         *
978         * @param superior The superior for this AttributeType
979         */
980        public void setSuperior( AttributeType superior )
981        {
982            if ( locked )
983            {
984                throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
985            }
986            
987            if ( !isReadOnly )
988            {
989                this.superior = superior;
990                this.superiorOid = superior.getOid();
991            }
992        }
993    
994    
995        /**
996         * Sets the superior oid for this AttributeType
997         *
998         * @param superior The superior oid for this AttributeType
999         */
1000        public void setSuperior( String superiorOid )
1001        {
1002            if ( locked )
1003            {
1004                throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
1005            }
1006            
1007            if ( !isReadOnly )
1008            {
1009                this.superiorOid = superiorOid;
1010            }
1011        }
1012    
1013    
1014        /**
1015         * Update the associated Superior AttributeType, even if the SchemaObject is readOnly
1016         *
1017         * @param superior The superior for this AttributeType
1018         */
1019        public void updateSuperior( AttributeType superior )
1020        {
1021            if ( locked )
1022            {
1023                throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
1024            }
1025            
1026            this.superior = superior;
1027            this.superiorOid = superior.getOid();
1028        }
1029    
1030    
1031        /**
1032         * Gets the Syntax for this AttributeType's values.
1033         * 
1034         * @return the value syntax
1035         */
1036        public LdapSyntax getSyntax()
1037        {
1038            return syntax;
1039        }
1040    
1041    
1042        /**
1043         * Gets the Syntax name for this AttributeType's values.
1044         * 
1045         * @return the value syntax name
1046         */
1047        public String getSyntaxName()
1048        {
1049            if ( syntax != null )
1050            {
1051                return syntax.getName();
1052            }
1053            else
1054            {
1055                return null;
1056            }
1057        }
1058    
1059    
1060        /**
1061         * Gets the Syntax OID for this AttributeType's values.
1062         * 
1063         * @return the value syntax's OID
1064         */
1065        public String getSyntaxOid()
1066        {
1067            return syntaxOid;
1068        }
1069    
1070    
1071        /**
1072         * Sets the Syntax OID for this AttributeType
1073         *
1074         * @param superiorOid The syntax OID for this AttributeType
1075         */
1076        public void setSyntaxOid( String syntaxOid )
1077        {
1078            if ( locked )
1079            {
1080                throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
1081            }
1082            
1083            if ( !isReadOnly )
1084            {
1085                this.syntaxOid = syntaxOid;
1086            }
1087        }
1088    
1089    
1090        /**
1091         * Sets the Syntax for this AttributeType
1092         *
1093         * @param syntax The Syntax for this AttributeType
1094         */
1095        public void setSyntax( LdapSyntax syntax )
1096        {
1097            if ( locked )
1098            {
1099                throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
1100            }
1101            
1102            if ( !isReadOnly )
1103            {
1104                this.syntax = syntax;
1105                this.syntaxOid = syntax.getOid();
1106            }
1107        }
1108    
1109    
1110        /**
1111         * Update the associated Syntax, even if the SchemaObject is readOnly
1112         *
1113         * @param syntax The Syntax for this AttributeType
1114         */
1115        public void updateSyntax( LdapSyntax syntax )
1116        {
1117            if ( locked )
1118            {
1119                throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
1120            }
1121            
1122            this.syntax = syntax;
1123            this.syntaxOid = syntax.getOid();
1124        }
1125    
1126    
1127        /**
1128         * Gets the MatchingRule for this AttributeType used for equality matching.
1129         * 
1130         * @return the equality matching rule
1131         */
1132        public MatchingRule getEquality()
1133        {
1134            return equality;
1135        }
1136    
1137    
1138        /**
1139         * Gets the Equality OID for this AttributeType's values.
1140         * 
1141         * @return the value Equality's OID
1142         */
1143        public String getEqualityOid()
1144        {
1145            return equalityOid;
1146        }
1147    
1148    
1149        /**
1150         * Gets the Equality Name for this AttributeType's values.
1151         * 
1152         * @return the value Equality's Name
1153         */
1154        public String getEqualityName()
1155        {
1156            if ( equality != null )
1157            {
1158                return equality.getName();
1159            }
1160            else
1161            {
1162                return equalityOid;
1163            }
1164        }
1165    
1166    
1167        /**
1168         * Sets the Equality OID for this AttributeType
1169         *
1170         * @param equalityOid The Equality OID for this AttributeType
1171         */
1172        public void setEqualityOid( String equalityOid )
1173        {
1174            if ( locked )
1175            {
1176                throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
1177            }
1178            
1179            if ( !isReadOnly )
1180            {
1181                this.equalityOid = equalityOid;
1182            }
1183        }
1184    
1185    
1186        /**
1187         * Sets the Equality MR for this AttributeType
1188         *
1189         * @param equality The Equality MR for this AttributeType
1190         */
1191        public void setEquality( MatchingRule equality )
1192        {
1193            if ( locked )
1194            {
1195                throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
1196            }
1197            
1198            if ( !isReadOnly )
1199            {
1200                this.equality = equality;
1201                this.equalityOid = equality.getOid();
1202            }
1203        }
1204    
1205    
1206        /**
1207         * Update the associated Equality MatchingRule, even if the SchemaObject is readOnly
1208         *
1209         * @param equality The Equality MR for this AttributeType
1210         */
1211        public void updateEquality( MatchingRule equality )
1212        {
1213            if ( locked )
1214            {
1215                throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
1216            }
1217            
1218            this.equality = equality;
1219            this.equalityOid = equality.getOid();
1220        }
1221    
1222    
1223        /**
1224         * Gets the MatchingRule for this AttributeType used for Ordering matching.
1225         * 
1226         * @return the Ordering matching rule
1227         */
1228        public MatchingRule getOrdering()
1229        {
1230            return ordering;
1231        }
1232    
1233    
1234        /**
1235         * Gets the MatchingRule name for this AttributeType used for Ordering matching.
1236         * 
1237         * @return the Ordering matching rule name
1238         */
1239        public String getOrderingName()
1240        {
1241            if ( ordering != null )
1242            {
1243                return ordering.getName();
1244            }
1245            else
1246            {
1247                return orderingOid;
1248            }
1249        }
1250    
1251    
1252        /**
1253         * Gets the Ordering OID for this AttributeType's values.
1254         * 
1255         * @return the value Equality's OID
1256         */
1257        public String getOrderingOid()
1258        {
1259            return orderingOid;
1260        }
1261    
1262    
1263        /**
1264         * Sets the Ordering OID for this AttributeType
1265         *
1266         * @param orderingOid The Ordering OID for this AttributeType
1267         */
1268        public void setOrderingOid( String orderingOid )
1269        {
1270            if ( locked )
1271            {
1272                throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
1273            }
1274            
1275            if ( !isReadOnly )
1276            {
1277                this.orderingOid = orderingOid;
1278            }
1279        }
1280    
1281    
1282        /**
1283         * Sets the Ordering MR for this AttributeType
1284         *
1285         * @param ordering The Ordering MR for this AttributeType
1286         */
1287        public void setOrdering( MatchingRule ordering )
1288        {
1289            if ( locked )
1290            {
1291                throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
1292            }
1293            
1294            if ( !isReadOnly )
1295            {
1296                this.ordering = ordering;
1297                this.orderingOid = ordering.getOid();
1298            }
1299        }
1300    
1301    
1302        /**
1303         * Update the associated Ordering MatchingRule, even if the SchemaObject is readOnly
1304         *
1305         * @param ordering The Ordering MR for this AttributeType
1306         */
1307        public void updateOrdering( MatchingRule ordering )
1308        {
1309            if ( locked )
1310            {
1311                throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
1312            }
1313            
1314            this.ordering = ordering;
1315            this.orderingOid = ordering.getOid();
1316        }
1317    
1318    
1319        /**
1320         * Gets the MatchingRule for this AttributeType used for Substr matching.
1321         * 
1322         * @return the Substr matching rule
1323         */
1324        public MatchingRule getSubstring()
1325        {
1326            return substring;
1327        }
1328    
1329    
1330        /**
1331         * Gets the MatchingRule name for this AttributeType used for Substring matching.
1332         * 
1333         * @return the Substring matching rule name
1334         */
1335        public String getSubstringName()
1336        {
1337            if ( substring != null )
1338            {
1339                return substring.getName();
1340            }
1341            else
1342            {
1343                return substringOid;
1344            }
1345        }
1346    
1347    
1348        /**
1349         * Gets the Substr OID for this AttributeType's values.
1350         * 
1351         * @return the value Substr's OID
1352         */
1353        public String getSubstringOid()
1354        {
1355            return substringOid;
1356        }
1357    
1358    
1359        /**
1360         * Sets the Substr OID for this AttributeType
1361         *
1362         * @param substrOid The Substr OID for this AttributeType
1363         */
1364        public void setSubstringOid( String substrOid )
1365        {
1366            if ( locked )
1367            {
1368                throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
1369            }
1370            
1371            if ( !isReadOnly )
1372            {
1373                this.substringOid = substrOid;
1374            }
1375        }
1376    
1377    
1378        /**
1379         * Sets the Substr MR for this AttributeType
1380         *
1381         * @param substring The Substr MR for this AttributeType
1382         */
1383        public void setSubstring( MatchingRule substring )
1384        {
1385            if ( locked )
1386            {
1387                throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
1388            }
1389            
1390            if ( !isReadOnly )
1391            {
1392                this.substring = substring;
1393                this.substringOid = substring.getOid();
1394            }
1395        }
1396    
1397    
1398        /**
1399         * Update the associated Substring MatchingRule, even if the SchemaObject is readOnly
1400         *
1401         * @param substring The Substr MR for this AttributeType
1402         */
1403        public void updateSubstring( MatchingRule substring )
1404        {
1405            if ( locked )
1406            {
1407                throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) );
1408            }
1409            
1410            this.substring = substring;
1411            this.substringOid = substring.getOid();
1412        }
1413    
1414    
1415        /**
1416         * Checks to see if this AttributeType is the ancestor of another
1417         * attributeType.
1418         *
1419         * @param descendant the perspective descendant to check
1420         * @return true if the descendant is truly a derived from this AttributeType
1421         */
1422        public boolean isAncestorOf( AttributeType descendant )
1423        {
1424            if ( ( descendant == null ) || this.equals( descendant ) )
1425            {
1426                return false;
1427            }
1428    
1429            return isAncestorOrEqual( this, descendant );
1430        }
1431    
1432    
1433        /**
1434         * Checks to see if this AttributeType is the descendant of another
1435         * attributeType.
1436         *
1437         * @param ancestor the perspective ancestor to check
1438         * @return true if this AttributeType truly descends from the ancestor
1439         */
1440        public boolean isDescendantOf( AttributeType ancestor )
1441        {
1442            if ( ( ancestor == null ) || equals( ancestor ) )
1443            {
1444                return false;
1445            }
1446    
1447            return isAncestorOrEqual( ancestor, this );
1448        }
1449    
1450    
1451        /**
1452         * Recursive method which checks to see if a descendant is really an ancestor or if the two
1453         * are equal.
1454         *
1455         * @param ancestor the possible ancestor of the descendant
1456         * @param descendant the possible descendant of the ancestor
1457         * @return true if the ancestor equals the descendant or if the descendant is really
1458         * a subtype of the ancestor. otherwise false
1459         */
1460        private boolean isAncestorOrEqual( AttributeType ancestor, AttributeType descendant )
1461        {
1462            if ( ( ancestor == null ) || ( descendant == null ) )
1463            {
1464                return false;
1465            }
1466    
1467            if ( ancestor.equals( descendant ) )
1468            {
1469                return true;
1470            }
1471    
1472            return isAncestorOrEqual( ancestor, descendant.getSuperior() );
1473        }
1474    
1475    
1476        /**
1477         * @see Object#toString()
1478         */
1479        public String toString()
1480        {
1481            return objectType + " " + DescriptionUtils.getDescription( this );
1482        }
1483    
1484    
1485        /**
1486         * Copy an AttributeType
1487         */
1488        public AttributeType copy()
1489        {
1490            AttributeType copy = new AttributeType( oid );
1491    
1492            // Copy the SchemaObject common data
1493            copy.copy( this );
1494    
1495            // Copy the canUserModify flag
1496            copy.canUserModify = canUserModify;
1497    
1498            // Copy the isCollective flag
1499            copy.isCollective = isCollective;
1500    
1501            // Copy the isSingleValue flag
1502            copy.isSingleValued = isSingleValued;
1503    
1504            // Copy the USAGE type
1505            copy.usage = usage;
1506    
1507            // All the references to other Registries object are set to null,
1508            // all the OIDs are copied
1509            // The EQUALITY MR
1510            copy.equality = null;
1511            copy.equalityOid = equalityOid;
1512    
1513            // The ORDERING MR
1514            copy.ordering = null;
1515            copy.orderingOid = orderingOid;
1516    
1517            // The SUBSTR MR
1518            copy.substring = null;
1519            copy.substringOid = substringOid;
1520    
1521            // The SUP AT
1522            copy.superior = null;
1523            copy.superiorOid = superiorOid;
1524    
1525            // The SYNTAX
1526            copy.syntax = null;
1527            copy.syntaxOid = syntaxOid;
1528            copy.syntaxLength = syntaxLength;
1529    
1530            return copy;
1531        }
1532    
1533    
1534        /**
1535         * {@inheritDoc}
1536         */
1537        public void clear()
1538        {
1539            // Clear the common elements
1540            super.clear();
1541    
1542            // Clear the references
1543            equality = null;
1544            ordering = null;
1545            substring = null;
1546            superior = null;
1547            syntax = null;
1548        }
1549    
1550    
1551        /**
1552         * @see Object#equals(Object)
1553         */
1554        public boolean equals( Object o )
1555        {
1556            if ( !super.equals( o ) )
1557            {
1558                return false;
1559            }
1560    
1561            if ( !( o instanceof AttributeType ) )
1562            {
1563                return false;
1564            }
1565    
1566            AttributeType that = ( AttributeType ) o;
1567    
1568            // The COLLECTIVE
1569            if ( isCollective != that.isCollective )
1570            {
1571                return false;
1572            }
1573    
1574            // The SINGLE_VALUE
1575            if ( isSingleValued != that.isSingleValued )
1576            {
1577                return false;
1578            }
1579    
1580            // The NO_USER_MODIFICATION
1581            if ( canUserModify != that.canUserModify )
1582            {
1583                return false;
1584            }
1585    
1586            // The USAGE
1587            if ( usage != that.usage )
1588            {
1589                return false;
1590            }
1591    
1592            // The equality
1593            if ( !compareOid( equalityOid, that.equalityOid ) )
1594            {
1595                return false;
1596            }
1597    
1598            if ( equality != null )
1599            {
1600                if ( !equality.equals( that.equality ) )
1601                {
1602                    return false;
1603                }
1604            }
1605            else
1606            {
1607                if ( that.equality != null )
1608                {
1609                    return false;
1610                }
1611            }
1612    
1613            // The ordering
1614            if ( !compareOid( orderingOid, that.orderingOid ) )
1615            {
1616                return false;
1617            }
1618    
1619            if ( ordering != null )
1620            {
1621                if ( !ordering.equals( that.ordering ) )
1622                {
1623                    return false;
1624                }
1625            }
1626            else
1627            {
1628                if ( that.ordering != null )
1629                {
1630                    return false;
1631                }
1632            }
1633    
1634            // The substring
1635            if ( !compareOid( substringOid, that.substringOid ) )
1636            {
1637                return false;
1638            }
1639    
1640            if ( substring != null )
1641            {
1642                if ( !substring.equals( that.substring ) )
1643                {
1644                    return false;
1645                }
1646            }
1647            else
1648            {
1649                if ( that.substring != null )
1650                {
1651                    return false;
1652                }
1653            }
1654    
1655            // The superior
1656            if ( !compareOid( superiorOid, that.superiorOid ) )
1657            {
1658                return false;
1659            }
1660    
1661            if ( superior != null )
1662            {
1663                if ( !superior.equals( that.superior ) )
1664                {
1665                    return false;
1666                }
1667            }
1668            else
1669            {
1670                if ( that.superior != null )
1671                {
1672                    return false;
1673                }
1674            }
1675    
1676            // The syntax
1677            if ( !compareOid( syntaxOid, that.syntaxOid ) )
1678            {
1679                return false;
1680            }
1681    
1682            if ( syntaxLength != that.syntaxLength )
1683            {
1684                return false;
1685            }
1686    
1687            if ( syntax == null )
1688            {
1689                return that.syntax == null;
1690            }
1691    
1692            if ( syntax.equals( that.syntax ) )
1693            {
1694                return syntaxLength == that.syntaxLength;
1695            }
1696            else
1697            {
1698                return false;
1699            }
1700        }
1701    }