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