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