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 }