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