001    /*
002     * Copyright 2007-2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2016 UnboundID Corp.
007     *
008     * This program is free software; you can redistribute it and/or modify
009     * it under the terms of the GNU General Public License (GPLv2 only)
010     * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011     * as published by the Free Software Foundation.
012     *
013     * This program is distributed in the hope that it will be useful,
014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program; if not, see <http://www.gnu.org/licenses>.
020     */
021    package com.unboundid.ldap.sdk.schema;
022    
023    
024    
025    import java.util.ArrayList;
026    import java.util.Collections;
027    import java.util.Map;
028    import java.util.LinkedHashMap;
029    
030    import com.unboundid.ldap.sdk.LDAPException;
031    import com.unboundid.ldap.sdk.ResultCode;
032    import com.unboundid.util.NotMutable;
033    import com.unboundid.util.ThreadSafety;
034    import com.unboundid.util.ThreadSafetyLevel;
035    
036    import static com.unboundid.ldap.sdk.schema.SchemaMessages.*;
037    import static com.unboundid.util.Debug.*;
038    import static com.unboundid.util.StaticUtils.*;
039    import static com.unboundid.util.Validator.*;
040    
041    
042    
043    /**
044     * This class provides a data structure that describes an LDAP attribute type
045     * schema element.
046     */
047    @NotMutable()
048    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
049    public final class AttributeTypeDefinition
050           extends SchemaElement
051    {
052      /**
053       * The serial version UID for this serializable class.
054       */
055      private static final long serialVersionUID = -6688185196734362719L;
056    
057    
058    
059      // The usage for this attribute type.
060      private final AttributeUsage usage;
061    
062      // Indicates whether this attribute type is declared collective.
063      private final boolean isCollective;
064    
065      // Indicates whether this attribute type is declared no-user-modification.
066      private final boolean isNoUserModification;
067    
068      // Indicates whether this attribute type is declared obsolete.
069      private final boolean isObsolete;
070    
071      // Indicates whether this attribute type is declared single-valued.
072      private final boolean isSingleValued;
073    
074      // The set of extensions for this attribute type.
075      private final Map<String,String[]> extensions;
076    
077      // The string representation of this attribute type.
078      private final String attributeTypeString;
079    
080      // The description for this attribute type.
081      private final String description;
082    
083      // The name/OID of the equality matching rule for this attribute type.
084      private final String equalityMatchingRule;
085    
086      // The OID for this attribute type.
087      private final String oid;
088    
089      // The name/OID of the ordering matching rule for this attribute type.
090      private final String orderingMatchingRule;
091    
092      // The name/OID of the substring matching rule for this attribute type.
093      private final String substringMatchingRule;
094    
095      // The name of the superior type for this attribute type.
096      private final String superiorType;
097    
098      // The OID of the syntax for this attribute type.
099      private final String syntaxOID;
100    
101      // The set of names for this attribute type.
102      private final String[] names;
103    
104    
105    
106      /**
107       * Creates a new attribute type from the provided string representation.
108       *
109       * @param  s  The string representation of the attribute type to create, using
110       *            the syntax described in RFC 4512 section 4.1.2.  It must not be
111       *            {@code null}.
112       *
113       * @throws  LDAPException  If the provided string cannot be decoded as an
114       *                         attribute type definition.
115       */
116      public AttributeTypeDefinition(final String s)
117             throws LDAPException
118      {
119        ensureNotNull(s);
120    
121        attributeTypeString = s.trim();
122    
123        // The first character must be an opening parenthesis.
124        final int length = attributeTypeString.length();
125        if (length == 0)
126        {
127          throw new LDAPException(ResultCode.DECODING_ERROR,
128                                  ERR_ATTRTYPE_DECODE_EMPTY.get());
129        }
130        else if (attributeTypeString.charAt(0) != '(')
131        {
132          throw new LDAPException(ResultCode.DECODING_ERROR,
133                                  ERR_ATTRTYPE_DECODE_NO_OPENING_PAREN.get(
134                                       attributeTypeString));
135        }
136    
137    
138        // Skip over any spaces until we reach the start of the OID, then read the
139        // OID until we find the next space.
140        int pos = skipSpaces(attributeTypeString, 1, length);
141    
142        StringBuilder buffer = new StringBuilder();
143        pos = readOID(attributeTypeString, pos, length, buffer);
144        oid = buffer.toString();
145    
146    
147        // Technically, attribute type elements are supposed to appear in a specific
148        // order, but we'll be lenient and allow remaining elements to come in any
149        // order.
150        final ArrayList<String> nameList = new ArrayList<String>(1);
151        AttributeUsage       attrUsage   = null;
152        Boolean              collective  = null;
153        Boolean              noUserMod   = null;
154        Boolean              obsolete    = null;
155        Boolean              singleValue = null;
156        final Map<String,String[]> exts  = new LinkedHashMap<String,String[]>();
157        String               descr       = null;
158        String               eqRule      = null;
159        String               ordRule     = null;
160        String               subRule     = null;
161        String               supType     = null;
162        String               synOID      = null;
163    
164        while (true)
165        {
166          // Skip over any spaces until we find the next element.
167          pos = skipSpaces(attributeTypeString, pos, length);
168    
169          // Read until we find the next space or the end of the string.  Use that
170          // token to figure out what to do next.
171          final int tokenStartPos = pos;
172          while ((pos < length) && (attributeTypeString.charAt(pos) != ' '))
173          {
174            pos++;
175          }
176    
177          String token = attributeTypeString.substring(tokenStartPos, pos);
178    
179          // It's possible that the token could be smashed right up against the
180          // closing parenthesis.  If that's the case, then extract just the token
181          // and handle the closing parenthesis the next time through.
182          if ((token.length() > 1) && (token.endsWith(")")))
183          {
184            token = token.substring(0, token.length() - 1);
185            pos--;
186          }
187    
188          final String lowerToken = toLowerCase(token);
189          if (lowerToken.equals(")"))
190          {
191            // This indicates that we're at the end of the value.  There should not
192            // be any more closing characters.
193            if (pos < length)
194            {
195              throw new LDAPException(ResultCode.DECODING_ERROR,
196                                      ERR_ATTRTYPE_DECODE_CLOSE_NOT_AT_END.get(
197                                           attributeTypeString));
198            }
199            break;
200          }
201          else if (lowerToken.equals("name"))
202          {
203            if (nameList.isEmpty())
204            {
205              pos = skipSpaces(attributeTypeString, pos, length);
206              pos = readQDStrings(attributeTypeString, pos, length, nameList);
207            }
208            else
209            {
210              throw new LDAPException(ResultCode.DECODING_ERROR,
211                                      ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get(
212                                           attributeTypeString, "NAME"));
213            }
214          }
215          else if (lowerToken.equals("desc"))
216          {
217            if (descr == null)
218            {
219              pos = skipSpaces(attributeTypeString, pos, length);
220    
221              buffer = new StringBuilder();
222              pos = readQDString(attributeTypeString, pos, length, buffer);
223              descr = buffer.toString();
224            }
225            else
226            {
227              throw new LDAPException(ResultCode.DECODING_ERROR,
228                                      ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get(
229                                           attributeTypeString, "DESC"));
230            }
231          }
232          else if (lowerToken.equals("obsolete"))
233          {
234            if (obsolete == null)
235            {
236              obsolete = true;
237            }
238            else
239            {
240              throw new LDAPException(ResultCode.DECODING_ERROR,
241                                      ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get(
242                                           attributeTypeString, "OBSOLETE"));
243            }
244          }
245          else if (lowerToken.equals("sup"))
246          {
247            if (supType == null)
248            {
249              pos = skipSpaces(attributeTypeString, pos, length);
250    
251              buffer = new StringBuilder();
252              pos = readOID(attributeTypeString, pos, length, buffer);
253              supType = buffer.toString();
254            }
255            else
256            {
257              throw new LDAPException(ResultCode.DECODING_ERROR,
258                                      ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get(
259                                           attributeTypeString, "SUP"));
260            }
261          }
262          else if (lowerToken.equals("equality"))
263          {
264            if (eqRule == null)
265            {
266              pos = skipSpaces(attributeTypeString, pos, length);
267    
268              buffer = new StringBuilder();
269              pos = readOID(attributeTypeString, pos, length, buffer);
270              eqRule = buffer.toString();
271            }
272            else
273            {
274              throw new LDAPException(ResultCode.DECODING_ERROR,
275                                      ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get(
276                                           attributeTypeString, "EQUALITY"));
277            }
278          }
279          else if (lowerToken.equals("ordering"))
280          {
281            if (ordRule == null)
282            {
283              pos = skipSpaces(attributeTypeString, pos, length);
284    
285              buffer = new StringBuilder();
286              pos = readOID(attributeTypeString, pos, length, buffer);
287              ordRule = buffer.toString();
288            }
289            else
290            {
291              throw new LDAPException(ResultCode.DECODING_ERROR,
292                                      ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get(
293                                           attributeTypeString, "ORDERING"));
294            }
295          }
296          else if (lowerToken.equals("substr"))
297          {
298            if (subRule == null)
299            {
300              pos = skipSpaces(attributeTypeString, pos, length);
301    
302              buffer = new StringBuilder();
303              pos = readOID(attributeTypeString, pos, length, buffer);
304              subRule = buffer.toString();
305            }
306            else
307            {
308              throw new LDAPException(ResultCode.DECODING_ERROR,
309                                      ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get(
310                                           attributeTypeString, "SUBSTR"));
311            }
312          }
313          else if (lowerToken.equals("syntax"))
314          {
315            if (synOID == null)
316            {
317              pos = skipSpaces(attributeTypeString, pos, length);
318    
319              buffer = new StringBuilder();
320              pos = readOID(attributeTypeString, pos, length, buffer);
321              synOID = buffer.toString();
322            }
323            else
324            {
325              throw new LDAPException(ResultCode.DECODING_ERROR,
326                                      ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get(
327                                           attributeTypeString, "SYNTAX"));
328            }
329          }
330          else if (lowerToken.equals("single-value"))
331          {
332            if (singleValue == null)
333            {
334              singleValue = true;
335            }
336            else
337            {
338              throw new LDAPException(ResultCode.DECODING_ERROR,
339                                      ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get(
340                                           attributeTypeString, "SINGLE-VALUE"));
341            }
342          }
343          else if (lowerToken.equals("collective"))
344          {
345            if (collective == null)
346            {
347              collective = true;
348            }
349            else
350            {
351              throw new LDAPException(ResultCode.DECODING_ERROR,
352                                      ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get(
353                                           attributeTypeString, "COLLECTIVE"));
354            }
355          }
356          else if (lowerToken.equals("no-user-modification"))
357          {
358            if (noUserMod == null)
359            {
360              noUserMod = true;
361            }
362            else
363            {
364              throw new LDAPException(ResultCode.DECODING_ERROR,
365                                      ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get(
366                                           attributeTypeString,
367                                           "NO-USER-MODIFICATION"));
368            }
369          }
370          else if (lowerToken.equals("usage"))
371          {
372            if (attrUsage == null)
373            {
374              pos = skipSpaces(attributeTypeString, pos, length);
375    
376              buffer = new StringBuilder();
377              pos = readOID(attributeTypeString, pos, length, buffer);
378    
379              final String usageStr = toLowerCase(buffer.toString());
380              if (usageStr.equals("userapplications"))
381              {
382                attrUsage = AttributeUsage.USER_APPLICATIONS;
383              }
384              else if (usageStr.equals("directoryoperation"))
385              {
386                attrUsage = AttributeUsage.DIRECTORY_OPERATION;
387              }
388              else if (usageStr.equals("distributedoperation"))
389              {
390                attrUsage = AttributeUsage.DISTRIBUTED_OPERATION;
391              }
392              else if (usageStr.equals("dsaoperation"))
393              {
394                attrUsage = AttributeUsage.DSA_OPERATION;
395              }
396              else
397              {
398                throw new LDAPException(ResultCode.DECODING_ERROR,
399                                        ERR_ATTRTYPE_DECODE_INVALID_USAGE.get(
400                                             attributeTypeString, usageStr));
401              }
402            }
403            else
404            {
405              throw new LDAPException(ResultCode.DECODING_ERROR,
406                                      ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get(
407                                           attributeTypeString, "USAGE"));
408            }
409          }
410          else if (lowerToken.startsWith("x-"))
411          {
412            pos = skipSpaces(attributeTypeString, pos, length);
413    
414            final ArrayList<String> valueList = new ArrayList<String>();
415            pos = readQDStrings(attributeTypeString, pos, length, valueList);
416    
417            final String[] values = new String[valueList.size()];
418            valueList.toArray(values);
419    
420            if (exts.containsKey(token))
421            {
422              throw new LDAPException(ResultCode.DECODING_ERROR,
423                                      ERR_ATTRTYPE_DECODE_DUP_EXT.get(
424                                           attributeTypeString, token));
425            }
426    
427            exts.put(token, values);
428          }
429          else
430          {
431            throw new LDAPException(ResultCode.DECODING_ERROR,
432                                    ERR_ATTRTYPE_DECODE_UNEXPECTED_TOKEN.get(
433                                         attributeTypeString, token));
434          }
435        }
436    
437        description           = descr;
438        equalityMatchingRule  = eqRule;
439        orderingMatchingRule  = ordRule;
440        substringMatchingRule = subRule;
441        superiorType          = supType;
442        syntaxOID             = synOID;
443    
444        names = new String[nameList.size()];
445        nameList.toArray(names);
446    
447        isObsolete           = (obsolete != null);
448        isSingleValued       = (singleValue != null);
449        isCollective         = (collective != null);
450        isNoUserModification = (noUserMod != null);
451    
452        if (attrUsage == null)
453        {
454          usage = AttributeUsage.USER_APPLICATIONS;
455        }
456        else
457        {
458          usage = attrUsage;
459        }
460    
461        extensions = Collections.unmodifiableMap(exts);
462      }
463    
464    
465    
466      /**
467       * Creates a new attribute type with the provided information.
468       *
469       * @param  oid                    The OID for this attribute type.  It must
470       *                                not be {@code null}.
471       * @param  name                   The name for this attribute type.  It may be
472       *                                {@code null} if the attribute type should
473       *                                only be referenced by OID.
474       * @param  description            The description for this attribute type.  It
475       *                                may be {@code null} if there is no
476       *                                description.
477       * @param  equalityMatchingRule   The name or OID of the equality matching
478       *                                rule for this attribute type.  It may be
479       *                                {@code null} if a default rule is to be
480       *                                inherited.
481       * @param  orderingMatchingRule   The name or OID of the ordering matching
482       *                                rule for this attribute type.  It may be
483       *                                {@code null} if a default rule is to be
484       *                                inherited.
485       * @param  substringMatchingRule  The name or OID of the substring matching
486       *                                rule for this attribute type.  It may be
487       *                                {@code null} if a default rule is to be
488       *                                inherited.
489       * @param  syntaxOID              The syntax OID for this attribute type.  It
490       *                                may be {@code null} if a default syntax is
491       *                                to be inherited.
492       * @param  isSingleValued         Indicates whether attributes of this type
493       *                                are only allowed to have a single value.
494       * @param  extensions             The set of extensions for this attribute
495       *                                type.  It may be {@code null} or empty if
496       *                                there should not be any extensions.
497       */
498      public AttributeTypeDefinition(final String oid, final String name,
499                                     final String description,
500                                     final String equalityMatchingRule,
501                                     final String orderingMatchingRule,
502                                     final String substringMatchingRule,
503                                     final String syntaxOID,
504                                     final boolean isSingleValued,
505                                     final Map<String,String[]> extensions)
506      {
507        this(oid, ((name == null) ? null : new String[] { name }), description,
508             false, null, equalityMatchingRule, orderingMatchingRule,
509             substringMatchingRule, syntaxOID, isSingleValued, false, false,
510             AttributeUsage.USER_APPLICATIONS, extensions);
511      }
512    
513    
514    
515      /**
516       * Creates a new attribute type with the provided information.
517       *
518       * @param  oid                    The OID for this attribute type.  It must
519       *                                not be {@code null}.
520       * @param  names                  The set of names for this attribute type.
521       *                                It may be {@code null} or empty if the
522       *                                attribute type should only be referenced by
523       *                                OID.
524       * @param  description            The description for this attribute type.  It
525       *                                may be {@code null} if there is no
526       *                                description.
527       * @param  isObsolete             Indicates whether this attribute type is
528       *                                declared obsolete.
529       * @param  superiorType           The name or OID of the superior attribute
530       *                                type.  It may be {@code null} if there is no
531       *                                superior type.
532       * @param  equalityMatchingRule   The name or OID of the equality matching
533       *                                rule for this attribute type.  It may be
534       *                                {@code null} if a default rule is to be
535       *                                inherited.
536       * @param  orderingMatchingRule   The name or OID of the ordering matching
537       *                                rule for this attribute type.  It may be
538       *                                {@code null} if a default rule is to be
539       *                                inherited.
540       * @param  substringMatchingRule  The name or OID of the substring matching
541       *                                rule for this attribute type.  It may be
542       *                                {@code null} if a default rule is to be
543       *                                inherited.
544       * @param  syntaxOID              The syntax OID for this attribute type.  It
545       *                                may be {@code null} if a default syntax is
546       *                                to be inherited.
547       * @param  isSingleValued         Indicates whether attributes of this type
548       *                                are only allowed to have a single value.
549       * @param  isCollective           Indicates whether this attribute type should
550       *                                be considered collective.
551       * @param  isNoUserModification   Indicates whether clients should be allowed
552       *                                to modify attributes of this type.
553       * @param  usage                  The attribute usage for this attribute type.
554       *                                It may be {@code null} if the default usage
555       *                                of userApplications is to be used.
556       * @param  extensions             The set of extensions for this attribute
557       *                                type.  It may be {@code null} or empty if
558       *                                there should not be any extensions.
559       */
560      public AttributeTypeDefinition(final String oid, final String[] names,
561                                     final String description,
562                                     final boolean isObsolete,
563                                     final String superiorType,
564                                     final String equalityMatchingRule,
565                                     final String orderingMatchingRule,
566                                     final String substringMatchingRule,
567                                     final String syntaxOID,
568                                     final boolean isSingleValued,
569                                     final boolean isCollective,
570                                     final boolean isNoUserModification,
571                                     final AttributeUsage usage,
572                                     final Map<String,String[]> extensions)
573      {
574        ensureNotNull(oid);
575    
576        this.oid                   = oid;
577        this.description           = description;
578        this.isObsolete            = isObsolete;
579        this.superiorType          = superiorType;
580        this.equalityMatchingRule  = equalityMatchingRule;
581        this.orderingMatchingRule  = orderingMatchingRule;
582        this.substringMatchingRule = substringMatchingRule;
583        this.syntaxOID             = syntaxOID;
584        this.isSingleValued        = isSingleValued;
585        this.isCollective          = isCollective;
586        this.isNoUserModification  = isNoUserModification;
587    
588        if (names == null)
589        {
590          this.names = NO_STRINGS;
591        }
592        else
593        {
594          this.names = names;
595        }
596    
597        if (usage == null)
598        {
599          this.usage = AttributeUsage.USER_APPLICATIONS;
600        }
601        else
602        {
603          this.usage = usage;
604        }
605    
606        if (extensions == null)
607        {
608          this.extensions = Collections.emptyMap();
609        }
610        else
611        {
612          this.extensions = Collections.unmodifiableMap(extensions);
613        }
614    
615        final StringBuilder buffer = new StringBuilder();
616        createDefinitionString(buffer);
617        attributeTypeString = buffer.toString();
618      }
619    
620    
621    
622      /**
623       * Constructs a string representation of this attribute type definition in the
624       * provided buffer.
625       *
626       * @param  buffer  The buffer in which to construct a string representation of
627       *                 this attribute type definition.
628       */
629      private void createDefinitionString(final StringBuilder buffer)
630      {
631        buffer.append("( ");
632        buffer.append(oid);
633    
634        if (names.length == 1)
635        {
636          buffer.append(" NAME '");
637          buffer.append(names[0]);
638          buffer.append('\'');
639        }
640        else if (names.length > 1)
641        {
642          buffer.append(" NAME (");
643          for (final String name : names)
644          {
645            buffer.append(" '");
646            buffer.append(name);
647            buffer.append('\'');
648          }
649          buffer.append(" )");
650        }
651    
652        if (description != null)
653        {
654          buffer.append(" DESC '");
655          encodeValue(description, buffer);
656          buffer.append('\'');
657        }
658    
659        if (isObsolete)
660        {
661          buffer.append(" OBSOLETE");
662        }
663    
664        if (superiorType != null)
665        {
666          buffer.append(" SUP ");
667          buffer.append(superiorType);
668        }
669    
670        if (equalityMatchingRule != null)
671        {
672          buffer.append(" EQUALITY ");
673          buffer.append(equalityMatchingRule);
674        }
675    
676        if (orderingMatchingRule != null)
677        {
678          buffer.append(" ORDERING ");
679          buffer.append(orderingMatchingRule);
680        }
681    
682        if (substringMatchingRule != null)
683        {
684          buffer.append(" SUBSTR ");
685          buffer.append(substringMatchingRule);
686        }
687    
688        if (syntaxOID != null)
689        {
690          buffer.append(" SYNTAX ");
691          buffer.append(syntaxOID);
692        }
693    
694        if (isSingleValued)
695        {
696          buffer.append(" SINGLE-VALUE");
697        }
698    
699        if (isCollective)
700        {
701          buffer.append(" COLLECTIVE");
702        }
703    
704        if (isNoUserModification)
705        {
706          buffer.append(" NO-USER-MODIFICATION");
707        }
708    
709        buffer.append(" USAGE ");
710        buffer.append(usage.getName());
711    
712        for (final Map.Entry<String,String[]> e : extensions.entrySet())
713        {
714          final String   name   = e.getKey();
715          final String[] values = e.getValue();
716          if (values.length == 1)
717          {
718            buffer.append(' ');
719            buffer.append(name);
720            buffer.append(" '");
721            encodeValue(values[0], buffer);
722            buffer.append('\'');
723          }
724          else
725          {
726            buffer.append(' ');
727            buffer.append(name);
728            buffer.append(" (");
729            for (final String value : values)
730            {
731              buffer.append(" '");
732              encodeValue(value, buffer);
733              buffer.append('\'');
734            }
735            buffer.append(" )");
736          }
737        }
738    
739        buffer.append(" )");
740      }
741    
742    
743    
744      /**
745       * Retrieves the OID for this attribute type.
746       *
747       * @return  The OID for this attribute type.
748       */
749      public String getOID()
750      {
751        return oid;
752      }
753    
754    
755    
756      /**
757       * Retrieves the set of names for this attribute type.
758       *
759       * @return  The set of names for this attribute type, or an empty array if it
760       *          does not have any names.
761       */
762      public String[] getNames()
763      {
764        return names;
765      }
766    
767    
768    
769      /**
770       * Retrieves the primary name that can be used to reference this attribute
771       * type.  If one or more names are defined, then the first name will be used.
772       * Otherwise, the OID will be returned.
773       *
774       * @return  The primary name that can be used to reference this attribute
775       *          type.
776       */
777      public String getNameOrOID()
778      {
779        if (names.length == 0)
780        {
781          return oid;
782        }
783        else
784        {
785          return names[0];
786        }
787      }
788    
789    
790    
791      /**
792       * Indicates whether the provided string matches the OID or any of the names
793       * for this attribute type.
794       *
795       * @param  s  The string for which to make the determination.  It must not be
796       *            {@code null}.
797       *
798       * @return  {@code true} if the provided string matches the OID or any of the
799       *          names for this attribute type, or {@code false} if not.
800       */
801      public boolean hasNameOrOID(final String s)
802      {
803        for (final String name : names)
804        {
805          if (s.equalsIgnoreCase(name))
806          {
807            return true;
808          }
809        }
810    
811        return s.equalsIgnoreCase(oid);
812      }
813    
814    
815    
816      /**
817       * Retrieves the description for this attribute type, if available.
818       *
819       * @return  The description for this attribute type, or {@code null} if there
820       *          is no description defined.
821       */
822      public String getDescription()
823      {
824        return description;
825      }
826    
827    
828    
829      /**
830       * Indicates whether this attribute type is declared obsolete.
831       *
832       * @return  {@code true} if this attribute type is declared obsolete, or
833       *          {@code false} if it is not.
834       */
835      public boolean isObsolete()
836      {
837        return isObsolete;
838      }
839    
840    
841    
842      /**
843       * Retrieves the name or OID of the superior type for this attribute type, if
844       * available.
845       *
846       * @return  The name or OID of the superior type for this attribute type, or
847       *          {@code null} if no superior type is defined.
848       */
849      public String getSuperiorType()
850      {
851        return superiorType;
852      }
853    
854    
855    
856      /**
857       * Retrieves the superior attribute type definition for this attribute type,
858       * if available.
859       *
860       * @param  schema  The schema to use to get the superior attribute type.
861       *
862       * @return  The superior attribute type definition for this attribute type, or
863       *          {@code null} if no superior type is defined, or if the superior
864       *          type is not included in the provided schema.
865       */
866      public AttributeTypeDefinition getSuperiorType(final Schema schema)
867      {
868        if (superiorType != null)
869        {
870          return schema.getAttributeType(superiorType);
871        }
872    
873        return null;
874      }
875    
876    
877    
878      /**
879       * Retrieves the name or OID of the equality matching rule for this attribute
880       * type, if available.
881       *
882       * @return  The name or OID of the equality matching rule for this attribute
883       *          type, or {@code null} if no equality matching rule is defined or a
884       *          default rule will be inherited.
885       */
886      public String getEqualityMatchingRule()
887      {
888        return equalityMatchingRule;
889      }
890    
891    
892    
893      /**
894       * Retrieves the name or OID of the equality matching rule for this attribute
895       * type, examining superior attribute types if necessary.
896       *
897       * @param  schema  The schema to use to get the superior attribute type.
898       *
899       * @return  The name or OID of the equality matching rule for this attribute
900       *          type, or {@code null} if no equality matching rule is defined.
901       */
902      public String getEqualityMatchingRule(final Schema schema)
903      {
904        if (equalityMatchingRule == null)
905        {
906          final AttributeTypeDefinition sup = getSuperiorType(schema);
907          if (sup != null)
908          {
909            return sup.getEqualityMatchingRule(schema);
910          }
911        }
912    
913        return equalityMatchingRule;
914      }
915    
916    
917    
918      /**
919       * Retrieves the name or OID of the ordering matching rule for this attribute
920       * type, if available.
921       *
922       * @return  The name or OID of the ordering matching rule for this attribute
923       *          type, or {@code null} if no ordering matching rule is defined or a
924       *          default rule will be inherited.
925       */
926      public String getOrderingMatchingRule()
927      {
928        return orderingMatchingRule;
929      }
930    
931    
932    
933      /**
934       * Retrieves the name or OID of the ordering matching rule for this attribute
935       * type, examining superior attribute types if necessary.
936       *
937       * @param  schema  The schema to use to get the superior attribute type.
938       *
939       * @return  The name or OID of the ordering matching rule for this attribute
940       *          type, or {@code null} if no ordering matching rule is defined.
941       */
942      public String getOrderingMatchingRule(final Schema schema)
943      {
944        if (orderingMatchingRule == null)
945        {
946          final AttributeTypeDefinition sup = getSuperiorType(schema);
947          if (sup != null)
948          {
949            return sup.getOrderingMatchingRule(schema);
950          }
951        }
952    
953        return orderingMatchingRule;
954      }
955    
956    
957    
958      /**
959       * Retrieves the name or OID of the substring matching rule for this attribute
960       * type, if available.
961       *
962       * @return  The name or OID of the substring matching rule for this attribute
963       *          type, or {@code null} if no substring matching rule is defined or
964       *          a default rule will be inherited.
965       */
966      public String getSubstringMatchingRule()
967      {
968        return substringMatchingRule;
969      }
970    
971    
972    
973      /**
974       * Retrieves the name or OID of the substring matching rule for this attribute
975       * type, examining superior attribute types if necessary.
976       *
977       * @param  schema  The schema to use to get the superior attribute type.
978       *
979       * @return  The name or OID of the substring matching rule for this attribute
980       *          type, or {@code null} if no substring matching rule is defined.
981       */
982      public String getSubstringMatchingRule(final Schema schema)
983      {
984        if (substringMatchingRule == null)
985        {
986          final AttributeTypeDefinition sup = getSuperiorType(schema);
987          if (sup != null)
988          {
989            return sup.getSubstringMatchingRule(schema);
990          }
991        }
992    
993        return substringMatchingRule;
994      }
995    
996    
997    
998      /**
999       * Retrieves the OID of the syntax for this attribute type, if available.  It
1000       * may optionally include a minimum upper bound in curly braces.
1001       *
1002       * @return  The OID of the syntax for this attribute type, or {@code null} if
1003       *          the syntax will be inherited.
1004       */
1005      public String getSyntaxOID()
1006      {
1007        return syntaxOID;
1008      }
1009    
1010    
1011    
1012      /**
1013       * Retrieves the OID of the syntax for this attribute type, examining superior
1014       * types if necessary.  It may optionally include a minimum upper bound in
1015       * curly braces.
1016       *
1017       * @param  schema  The schema to use to get the superior attribute type.
1018       *
1019       * @return  The OID of the syntax for this attribute type, or {@code null} if
1020       *          no syntax is defined.
1021       */
1022      public String getSyntaxOID(final Schema schema)
1023      {
1024        if (syntaxOID == null)
1025        {
1026          final AttributeTypeDefinition sup = getSuperiorType(schema);
1027          if (sup != null)
1028          {
1029            return sup.getSyntaxOID(schema);
1030          }
1031        }
1032    
1033        return syntaxOID;
1034      }
1035    
1036    
1037    
1038      /**
1039       * Retrieves the OID of the syntax for this attribute type, if available.  If
1040       * the attribute type definition includes a minimum upper bound in curly
1041       * braces, it will be removed from the value that is returned.
1042       *
1043       * @return  The OID of the syntax for this attribute type, or {@code null} if
1044       *          the syntax will be inherited.
1045       */
1046      public String getBaseSyntaxOID()
1047      {
1048        return getBaseSyntaxOID(syntaxOID);
1049      }
1050    
1051    
1052    
1053      /**
1054       * Retrieves the base OID of the syntax for this attribute type, examining
1055       * superior types if necessary.    If the attribute type definition includes a
1056       * minimum upper bound in curly braces, it will be removed from the value that
1057       * is returned.
1058       *
1059       * @param  schema  The schema to use to get the superior attribute type, if
1060       *                 necessary.
1061       *
1062       * @return  The OID of the syntax for this attribute type, or {@code null} if
1063       *          no syntax is defined.
1064       */
1065      public String getBaseSyntaxOID(final Schema schema)
1066      {
1067        return getBaseSyntaxOID(getSyntaxOID(schema));
1068      }
1069    
1070    
1071    
1072      /**
1073       * Retrieves the base OID of the syntax for this attribute type, examining
1074       * superior types if necessary.    If the attribute type definition includes a
1075       * minimum upper bound in curly braces, it will be removed from the value that
1076       * is returned.
1077       *
1078       * @param  syntaxOID  The syntax OID (optionally including the minimum upper
1079       *                    bound element) to examine.
1080       *
1081       * @return  The OID of the syntax for this attribute type, or {@code null} if
1082       *          no syntax is defined.
1083       */
1084      public static String getBaseSyntaxOID(final String syntaxOID)
1085      {
1086        if (syntaxOID == null)
1087        {
1088          return null;
1089        }
1090    
1091        final int curlyPos = syntaxOID.indexOf('{');
1092        if (curlyPos > 0)
1093        {
1094          return syntaxOID.substring(0, curlyPos);
1095        }
1096        else
1097        {
1098          return syntaxOID;
1099        }
1100      }
1101    
1102    
1103    
1104      /**
1105       * Retrieves the value of the minimum upper bound element of the syntax
1106       * definition for this attribute type, if defined.  If a minimum upper bound
1107       * is present (as signified by an integer value in curly braces immediately
1108       * following the syntax OID without any space between them), then it should
1109       * serve as an indication to the directory server that it should be prepared
1110       * to handle values with at least that number of (possibly multi-byte)
1111       * characters.
1112       *
1113       * @return  The value of the minimum upper bound element of the syntax
1114       *          definition for this attribute type, or -1 if no syntax is defined
1115       *          defined or if it does not have a valid minimum upper bound.
1116       */
1117      public int getSyntaxMinimumUpperBound()
1118      {
1119        return getSyntaxMinimumUpperBound(syntaxOID);
1120      }
1121    
1122    
1123    
1124      /**
1125       * Retrieves the value of the minimum upper bound element of the syntax
1126       * definition for this attribute type, if defined.  If a minimum upper bound
1127       * is present (as signified by an integer value in curly braces immediately
1128       * following the syntax OID without any space between them), then it should
1129       * serve as an indication to the directory server that it should be prepared
1130       * to handle values with at least that number of (possibly multi-byte)
1131       * characters.
1132       *
1133       * @param  schema  The schema to use to get the superior attribute type, if
1134       *                 necessary.
1135       *
1136       * @return  The value of the minimum upper bound element of the syntax
1137       *          definition for this attribute type, or -1 if no syntax is defined
1138       *          defined or if it does not have a valid minimum upper bound.
1139       */
1140      public int getSyntaxMinimumUpperBound(final Schema schema)
1141      {
1142        return getSyntaxMinimumUpperBound(getSyntaxOID(schema));
1143      }
1144    
1145    
1146    
1147      /**
1148       * Retrieves the value of the minimum upper bound element of the syntax
1149       * definition for this attribute type, if defined.  If a minimum upper bound
1150       * is present (as signified by an integer value in curly braces immediately
1151       * following the syntax OID without any space between them), then it should
1152       * serve as an indication to the directory server that it should be prepared
1153       * to handle values with at least that number of (possibly multi-byte)
1154       * characters.
1155       *
1156       * @param  syntaxOID  The syntax OID (optionally including the minimum upper
1157       *                    bound element) to examine.
1158       *
1159       * @return  The value of the minimum upper bound element of the provided
1160       *          syntax OID, or -1 if the provided syntax OID is {@code null} or
1161       *          does not have a valid minimum upper bound.
1162       */
1163      public static int getSyntaxMinimumUpperBound(final String syntaxOID)
1164      {
1165        if (syntaxOID == null)
1166        {
1167          return -1;
1168        }
1169    
1170        final int curlyPos = syntaxOID.indexOf('{');
1171        if ((curlyPos > 0) && syntaxOID.endsWith("}"))
1172        {
1173          try
1174          {
1175            return Integer.parseInt(syntaxOID.substring(curlyPos+1,
1176                 syntaxOID.length()-1));
1177          }
1178          catch (final Exception e)
1179          {
1180            debugException(e);
1181            return -1;
1182          }
1183        }
1184        else
1185        {
1186          return -1;
1187        }
1188      }
1189    
1190    
1191    
1192      /**
1193       * Indicates whether this attribute type is declared single-valued, and
1194       * therefore attributes of this type will only be allowed to have at most one
1195       * value.
1196       *
1197       * @return  {@code true} if this attribute type is declared single-valued, or
1198       *          {@code false} if not.
1199       */
1200      public boolean isSingleValued()
1201      {
1202        return isSingleValued;
1203      }
1204    
1205    
1206    
1207      /**
1208       * Indicates whether this attribute type is declared collective, and therefore
1209       * values may be dynamically generated as described in RFC 3671.
1210       *
1211       * @return  {@code true} if this attribute type is declared collective, or
1212       *          {@code false} if not.
1213       */
1214      public boolean isCollective()
1215      {
1216        return isCollective;
1217      }
1218    
1219    
1220    
1221      /**
1222       * Indicates whether this attribute type is declared no-user-modification,
1223       * and therefore attributes of this type will not be allowed to be altered
1224       * by clients.
1225       *
1226       * @return  {@code true} if this attribute type is declared
1227       *          no-user-modification, or {@code false} if not.
1228       */
1229      public boolean isNoUserModification()
1230      {
1231        return isNoUserModification;
1232      }
1233    
1234    
1235    
1236      /**
1237       * Retrieves the attribute usage for this attribute type.
1238       *
1239       * @return  The attribute usage for this attribute type.
1240       */
1241      public AttributeUsage getUsage()
1242      {
1243        return usage;
1244      }
1245    
1246    
1247    
1248      /**
1249       * Indicates whether this attribute type has an operational attribute usage.
1250       *
1251       * @return  {@code true} if this attribute type has an operational attribute
1252       *          usage, or {@code false} if not.
1253       */
1254      public boolean isOperational()
1255      {
1256        return usage.isOperational();
1257      }
1258    
1259    
1260    
1261      /**
1262       * Retrieves the set of extensions for this attribute type.  They will be
1263       * mapped from the extension name (which should start with "X-") to the set of
1264       * values for that extension.
1265       *
1266       * @return  The set of extensions for this attribute type.
1267       */
1268      public Map<String,String[]> getExtensions()
1269      {
1270        return extensions;
1271      }
1272    
1273    
1274    
1275      /**
1276       * {@inheritDoc}
1277       */
1278      @Override()
1279      public int hashCode()
1280      {
1281        return oid.hashCode();
1282      }
1283    
1284    
1285    
1286      /**
1287       * {@inheritDoc}
1288       */
1289      @Override()
1290      public boolean equals(final Object o)
1291      {
1292        if (o == null)
1293        {
1294          return false;
1295        }
1296    
1297        if (o == this)
1298        {
1299          return true;
1300        }
1301    
1302        if (! (o instanceof AttributeTypeDefinition))
1303        {
1304          return false;
1305        }
1306    
1307        final AttributeTypeDefinition d = (AttributeTypeDefinition) o;
1308        return(oid.equals(d.oid) &&
1309             stringsEqualIgnoreCaseOrderIndependent(names, d.names) &&
1310             bothNullOrEqual(usage, d.usage) &&
1311             bothNullOrEqualIgnoreCase(description, d.description) &&
1312             bothNullOrEqualIgnoreCase(equalityMatchingRule,
1313                  d.equalityMatchingRule) &&
1314             bothNullOrEqualIgnoreCase(orderingMatchingRule,
1315                  d.orderingMatchingRule) &&
1316             bothNullOrEqualIgnoreCase(substringMatchingRule,
1317                  d.substringMatchingRule) &&
1318             bothNullOrEqualIgnoreCase(superiorType, d.superiorType) &&
1319             bothNullOrEqualIgnoreCase(syntaxOID, d.syntaxOID) &&
1320             (isCollective == d.isCollective) &&
1321             (isNoUserModification == d.isNoUserModification) &&
1322             (isObsolete == d.isObsolete) &&
1323             (isSingleValued == d.isSingleValued) &&
1324             extensionsEqual(extensions, d.extensions));
1325      }
1326    
1327    
1328    
1329      /**
1330       * Retrieves a string representation of this attribute type definition, in the
1331       * format described in RFC 4512 section 4.1.2.
1332       *
1333       * @return  A string representation of this attribute type definition.
1334       */
1335      @Override()
1336      public String toString()
1337      {
1338        return attributeTypeString;
1339      }
1340    }