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.Collection; 032 import java.util.Collections; 033 import java.util.HashSet; 034 import java.util.Iterator; 035 import java.util.LinkedHashSet; 036 import java.util.List; 037 import java.util.Map; 038 import java.util.Set; 039 040 import org.opends.server.schema.ObjectClassSyntax; 041 042 import static org.opends.server.loggers.debug.DebugLogger.*; 043 import org.opends.server.loggers.debug.DebugTracer; 044 import static org.opends.server.util.ServerConstants.*; 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 an objectclass, which contains a collection of attributes that 052 * must and/or may be present in an entry with that objectclass. 053 * <p> 054 * Any methods which accesses the set of names associated with this 055 * object class, will retrieve the primary name as the first name, 056 * regardless of whether or not it was contained in the original set 057 * of <code>names</code> passed to the constructor. 058 * <p> 059 * Where ordered sets of names, attribute types, or extra properties 060 * are provided, the ordering will be preserved when the associated 061 * fields are accessed via their getters or via the 062 * {@link #toString()} methods. 063 */ 064 @org.opends.server.types.PublicAPI( 065 stability=org.opends.server.types.StabilityLevel.UNCOMMITTED, 066 mayInstantiate=false, 067 mayExtend=false, 068 mayInvoke=true) 069 public final class ObjectClass 070 extends CommonSchemaElements 071 implements SchemaFileElement 072 { 073 /** 074 * The tracer object for the debug logger. 075 */ 076 private static final DebugTracer TRACER = getTracer(); 077 078 // The set of optional attribute types for this objectclass. 079 private final Set<AttributeType> optionalAttributes; 080 081 // The set of optional attribute types for this objectclass and its 082 // superclasses. 083 private final Set<AttributeType> optionalAttributesChain; 084 085 // The set of required attribute types for this objectclass. 086 private final Set<AttributeType> requiredAttributes; 087 088 // The set of required attribute types for this objectclass and its 089 // superclasses. 090 private final Set<AttributeType> requiredAttributesChain; 091 092 // The set of required and optional attributes for this objectclass 093 // and its superclasses. 094 private final Set<AttributeType> requiredAndOptionalChain; 095 096 // The reference to the superior objectclass. 097 private final ObjectClass superiorClass; 098 099 // The objectclass type for this objectclass. 100 private final ObjectClassType objectClassType; 101 102 // Indicates whether or not this object class is allowed to 103 // contain any attribute. 104 private final boolean isExtensibleObject; 105 106 // The definition string used to create this objectclass. 107 private final String definition; 108 109 110 111 /** 112 * Creates a new objectclass definition with the provided 113 * information. 114 * <p> 115 * If no <code>primaryName</code> is specified, but a set of 116 * <code>names</code> is specified, then the first name retrieved 117 * from the set of <code>names</code> will be used as the primary 118 * name. 119 * 120 * @param definition 121 * The definition string used to create this objectclass. 122 * It must not be {@code null}. 123 * @param primaryName 124 * The primary name for this objectclass, or 125 * {@code null} if there is no primary name. 126 * @param names 127 * The set of names that may be used to reference this 128 * objectclass. 129 * @param oid 130 * The OID for this objectclass. It must not be 131 * {@code null}. 132 * @param description 133 * The description for this objectclass, or {@code null} if 134 * there is no description. 135 * @param superiorClass 136 * The superior class for this objectclass, or {@code null} 137 * if there is no superior object class. 138 * @param requiredAttributes 139 * The set of required attribute types for this 140 * objectclass. 141 * @param optionalAttributes 142 * The set of optional attribute types for this 143 * objectclass. 144 * @param objectClassType 145 * The objectclass type for this objectclass, or 146 * {@code null} to default to structural. 147 * @param isObsolete 148 * Indicates whether this objectclass is declared 149 * "obsolete". 150 * @param extraProperties 151 * A set of extra properties for this objectclass. 152 */ 153 public ObjectClass(String definition, String primaryName, 154 Collection<String> names, String oid, 155 String description, ObjectClass superiorClass, 156 Set<AttributeType> requiredAttributes, 157 Set<AttributeType> optionalAttributes, 158 ObjectClassType objectClassType, 159 boolean isObsolete, 160 Map<String, List<String>> extraProperties) 161 { 162 super(primaryName, names, oid, description, isObsolete, 163 extraProperties); 164 165 166 ensureNotNull(definition, oid); 167 168 this.superiorClass = superiorClass; 169 170 int schemaFilePos = definition.indexOf(SCHEMA_PROPERTY_FILENAME); 171 if (schemaFilePos > 0) 172 { 173 String defStr; 174 try 175 { 176 int firstQuotePos = definition.indexOf('\'', schemaFilePos); 177 int secondQuotePos = definition.indexOf('\'', 178 firstQuotePos+1); 179 180 defStr = definition.substring(0, schemaFilePos).trim() + " " + 181 definition.substring(secondQuotePos+1).trim(); 182 } 183 catch (Exception e) 184 { 185 if (debugEnabled()) 186 { 187 TRACER.debugCaught(DebugLogLevel.ERROR, e); 188 } 189 190 defStr = definition; 191 } 192 193 this.definition = defStr; 194 } 195 else 196 { 197 this.definition = definition; 198 } 199 200 // Set flag indicating whether or not this object class allows any 201 // attributes. 202 if (hasName(OC_EXTENSIBLE_OBJECT_LC) 203 || oid.equals(OID_EXTENSIBLE_OBJECT)) { 204 this.isExtensibleObject = true; 205 } else { 206 this.isExtensibleObject = false; 207 } 208 209 // Construct unmodifiable views of the required attributes. 210 if (requiredAttributes != null) { 211 this.requiredAttributes = Collections 212 .unmodifiableSet(new LinkedHashSet<AttributeType>( 213 requiredAttributes)); 214 } else { 215 this.requiredAttributes = Collections.emptySet(); 216 } 217 218 if (this.superiorClass == null) { 219 this.requiredAttributesChain = this.requiredAttributes; 220 } else { 221 Set<AttributeType> tmp = new HashSet<AttributeType>( 222 this.requiredAttributes); 223 tmp.addAll(this.superiorClass.getRequiredAttributeChain()); 224 this.requiredAttributesChain = Collections.unmodifiableSet(tmp); 225 } 226 227 // Construct unmodifiable views of the optional attributes. 228 if (optionalAttributes != null) { 229 this.optionalAttributes = Collections 230 .unmodifiableSet(new LinkedHashSet<AttributeType>( 231 optionalAttributes)); 232 } else { 233 this.optionalAttributes = Collections.emptySet(); 234 } 235 236 if (this.superiorClass == null) { 237 this.optionalAttributesChain = this.optionalAttributes; 238 } else { 239 Set<AttributeType> tmp = new HashSet<AttributeType>( 240 this.optionalAttributes); 241 tmp.addAll(this.superiorClass.getOptionalAttributeChain()); 242 this.optionalAttributesChain = Collections.unmodifiableSet(tmp); 243 } 244 245 // Construct unmodifiable views of the required and optional 246 // attribute chains. 247 HashSet<AttributeType> reqAndOptSet = 248 new HashSet<AttributeType>(requiredAttributesChain.size() + 249 optionalAttributesChain.size()); 250 reqAndOptSet.addAll(requiredAttributesChain); 251 reqAndOptSet.addAll(optionalAttributesChain); 252 requiredAndOptionalChain = 253 Collections.<AttributeType>unmodifiableSet(reqAndOptSet); 254 255 // Object class type defaults to structural. 256 if (objectClassType != null) { 257 this.objectClassType = objectClassType; 258 } else { 259 this.objectClassType = ObjectClassType.STRUCTURAL; 260 } 261 } 262 263 264 265 /** 266 * Retrieves the definition string used to create this objectclass. 267 * 268 * @return The definition string used to create this objectclass. 269 */ 270 public String getDefinition() 271 { 272 return definition; 273 } 274 275 276 277 /** 278 * Retrieves the definition string used to create this objectclass 279 * including the X-SCHEMA-FILE extension. 280 * 281 * @return The definition string used to create this objectclass 282 * including the X-SCHEMA-FILE extension. 283 */ 284 public String getDefinitionWithFileName() 285 { 286 if (getSchemaFile() != null) 287 { 288 int pos = definition.lastIndexOf(')'); 289 String defStr = definition.substring(0, pos).trim() + " " + 290 SCHEMA_PROPERTY_FILENAME + " '" + 291 getSchemaFile() + "' )"; 292 return defStr; 293 } 294 else 295 return definition; 296 } 297 298 299 300 /** 301 * Creates a new instance of this objectclass based on the 302 * definition string. It will also preserve other state information 303 * associated with this objectclass that is not included in the 304 * definition string (e.g., the name of the schema file with which 305 * it is associated). 306 * 307 * @return The new instance of this objectclass based on the 308 * definition string. 309 * 310 * @throws DirectoryException If a problem occurs while attempting 311 * to create a new objectclass instance 312 * from the definition string. 313 */ 314 public ObjectClass recreateFromDefinition() 315 throws DirectoryException 316 { 317 ByteString value = ByteStringFactory.create(definition); 318 Schema schema = DirectoryConfig.getSchema(); 319 320 ObjectClass oc = ObjectClassSyntax.decodeObjectClass(value, 321 schema, false); 322 oc.setSchemaFile(getSchemaFile()); 323 324 return oc; 325 } 326 327 328 329 /** 330 * Retrieves the reference to the superior class for this 331 * objectclass. 332 * 333 * @return The reference to the superior class for this objectlass, 334 * or <code>null</code> if there is none. 335 */ 336 public ObjectClass getSuperiorClass() { 337 338 return superiorClass; 339 } 340 341 342 343 /** 344 * Indicates whether this objectclass is a descendant of the 345 * provided class. 346 * 347 * @param objectClass 348 * The objectClass for which to make the determination. 349 * @return <code>true</code> if this objectclass is a descendant 350 * of the provided class, or <code>false</code> if not. 351 */ 352 public boolean isDescendantOf(ObjectClass objectClass) { 353 354 if (superiorClass == null) { 355 return false; 356 } 357 358 return (superiorClass.equals(objectClass) || superiorClass 359 .isDescendantOf(objectClass)); 360 } 361 362 363 364 /** 365 * Retrieves an unmodifiable view of the set of required attributes 366 * for this objectclass. Note that this set will not automatically 367 * include any required attributes for superior objectclasses. 368 * 369 * @return Returns an unmodifiable view of the set of required 370 * attributes for this objectclass. 371 */ 372 public Set<AttributeType> getRequiredAttributes() { 373 374 return requiredAttributes; 375 } 376 377 378 379 /** 380 * Retrieves an unmodifiable view of the set of all required 381 * attributes for this objectclass and any superior objectclasses 382 * that it might have. 383 * 384 * @return Returns an unmodifiable view of the set of all required 385 * attributes for this objectclass and any superior 386 * objectclasses that it might have. 387 */ 388 public Set<AttributeType> getRequiredAttributeChain() { 389 390 return requiredAttributesChain; 391 } 392 393 394 395 /** 396 * Indicates whether the provided attribute type is included in the 397 * required attribute list for this or any of its superior 398 * objectclasses. 399 * 400 * @param attributeType 401 * The attribute type for which to make the determination. 402 * @return <code>true</code> if the provided attribute type is 403 * required by this objectclass or any of its superior 404 * classes, or <code>false</code> if not. 405 */ 406 public boolean isRequired(AttributeType attributeType) { 407 408 return requiredAttributesChain.contains(attributeType); 409 } 410 411 412 413 /** 414 * Retrieves an unmodifiable view of the set of optional attributes 415 * for this objectclass. Note that this list will not automatically 416 * include any optional attributes for superior objectclasses. 417 * 418 * @return Returns an unmodifiable view of the set of optional 419 * attributes for this objectclass. 420 */ 421 public Set<AttributeType> getOptionalAttributes() { 422 423 return optionalAttributes; 424 } 425 426 427 428 /** 429 * Retrieves an unmodifiable view of the set of optional attributes 430 * for this objectclass and any superior objectclasses that it might 431 * have. 432 * 433 * @return Returns an unmodifiable view of the set of optional 434 * attributes for this objectclass and any superior 435 * objectclasses that it might have. 436 */ 437 public Set<AttributeType> getOptionalAttributeChain() { 438 439 return optionalAttributesChain; 440 } 441 442 443 444 /** 445 * Indicates whether the provided attribute type is included in the 446 * optional attribute list for this or any of its superior 447 * objectclasses. 448 * 449 * @param attributeType 450 * The attribute type for which to make the determination. 451 * @return <code>true</code> if the provided attribute type is 452 * optional for this objectclass or any of its superior 453 * classes, or <code>false</code> if not. 454 */ 455 public boolean isOptional(AttributeType attributeType) { 456 457 if (optionalAttributesChain.contains(attributeType)) { 458 return true; 459 } 460 461 if (isExtensibleObject 462 && !requiredAttributesChain.contains(attributeType)) { 463 // FIXME -- Do we need to do other checks here, like whether the 464 // attribute type is actually defined in the schema? 465 // What about DIT content rules? 466 return true; 467 } 468 469 return false; 470 } 471 472 473 474 /** 475 * Indicates whether the provided attribute type is in the list of 476 * required or optional attributes for this objectclass or any of 477 * its superior classes. 478 * 479 * @param attributeType 480 * The attribute type for which to make the determination. 481 * @return <code>true</code> if the provided attribute type is 482 * required or allowed for this objectclass or any of its 483 * superior classes, or <code>false</code> if it is not. 484 */ 485 public boolean isRequiredOrOptional(AttributeType attributeType) { 486 487 // FIXME -- Do we need to do any other checks here, like whether 488 // the attribute type is actually defined in the schema? 489 return (isExtensibleObject || 490 requiredAndOptionalChain.contains(attributeType)); 491 } 492 493 494 495 /** 496 * Retrieves the objectclass type for this objectclass. 497 * 498 * @return The objectclass type for this objectclass. 499 */ 500 public ObjectClassType getObjectClassType() { 501 502 return objectClassType; 503 } 504 505 506 507 /** 508 * Indicates whether this objectclass is the extensibleObject 509 * objectclass. 510 * 511 * @return <code>true</code> if this objectclass is the 512 * extensibleObject objectclass, or <code>false</code> if 513 * it is not. 514 */ 515 public boolean isExtensibleObject() { 516 517 return isExtensibleObject; 518 } 519 520 521 522 /** 523 * Appends a string representation of this schema definition's 524 * non-generic properties to the provided buffer. 525 * 526 * @param buffer The buffer to which the information should be 527 * appended. 528 */ 529 protected void toStringContent(StringBuilder buffer) { 530 531 if (superiorClass != null) { 532 buffer.append(" SUP "); 533 buffer.append(superiorClass.getNameOrOID()); 534 } 535 536 if (objectClassType != null) { 537 buffer.append(" "); 538 buffer.append(objectClassType.toString()); 539 } 540 541 if (!requiredAttributes.isEmpty()) { 542 Iterator<AttributeType> iterator = requiredAttributes 543 .iterator(); 544 545 String firstName = iterator.next().getNameOrOID(); 546 if (iterator.hasNext()) { 547 buffer.append(" MUST ( "); 548 buffer.append(firstName); 549 550 while (iterator.hasNext()) { 551 buffer.append(" $ "); 552 buffer.append(iterator.next().getNameOrOID()); 553 } 554 555 buffer.append(" )"); 556 } else { 557 buffer.append(" MUST "); 558 buffer.append(firstName); 559 } 560 } 561 562 if (!optionalAttributes.isEmpty()) { 563 Iterator<AttributeType> iterator = optionalAttributes 564 .iterator(); 565 566 String firstName = iterator.next().getNameOrOID(); 567 if (iterator.hasNext()) { 568 buffer.append(" MAY ( "); 569 buffer.append(firstName); 570 571 while (iterator.hasNext()) { 572 buffer.append(" $ "); 573 buffer.append(iterator.next().getNameOrOID()); 574 } 575 576 buffer.append(" )"); 577 } else { 578 buffer.append(" MAY "); 579 buffer.append(firstName); 580 } 581 } 582 } 583 }