001/*
002 * Copyright 2007-2017 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2017 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 */
021package com.unboundid.ldap.sdk.schema;
022
023
024
025import java.util.ArrayList;
026import java.util.Collections;
027import java.util.Map;
028import java.util.LinkedHashMap;
029
030import com.unboundid.ldap.sdk.LDAPException;
031import com.unboundid.ldap.sdk.ResultCode;
032import com.unboundid.util.NotMutable;
033import com.unboundid.util.ThreadSafety;
034import com.unboundid.util.ThreadSafetyLevel;
035
036import static com.unboundid.ldap.sdk.schema.SchemaMessages.*;
037import static com.unboundid.util.Debug.*;
038import static com.unboundid.util.StaticUtils.*;
039import 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)
049public 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}