001 /*
002 * Copyright 2007-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2014 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 names The set of names for this attribute type.
467 * It may be {@code null} or empty if the
468 * attribute type should only be referenced by
469 * OID.
470 * @param description The description for this attribute type. It
471 * may be {@code null} if there is no
472 * description.
473 * @param isObsolete Indicates whether this attribute type is
474 * declared obsolete.
475 * @param superiorType The name or OID of the superior attribute
476 * type. It may be {@code null} if there is no
477 * superior type.
478 * @param equalityMatchingRule The name or OID of the equality matching
479 * rule for this attribute type. It may be
480 * {@code null} if a default rule is to be
481 * inherited.
482 * @param orderingMatchingRule The name or OID of the ordering matching
483 * rule for this attribute type. It may be
484 * {@code null} if a default rule is to be
485 * inherited.
486 * @param substringMatchingRule The name or OID of the substring matching
487 * rule for this attribute type. It may be
488 * {@code null} if a default rule is to be
489 * inherited.
490 * @param syntaxOID The syntax OID for this attribute type. It
491 * may be {@code null} if a default syntax is
492 * to be inherited.
493 * @param isSingleValued Indicates whether attributes of this type
494 * are only allowed to have a single value.
495 * @param isCollective Indicates whether this attribute type should
496 * be considered collective.
497 * @param isNoUserModification Indicates whether clients should be allowed
498 * to modify attributes of this type.
499 * @param usage The attribute usage for this attribute type.
500 * It may be {@code null} if the default usage
501 * of userApplications is to be used.
502 * @param extensions The set of extensions for this attribute
503 * type. It may be {@code null} or empty if
504 * there should not be any extensions.
505 */
506 public AttributeTypeDefinition(final String oid, final String[] names,
507 final String description,
508 final boolean isObsolete,
509 final String superiorType,
510 final String equalityMatchingRule,
511 final String orderingMatchingRule,
512 final String substringMatchingRule,
513 final String syntaxOID,
514 final boolean isSingleValued,
515 final boolean isCollective,
516 final boolean isNoUserModification,
517 final AttributeUsage usage,
518 final Map<String,String[]> extensions)
519 {
520 ensureNotNull(oid);
521
522 this.oid = oid;
523 this.description = description;
524 this.isObsolete = isObsolete;
525 this.superiorType = superiorType;
526 this.equalityMatchingRule = equalityMatchingRule;
527 this.orderingMatchingRule = orderingMatchingRule;
528 this.substringMatchingRule = substringMatchingRule;
529 this.syntaxOID = syntaxOID;
530 this.isSingleValued = isSingleValued;
531 this.isCollective = isCollective;
532 this.isNoUserModification = isNoUserModification;
533
534 if (names == null)
535 {
536 this.names = NO_STRINGS;
537 }
538 else
539 {
540 this.names = names;
541 }
542
543 if (usage == null)
544 {
545 this.usage = AttributeUsage.USER_APPLICATIONS;
546 }
547 else
548 {
549 this.usage = usage;
550 }
551
552 if (extensions == null)
553 {
554 this.extensions = Collections.emptyMap();
555 }
556 else
557 {
558 this.extensions = Collections.unmodifiableMap(extensions);
559 }
560
561 final StringBuilder buffer = new StringBuilder();
562 createDefinitionString(buffer);
563 attributeTypeString = buffer.toString();
564 }
565
566
567
568 /**
569 * Constructs a string representation of this attribute type definition in the
570 * provided buffer.
571 *
572 * @param buffer The buffer in which to construct a string representation of
573 * this attribute type definition.
574 */
575 private void createDefinitionString(final StringBuilder buffer)
576 {
577 buffer.append("( ");
578 buffer.append(oid);
579
580 if (names.length == 1)
581 {
582 buffer.append(" NAME '");
583 buffer.append(names[0]);
584 buffer.append('\'');
585 }
586 else if (names.length > 1)
587 {
588 buffer.append(" NAME (");
589 for (final String name : names)
590 {
591 buffer.append(" '");
592 buffer.append(name);
593 buffer.append('\'');
594 }
595 buffer.append(" )");
596 }
597
598 if (description != null)
599 {
600 buffer.append(" DESC '");
601 encodeValue(description, buffer);
602 buffer.append('\'');
603 }
604
605 if (isObsolete)
606 {
607 buffer.append(" OBSOLETE");
608 }
609
610 if (superiorType != null)
611 {
612 buffer.append(" SUP ");
613 buffer.append(superiorType);
614 }
615
616 if (equalityMatchingRule != null)
617 {
618 buffer.append(" EQUALITY ");
619 buffer.append(equalityMatchingRule);
620 }
621
622 if (orderingMatchingRule != null)
623 {
624 buffer.append(" ORDERING ");
625 buffer.append(orderingMatchingRule);
626 }
627
628 if (substringMatchingRule != null)
629 {
630 buffer.append(" SUBSTR ");
631 buffer.append(substringMatchingRule);
632 }
633
634 if (syntaxOID != null)
635 {
636 buffer.append(" SYNTAX ");
637 buffer.append(syntaxOID);
638 }
639
640 if (isSingleValued)
641 {
642 buffer.append(" SINGLE-VALUE");
643 }
644
645 if (isCollective)
646 {
647 buffer.append(" COLLECTIVE");
648 }
649
650 if (isNoUserModification)
651 {
652 buffer.append(" NO-USER-MODIFICATION");
653 }
654
655 buffer.append(" USAGE ");
656 buffer.append(usage.getName());
657
658 for (final Map.Entry<String,String[]> e : extensions.entrySet())
659 {
660 final String name = e.getKey();
661 final String[] values = e.getValue();
662 if (values.length == 1)
663 {
664 buffer.append(' ');
665 buffer.append(name);
666 buffer.append(" '");
667 encodeValue(values[0], buffer);
668 buffer.append('\'');
669 }
670 else
671 {
672 buffer.append(' ');
673 buffer.append(name);
674 buffer.append(" (");
675 for (final String value : values)
676 {
677 buffer.append(" '");
678 encodeValue(value, buffer);
679 buffer.append('\'');
680 }
681 buffer.append(" )");
682 }
683 }
684
685 buffer.append(" )");
686 }
687
688
689
690 /**
691 * Retrieves the OID for this attribute type.
692 *
693 * @return The OID for this attribute type.
694 */
695 public String getOID()
696 {
697 return oid;
698 }
699
700
701
702 /**
703 * Retrieves the set of names for this attribute type.
704 *
705 * @return The set of names for this attribute type, or an empty array if it
706 * does not have any names.
707 */
708 public String[] getNames()
709 {
710 return names;
711 }
712
713
714
715 /**
716 * Retrieves the primary name that can be used to reference this attribute
717 * type. If one or more names are defined, then the first name will be used.
718 * Otherwise, the OID will be returned.
719 *
720 * @return The primary name that can be used to reference this attribute
721 * type.
722 */
723 public String getNameOrOID()
724 {
725 if (names.length == 0)
726 {
727 return oid;
728 }
729 else
730 {
731 return names[0];
732 }
733 }
734
735
736
737 /**
738 * Indicates whether the provided string matches the OID or any of the names
739 * for this attribute type.
740 *
741 * @param s The string for which to make the determination. It must not be
742 * {@code null}.
743 *
744 * @return {@code true} if the provided string matches the OID or any of the
745 * names for this attribute type, or {@code false} if not.
746 */
747 public boolean hasNameOrOID(final String s)
748 {
749 for (final String name : names)
750 {
751 if (s.equalsIgnoreCase(name))
752 {
753 return true;
754 }
755 }
756
757 return s.equalsIgnoreCase(oid);
758 }
759
760
761
762 /**
763 * Retrieves the description for this attribute type, if available.
764 *
765 * @return The description for this attribute type, or {@code null} if there
766 * is no description defined.
767 */
768 public String getDescription()
769 {
770 return description;
771 }
772
773
774
775 /**
776 * Indicates whether this attribute type is declared obsolete.
777 *
778 * @return {@code true} if this attribute type is declared obsolete, or
779 * {@code false} if it is not.
780 */
781 public boolean isObsolete()
782 {
783 return isObsolete;
784 }
785
786
787
788 /**
789 * Retrieves the name or OID of the superior type for this attribute type, if
790 * available.
791 *
792 * @return The name or OID of the superior type for this attribute type, or
793 * {@code null} if no superior type is defined.
794 */
795 public String getSuperiorType()
796 {
797 return superiorType;
798 }
799
800
801
802 /**
803 * Retrieves the superior attribute type definition for this attribute type,
804 * if available.
805 *
806 * @param schema The schema to use to get the superior attribute type.
807 *
808 * @return The superior attribute type definition for this attribute type, or
809 * {@code null} if no superior type is defined, or if the superior
810 * type is not included in the provided schema.
811 */
812 public AttributeTypeDefinition getSuperiorType(final Schema schema)
813 {
814 if (superiorType != null)
815 {
816 return schema.getAttributeType(superiorType);
817 }
818
819 return null;
820 }
821
822
823
824 /**
825 * Retrieves the name or OID of the equality matching rule for this attribute
826 * type, if available.
827 *
828 * @return The name or OID of the equality matching rule for this attribute
829 * type, or {@code null} if no equality matching rule is defined or a
830 * default rule will be inherited.
831 */
832 public String getEqualityMatchingRule()
833 {
834 return equalityMatchingRule;
835 }
836
837
838
839 /**
840 * Retrieves the name or OID of the equality matching rule for this attribute
841 * type, examining superior attribute types if necessary.
842 *
843 * @param schema The schema to use to get the superior attribute type.
844 *
845 * @return The name or OID of the equality matching rule for this attribute
846 * type, or {@code null} if no equality matching rule is defined.
847 */
848 public String getEqualityMatchingRule(final Schema schema)
849 {
850 if (equalityMatchingRule == null)
851 {
852 final AttributeTypeDefinition sup = getSuperiorType(schema);
853 if (sup != null)
854 {
855 return sup.getEqualityMatchingRule(schema);
856 }
857 }
858
859 return equalityMatchingRule;
860 }
861
862
863
864 /**
865 * Retrieves the name or OID of the ordering matching rule for this attribute
866 * type, if available.
867 *
868 * @return The name or OID of the ordering matching rule for this attribute
869 * type, or {@code null} if no ordering matching rule is defined or a
870 * default rule will be inherited.
871 */
872 public String getOrderingMatchingRule()
873 {
874 return orderingMatchingRule;
875 }
876
877
878
879 /**
880 * Retrieves the name or OID of the ordering matching rule for this attribute
881 * type, examining superior attribute types if necessary.
882 *
883 * @param schema The schema to use to get the superior attribute type.
884 *
885 * @return The name or OID of the ordering matching rule for this attribute
886 * type, or {@code null} if no ordering matching rule is defined.
887 */
888 public String getOrderingMatchingRule(final Schema schema)
889 {
890 if (orderingMatchingRule == null)
891 {
892 final AttributeTypeDefinition sup = getSuperiorType(schema);
893 if (sup != null)
894 {
895 return sup.getOrderingMatchingRule(schema);
896 }
897 }
898
899 return orderingMatchingRule;
900 }
901
902
903
904 /**
905 * Retrieves the name or OID of the substring matching rule for this attribute
906 * type, if available.
907 *
908 * @return The name or OID of the substring matching rule for this attribute
909 * type, or {@code null} if no substring matching rule is defined or
910 * a default rule will be inherited.
911 */
912 public String getSubstringMatchingRule()
913 {
914 return substringMatchingRule;
915 }
916
917
918
919 /**
920 * Retrieves the name or OID of the substring matching rule for this attribute
921 * type, examining superior attribute types if necessary.
922 *
923 * @param schema The schema to use to get the superior attribute type.
924 *
925 * @return The name or OID of the substring matching rule for this attribute
926 * type, or {@code null} if no substring matching rule is defined.
927 */
928 public String getSubstringMatchingRule(final Schema schema)
929 {
930 if (substringMatchingRule == null)
931 {
932 final AttributeTypeDefinition sup = getSuperiorType(schema);
933 if (sup != null)
934 {
935 return sup.getSubstringMatchingRule(schema);
936 }
937 }
938
939 return substringMatchingRule;
940 }
941
942
943
944 /**
945 * Retrieves the OID of the syntax for this attribute type, if available. It
946 * may optionally include a minimum upper bound in curly braces.
947 *
948 * @return The OID of the syntax for this attribute type, or {@code null} if
949 * the syntax will be inherited.
950 */
951 public String getSyntaxOID()
952 {
953 return syntaxOID;
954 }
955
956
957
958 /**
959 * Retrieves the OID of the syntax for this attribute type, examining superior
960 * types if necessary. It may optionally include a minimum upper bound in
961 * curly braces.
962 *
963 * @param schema The schema to use to get the superior attribute type.
964 *
965 * @return The OID of the syntax for this attribute type, or {@code null} if
966 * no syntax is defined.
967 */
968 public String getSyntaxOID(final Schema schema)
969 {
970 if (syntaxOID == null)
971 {
972 final AttributeTypeDefinition sup = getSuperiorType(schema);
973 if (sup != null)
974 {
975 return sup.getSyntaxOID(schema);
976 }
977 }
978
979 return syntaxOID;
980 }
981
982
983
984 /**
985 * Retrieves the OID of the syntax for this attribute type, if available. If
986 * the attribute type definition includes a minimum upper bound in curly
987 * braces, it will be removed from the value that is returned.
988 *
989 * @return The OID of the syntax for this attribute type, or {@code null} if
990 * the syntax will be inherited.
991 */
992 public String getBaseSyntaxOID()
993 {
994 return getBaseSyntaxOID(syntaxOID);
995 }
996
997
998
999 /**
1000 * Retrieves the base OID of the syntax for this attribute type, examining
1001 * superior types if necessary. If the attribute type definition includes a
1002 * minimum upper bound in curly braces, it will be removed from the value that
1003 * is returned.
1004 *
1005 * @param schema The schema to use to get the superior attribute type, if
1006 * necessary.
1007 *
1008 * @return The OID of the syntax for this attribute type, or {@code null} if
1009 * no syntax is defined.
1010 */
1011 public String getBaseSyntaxOID(final Schema schema)
1012 {
1013 return getBaseSyntaxOID(getSyntaxOID(schema));
1014 }
1015
1016
1017
1018 /**
1019 * Retrieves the base OID of the syntax for this attribute type, examining
1020 * superior types if necessary. If the attribute type definition includes a
1021 * minimum upper bound in curly braces, it will be removed from the value that
1022 * is returned.
1023 *
1024 * @param syntaxOID The syntax OID (optionally including the minimum upper
1025 * bound element) to examine.
1026 *
1027 * @return The OID of the syntax for this attribute type, or {@code null} if
1028 * no syntax is defined.
1029 */
1030 public static String getBaseSyntaxOID(final String syntaxOID)
1031 {
1032 if (syntaxOID == null)
1033 {
1034 return null;
1035 }
1036
1037 final int curlyPos = syntaxOID.indexOf('{');
1038 if (curlyPos > 0)
1039 {
1040 return syntaxOID.substring(0, curlyPos);
1041 }
1042 else
1043 {
1044 return syntaxOID;
1045 }
1046 }
1047
1048
1049
1050 /**
1051 * Retrieves the value of the minimum upper bound element of the syntax
1052 * definition for this attribute type, if defined. If a minimum upper bound
1053 * is present (as signified by an integer value in curly braces immediately
1054 * following the syntax OID without any space between them), then it should
1055 * serve as an indication to the directory server that it should be prepared
1056 * to handle values with at least that number of (possibly multi-byte)
1057 * characters.
1058 *
1059 * @return The value of the minimum upper bound element of the syntax
1060 * definition for this attribute type, or -1 if no syntax is defined
1061 * defined or if it does not have a valid minimum upper bound.
1062 */
1063 public int getSyntaxMinimumUpperBound()
1064 {
1065 return getSyntaxMinimumUpperBound(syntaxOID);
1066 }
1067
1068
1069
1070 /**
1071 * Retrieves the value of the minimum upper bound element of the syntax
1072 * definition for this attribute type, if defined. If a minimum upper bound
1073 * is present (as signified by an integer value in curly braces immediately
1074 * following the syntax OID without any space between them), then it should
1075 * serve as an indication to the directory server that it should be prepared
1076 * to handle values with at least that number of (possibly multi-byte)
1077 * characters.
1078 *
1079 * @param schema The schema to use to get the superior attribute type, if
1080 * necessary.
1081 *
1082 * @return The value of the minimum upper bound element of the syntax
1083 * definition for this attribute type, or -1 if no syntax is defined
1084 * defined or if it does not have a valid minimum upper bound.
1085 */
1086 public int getSyntaxMinimumUpperBound(final Schema schema)
1087 {
1088 return getSyntaxMinimumUpperBound(getSyntaxOID(schema));
1089 }
1090
1091
1092
1093 /**
1094 * Retrieves the value of the minimum upper bound element of the syntax
1095 * definition for this attribute type, if defined. If a minimum upper bound
1096 * is present (as signified by an integer value in curly braces immediately
1097 * following the syntax OID without any space between them), then it should
1098 * serve as an indication to the directory server that it should be prepared
1099 * to handle values with at least that number of (possibly multi-byte)
1100 * characters.
1101 *
1102 * @param syntaxOID The syntax OID (optionally including the minimum upper
1103 * bound element) to examine.
1104 *
1105 * @return The value of the minimum upper bound element of the provided
1106 * syntax OID, or -1 if the provided syntax OID is {@code null} or
1107 * does not have a valid minimum upper bound.
1108 */
1109 public static int getSyntaxMinimumUpperBound(final String syntaxOID)
1110 {
1111 if (syntaxOID == null)
1112 {
1113 return -1;
1114 }
1115
1116 final int curlyPos = syntaxOID.indexOf('{');
1117 if ((curlyPos > 0) && syntaxOID.endsWith("}"))
1118 {
1119 try
1120 {
1121 return Integer.parseInt(syntaxOID.substring(curlyPos+1,
1122 syntaxOID.length()-1));
1123 }
1124 catch (final Exception e)
1125 {
1126 debugException(e);
1127 return -1;
1128 }
1129 }
1130 else
1131 {
1132 return -1;
1133 }
1134 }
1135
1136
1137
1138 /**
1139 * Indicates whether this attribute type is declared single-valued, and
1140 * therefore attributes of this type will only be allowed to have at most one
1141 * value.
1142 *
1143 * @return {@code true} if this attribute type is declared single-valued, or
1144 * {@code false} if not.
1145 */
1146 public boolean isSingleValued()
1147 {
1148 return isSingleValued;
1149 }
1150
1151
1152
1153 /**
1154 * Indicates whether this attribute type is declared collective, and therefore
1155 * values may be dynamically generated as described in RFC 3671.
1156 *
1157 * @return {@code true} if this attribute type is declared collective, or
1158 * {@code false} if not.
1159 */
1160 public boolean isCollective()
1161 {
1162 return isCollective;
1163 }
1164
1165
1166
1167 /**
1168 * Indicates whether this attribute type is declared no-user-modification,
1169 * and therefore attributes of this type will not be allowed to be altered
1170 * by clients.
1171 *
1172 * @return {@code true} if this attribute type is declared
1173 * no-user-modification, or {@code false} if not.
1174 */
1175 public boolean isNoUserModification()
1176 {
1177 return isNoUserModification;
1178 }
1179
1180
1181
1182 /**
1183 * Retrieves the attribute usage for this attribute type.
1184 *
1185 * @return The attribute usage for this attribute type.
1186 */
1187 public AttributeUsage getUsage()
1188 {
1189 return usage;
1190 }
1191
1192
1193
1194 /**
1195 * Indicates whether this attribute type has an operational attribute usage.
1196 *
1197 * @return {@code true} if this attribute type has an operational attribute
1198 * usage, or {@code false} if not.
1199 */
1200 public boolean isOperational()
1201 {
1202 return usage.isOperational();
1203 }
1204
1205
1206
1207 /**
1208 * Retrieves the set of extensions for this attribute type. They will be
1209 * mapped from the extension name (which should start with "X-") to the set of
1210 * values for that extension.
1211 *
1212 * @return The set of extensions for this attribute type.
1213 */
1214 public Map<String,String[]> getExtensions()
1215 {
1216 return extensions;
1217 }
1218
1219
1220
1221 /**
1222 * {@inheritDoc}
1223 */
1224 @Override()
1225 public int hashCode()
1226 {
1227 return oid.hashCode();
1228 }
1229
1230
1231
1232 /**
1233 * {@inheritDoc}
1234 */
1235 @Override()
1236 public boolean equals(final Object o)
1237 {
1238 if (o == null)
1239 {
1240 return false;
1241 }
1242
1243 if (o == this)
1244 {
1245 return true;
1246 }
1247
1248 if (! (o instanceof AttributeTypeDefinition))
1249 {
1250 return false;
1251 }
1252
1253 final AttributeTypeDefinition d = (AttributeTypeDefinition) o;
1254 return(oid.equals(d.oid) &&
1255 stringsEqualIgnoreCaseOrderIndependent(names, d.names) &&
1256 bothNullOrEqual(usage, d.usage) &&
1257 bothNullOrEqualIgnoreCase(description, d.description) &&
1258 bothNullOrEqualIgnoreCase(equalityMatchingRule,
1259 d.equalityMatchingRule) &&
1260 bothNullOrEqualIgnoreCase(orderingMatchingRule,
1261 d.orderingMatchingRule) &&
1262 bothNullOrEqualIgnoreCase(substringMatchingRule,
1263 d.substringMatchingRule) &&
1264 bothNullOrEqualIgnoreCase(superiorType, d.superiorType) &&
1265 bothNullOrEqualIgnoreCase(syntaxOID, d.syntaxOID) &&
1266 (isCollective == d.isCollective) &&
1267 (isNoUserModification == d.isNoUserModification) &&
1268 (isObsolete == d.isObsolete) &&
1269 (isSingleValued == d.isSingleValued) &&
1270 extensionsEqual(extensions, d.extensions));
1271 }
1272
1273
1274
1275 /**
1276 * Retrieves a string representation of this attribute type definition, in the
1277 * format described in RFC 4512 section 4.1.2.
1278 *
1279 * @return A string representation of this attribute type definition.
1280 */
1281 @Override()
1282 public String toString()
1283 {
1284 return attributeTypeString;
1285 }
1286 }