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}