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.NameFormSyntax;
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.StaticUtils.*;
045 import static org.opends.server.util.Validator.*;
046
047
048
049 /**
050 * This class defines a data structure for storing and interacting
051 * with a name form, which defines the attribute type(s) that must
052 * and/or may be used in the RDN of an entry with a given structural
053 * objectclass.
054 */
055 @org.opends.server.types.PublicAPI(
056 stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
057 mayInstantiate=false,
058 mayExtend=false,
059 mayInvoke=true)
060 public final class NameForm
061 implements SchemaFileElement
062 {
063 /**
064 * The tracer object for the debug logger.
065 */
066 private static final DebugTracer TRACER = getTracer();
067
068 // Indicates whether this name form is declared "obsolete".
069 private final boolean isObsolete;
070
071 // The set of additional name-value pairs associated with this name
072 // form definition.
073 private final Map<String,List<String>> extraProperties;
074
075 // The mapping between the lowercase names and the user-provided
076 // names for this name form.
077 private final Map<String,String> names;
078
079 // The reference to the structural objectclass for this name form.
080 private final ObjectClass structuralClass;
081
082 // The set of optional attribute types for this name form.
083 private final Set<AttributeType> optionalAttributes;
084
085 // The set of required attribute types for this name form.
086 private final Set<AttributeType> requiredAttributes;
087
088 // The definition string used to create this name form.
089 private final String definition;
090
091 // The description for this name form.
092 private final String description;
093
094 // The OID for this name form.
095 private final String oid;
096
097
098
099 /**
100 * Creates a new name form definition with the provided information.
101 *
102 * @param definition The definition string used to create
103 * this name form. It must not be
104 * {@code null}.
105 * @param names The set of names that may be used to
106 * reference this name form.
107 * @param oid The OID for this name form. It must
108 * not be {@code null}.
109 * @param description The description for this name form.
110 * @param isObsolete Indicates whether this name form is
111 * declared "obsolete".
112 * @param structuralClass The structural objectclass with which
113 * this name form is associated. It
114 * must not be {@code null}.
115 * @param requiredAttributes The set of required attribute types
116 * for this name form.
117 * @param optionalAttributes The set of optional attribute types
118 * for this name form.
119 * @param extraProperties A set of extra properties for this
120 * name form.
121 */
122 public NameForm(String definition, Map<String,String> names,
123 String oid, String description, boolean isObsolete,
124 ObjectClass structuralClass,
125 Set<AttributeType> requiredAttributes,
126 Set<AttributeType> optionalAttributes,
127 Map<String,List<String>> extraProperties)
128 {
129 ensureNotNull(definition, oid, structuralClass);
130
131 this.oid = oid;
132 this.description = description;
133 this.isObsolete = isObsolete;
134 this.structuralClass = structuralClass;
135
136 int schemaFilePos = definition.indexOf(SCHEMA_PROPERTY_FILENAME);
137 if (schemaFilePos > 0)
138 {
139 String defStr;
140 try
141 {
142 int firstQuotePos = definition.indexOf('\'', schemaFilePos);
143 int secondQuotePos = definition.indexOf('\'',
144 firstQuotePos+1);
145
146 defStr = definition.substring(0, schemaFilePos).trim() + " " +
147 definition.substring(secondQuotePos+1).trim();
148 }
149 catch (Exception e)
150 {
151 if (debugEnabled())
152 {
153 TRACER.debugCaught(DebugLogLevel.ERROR, e);
154 }
155
156 defStr = definition;
157 }
158
159 this.definition = defStr;
160 }
161 else
162 {
163 this.definition = definition;
164 }
165
166 if ((names == null) || names.isEmpty())
167 {
168 this.names = new LinkedHashMap<String,String>(0);
169 }
170 else
171 {
172 this.names = new LinkedHashMap<String,String>(names);
173 }
174
175 if ((requiredAttributes == null) || requiredAttributes.isEmpty())
176 {
177 this.requiredAttributes = new LinkedHashSet<AttributeType>(0);
178 }
179 else
180 {
181 this.requiredAttributes =
182 new LinkedHashSet<AttributeType>(requiredAttributes);
183 }
184
185 if ((optionalAttributes == null) || optionalAttributes.isEmpty())
186 {
187 this.optionalAttributes = new LinkedHashSet<AttributeType>(0);
188 }
189 else
190 {
191 this.optionalAttributes =
192 new LinkedHashSet<AttributeType>(optionalAttributes);
193 }
194
195 if ((extraProperties == null) || extraProperties.isEmpty())
196 {
197 this.extraProperties =
198 new LinkedHashMap<String,List<String>>(0);
199 }
200 else
201 {
202 this.extraProperties =
203 new LinkedHashMap<String,List<String>>(extraProperties);
204 }
205 }
206
207
208
209 /**
210 * Retrieves the definition string used to create this name form.
211 *
212 * @return The definition string used to create this name form.
213 */
214 public String getDefinition()
215 {
216 return definition;
217 }
218
219
220
221 /**
222 * Creates a new instance of this name form based on the definition
223 * string. It will also preserve other state information associated
224 * with this name form that is not included in the definition string
225 * (e.g., the name of the schema file with which it is associated).
226 *
227 * @return The new instance of this name form based on the
228 * definition string.
229 *
230 * @throws DirectoryException If a problem occurs while attempting
231 * to create a new name form instance
232 * from the definition string.
233 */
234 public NameForm recreateFromDefinition()
235 throws DirectoryException
236 {
237 ByteString value = ByteStringFactory.create(definition);
238 Schema schema = DirectoryConfig.getSchema();
239
240 NameForm nf = NameFormSyntax.decodeNameForm(value, schema, false);
241 nf.setSchemaFile(getSchemaFile());
242
243 return nf;
244 }
245
246
247
248 /**
249 * Retrieves the set of names that may be used to reference this
250 * name form. The returned object will be a mapping between each
251 * name in all lowercase characters and that name in a user-defined
252 * form (which may include mixed capitalization).
253 *
254 * @return The set of names that may be used to reference this
255 * name form.
256 */
257 public Map<String,String> getNames()
258 {
259 return names;
260 }
261
262
263
264 /**
265 * Indicates whether the provided lowercase name may be used to
266 * reference this name form.
267 *
268 * @param lowerName The name for which to make the determination,
269 * in all lowercase characters.
270 *
271 * @return {@code true} if the provided lowercase name may be used
272 * to reference this name form, or {@code false} if not.
273 */
274 public boolean hasName(String lowerName)
275 {
276 return names.containsKey(lowerName);
277 }
278
279
280
281 /**
282 * Retrieves the OID for this name form.
283 *
284 * @return The OID for this name form.
285 */
286 public String getOID()
287 {
288 return oid;
289 }
290
291
292
293 /**
294 * Retrieves the name or OID that should be used to reference this
295 * name form. If at least one name is defined, then the first will
296 * be returned. Otherwise, the OID will be returned.
297 *
298 * @return The name or OID that should be used to reference this
299 * name form.
300 */
301 public String getNameOrOID()
302 {
303 if (names.isEmpty())
304 {
305 return oid;
306 }
307 else
308 {
309 return names.values().iterator().next();
310 }
311 }
312
313
314
315 /**
316 * Indicates whether the provided lowercase value is equal to the
317 * OID or any of the names that may be used to reference this name
318 * form.
319 *
320 * @param lowerValue The value, in all lowercase characters, that
321 * may be used to make the determination.
322 *
323 * @return {@code true} if the provided lowercase value is one of
324 * the names or the OID of this name form, or {@code false}
325 * if it is not.
326 */
327 public boolean hasNameOrOID(String lowerValue)
328 {
329 if (names.containsKey(lowerValue))
330 {
331 return true;
332 }
333
334 return lowerValue.equals(oid);
335 }
336
337
338
339 /**
340 * Retrieves the path to the schema file that contains the
341 * definition for this name form.
342 *
343 * @return The path to the schema file that contains the definition
344 * for this name form, or {@code null} if it is not known
345 * or if it is not stored in any schema file.
346 */
347 public String getSchemaFile()
348 {
349 List<String> values =
350 extraProperties.get(SCHEMA_PROPERTY_FILENAME);
351 if ((values == null) || values.isEmpty())
352 {
353 return null;
354 }
355
356 return values.get(0);
357 }
358
359
360
361 /**
362 * Specifies the path to the schema file that contains the
363 * definition for this name form.
364 *
365 * @param schemaFile The path to the schema file that contains the
366 * definition for this name form.
367 */
368 public void setSchemaFile(String schemaFile)
369 {
370 setExtraProperty(SCHEMA_PROPERTY_FILENAME, schemaFile);
371 }
372
373
374
375 /**
376 * Retrieves the description for this name form.
377 *
378 * @return The description for this name form, or {@code true} if
379 * there is none.
380 */
381 public String getDescription()
382 {
383 return description;
384 }
385
386
387
388 /**
389 * Retrieves the reference to the structural objectclass for this
390 * name form.
391 *
392 * @return The reference to the structural objectclass for this
393 * name form.
394 */
395 public ObjectClass getStructuralClass()
396 {
397 return structuralClass;
398 }
399
400
401
402 /**
403 * Retrieves the set of required attributes for this name form.
404 *
405 * @return The set of required attributes for this name form.
406 */
407 public Set<AttributeType> getRequiredAttributes()
408 {
409 return requiredAttributes;
410 }
411
412
413
414 /**
415 * Indicates whether the provided attribute type is included in the
416 * required attribute list for this name form.
417 *
418 * @param attributeType The attribute type for which to make the
419 * determination.
420 *
421 * @return {@code true} if the provided attribute type is required
422 * by this name form, or {@code false} if not.
423 */
424 public boolean isRequired(AttributeType attributeType)
425 {
426 return requiredAttributes.contains(attributeType);
427 }
428
429
430
431 /**
432 * Retrieves the set of optional attributes for this name form.
433 *
434 * @return The set of optional attributes for this name form.
435 */
436 public Set<AttributeType> getOptionalAttributes()
437 {
438 return optionalAttributes;
439 }
440
441
442
443 /**
444 * Indicates whether the provided attribute type is included in the
445 * optional attribute list for this name form.
446 *
447 * @param attributeType The attribute type for which to make the
448 * determination.
449 *
450 * @return {@code true} if the provided attribute type is optional
451 * for this name form, or {@code false} if not.
452 */
453 public boolean isOptional(AttributeType attributeType)
454 {
455 return optionalAttributes.contains(attributeType);
456 }
457
458
459
460 /**
461 * Indicates whether the provided attribute type is in the list of
462 * required or optional attributes for this name form.
463 *
464 * @param attributeType The attribute type for which to make the
465 * determination.
466 *
467 * @return {@code true} if the provided attribute type is required
468 * or optional for this name form, or {@code false} if it
469 * is not.
470 */
471 public boolean isRequiredOrOptional(AttributeType attributeType)
472 {
473 return (requiredAttributes.contains(attributeType) ||
474 optionalAttributes.contains(attributeType));
475 }
476
477
478
479 /**
480 * Indicates whether this name form is declared "obsolete".
481 *
482 * @return {@code true} if this name form is declared
483 * "obsolete", or {@code false} if it is not.
484 */
485 public boolean isObsolete()
486 {
487 return isObsolete;
488 }
489
490
491
492 /**
493 * Retrieves a mapping between the names of any extra non-standard
494 * properties that may be associated with this name form and the
495 * value for that property.
496 *
497 * @return A mapping between the names of any extra non-standard
498 * properties that may be associated with this name form
499 * and the value for that property.
500 */
501 public Map<String,List<String>> getExtraProperties()
502 {
503 return extraProperties;
504 }
505
506
507
508 /**
509 * Retrieves the value of the specified "extra" property for this
510 * name form.
511 *
512 * @param propertyName The name of the "extra" property for which
513 * to retrieve the value.
514 *
515 * @return The value of the specified "extra" property for this
516 * name form, or {@code null} if no such property is
517 * defined.
518 */
519 public List<String> getExtraProperty(String propertyName)
520 {
521 return extraProperties.get(propertyName);
522 }
523
524
525
526 /**
527 * Specifies the provided "extra" property for this name form.
528 *
529 * @param name The name for the "extra" property. It must not be
530 * {@code null}.
531 * @param value The value for the "extra" property, or
532 * {@code null} if the property is to be removed.
533 */
534 public void setExtraProperty(String name, String value)
535 {
536 ensureNotNull(name);
537
538 if (value == null)
539 {
540 extraProperties.remove(name);
541 }
542 else
543 {
544 LinkedList<String> values = new LinkedList<String>();
545 values.add(value);
546
547 extraProperties.put(name, values);
548 }
549 }
550
551
552
553 /**
554 * Specifies the provided "extra" property for this name form.
555 *
556 * @param name The name for the "extra" property. It must not
557 * be {@code null}.
558 * @param values The set of value for the "extra" property, or
559 * {@code null} if the property is to be removed.
560 */
561 public void setExtraProperty(String name, List<String> values)
562 {
563 ensureNotNull(name);
564
565 if ((values == null) || values.isEmpty())
566 {
567 extraProperties.remove(name);
568 }
569 else
570 {
571 LinkedList<String> valuesCopy = new LinkedList<String>(values);
572 extraProperties.put(name, valuesCopy);
573 }
574 }
575
576
577
578 /**
579 * Indicates whether the provided object is equal to this name form.
580 * The object will be considered equal if it is a name form with the
581 * same OID as the current name form.
582 *
583 * @param o The object for which to make the determination.
584 *
585 * @return {@code true} if the provided object is equal to this
586 * name form, or {@code true} if not.
587 */
588 public boolean equals(Object o)
589 {
590 if (this == o)
591 {
592 return true;
593 }
594
595 if ((o == null) || (! (o instanceof NameForm)))
596 {
597 return false;
598 }
599
600 return oid.equals(((NameForm) o).oid);
601 }
602
603
604
605 /**
606 * Retrieves the hash code for this name form. It will be based on
607 * the sum of the bytes of the OID.
608 *
609 * @return The hash code for this name form.
610 */
611 public int hashCode()
612 {
613 int oidLength = oid.length();
614 int hashCode = 0;
615 for (int i=0; i < oidLength; i++)
616 {
617 hashCode += oid.charAt(i);
618 }
619
620 return hashCode;
621 }
622
623
624
625 /**
626 * Retrieves the string representation of this name form in the form
627 * specified in RFC 2252.
628 *
629 * @return The string representation of this name form in the form
630 * specified in RFC 2252.
631 */
632 public String toString()
633 {
634 StringBuilder buffer = new StringBuilder();
635 toString(buffer, true);
636 return buffer.toString();
637 }
638
639
640
641 /**
642 * Appends a string representation of this name form in the form
643 * specified in RFC 2252 to the provided buffer.
644 *
645 * @param buffer The buffer to which the information
646 * should be appended.
647 * @param includeFileElement Indicates whether to include an
648 * "extra" property that specifies the
649 * path to the schema file from which
650 * this name form was loaded.
651 */
652 public void toString(StringBuilder buffer,
653 boolean includeFileElement)
654 {
655 buffer.append("( ");
656 buffer.append(oid);
657
658 if (! names.isEmpty())
659 {
660 Iterator<String> iterator = names.values().iterator();
661
662 String firstName = iterator.next();
663 if (iterator.hasNext())
664 {
665 buffer.append(" NAME ( '");
666 buffer.append(firstName);
667
668 while (iterator.hasNext())
669 {
670 buffer.append("' '");
671 buffer.append(iterator.next());
672 }
673
674 buffer.append("' )");
675 }
676 else
677 {
678 buffer.append(" NAME '");
679 buffer.append(firstName);
680 buffer.append("'");
681 }
682 }
683
684 if ((description != null) && (description.length() > 0))
685 {
686 buffer.append(" DESC '");
687 buffer.append(description);
688 buffer.append("'");
689 }
690
691 if (isObsolete)
692 {
693 buffer.append(" OBSOLETE");
694 }
695
696 buffer.append(" OC ");
697 buffer.append(structuralClass.getNameOrOID());
698
699 if (! requiredAttributes.isEmpty())
700 {
701 Iterator<AttributeType> iterator =
702 requiredAttributes.iterator();
703
704 String firstName = iterator.next().getNameOrOID();
705 if (iterator.hasNext())
706 {
707 buffer.append(" MUST ( ");
708 buffer.append(firstName);
709
710 while (iterator.hasNext())
711 {
712 buffer.append(" $ ");
713 buffer.append(iterator.next().getNameOrOID());
714 }
715
716 buffer.append(" )");
717 }
718 else
719 {
720 buffer.append(" MUST ");
721 buffer.append(firstName);
722 }
723 }
724
725 if (! optionalAttributes.isEmpty())
726 {
727 Iterator<AttributeType> iterator =
728 optionalAttributes.iterator();
729
730 String firstName = iterator.next().getNameOrOID();
731 if (iterator.hasNext())
732 {
733 buffer.append(" MAY ( ");
734 buffer.append(firstName);
735
736 while (iterator.hasNext())
737 {
738 buffer.append(" $ ");
739 buffer.append(iterator.next().getNameOrOID());
740 }
741
742 buffer.append(" )");
743 }
744 else
745 {
746 buffer.append(" MAY ");
747 buffer.append(firstName);
748 }
749 }
750
751 if (! extraProperties.isEmpty())
752 {
753 for (String property : extraProperties.keySet())
754 {
755 if ((! includeFileElement) &&
756 property.equals(SCHEMA_PROPERTY_FILENAME))
757 {
758 continue;
759 }
760
761 List<String> valueList = extraProperties.get(property);
762
763 buffer.append(" ");
764 buffer.append(property);
765
766 if (valueList.size() == 1)
767 {
768 buffer.append(" '");
769 buffer.append(valueList.get(0));
770 buffer.append("'");
771 }
772 else
773 {
774 buffer.append(" ( ");
775
776 for (String value : valueList)
777 {
778 buffer.append("'");
779 buffer.append(value);
780 buffer.append("' ");
781 }
782
783 buffer.append(")");
784 }
785 }
786 }
787
788 buffer.append(" )");
789 }
790 }
791