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