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