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 }