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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2013-2015 ForgeRock AS. 026 */ 027package org.opends.server.types; 028 029import java.util.Collection; 030import java.util.Collections; 031import java.util.HashSet; 032import java.util.LinkedHashSet; 033import java.util.List; 034import java.util.Map; 035import java.util.Set; 036 037import org.forgerock.i18n.slf4j.LocalizedLogger; 038import org.forgerock.opendj.ldap.schema.ObjectClassType; 039 040import static org.forgerock.util.Reject.*; 041import static org.opends.server.util.ServerConstants.*; 042 043/** 044 * This class defines a data structure for storing and interacting 045 * with an objectclass, which contains a collection of attributes that 046 * must and/or may be present in an entry with that objectclass. 047 * <p> 048 * Any methods which accesses the set of names associated with this 049 * object class, will retrieve the primary name as the first name, 050 * regardless of whether or not it was contained in the original set 051 * of <code>names</code> passed to the constructor. 052 * <p> 053 * Where ordered sets of names, attribute types, or extra properties 054 * are provided, the ordering will be preserved when the associated 055 * fields are accessed via their getters or via the 056 * {@link #toString()} methods. 057 */ 058@org.opends.server.types.PublicAPI( 059 stability=org.opends.server.types.StabilityLevel.UNCOMMITTED, 060 mayInstantiate=false, 061 mayExtend=false, 062 mayInvoke=true) 063public final class ObjectClass 064 extends CommonSchemaElements 065{ 066 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 067 068 /** The set of optional attribute types for this objectclass. */ 069 private final Set<AttributeType> optionalAttributes; 070 071 /** 072 * The set of optional attribute types for this objectclass and its 073 * superclasses. 074 */ 075 private final Set<AttributeType> optionalAttributesChain; 076 077 /** The set of required attribute types for this objectclass. */ 078 private final Set<AttributeType> requiredAttributes; 079 080 /** 081 * The set of required attribute types for this objectclass and its 082 * superclasses. 083 */ 084 private final Set<AttributeType> requiredAttributesChain; 085 086 /** 087 * The set of required and optional attributes for this objectclass 088 * and its superclasses. 089 */ 090 private final Set<AttributeType> requiredAndOptionalChain; 091 092 /** The reference to one or more superior objectclasses. */ 093 private final Set<ObjectClass> superiorClasses; 094 095 /** The objectclass type for this objectclass. */ 096 private final ObjectClassType objectClassType; 097 098 /** 099 * Indicates whether or not this object class is allowed to 100 * contain any attribute. 101 */ 102 private final boolean isExtensibleObject; 103 104 /** The definition string used to create this objectclass. */ 105 private final String definition; 106 107 /** True once this object class has been removed from the schema. */ 108 private volatile boolean isDirty; 109 110 111 112 /** 113 * Creates a new objectclass definition with the provided 114 * information. 115 * <p> 116 * If no <code>primaryName</code> is specified, but a set of 117 * <code>names</code> is specified, then the first name retrieved 118 * from the set of <code>names</code> will be used as the primary 119 * name. 120 * 121 * @param definition 122 * The definition string used to create this objectclass. 123 * It must not be {@code null}. 124 * @param primaryName 125 * The primary name for this objectclass, or 126 * {@code null} if there is no primary name. 127 * @param names 128 * The set of names that may be used to reference this 129 * objectclass. 130 * @param oid 131 * The OID for this objectclass. It must not be 132 * {@code null}. 133 * @param description 134 * The description for this objectclass, or {@code null} if 135 * there is no description. 136 * @param superiorClasses 137 * The superior classes for this objectclass, or 138 * {@code null} if there is no superior object class. 139 * @param requiredAttributes 140 * The set of required attribute types for this 141 * objectclass. 142 * @param optionalAttributes 143 * The set of optional attribute types for this 144 * objectclass. 145 * @param objectClassType 146 * The objectclass type for this objectclass, or 147 * {@code null} to default to structural. 148 * @param isObsolete 149 * Indicates whether this objectclass is declared 150 * "obsolete". 151 * @param extraProperties 152 * A set of extra properties for this objectclass. 153 */ 154 public ObjectClass(String definition, String primaryName, 155 Collection<String> names, String oid, 156 String description, 157 Set<ObjectClass> superiorClasses, 158 Set<AttributeType> requiredAttributes, 159 Set<AttributeType> optionalAttributes, 160 ObjectClassType objectClassType, 161 boolean isObsolete, 162 Map<String, List<String>> extraProperties) 163 { 164 super(primaryName, names, oid, description, isObsolete, 165 extraProperties); 166 167 168 ifNull(definition, oid); 169 170 // Construct unmodifiable views of the superior classes. 171 if (superiorClasses != null) { 172 this.superiorClasses = Collections 173 .unmodifiableSet(new LinkedHashSet<ObjectClass>( 174 superiorClasses)); 175 } else { 176 this.superiorClasses = Collections.emptySet(); 177 } 178 179 int schemaFilePos = definition.indexOf(SCHEMA_PROPERTY_FILENAME); 180 if (schemaFilePos > 0) 181 { 182 String defStr; 183 try 184 { 185 int firstQuotePos = definition.indexOf('\'', schemaFilePos); 186 int secondQuotePos = definition.indexOf('\'', 187 firstQuotePos+1); 188 189 defStr = definition.substring(0, schemaFilePos).trim() + " " + 190 definition.substring(secondQuotePos+1).trim(); 191 } 192 catch (Exception e) 193 { 194 logger.traceException(e); 195 196 defStr = definition; 197 } 198 199 this.definition = defStr; 200 } 201 else 202 { 203 this.definition = definition; 204 } 205 206 // Set flag indicating whether or not this object class allows any attributes 207 this.isExtensibleObject = hasName(OC_EXTENSIBLE_OBJECT_LC) 208 || oid.equals(OID_EXTENSIBLE_OBJECT); 209 210 // Construct unmodifiable views of the required attributes. 211 if (requiredAttributes != null) { 212 this.requiredAttributes = Collections 213 .unmodifiableSet(new LinkedHashSet<AttributeType>( 214 requiredAttributes)); 215 } else { 216 this.requiredAttributes = Collections.emptySet(); 217 } 218 219 if (this.superiorClasses.isEmpty()) { 220 this.requiredAttributesChain = this.requiredAttributes; 221 } else { 222 Set<AttributeType> tmp = new HashSet<>(this.requiredAttributes); 223 for(ObjectClass oc: this.superiorClasses) 224 { 225 tmp.addAll(oc.getRequiredAttributeChain()); 226 } 227 this.requiredAttributesChain = Collections.unmodifiableSet(tmp); 228 } 229 230 // Construct unmodifiable views of the optional attributes. 231 if (optionalAttributes != null) { 232 this.optionalAttributes = Collections 233 .unmodifiableSet(new LinkedHashSet<AttributeType>( 234 optionalAttributes)); 235 } else { 236 this.optionalAttributes = Collections.emptySet(); 237 } 238 239 if (this.superiorClasses.isEmpty()) { 240 this.optionalAttributesChain = this.optionalAttributes; 241 } else { 242 Set<AttributeType> tmp = new HashSet<>(this.optionalAttributes); 243 for(ObjectClass oc : this.superiorClasses) 244 { 245 tmp.addAll(oc.getOptionalAttributeChain()); 246 } 247 this.optionalAttributesChain = Collections.unmodifiableSet(tmp); 248 } 249 250 // Construct unmodifiable views of the required and optional attribute chains. 251 int size = requiredAttributesChain.size() + optionalAttributesChain.size(); 252 HashSet<AttributeType> reqAndOptSet = new HashSet<>(size); 253 reqAndOptSet.addAll(requiredAttributesChain); 254 reqAndOptSet.addAll(optionalAttributesChain); 255 requiredAndOptionalChain = 256 Collections.<AttributeType>unmodifiableSet(reqAndOptSet); 257 258 // Object class type defaults to structural. 259 if (objectClassType != null) { 260 this.objectClassType = objectClassType; 261 } else { 262 this.objectClassType = ObjectClassType.STRUCTURAL; 263 } 264 } 265 266 267 268 /** 269 * Retrieves an unmodifiable view of the set of direct superior 270 * classes for this objectclass. 271 * 272 * @return An unmodifiable view of the set of direct superior 273 * classes for this objectclass, 274 */ 275 public Set<ObjectClass> getSuperiorClasses() { 276 return superiorClasses; 277 } 278 279 280 281 /** 282 * Indicates whether this objectclass is a descendant of the 283 * provided class. 284 * 285 * @param objectClass 286 * The objectClass for which to make the determination. 287 * @return <code>true</code> if this objectclass is a descendant 288 * of the provided class, or <code>false</code> if not. 289 */ 290 public boolean isDescendantOf(ObjectClass objectClass) { 291 292 for(ObjectClass oc : superiorClasses) { 293 if(oc.equals(objectClass) || oc.isDescendantOf(objectClass)) { 294 return true; 295 } 296 } 297 return false; 298 } 299 300 301 302 /** 303 * Retrieves an unmodifiable view of the set of required attributes 304 * for this objectclass. Note that this set will not automatically 305 * include any required attributes for superior objectclasses. 306 * 307 * @return Returns an unmodifiable view of the set of required 308 * attributes for this objectclass. 309 */ 310 public Set<AttributeType> getRequiredAttributes() { 311 312 return requiredAttributes; 313 } 314 315 316 317 /** 318 * Retrieves an unmodifiable view of the set of all required 319 * attributes for this objectclass and any superior objectclasses 320 * that it might have. 321 * 322 * @return Returns an unmodifiable view of the set of all required 323 * attributes for this objectclass and any superior 324 * objectclasses that it might have. 325 */ 326 public Set<AttributeType> getRequiredAttributeChain() { 327 328 return requiredAttributesChain; 329 } 330 331 332 333 /** 334 * Indicates whether the provided attribute type is included in the 335 * required attribute list for this or any of its superior 336 * objectclasses. 337 * 338 * @param attributeType 339 * The attribute type for which to make the determination. 340 * @return <code>true</code> if the provided attribute type is 341 * required by this objectclass or any of its superior 342 * classes, or <code>false</code> if not. 343 */ 344 public boolean isRequired(AttributeType attributeType) { 345 346 return requiredAttributesChain.contains(attributeType); 347 } 348 349 350 351 /** 352 * Retrieves an unmodifiable view of the set of optional attributes 353 * for this objectclass. Note that this list will not automatically 354 * include any optional attributes for superior objectclasses. 355 * 356 * @return Returns an unmodifiable view of the set of optional 357 * attributes for this objectclass. 358 */ 359 public Set<AttributeType> getOptionalAttributes() { 360 361 return optionalAttributes; 362 } 363 364 365 366 /** 367 * Retrieves an unmodifiable view of the set of optional attributes 368 * for this objectclass and any superior objectclasses that it might 369 * have. 370 * 371 * @return Returns an unmodifiable view of the set of optional 372 * attributes for this objectclass and any superior 373 * objectclasses that it might have. 374 */ 375 public Set<AttributeType> getOptionalAttributeChain() { 376 377 return optionalAttributesChain; 378 } 379 380 381 382 /** 383 * Indicates whether the provided attribute type is included in the 384 * optional attribute list for this or any of its superior 385 * objectclasses. 386 * 387 * @param attributeType 388 * The attribute type for which to make the determination. 389 * @return <code>true</code> if the provided attribute type is 390 * optional for this objectclass or any of its superior 391 * classes, or <code>false</code> if not. 392 */ 393 public boolean isOptional(AttributeType attributeType) { 394 395 return optionalAttributesChain.contains(attributeType) 396 || (isExtensibleObject && !requiredAttributesChain.contains(attributeType)); 397 // FIXME -- Do we need to do other checks here, like whether the 398 // attribute type is actually defined in the schema? 399 // What about DIT content rules? 400 } 401 402 403 404 /** 405 * Indicates whether the provided attribute type is in the list of 406 * required or optional attributes for this objectclass or any of 407 * its superior classes. 408 * 409 * @param attributeType 410 * The attribute type for which to make the determination. 411 * @return <code>true</code> if the provided attribute type is 412 * required or allowed for this objectclass or any of its 413 * superior classes, or <code>false</code> if it is not. 414 */ 415 public boolean isRequiredOrOptional(AttributeType attributeType) { 416 417 // FIXME -- Do we need to do any other checks here, like whether 418 // the attribute type is actually defined in the schema? 419 return isExtensibleObject || requiredAndOptionalChain.contains(attributeType); 420 } 421 422 423 424 /** 425 * Retrieves the objectclass type for this objectclass. 426 * 427 * @return The objectclass type for this objectclass. 428 */ 429 public ObjectClassType getObjectClassType() { 430 431 return objectClassType; 432 } 433 434 435 436 /** 437 * Indicates whether this objectclass is the extensibleObject 438 * objectclass. 439 * 440 * @return <code>true</code> if this objectclass is the 441 * extensibleObject objectclass, or <code>false</code> if 442 * it is not. 443 */ 444 public boolean isExtensibleObject() { 445 446 return isExtensibleObject; 447 } 448 449 /** {@inheritDoc} */ 450 @Override 451 public String toString() 452 { 453 return definition; 454 } 455 456 457 458 /** 459 * Marks this object class as dirty, indicating that it has been removed or 460 * replaced in the schema. 461 * 462 * @return A reference to this object class. 463 */ 464 public ObjectClass setDirty() 465 { 466 isDirty = true; 467 return this; 468 } 469 470 471 472 /** 473 * Returns {@code true} if this object class has been removed or replaced in 474 * the schema. 475 * 476 * @return {@code true} if this object class has been removed or replaced in 477 * the schema. 478 */ 479 public boolean isDirty() 480 { 481 return isDirty; 482 } 483}