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