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