001 /*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License"). You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2006-2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.types;
028
029
030
031 import java.util.Iterator;
032 import java.util.LinkedHashMap;
033 import java.util.LinkedHashSet;
034 import java.util.LinkedList;
035 import java.util.List;
036 import java.util.Map;
037 import java.util.Set;
038
039 import org.opends.server.schema.DITContentRuleSyntax;
040
041 import static org.opends.server.loggers.debug.DebugLogger.*;
042 import org.opends.server.loggers.debug.DebugTracer;
043 import static org.opends.server.util.ServerConstants.*;
044 import static org.opends.server.util.Validator.*;
045
046
047
048 /**
049 * This class defines a DIT content rule, which defines the set of
050 * allowed, required, and prohibited attributes for entries with a
051 * given structural objectclass, and also indicates which auxiliary
052 * classes that may be included in the entry.
053 */
054 @org.opends.server.types.PublicAPI(
055 stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
056 mayInstantiate=false,
057 mayExtend=false,
058 mayInvoke=true)
059 public final class DITContentRule
060 implements SchemaFileElement
061 {
062 /**
063 * The tracer object for the debug logger.
064 */
065 private static final DebugTracer TRACER = getTracer();
066
067 // Indicates whether this content rule is declared "obsolete".
068 private final boolean isObsolete;
069
070 // The set of additional name-value pairs associated with this
071 // content rule definition.
072 private final Map<String,List<String>> extraProperties;
073
074 // The set of names for this DIT content rule, in a mapping between
075 // the all-lowercase form and the user-defined form.
076 private final Map<String,String> names;
077
078 // The structural objectclass for this DIT content rule.
079 private final ObjectClass structuralClass;
080
081 // The set of auxiliary objectclasses that entries with this content
082 // rule may contain, in a mapping between the objectclass and the
083 // user-defined name for that class.
084 private final Set<ObjectClass> auxiliaryClasses;
085
086 // The set of optional attribute types for this DIT content rule.
087 private final Set<AttributeType> optionalAttributes;
088
089 // The set of prohibited attribute types for this DIT content rule.
090 private final Set<AttributeType> prohibitedAttributes;
091
092 // The set of required attribute types for this DIT content rule.
093 private final Set<AttributeType> requiredAttributes;
094
095 // The definition string used to create this DIT content rule.
096 private final String definition;
097
098 // The description for this DIT content rule.
099 private final String description;
100
101
102
103 /**
104 * Creates a new DIT content rule definition with the provided
105 * information.
106 *
107 * @param definition The definition string used to
108 * create this DIT content rule. It
109 * must not be {@code null}.
110 * @param structuralClass The structural objectclass for this
111 * DIT content rule. It must not be
112 * {@code null}.
113 * @param names The set of names that may be used
114 * to reference this DIT content rule.
115 * @param description The description for this DIT
116 * content rule.
117 * @param auxiliaryClasses The set of auxiliary classes for
118 * this DIT content rule
119 * @param requiredAttributes The set of required attribute types
120 * for this DIT content rule.
121 * @param optionalAttributes The set of optional attribute types
122 * for this DIT content rule.
123 * @param prohibitedAttributes The set of prohibited attribute
124 * types for this DIT content rule.
125 * @param isObsolete Indicates whether this DIT content
126 * rule is declared "obsolete".
127 * @param extraProperties A set of extra properties for this
128 * DIT content rule.
129 */
130 public DITContentRule(String definition,
131 ObjectClass structuralClass,
132 Map<String,String> names, String description,
133 Set<ObjectClass> auxiliaryClasses,
134 Set<AttributeType> requiredAttributes,
135 Set<AttributeType> optionalAttributes,
136 Set<AttributeType> prohibitedAttributes,
137 boolean isObsolete,
138 Map<String,List<String>> extraProperties)
139 {
140 ensureNotNull(definition, structuralClass);
141
142 this.structuralClass = structuralClass;
143 this.description = description;
144 this.isObsolete = isObsolete;
145
146 int schemaFilePos = definition.indexOf(SCHEMA_PROPERTY_FILENAME);
147 if (schemaFilePos > 0)
148 {
149 String defStr;
150 try
151 {
152 int firstQuotePos = definition.indexOf('\'', schemaFilePos);
153 int secondQuotePos = definition.indexOf('\'',
154 firstQuotePos+1);
155
156 defStr = definition.substring(0, schemaFilePos).trim() + " " +
157 definition.substring(secondQuotePos+1).trim();
158 }
159 catch (Exception e)
160 {
161 if (debugEnabled())
162 {
163 TRACER.debugCaught(DebugLogLevel.ERROR, e);
164 }
165
166 defStr = definition;
167 }
168
169 this.definition = defStr;
170 }
171 else
172 {
173 this.definition = definition;
174 }
175
176 if ((names == null) || names.isEmpty())
177 {
178 this.names = new LinkedHashMap<String,String>(0);
179 }
180 else
181 {
182 this.names = new LinkedHashMap<String,String>(names);
183 }
184
185 if ((auxiliaryClasses == null) || auxiliaryClasses.isEmpty())
186 {
187 this.auxiliaryClasses = new LinkedHashSet<ObjectClass>(0);
188 }
189 else
190 {
191 this.auxiliaryClasses =
192 new LinkedHashSet<ObjectClass>(auxiliaryClasses);
193 }
194
195 if ((requiredAttributes == null) || requiredAttributes.isEmpty())
196 {
197 this.requiredAttributes = new LinkedHashSet<AttributeType>(0);
198 }
199 else
200 {
201 this.requiredAttributes =
202 new LinkedHashSet<AttributeType>(requiredAttributes);
203 }
204
205 if ((optionalAttributes == null) || optionalAttributes.isEmpty())
206 {
207 this.optionalAttributes = new LinkedHashSet<AttributeType>(0);
208 }
209 else
210 {
211 this.optionalAttributes =
212 new LinkedHashSet<AttributeType>(optionalAttributes);
213 }
214
215 if ((prohibitedAttributes == null) ||
216 prohibitedAttributes.isEmpty())
217 {
218 this.prohibitedAttributes = new LinkedHashSet<AttributeType>(0);
219 }
220 else
221 {
222 this.prohibitedAttributes =
223 new LinkedHashSet<AttributeType>(prohibitedAttributes);
224 }
225
226 if ((extraProperties == null) || extraProperties.isEmpty())
227 {
228 this.extraProperties =
229 new LinkedHashMap<String,List<String>>(0);
230 }
231 else
232 {
233 this.extraProperties =
234 new LinkedHashMap<String,List<String>>(extraProperties);
235 }
236 }
237
238
239
240 /**
241 * Retrieves the definition string used to create this DIT content
242 * rule.
243 *
244 * @return The definition string used to create this DIT content
245 * rule.
246 */
247 public String getDefinition()
248 {
249 return definition;
250 }
251
252
253
254 /**
255 * Creates a new instance of this DIT content rule based on the
256 * definition string. It will also preserve other state information
257 * associated with this DIT content rule that is not included in the
258 * definition string (e.g., the name of the schema file with which
259 * it is associated).
260 *
261 * @return The new instance of this DIT content rule based on the
262 * definition string.
263 *
264 * @throws DirectoryException If a problem occurs while attempting
265 * to create a new DIT content rule
266 * instance from the definition string.
267 */
268 public DITContentRule recreateFromDefinition()
269 throws DirectoryException
270 {
271 ByteString value = ByteStringFactory.create(definition);
272 Schema schema = DirectoryConfig.getSchema();
273
274 DITContentRule dcr =
275 DITContentRuleSyntax.decodeDITContentRule(value, schema,
276 false);
277 dcr.setSchemaFile(getSchemaFile());
278
279 return dcr;
280 }
281
282
283
284 /**
285 * Retrieves the structural objectclass for this DIT content rule.
286 *
287 * @return The structural objectclass for this DIT content rule.
288 */
289 public ObjectClass getStructuralClass()
290 {
291 return structuralClass;
292 }
293
294
295
296 /**
297 * Retrieves the set of names that may be used to reference this DIT
298 * content rule. The returned object will be a mapping between each
299 * name in all lowercase characters and that name in a user-defined
300 * form (which may include mixed capitalization).
301 *
302 * @return The set of names that may be used to reference this DIT
303 * content rule.
304 */
305 public Map<String,String> getNames()
306 {
307 return names;
308 }
309
310
311
312 /**
313 * Retrieves the primary name to use to reference this DIT content
314 * rule.
315 *
316 * @return The primary name to use to reference this DIT content
317 * rule, or {@code null} if there is none.
318 */
319 public String getName()
320 {
321 if (names.isEmpty())
322 {
323 return null;
324 }
325 else
326 {
327 return names.values().iterator().next();
328 }
329 }
330
331
332
333 /**
334 * Indicates whether the provided lowercase name may be used to
335 * reference this DIT content rule.
336 *
337 * @param lowerName The name for which to make the determination,
338 * in all lowercase characters.
339 *
340 * @return {@code true} if the provided lowercase name may be used
341 * to reference this DIT content rule, or {@code false} if
342 * not.
343 */
344 public boolean hasName(String lowerName)
345 {
346 return names.containsKey(lowerName);
347 }
348
349
350
351 /**
352 * Retrieves the name of the schema file that contains the
353 * definition for this DIT content rule.
354 *
355 * @return The name of the schema file that contains the definition
356 * for this DIT content rule, or {@code null} if it is not
357 * known or if it is not stored in any schema file.
358 */
359 public String getSchemaFile()
360 {
361 List<String> values =
362 extraProperties.get(SCHEMA_PROPERTY_FILENAME);
363 if ((values == null) || values.isEmpty())
364 {
365 return null;
366 }
367
368 return values.get(0);
369 }
370
371
372
373 /**
374 * Specifies the name of the schema file that contains the
375 * definition for this DIT content rule.
376 *
377 * @param schemaFile The name of the schema file that contains the
378 * definition for this DIT content rule.
379 */
380 public void setSchemaFile(String schemaFile)
381 {
382 setExtraProperty(SCHEMA_PROPERTY_FILENAME, schemaFile);
383 }
384
385
386
387 /**
388 * Retrieves the description for this DIT content rule.
389 *
390 * @return The description for this DIT content rule, or
391 * {@code null} if there is none.
392 */
393 public String getDescription()
394 {
395 return description;
396 }
397
398
399
400 /**
401 * Retrieves the set of auxiliary objectclasses that may be used for
402 * entries associated with this DIT content rule.
403 *
404 * @return The set of auxiliary objectclasses that may be used for
405 * entries associated with this DIT content rule.
406 */
407 public Set<ObjectClass> getAuxiliaryClasses()
408 {
409 return auxiliaryClasses;
410 }
411
412
413
414 /**
415 * Indicates whether the provided auxiliary objectclass is allowed
416 * for use by this DIT content rule.
417 *
418 * @param auxiliaryClass The auxiliary objectclass for which to
419 * make the determination.
420 *
421 * @return {@code true} if the provided auxiliary objectclass is
422 * allowed for use by this DIT content rule, or
423 * {@code false} if not.
424 */
425 public boolean isAllowedAuxiliaryClass(ObjectClass auxiliaryClass)
426 {
427 return auxiliaryClasses.contains(auxiliaryClass);
428 }
429
430
431
432 /**
433 * Retrieves the set of required attributes for this DIT content
434 * rule.
435 *
436 * @return The set of required attributes for this DIT content
437 * rule.
438 */
439 public Set<AttributeType> getRequiredAttributes()
440 {
441 return requiredAttributes;
442 }
443
444
445
446 /**
447 * Indicates whether the provided attribute type is included in the
448 * required attribute list for this DIT content rule.
449 *
450 * @param attributeType The attribute type for which to make the
451 * determination.
452 *
453 * @return {@code true} if the provided attribute type is required
454 * by this DIT content rule, or {@code false} if not.
455 */
456 public boolean isRequired(AttributeType attributeType)
457 {
458 return requiredAttributes.contains(attributeType);
459 }
460
461
462
463 /**
464 * Retrieves the set of optional attributes for this DIT content
465 * rule.
466 *
467 * @return The set of optional attributes for this DIT content
468 * rule.
469 */
470 public Set<AttributeType> getOptionalAttributes()
471 {
472 return optionalAttributes;
473 }
474
475
476
477 /**
478 * Indicates whether the provided attribute type is included in the
479 * optional attribute list for this DIT content rule.
480 *
481 * @param attributeType The attribute type for which to make the
482 * determination.
483 *
484 * @return {@code true} if the provided attribute type is optional
485 * for this DIT content rule, or {@code false} if not.
486 */
487 public boolean isOptional(AttributeType attributeType)
488 {
489 return optionalAttributes.contains(attributeType);
490 }
491
492
493
494 /**
495 * Indicates whether the provided attribute type is in the list of
496 * required or optional attributes for this DIT content rule.
497 *
498 * @param attributeType The attribute type for which to make the
499 * determination.
500 *
501 * @return {@code true} if the provided attribute type is required
502 * or allowed for this DIT content rule, or {@code false}
503 * if it is not.
504 */
505 public boolean isRequiredOrOptional(AttributeType attributeType)
506 {
507 return (requiredAttributes.contains(attributeType) ||
508 optionalAttributes.contains(attributeType));
509 }
510
511
512
513 /**
514 * Indicates whether the provided attribute type is in the list of
515 * required or optional attributes for this DIT content rule.
516 *
517 * @param attributeType The attribute type for which to make the
518 * determination.
519 * @param acceptEmpty Indicates whether an empty list of
520 * required or optional attributes should be
521 * taken to indicate that all attributes
522 * allowed for an objectclass will be
523 * acceptable.
524 *
525 * @return {@code true} if the provided attribute type is required
526 * or allowed for this DIT content rule, or {@code false}
527 * if it is not.
528 */
529 public boolean isRequiredOrOptional(AttributeType attributeType,
530 boolean acceptEmpty)
531 {
532 if (acceptEmpty &&
533 (requiredAttributes.isEmpty() ||
534 optionalAttributes.isEmpty()))
535 {
536 return true;
537 }
538
539 return (requiredAttributes.contains(attributeType) ||
540 optionalAttributes.contains(attributeType));
541 }
542
543
544
545 /**
546 * Retrieves the set of prohibited attributes for this DIT content
547 * rule.
548 *
549 * @return The set of prohibited attributes for this DIT content
550 * rule.
551 */
552 public Set<AttributeType> getProhibitedAttributes()
553 {
554 return prohibitedAttributes;
555 }
556
557
558
559 /**
560 * Indicates whether the provided attribute type is included in the
561 * prohibited attribute list for this DIT content rule.
562 *
563 * @param attributeType The attribute type for which to make the
564 * determination.
565 *
566 * @return {@code true} if the provided attribute type is
567 * prohibited for this DIT content rule, or {@code false}
568 * if not.
569 */
570 public boolean isProhibited(AttributeType attributeType)
571 {
572 return prohibitedAttributes.contains(attributeType);
573 }
574
575
576
577 /**
578 * Indicates whether this DIT content rule is declared "obsolete".
579 *
580 * @return {@code true} if this DIT content rule is declared
581 * "obsolete", or {@code false} if it is not.
582 */
583 public boolean isObsolete()
584 {
585 return isObsolete;
586 }
587
588
589
590 /**
591 * Retrieves a mapping between the names of any extra non-standard
592 * properties that may be associated with this DIT content rule and
593 * the value for that property.
594 *
595 * @return A mapping between the names of any extra non-standard
596 * properties that may be associated with this DIT content
597 * rule and the value for that property.
598 */
599 public Map<String,List<String>> getExtraProperties()
600 {
601 return extraProperties;
602 }
603
604
605
606 /**
607 * Retrieves the value of the specified "extra" property for this
608 * DIT content rule.
609 *
610 * @param propertyName The name of the "extra" property for which
611 * to retrieve the value.
612 *
613 * @return The value of the specified "extra" property for this DIT
614 * content rule, or {@code null} if no such property is
615 * defined.
616 */
617 public List<String> getExtraProperty(String propertyName)
618 {
619 return extraProperties.get(propertyName);
620 }
621
622
623
624 /**
625 * Specifies the provided "extra" property for this DIT content
626 * rule.
627 *
628 * @param name The name for the "extra" property. It must not be
629 * {@code null}.
630 * @param value The value for the "extra" property, or
631 * {@code null} if the property is to be removed.
632 */
633 public void setExtraProperty(String name, String value)
634 {
635 ensureNotNull(name);
636
637 if (value == null)
638 {
639 extraProperties.remove(name);
640 }
641 else
642 {
643 LinkedList<String> values = new LinkedList<String>();
644 values.add(value);
645
646 extraProperties.put(name, values);
647 }
648 }
649
650
651
652 /**
653 * Specifies the provided "extra" property for this DIT content
654 * rule.
655 *
656 * @param name The name for the "extra" property. It must not
657 * be {@code null}.
658 * @param values The set of value for the "extra" property, or
659 * {@code null} if the property is to be removed.
660 */
661 public void setExtraProperty(String name, List<String> values)
662 {
663 ensureNotNull(name);
664
665 if ((values == null) || values.isEmpty())
666 {
667 extraProperties.remove(name);
668 }
669 else
670 {
671 LinkedList<String> valuesCopy = new LinkedList<String>(values);
672 extraProperties.put(name, valuesCopy);
673 }
674 }
675
676
677
678 /**
679 * Indicates whether the provided object is equal to this DIT
680 * content rule. The object will be considered equal if it is a DIT
681 * content rule for the same structural objectclass and the same
682 * sets of names. For performance reasons, the set of auxiliary
683 * classes, and the sets of required, optional, and prohibited
684 * attribute types will not be checked, so that should be done
685 * manually if a more thorough equality comparison is required.
686 *
687 * @param o The object for which to make the determination.
688 *
689 * @return {@code true} if the provided object is equal to
690 * this DIT content rule, or {@code false} if not.
691 */
692 public boolean equals(Object o)
693 {
694 if (this == o)
695 {
696 return true;
697 }
698
699 if ((o == null) || (! (o instanceof DITContentRule)))
700 {
701 return false;
702 }
703
704 DITContentRule dcr = (DITContentRule) o;
705 if (! structuralClass.equals(dcr.structuralClass))
706 {
707 return false;
708 }
709
710 if (names.size() != dcr.names.size())
711 {
712 return false;
713 }
714
715 Iterator<String> iterator = names.keySet().iterator();
716 while (iterator.hasNext())
717 {
718 if (! dcr.names.containsKey(iterator.next()))
719 {
720 return false;
721 }
722 }
723
724 return true;
725 }
726
727
728
729 /**
730 * Retrieves the hash code for this DIT content rule. It will be
731 * equal to the hash code for the associated structural objectclass.
732 *
733 * @return The hash code for this DIT content rule.
734 */
735 public int hashCode()
736 {
737 return structuralClass.hashCode();
738 }
739
740
741
742 /**
743 * Retrieves the string representation of this DIT content rule in
744 * the form specified in RFC 2252.
745 *
746 * @return The string representation of this DIT content rule in
747 * the form specified in RFC 2252.
748 */
749 public String toString()
750 {
751 StringBuilder buffer = new StringBuilder();
752 toString(buffer, true);
753 return buffer.toString();
754 }
755
756
757
758 /**
759 * Appends a string representation of this attribute type in the
760 * form specified in RFC 2252 to the provided buffer.
761 *
762 * @param buffer The buffer to which the information
763 * should be appended.
764 * @param includeFileElement Indicates whether to include an
765 * "extra" property that specifies the
766 * path to the schema file from which
767 * this DIT content rule was loaded.
768 */
769 public void toString(StringBuilder buffer,
770 boolean includeFileElement)
771 {
772 buffer.append("( ");
773 buffer.append(structuralClass.getOID());
774
775 if (! names.isEmpty())
776 {
777 Iterator<String> iterator = names.values().iterator();
778
779 String firstName = iterator.next();
780 if (iterator.hasNext())
781 {
782 buffer.append(" NAME ( '");
783 buffer.append(firstName);
784
785 while (iterator.hasNext())
786 {
787 buffer.append("' '");
788 buffer.append(iterator.next());
789 }
790
791 buffer.append("' )");
792 }
793 else
794 {
795 buffer.append(" NAME '");
796 buffer.append(firstName);
797 buffer.append("'");
798 }
799 }
800
801 if ((description != null) && (description.length() > 0))
802 {
803 buffer.append(" DESC '");
804 buffer.append(description);
805 buffer.append("'");
806 }
807
808 if (isObsolete)
809 {
810 buffer.append(" OBSOLETE");
811 }
812
813 if (! auxiliaryClasses.isEmpty())
814 {
815 Iterator<ObjectClass> iterator = auxiliaryClasses.iterator();
816
817 String firstClass = iterator.next().getNameOrOID();
818 if (iterator.hasNext())
819 {
820 buffer.append(" AUX (");
821 buffer.append(firstClass);
822
823 while (iterator.hasNext())
824 {
825 buffer.append(" $ ");
826 buffer.append(iterator.next());
827 }
828
829 buffer.append(" )");
830 }
831 else
832 {
833 buffer.append(" AUX ");
834 buffer.append(firstClass);
835 }
836 }
837
838 if (! requiredAttributes.isEmpty())
839 {
840 Iterator<AttributeType> iterator =
841 requiredAttributes.iterator();
842
843 String firstName = iterator.next().getNameOrOID();
844 if (iterator.hasNext())
845 {
846 buffer.append(" MUST ( ");
847 buffer.append(firstName);
848
849 while (iterator.hasNext())
850 {
851 buffer.append(" $ ");
852 buffer.append(iterator.next().getNameOrOID());
853 }
854
855 buffer.append(" )");
856 }
857 else
858 {
859 buffer.append(" MUST ");
860 buffer.append(firstName);
861 }
862 }
863
864 if (! optionalAttributes.isEmpty())
865 {
866 Iterator<AttributeType> iterator =
867 optionalAttributes.iterator();
868
869 String firstName = iterator.next().getNameOrOID();
870 if (iterator.hasNext())
871 {
872 buffer.append(" MAY ( ");
873 buffer.append(firstName);
874
875 while (iterator.hasNext())
876 {
877 buffer.append(" $ ");
878 buffer.append(iterator.next().getNameOrOID());
879 }
880
881 buffer.append(" )");
882 }
883 else
884 {
885 buffer.append(" MAY ");
886 buffer.append(firstName);
887 }
888 }
889
890 if (! prohibitedAttributes.isEmpty())
891 {
892 Iterator<AttributeType> iterator =
893 prohibitedAttributes.iterator();
894
895 String firstName = iterator.next().getNameOrOID();
896 if (iterator.hasNext())
897 {
898 buffer.append(" NOT ( ");
899 buffer.append(firstName);
900
901 while (iterator.hasNext())
902 {
903 buffer.append(" $ ");
904 buffer.append(iterator.next().getNameOrOID());
905 }
906
907 buffer.append(" )");
908 }
909 else
910 {
911 buffer.append(" NOT ");
912 buffer.append(firstName);
913 }
914 }
915
916 if (! extraProperties.isEmpty())
917 {
918 for (String property : extraProperties.keySet())
919 {
920 if ((! includeFileElement) &&
921 property.equals(SCHEMA_PROPERTY_FILENAME))
922 {
923 continue;
924 }
925
926 List<String> valueList = extraProperties.get(property);
927
928 buffer.append(" ");
929 buffer.append(property);
930
931 if (valueList.size() == 1)
932 {
933 buffer.append(" '");
934 buffer.append(valueList.get(0));
935 buffer.append("'");
936 }
937 else
938 {
939 buffer.append(" ( ");
940
941 for (String value : valueList)
942 {
943 buffer.append("'");
944 buffer.append(value);
945 buffer.append("' ");
946 }
947
948 buffer.append(")");
949 }
950 }
951 }
952
953 buffer.append(" )");
954 }
955 }
956