001/*
002 * Copyright 2007-2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2016 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.ldap.sdk.schema;
022
023
024
025import java.io.File;
026import java.io.IOException;
027import java.io.InputStream;
028import java.io.Serializable;
029import java.util.ArrayList;
030import java.util.Arrays;
031import java.util.Collections;
032import java.util.LinkedHashMap;
033import java.util.LinkedHashSet;
034import java.util.List;
035import java.util.Map;
036import java.util.Set;
037import java.util.concurrent.atomic.AtomicReference;
038
039import com.unboundid.ldap.sdk.Attribute;
040import com.unboundid.ldap.sdk.Entry;
041import com.unboundid.ldap.sdk.Filter;
042import com.unboundid.ldap.sdk.LDAPConnection;
043import com.unboundid.ldap.sdk.LDAPException;
044import com.unboundid.ldap.sdk.ReadOnlyEntry;
045import com.unboundid.ldap.sdk.ResultCode;
046import com.unboundid.ldap.sdk.SearchScope;
047import com.unboundid.ldif.LDIFException;
048import com.unboundid.ldif.LDIFReader;
049import com.unboundid.util.NotMutable;
050import com.unboundid.util.ThreadSafety;
051import com.unboundid.util.ThreadSafetyLevel;
052
053import static com.unboundid.ldap.sdk.schema.SchemaMessages.*;
054import static com.unboundid.util.Debug.*;
055import static com.unboundid.util.StaticUtils.*;
056import static com.unboundid.util.Validator.*;
057
058
059
060/**
061 * This class provides a data structure for representing a directory server
062 * subschema subentry.  This includes information about the attribute syntaxes,
063 * matching rules, attribute types, object classes, name forms, DIT content
064 * rules, DIT structure rules, and matching rule uses defined in the server
065 * schema.
066 */
067@NotMutable()
068@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
069public final class Schema
070       implements Serializable
071{
072  /**
073   * The name of the attribute used to hold the attribute syntax definitions.
074   */
075  public static final String ATTR_ATTRIBUTE_SYNTAX = "ldapSyntaxes";
076
077
078
079  /**
080   * The name of the attribute used to hold the attribute type definitions.
081   */
082  public static final String ATTR_ATTRIBUTE_TYPE = "attributeTypes";
083
084
085
086  /**
087   * The name of the attribute used to hold the DIT content rule definitions.
088   */
089  public static final String ATTR_DIT_CONTENT_RULE = "dITContentRules";
090
091
092
093  /**
094   * The name of the attribute used to hold the DIT structure rule definitions.
095   */
096  public static final String ATTR_DIT_STRUCTURE_RULE = "dITStructureRules";
097
098
099
100  /**
101   * The name of the attribute used to hold the matching rule definitions.
102   */
103  public static final String ATTR_MATCHING_RULE = "matchingRules";
104
105
106
107  /**
108   * The name of the attribute used to hold the matching rule use definitions.
109   */
110  public static final String ATTR_MATCHING_RULE_USE = "matchingRuleUse";
111
112
113
114  /**
115   * The name of the attribute used to hold the name form definitions.
116   */
117  public static final String ATTR_NAME_FORM = "nameForms";
118
119
120
121  /**
122   * The name of the attribute used to hold the object class definitions.
123   */
124  public static final String ATTR_OBJECT_CLASS = "objectClasses";
125
126
127
128  /**
129   * The name of the attribute used to hold the DN of the subschema subentry
130   * with the schema information that governs a specified entry.
131   */
132  public static final String ATTR_SUBSCHEMA_SUBENTRY = "subschemaSubentry";
133
134
135
136  /**
137   * The default standard schema available for use in the LDAP SDK.
138   */
139  private static final AtomicReference<Schema> DEFAULT_STANDARD_SCHEMA =
140       new AtomicReference<Schema>();
141
142
143
144  /**
145   * The set of request attributes that will be used when retrieving the server
146   * subschema subentry in order to retrieve all of the schema elements.
147   */
148  private static final String[] SCHEMA_REQUEST_ATTRS =
149  {
150    "*",
151    ATTR_ATTRIBUTE_SYNTAX,
152    ATTR_ATTRIBUTE_TYPE,
153    ATTR_DIT_CONTENT_RULE,
154    ATTR_DIT_STRUCTURE_RULE,
155    ATTR_MATCHING_RULE,
156    ATTR_MATCHING_RULE_USE,
157    ATTR_NAME_FORM,
158    ATTR_OBJECT_CLASS
159  };
160
161
162
163  /**
164   * The set of request attributes that will be used when retrieving the
165   * subschema subentry attribute from a specified entry in order to determine
166   * the location of the server schema definitions.
167   */
168  private static final String[] SUBSCHEMA_SUBENTRY_REQUEST_ATTRS =
169  {
170    ATTR_SUBSCHEMA_SUBENTRY
171  };
172
173
174
175  /**
176   * Retrieves the resource path that may be used to obtain a file with a number
177   * of standard schema definitions.
178   */
179  private static final String DEFAULT_SCHEMA_RESOURCE_PATH =
180       "com/unboundid/ldap/sdk/schema/standard-schema.ldif";
181
182
183
184  /**
185   * The serial version UID for this serializable class.
186   */
187  private static final long serialVersionUID = 8081839633831517925L;
188
189
190
191  // A map of all subordinate attribute type definitions for each attribute
192  // type definition.
193  private final Map<AttributeTypeDefinition,List<AttributeTypeDefinition>>
194       subordinateAttributeTypes;
195
196  // The set of attribute syntaxes mapped from lowercase name/OID to syntax.
197  private final Map<String,AttributeSyntaxDefinition> asMap;
198
199  // The set of attribute types mapped from lowercase name/OID to type.
200  private final Map<String,AttributeTypeDefinition> atMap;
201
202  // The set of DIT content rules mapped from lowercase name/OID to rule.
203  private final Map<String,DITContentRuleDefinition> dcrMap;
204
205  // The set of DIT structure rules mapped from rule ID to rule.
206  private final Map<Integer,DITStructureRuleDefinition> dsrMapByID;
207
208  // The set of DIT structure rules mapped from lowercase name to rule.
209  private final Map<String,DITStructureRuleDefinition> dsrMapByName;
210
211  // The set of DIT structure rules mapped from lowercase name to rule.
212  private final Map<String,DITStructureRuleDefinition> dsrMapByNameForm;
213
214  // The set of matching rules mapped from lowercase name/OID to rule.
215  private final Map<String,MatchingRuleDefinition> mrMap;
216
217  // The set of matching rule uses mapped from matching rule OID to use.
218  private final Map<String,MatchingRuleUseDefinition> mruMap;
219
220  // The set of name forms mapped from lowercase name/OID to name form.
221  private final Map<String,NameFormDefinition> nfMapByName;
222
223  // The set of name forms mapped from structural class OID to name form.
224  private final Map<String,NameFormDefinition> nfMapByOC;
225
226  // The set of object classes mapped from lowercase name/OID to class.
227  private final Map<String,ObjectClassDefinition> ocMap;
228
229  // The entry used to create this schema object.
230  private final ReadOnlyEntry schemaEntry;
231
232  // The set of attribute syntaxes defined in the schema.
233  private final Set<AttributeSyntaxDefinition> asSet;
234
235  // The set of attribute types defined in the schema.
236  private final Set<AttributeTypeDefinition> atSet;
237
238  // The set of operational attribute types defined in the schema.
239  private final Set<AttributeTypeDefinition> operationalATSet;
240
241  // The set of user attribute types defined in the schema.
242  private final Set<AttributeTypeDefinition> userATSet;
243
244  // The set of DIT content rules defined in the schema.
245  private final Set<DITContentRuleDefinition> dcrSet;
246
247  // The set of DIT structure rules defined in the schema.
248  private final Set<DITStructureRuleDefinition> dsrSet;
249
250  // The set of matching rules defined in the schema.
251  private final Set<MatchingRuleDefinition> mrSet;
252
253  // The set of matching rule uses defined in the schema.
254  private final Set<MatchingRuleUseDefinition> mruSet;
255
256  // The set of name forms defined in the schema.
257  private final Set<NameFormDefinition> nfSet;
258
259  // The set of object classes defined in the schema.
260  private final Set<ObjectClassDefinition> ocSet;
261
262  // The set of abstract object classes defined in the schema.
263  private final Set<ObjectClassDefinition> abstractOCSet;
264
265  // The set of auxiliary object classes defined in the schema.
266  private final Set<ObjectClassDefinition> auxiliaryOCSet;
267
268  // The set of structural object classes defined in the schema.
269  private final Set<ObjectClassDefinition> structuralOCSet;
270
271
272
273  /**
274   * Creates a new schema object by decoding the information in the provided
275   * entry.
276   *
277   * @param  schemaEntry  The schema entry to decode.
278   */
279  public Schema(final Entry schemaEntry)
280  {
281    this.schemaEntry = new ReadOnlyEntry(schemaEntry);
282
283    // Decode the attribute syntaxes from the schema entry.
284    String[] defs = schemaEntry.getAttributeValues(ATTR_ATTRIBUTE_SYNTAX);
285    if (defs == null)
286    {
287      asMap = Collections.emptyMap();
288      asSet = Collections.emptySet();
289    }
290    else
291    {
292      final LinkedHashMap<String,AttributeSyntaxDefinition> m =
293           new LinkedHashMap<String,AttributeSyntaxDefinition>(defs.length);
294      final LinkedHashSet<AttributeSyntaxDefinition> s =
295           new LinkedHashSet<AttributeSyntaxDefinition>(defs.length);
296
297      for (final String def : defs)
298      {
299        try
300        {
301          final AttributeSyntaxDefinition as =
302               new AttributeSyntaxDefinition(def);
303          s.add(as);
304          m.put(toLowerCase(as.getOID()), as);
305        }
306        catch (final LDAPException le)
307        {
308          debugException(le);
309        }
310      }
311
312      asMap = Collections.unmodifiableMap(m);
313      asSet = Collections.unmodifiableSet(s);
314    }
315
316
317    // Decode the attribute types from the schema entry.
318    defs = schemaEntry.getAttributeValues(ATTR_ATTRIBUTE_TYPE);
319    if (defs == null)
320    {
321      atMap            = Collections.emptyMap();
322      atSet            = Collections.emptySet();
323      operationalATSet = Collections.emptySet();
324      userATSet        = Collections.emptySet();
325    }
326    else
327    {
328      final LinkedHashMap<String,AttributeTypeDefinition> m =
329           new LinkedHashMap<String,AttributeTypeDefinition>(2*defs.length);
330      final LinkedHashSet<AttributeTypeDefinition> s =
331           new LinkedHashSet<AttributeTypeDefinition>(defs.length);
332      final LinkedHashSet<AttributeTypeDefinition> sUser =
333           new LinkedHashSet<AttributeTypeDefinition>(defs.length);
334      final LinkedHashSet<AttributeTypeDefinition> sOperational =
335           new LinkedHashSet<AttributeTypeDefinition>(defs.length);
336
337      for (final String def : defs)
338      {
339        try
340        {
341          final AttributeTypeDefinition at = new AttributeTypeDefinition(def);
342          s.add(at);
343          m.put(toLowerCase(at.getOID()), at);
344          for (final String name : at.getNames())
345          {
346            m.put(toLowerCase(name), at);
347          }
348
349          if (at.isOperational())
350          {
351            sOperational.add(at);
352          }
353          else
354          {
355            sUser.add(at);
356          }
357        }
358        catch (final LDAPException le)
359        {
360          debugException(le);
361        }
362      }
363
364      atMap            = Collections.unmodifiableMap(m);
365      atSet            = Collections.unmodifiableSet(s);
366      operationalATSet = Collections.unmodifiableSet(sOperational);
367      userATSet        = Collections.unmodifiableSet(sUser);
368    }
369
370
371    // Decode the DIT content rules from the schema entry.
372    defs = schemaEntry.getAttributeValues(ATTR_DIT_CONTENT_RULE);
373    if (defs == null)
374    {
375      dcrMap = Collections.emptyMap();
376      dcrSet = Collections.emptySet();
377    }
378    else
379    {
380      final LinkedHashMap<String,DITContentRuleDefinition> m =
381           new LinkedHashMap<String,DITContentRuleDefinition>(2*defs.length);
382      final LinkedHashSet<DITContentRuleDefinition> s =
383           new LinkedHashSet<DITContentRuleDefinition>(defs.length);
384
385      for (final String def : defs)
386      {
387        try
388        {
389          final DITContentRuleDefinition dcr =
390               new DITContentRuleDefinition(def);
391          s.add(dcr);
392          m.put(toLowerCase(dcr.getOID()), dcr);
393          for (final String name : dcr.getNames())
394          {
395            m.put(toLowerCase(name), dcr);
396          }
397        }
398        catch (final LDAPException le)
399        {
400          debugException(le);
401        }
402      }
403
404      dcrMap = Collections.unmodifiableMap(m);
405      dcrSet = Collections.unmodifiableSet(s);
406    }
407
408
409    // Decode the DIT structure rules from the schema entry.
410    defs = schemaEntry.getAttributeValues(ATTR_DIT_STRUCTURE_RULE);
411    if (defs == null)
412    {
413      dsrMapByID       = Collections.emptyMap();
414      dsrMapByName     = Collections.emptyMap();
415      dsrMapByNameForm = Collections.emptyMap();
416      dsrSet           = Collections.emptySet();
417    }
418    else
419    {
420      final LinkedHashMap<Integer,DITStructureRuleDefinition> mID =
421           new LinkedHashMap<Integer,DITStructureRuleDefinition>(defs.length);
422      final LinkedHashMap<String,DITStructureRuleDefinition> mN =
423           new LinkedHashMap<String,DITStructureRuleDefinition>(defs.length);
424      final LinkedHashMap<String,DITStructureRuleDefinition> mNF =
425           new LinkedHashMap<String,DITStructureRuleDefinition>(defs.length);
426      final LinkedHashSet<DITStructureRuleDefinition> s =
427           new LinkedHashSet<DITStructureRuleDefinition>(defs.length);
428
429      for (final String def : defs)
430      {
431        try
432        {
433          final DITStructureRuleDefinition dsr =
434               new DITStructureRuleDefinition(def);
435          s.add(dsr);
436          mID.put(dsr.getRuleID(), dsr);
437          mNF.put(toLowerCase(dsr.getNameFormID()), dsr);
438          for (final String name : dsr.getNames())
439          {
440            mN.put(toLowerCase(name), dsr);
441          }
442        }
443        catch (final LDAPException le)
444        {
445          debugException(le);
446        }
447      }
448
449      dsrMapByID       = Collections.unmodifiableMap(mID);
450      dsrMapByName     = Collections.unmodifiableMap(mN);
451      dsrMapByNameForm = Collections.unmodifiableMap(mNF);
452      dsrSet           = Collections.unmodifiableSet(s);
453    }
454
455
456    // Decode the matching rules from the schema entry.
457    defs = schemaEntry.getAttributeValues(ATTR_MATCHING_RULE);
458    if (defs == null)
459    {
460      mrMap = Collections.emptyMap();
461      mrSet = Collections.emptySet();
462    }
463    else
464    {
465      final LinkedHashMap<String,MatchingRuleDefinition> m =
466           new LinkedHashMap<String,MatchingRuleDefinition>(2*defs.length);
467      final LinkedHashSet<MatchingRuleDefinition> s =
468           new LinkedHashSet<MatchingRuleDefinition>(defs.length);
469
470      for (final String def : defs)
471      {
472        try
473        {
474          final MatchingRuleDefinition mr = new MatchingRuleDefinition(def);
475          s.add(mr);
476          m.put(toLowerCase(mr.getOID()), mr);
477          for (final String name : mr.getNames())
478          {
479            m.put(toLowerCase(name), mr);
480          }
481        }
482        catch (final LDAPException le)
483        {
484          debugException(le);
485        }
486      }
487
488      mrMap = Collections.unmodifiableMap(m);
489      mrSet = Collections.unmodifiableSet(s);
490    }
491
492
493    // Decode the matching rule uses from the schema entry.
494    defs = schemaEntry.getAttributeValues(ATTR_MATCHING_RULE_USE);
495    if (defs == null)
496    {
497      mruMap = Collections.emptyMap();
498      mruSet = Collections.emptySet();
499    }
500    else
501    {
502      final LinkedHashMap<String,MatchingRuleUseDefinition> m =
503           new LinkedHashMap<String,MatchingRuleUseDefinition>(2*defs.length);
504      final LinkedHashSet<MatchingRuleUseDefinition> s =
505           new LinkedHashSet<MatchingRuleUseDefinition>(defs.length);
506
507      for (final String def : defs)
508      {
509        try
510        {
511          final MatchingRuleUseDefinition mru =
512               new MatchingRuleUseDefinition(def);
513          s.add(mru);
514          m.put(toLowerCase(mru.getOID()), mru);
515          for (final String name : mru.getNames())
516          {
517            m.put(toLowerCase(name), mru);
518          }
519        }
520        catch (final LDAPException le)
521        {
522          debugException(le);
523        }
524      }
525
526      mruMap = Collections.unmodifiableMap(m);
527      mruSet = Collections.unmodifiableSet(s);
528    }
529
530
531    // Decode the name forms from the schema entry.
532    defs = schemaEntry.getAttributeValues(ATTR_NAME_FORM);
533    if (defs == null)
534    {
535      nfMapByName = Collections.emptyMap();
536      nfMapByOC   = Collections.emptyMap();
537      nfSet       = Collections.emptySet();
538    }
539    else
540    {
541      final LinkedHashMap<String,NameFormDefinition> mN =
542           new LinkedHashMap<String,NameFormDefinition>(2*defs.length);
543      final LinkedHashMap<String,NameFormDefinition> mOC =
544           new LinkedHashMap<String,NameFormDefinition>(defs.length);
545      final LinkedHashSet<NameFormDefinition> s =
546           new LinkedHashSet<NameFormDefinition>(defs.length);
547
548      for (final String def : defs)
549      {
550        try
551        {
552          final NameFormDefinition nf = new NameFormDefinition(def);
553          s.add(nf);
554          mOC.put(toLowerCase(nf.getStructuralClass()), nf);
555          mN.put(toLowerCase(nf.getOID()), nf);
556          for (final String name : nf.getNames())
557          {
558            mN.put(toLowerCase(name), nf);
559          }
560        }
561        catch (final LDAPException le)
562        {
563          debugException(le);
564        }
565      }
566
567      nfMapByName = Collections.unmodifiableMap(mN);
568      nfMapByOC   = Collections.unmodifiableMap(mOC);
569      nfSet       = Collections.unmodifiableSet(s);
570    }
571
572
573    // Decode the object classes from the schema entry.
574    defs = schemaEntry.getAttributeValues(ATTR_OBJECT_CLASS);
575    if (defs == null)
576    {
577      ocMap           = Collections.emptyMap();
578      ocSet           = Collections.emptySet();
579      abstractOCSet   = Collections.emptySet();
580      auxiliaryOCSet  = Collections.emptySet();
581      structuralOCSet = Collections.emptySet();
582    }
583    else
584    {
585      final LinkedHashMap<String,ObjectClassDefinition> m =
586           new LinkedHashMap<String,ObjectClassDefinition>(2*defs.length);
587      final LinkedHashSet<ObjectClassDefinition> s =
588           new LinkedHashSet<ObjectClassDefinition>(defs.length);
589      final LinkedHashSet<ObjectClassDefinition> sAbstract =
590           new LinkedHashSet<ObjectClassDefinition>(defs.length);
591      final LinkedHashSet<ObjectClassDefinition> sAuxiliary =
592           new LinkedHashSet<ObjectClassDefinition>(defs.length);
593      final LinkedHashSet<ObjectClassDefinition> sStructural =
594           new LinkedHashSet<ObjectClassDefinition>(defs.length);
595
596      for (final String def : defs)
597      {
598        try
599        {
600          final ObjectClassDefinition oc = new ObjectClassDefinition(def);
601          s.add(oc);
602          m.put(toLowerCase(oc.getOID()), oc);
603          for (final String name : oc.getNames())
604          {
605            m.put(toLowerCase(name), oc);
606          }
607
608          switch (getOCType(oc, m))
609          {
610            case ABSTRACT:
611              sAbstract.add(oc);
612              break;
613            case AUXILIARY:
614              sAuxiliary.add(oc);
615              break;
616            case STRUCTURAL:
617              sStructural.add(oc);
618              break;
619          }
620        }
621        catch (final LDAPException le)
622        {
623          debugException(le);
624        }
625      }
626
627      ocMap           = Collections.unmodifiableMap(m);
628      ocSet           = Collections.unmodifiableSet(s);
629      abstractOCSet   = Collections.unmodifiableSet(sAbstract);
630      auxiliaryOCSet  = Collections.unmodifiableSet(sAuxiliary);
631      structuralOCSet = Collections.unmodifiableSet(sStructural);
632    }
633
634
635    // Populate the map of subordinate attribute types.
636    final LinkedHashMap<AttributeTypeDefinition,List<AttributeTypeDefinition>>
637         subAttrTypes = new LinkedHashMap<AttributeTypeDefinition,
638              List<AttributeTypeDefinition>>(atSet.size());
639    for (final AttributeTypeDefinition d : atSet)
640    {
641      AttributeTypeDefinition sup = d.getSuperiorType(this);
642      while (sup != null)
643      {
644        List<AttributeTypeDefinition> l = subAttrTypes.get(sup);
645        if (l == null)
646        {
647          l = new ArrayList<AttributeTypeDefinition>(1);
648          subAttrTypes.put(sup, l);
649        }
650        l.add(d);
651
652        sup = sup.getSuperiorType(this);
653      }
654    }
655    subordinateAttributeTypes = Collections.unmodifiableMap(subAttrTypes);
656  }
657
658
659
660  /**
661   * Retrieves the directory server schema over the provided connection.  The
662   * root DSE will first be retrieved in order to get its subschemaSubentry DN,
663   * and then that entry will be retrieved from the server and its contents
664   * decoded as schema elements.  This should be sufficient for directories that
665   * only provide a single schema, but for directories with multiple schemas it
666   * may be necessary to specify the DN of an entry for which to retrieve the
667   * subschema subentry.
668   *
669   * @param  connection  The connection to use in order to retrieve the server
670   *                     schema.  It must not be {@code null}.
671   *
672   * @return  A decoded representation of the server schema.
673   *
674   * @throws  LDAPException  If a problem occurs while obtaining the server
675   *                         schema.
676   */
677  public static Schema getSchema(final LDAPConnection connection)
678         throws LDAPException
679  {
680    return getSchema(connection, "");
681  }
682
683
684
685  /**
686   * Retrieves the directory server schema that governs the specified entry.
687   * In some servers, different portions of the DIT may be served by different
688   * schemas, and in such cases it will be necessary to provide the DN of the
689   * target entry in order to ensure that the appropriate schema which governs
690   * that entry is returned.  For servers that support only a single schema,
691   * any entry DN (including that of the root DSE) should be sufficient.
692   *
693   * @param  connection  The connection to use in order to retrieve the server
694   *                     schema.  It must not be {@code null}.
695   * @param  entryDN     The DN of the entry for which to retrieve the governing
696   *                     schema.  It may be {@code null} or an empty string in
697   *                     order to retrieve the schema that governs the server's
698   *                     root DSE.
699   *
700   * @return  A decoded representation of the server schema, or {@code null} if
701   *          it is not available for some reason (e.g., the client does not
702   *          have permission to read the server schema).
703   *
704   * @throws  LDAPException  If a problem occurs while obtaining the server
705   *                         schema.
706   */
707  public static Schema getSchema(final LDAPConnection connection,
708                                 final String entryDN)
709         throws LDAPException
710  {
711    ensureNotNull(connection);
712
713    final String subschemaSubentryDN;
714    if (entryDN == null)
715    {
716      subschemaSubentryDN = getSubschemaSubentryDN(connection, "");
717    }
718    else
719    {
720      subschemaSubentryDN = getSubschemaSubentryDN(connection, entryDN);
721    }
722
723    if (subschemaSubentryDN == null)
724    {
725      return null;
726    }
727
728    final Entry schemaEntry = connection.searchForEntry(subschemaSubentryDN,
729         SearchScope.BASE,
730         Filter.createEqualityFilter("objectClass", "subschema"),
731         SCHEMA_REQUEST_ATTRS);
732    if (schemaEntry == null)
733    {
734      return null;
735    }
736
737    return new Schema(schemaEntry);
738  }
739
740
741
742  /**
743   * Reads schema information from one or more files containing the schema
744   * represented in LDIF form, with the definitions represented in the form
745   * described in section 4.1 of RFC 4512.  Each file should contain a single
746   * entry.
747   *
748   * @param  schemaFiles  The paths to the LDIF files containing the schema
749   *                      information to be read.  At least one file must be
750   *                      specified.  If multiple files are specified, then they
751   *                      will be processed in the order in which they have been
752   *                      listed.
753   *
754   * @return  The schema read from the specified schema files, or {@code null}
755   *          if none of the files contains any LDIF data to be read.
756   *
757   * @throws  IOException  If a problem occurs while attempting to read from
758   *                       any of the specified files.
759   *
760   * @throws  LDIFException  If a problem occurs while attempting to parse the
761   *                         contents of any of the schema files.
762   */
763  public static Schema getSchema(final String... schemaFiles)
764         throws IOException, LDIFException
765  {
766    ensureNotNull(schemaFiles);
767    ensureFalse(schemaFiles.length == 0);
768
769    final ArrayList<File> files = new ArrayList<File>(schemaFiles.length);
770    for (final String s : schemaFiles)
771    {
772      files.add(new File(s));
773    }
774
775    return getSchema(files);
776  }
777
778
779
780  /**
781   * Reads schema information from one or more files containing the schema
782   * represented in LDIF form, with the definitions represented in the form
783   * described in section 4.1 of RFC 4512.  Each file should contain a single
784   * entry.
785   *
786   * @param  schemaFiles  The paths to the LDIF files containing the schema
787   *                      information to be read.  At least one file must be
788   *                      specified.  If multiple files are specified, then they
789   *                      will be processed in the order in which they have been
790   *                      listed.
791   *
792   * @return  The schema read from the specified schema files, or {@code null}
793   *          if none of the files contains any LDIF data to be read.
794   *
795   * @throws  IOException  If a problem occurs while attempting to read from
796   *                       any of the specified files.
797   *
798   * @throws  LDIFException  If a problem occurs while attempting to parse the
799   *                         contents of any of the schema files.
800   */
801  public static Schema getSchema(final File... schemaFiles)
802         throws IOException, LDIFException
803  {
804    ensureNotNull(schemaFiles);
805    ensureFalse(schemaFiles.length == 0);
806
807    return getSchema(Arrays.asList(schemaFiles));
808  }
809
810
811
812  /**
813   * Reads schema information from one or more files containing the schema
814   * represented in LDIF form, with the definitions represented in the form
815   * described in section 4.1 of RFC 4512.  Each file should contain a single
816   * entry.
817   *
818   * @param  schemaFiles  The paths to the LDIF files containing the schema
819   *                      information to be read.  At least one file must be
820   *                      specified.  If multiple files are specified, then they
821   *                      will be processed in the order in which they have been
822   *                      listed.
823   *
824   * @return  The schema read from the specified schema files, or {@code null}
825   *          if none of the files contains any LDIF data to be read.
826   *
827   * @throws  IOException  If a problem occurs while attempting to read from
828   *                       any of the specified files.
829   *
830   * @throws  LDIFException  If a problem occurs while attempting to parse the
831   *                         contents of any of the schema files.
832   */
833  public static Schema getSchema(final List<File> schemaFiles)
834         throws IOException, LDIFException
835  {
836    ensureNotNull(schemaFiles);
837    ensureFalse(schemaFiles.isEmpty());
838
839    Entry schemaEntry = null;
840    for (final File f : schemaFiles)
841    {
842      final LDIFReader ldifReader = new LDIFReader(f);
843
844      try
845      {
846        final Entry e = ldifReader.readEntry();
847        if (e == null)
848        {
849          continue;
850        }
851
852        e.addAttribute("objectClass", "top", "ldapSubentry", "subschema");
853
854        if (schemaEntry == null)
855        {
856          schemaEntry = e;
857        }
858        else
859        {
860          for (final Attribute a : e.getAttributes())
861          {
862            schemaEntry.addAttribute(a);
863          }
864        }
865      }
866      finally
867      {
868        ldifReader.close();
869      }
870    }
871
872    if (schemaEntry == null)
873    {
874      return null;
875    }
876
877    return new Schema(schemaEntry);
878  }
879
880
881
882  /**
883   * Retrieves a schema object that contains definitions for a number of
884   * standard attribute types and object classes from LDAP-related RFCs and
885   * Internet Drafts.
886   *
887   * @return  A schema object that contains definitions for a number of standard
888   *          attribute types and object classes from LDAP-related RFCs and
889   *          Internet Drafts.
890   *
891   * @throws  LDAPException  If a problem occurs while attempting to obtain or
892   *                         parse the default standard schema definitions.
893   */
894  public static Schema getDefaultStandardSchema()
895         throws LDAPException
896  {
897    final Schema s = DEFAULT_STANDARD_SCHEMA.get();
898    if (s != null)
899    {
900      return s;
901    }
902
903    synchronized (DEFAULT_STANDARD_SCHEMA)
904    {
905      try
906      {
907        final ClassLoader classLoader = Schema.class.getClassLoader();
908        final InputStream inputStream =
909             classLoader.getResourceAsStream(DEFAULT_SCHEMA_RESOURCE_PATH);
910        final LDIFReader ldifReader = new LDIFReader(inputStream);
911        final Entry schemaEntry = ldifReader.readEntry();
912        ldifReader.close();
913
914        final Schema schema = new Schema(schemaEntry);
915        DEFAULT_STANDARD_SCHEMA.set(schema);
916        return schema;
917      }
918      catch (final Exception e)
919      {
920        debugException(e);
921        throw new LDAPException(ResultCode.LOCAL_ERROR,
922             ERR_SCHEMA_CANNOT_LOAD_DEFAULT_DEFINITIONS.get(
923                  getExceptionMessage(e)),
924             e);
925      }
926    }
927  }
928
929
930
931  /**
932   * Retrieves a schema containing all of the elements of each of the provided
933   * schemas.
934   *
935   * @param  schemas  The schemas to be merged.  It must not be {@code null} or
936   *                  empty.
937   *
938   * @return  A merged representation of the provided schemas.
939   */
940  public static Schema mergeSchemas(final Schema... schemas)
941  {
942    if ((schemas == null) || (schemas.length == 0))
943    {
944      return null;
945    }
946    else if (schemas.length == 1)
947    {
948      return schemas[0];
949    }
950
951    final LinkedHashMap<String,String> asMap =
952         new LinkedHashMap<String,String>();
953    final LinkedHashMap<String,String> atMap =
954         new LinkedHashMap<String,String>();
955    final LinkedHashMap<String,String> dcrMap =
956         new LinkedHashMap<String,String>();
957    final LinkedHashMap<Integer,String> dsrMap =
958         new LinkedHashMap<Integer,String>();
959    final LinkedHashMap<String,String> mrMap =
960         new LinkedHashMap<String,String>();
961    final LinkedHashMap<String,String> mruMap =
962         new LinkedHashMap<String,String>();
963    final LinkedHashMap<String,String> nfMap =
964         new LinkedHashMap<String,String>();
965    final LinkedHashMap<String,String> ocMap =
966         new LinkedHashMap<String,String>();
967
968    for (final Schema s : schemas)
969    {
970      for (final AttributeSyntaxDefinition as : s.asSet)
971      {
972        asMap.put(toLowerCase(as.getOID()), as.toString());
973      }
974
975      for (final AttributeTypeDefinition at : s.atSet)
976      {
977        atMap.put(toLowerCase(at.getOID()), at.toString());
978      }
979
980      for (final DITContentRuleDefinition dcr : s.dcrSet)
981      {
982        dcrMap.put(toLowerCase(dcr.getOID()), dcr.toString());
983      }
984
985      for (final DITStructureRuleDefinition dsr : s.dsrSet)
986      {
987        dsrMap.put(dsr.getRuleID(), dsr.toString());
988      }
989
990      for (final MatchingRuleDefinition mr : s.mrSet)
991      {
992        mrMap.put(toLowerCase(mr.getOID()), mr.toString());
993      }
994
995      for (final MatchingRuleUseDefinition mru : s.mruSet)
996      {
997        mruMap.put(toLowerCase(mru.getOID()), mru.toString());
998      }
999
1000      for (final NameFormDefinition nf : s.nfSet)
1001      {
1002        nfMap.put(toLowerCase(nf.getOID()), nf.toString());
1003      }
1004
1005      for (final ObjectClassDefinition oc : s.ocSet)
1006      {
1007        ocMap.put(toLowerCase(oc.getOID()), oc.toString());
1008      }
1009    }
1010
1011    final Entry e = new Entry(schemas[0].getSchemaEntry().getDN());
1012
1013    final Attribute ocAttr =
1014         schemas[0].getSchemaEntry().getObjectClassAttribute();
1015    if (ocAttr == null)
1016    {
1017      e.addAttribute("objectClass", "top", "ldapSubEntry", "subschema");
1018    }
1019    else
1020    {
1021      e.addAttribute(ocAttr);
1022    }
1023
1024    if (! asMap.isEmpty())
1025    {
1026      final String[] values = new String[asMap.size()];
1027      e.addAttribute(ATTR_ATTRIBUTE_SYNTAX, asMap.values().toArray(values));
1028    }
1029
1030    if (! mrMap.isEmpty())
1031    {
1032      final String[] values = new String[mrMap.size()];
1033      e.addAttribute(ATTR_MATCHING_RULE, mrMap.values().toArray(values));
1034    }
1035
1036    if (! atMap.isEmpty())
1037    {
1038      final String[] values = new String[atMap.size()];
1039      e.addAttribute(ATTR_ATTRIBUTE_TYPE, atMap.values().toArray(values));
1040    }
1041
1042    if (! ocMap.isEmpty())
1043    {
1044      final String[] values = new String[ocMap.size()];
1045      e.addAttribute(ATTR_OBJECT_CLASS, ocMap.values().toArray(values));
1046    }
1047
1048    if (! dcrMap.isEmpty())
1049    {
1050      final String[] values = new String[dcrMap.size()];
1051      e.addAttribute(ATTR_DIT_CONTENT_RULE, dcrMap.values().toArray(values));
1052    }
1053
1054    if (! dsrMap.isEmpty())
1055    {
1056      final String[] values = new String[dsrMap.size()];
1057      e.addAttribute(ATTR_DIT_STRUCTURE_RULE, dsrMap.values().toArray(values));
1058    }
1059
1060    if (! nfMap.isEmpty())
1061    {
1062      final String[] values = new String[nfMap.size()];
1063      e.addAttribute(ATTR_NAME_FORM, nfMap.values().toArray(values));
1064    }
1065
1066    if (! mruMap.isEmpty())
1067    {
1068      final String[] values = new String[mruMap.size()];
1069      e.addAttribute(ATTR_MATCHING_RULE_USE, mruMap.values().toArray(values));
1070    }
1071
1072    return new Schema(e);
1073  }
1074
1075
1076
1077  /**
1078   * Retrieves the entry used to create this schema object.
1079   *
1080   * @return  The entry used to create this schema object.
1081   */
1082  public ReadOnlyEntry getSchemaEntry()
1083  {
1084    return schemaEntry;
1085  }
1086
1087
1088
1089  /**
1090   * Retrieves the object class type for the specified object class, recursively
1091   * checking its parents as needed.
1092   *
1093   * @param  oc  The object class definition for which to make the
1094   *             determination.
1095   * @param  m   The map of defined object classes.
1096   *
1097   * @return  The object class type for the object class.
1098   */
1099  private static ObjectClassType getOCType(final ObjectClassDefinition oc,
1100                                      final Map<String,ObjectClassDefinition> m)
1101  {
1102    ObjectClassType t = oc.getObjectClassType();
1103    if (t != null)
1104    {
1105      return t;
1106    }
1107
1108    for (final String s : oc.getSuperiorClasses())
1109    {
1110      final ObjectClassDefinition d = m.get(toLowerCase(s));
1111      if (d != null)
1112      {
1113        t = getOCType(d, m);
1114        if (t != null)
1115        {
1116          return t;
1117        }
1118      }
1119    }
1120
1121    return ObjectClassType.STRUCTURAL;
1122  }
1123
1124
1125
1126  /**
1127   * Retrieves the value of the subschemaSubentry attribute from the specified
1128   * entry using the provided connection.
1129   *
1130   * @param  connection  The connection to use in order to perform the search.
1131   *                     It must not be {@code null}.
1132   * @param  entryDN     The DN of the entry from which to retrieve the
1133   *                     subschemaSubentry attribute.  It may be {@code null} or
1134   *                     an empty string in order to retrieve the value from the
1135   *                     server's root DSE.
1136   *
1137   * @return  The value of the subschemaSubentry attribute from the specified
1138   *          entry, or {@code null} if it is not available for some reason
1139   *          (e.g., the client does not have permission to read the target
1140   *          entry or the subschemaSubentry attribute).
1141   *
1142   * @throws  LDAPException  If a problem occurs while attempting to retrieve
1143   *                         the specified entry.
1144   */
1145  public static String getSubschemaSubentryDN(final LDAPConnection connection,
1146                                              final String entryDN)
1147         throws LDAPException
1148  {
1149    ensureNotNull(connection);
1150
1151    final Entry e;
1152    if (entryDN == null)
1153    {
1154      e = connection.getEntry("", SUBSCHEMA_SUBENTRY_REQUEST_ATTRS);
1155    }
1156    else
1157    {
1158      e = connection.getEntry(entryDN, SUBSCHEMA_SUBENTRY_REQUEST_ATTRS);
1159    }
1160
1161    if (e == null)
1162    {
1163      return null;
1164    }
1165
1166    return e.getAttributeValue(ATTR_SUBSCHEMA_SUBENTRY);
1167  }
1168
1169
1170
1171  /**
1172   * Retrieves the set of attribute syntax definitions contained in the server
1173   * schema.
1174   *
1175   * @return  The set of attribute syntax definitions contained in the server
1176   *          schema.
1177   */
1178  public Set<AttributeSyntaxDefinition> getAttributeSyntaxes()
1179  {
1180    return asSet;
1181  }
1182
1183
1184
1185  /**
1186   * Retrieves the attribute syntax with the specified OID from the server
1187   * schema.
1188   *
1189   * @param  oid  The OID of the attribute syntax to retrieve.  It must not be
1190   *              {@code null}.  It may optionally include a minimum upper bound
1191   *              (as may appear when the syntax OID is included in an attribute
1192   *              type definition), but if it does then that portion will be
1193   *              ignored when retrieving the attribute syntax.
1194   *
1195   * @return  The requested attribute syntax, or {@code null} if there is no
1196   *          such syntax defined in the server schema.
1197   */
1198  public AttributeSyntaxDefinition getAttributeSyntax(final String oid)
1199  {
1200    ensureNotNull(oid);
1201
1202    final String lowerOID = toLowerCase(oid);
1203    final int    curlyPos = lowerOID.indexOf('{');
1204
1205    if (curlyPos > 0)
1206    {
1207      return asMap.get(lowerOID.substring(0, curlyPos));
1208    }
1209    else
1210    {
1211      return asMap.get(lowerOID);
1212    }
1213  }
1214
1215
1216
1217  /**
1218   * Retrieves the set of attribute type definitions contained in the server
1219   * schema.
1220   *
1221   * @return  The set of attribute type definitions contained in the server
1222   *          schema.
1223   */
1224  public Set<AttributeTypeDefinition> getAttributeTypes()
1225  {
1226    return atSet;
1227  }
1228
1229
1230
1231  /**
1232   * Retrieves the set of operational attribute type definitions (i.e., those
1233   * definitions with a usage of directoryOperation, distributedOperation, or
1234   * dSAOperation) contained in the  server  schema.
1235   *
1236   * @return  The set of operational attribute type definitions contained in the
1237   *          server schema.
1238   */
1239  public Set<AttributeTypeDefinition> getOperationalAttributeTypes()
1240  {
1241    return operationalATSet;
1242  }
1243
1244
1245
1246  /**
1247   * Retrieves the set of user attribute type definitions (i.e., those
1248   * definitions with a usage of userApplications) contained in the  server
1249   * schema.
1250   *
1251   * @return  The set of user attribute type definitions contained in the server
1252   *          schema.
1253   */
1254  public Set<AttributeTypeDefinition> getUserAttributeTypes()
1255  {
1256    return userATSet;
1257  }
1258
1259
1260
1261  /**
1262   * Retrieves the attribute type with the specified name or OID from the server
1263   * schema.
1264   *
1265   * @param  name  The name or OID of the attribute type to retrieve.  It must
1266   *               not be {@code null}.
1267   *
1268   * @return  The requested attribute type, or {@code null} if there is no
1269   *          such attribute type defined in the server schema.
1270   */
1271  public AttributeTypeDefinition getAttributeType(final String name)
1272  {
1273    ensureNotNull(name);
1274
1275    return atMap.get(toLowerCase(name));
1276  }
1277
1278
1279
1280  /**
1281   * Retrieves a list of all subordinate attribute type definitions for the
1282   * provided attribute type definition.
1283   *
1284   * @param  d  The attribute type definition for which to retrieve all
1285   *            subordinate attribute types.  It must not be {@code null}.
1286   *
1287   * @return  A list of all subordinate attribute type definitions for the
1288   *          provided attribute type definition, or an empty list if it does
1289   *          not have any subordinate types or the provided attribute type is
1290   *          not defined in the schema.
1291   */
1292  public List<AttributeTypeDefinition> getSubordinateAttributeTypes(
1293                                            final AttributeTypeDefinition d)
1294  {
1295    ensureNotNull(d);
1296
1297    final List<AttributeTypeDefinition> l = subordinateAttributeTypes.get(d);
1298    if (l == null)
1299    {
1300      return Collections.emptyList();
1301    }
1302    else
1303    {
1304      return Collections.unmodifiableList(l);
1305    }
1306  }
1307
1308
1309
1310  /**
1311   * Retrieves the set of DIT content rule definitions contained in the server
1312   * schema.
1313   *
1314   * @return  The set of DIT content rule definitions contained in the server
1315   *          schema.
1316   */
1317  public Set<DITContentRuleDefinition> getDITContentRules()
1318  {
1319    return dcrSet;
1320  }
1321
1322
1323
1324  /**
1325   * Retrieves the DIT content rule with the specified name or OID from the
1326   * server schema.
1327   *
1328   * @param  name  The name or OID of the DIT content rule to retrieve.  It must
1329   *               not be {@code null}.
1330   *
1331   * @return  The requested DIT content rule, or {@code null} if there is no
1332   *          such rule defined in the server schema.
1333   */
1334  public DITContentRuleDefinition getDITContentRule(final String name)
1335  {
1336    ensureNotNull(name);
1337
1338    return dcrMap.get(toLowerCase(name));
1339  }
1340
1341
1342
1343  /**
1344   * Retrieves the set of DIT structure rule definitions contained in the server
1345   * schema.
1346   *
1347   * @return  The set of DIT structure rule definitions contained in the server
1348   *          schema.
1349   */
1350  public Set<DITStructureRuleDefinition> getDITStructureRules()
1351  {
1352    return dsrSet;
1353  }
1354
1355
1356
1357  /**
1358   * Retrieves the DIT content rule with the specified rule ID from the server
1359   * schema.
1360   *
1361   * @param  ruleID  The rule ID for the DIT structure rule to retrieve.
1362   *
1363   * @return  The requested DIT structure rule, or {@code null} if there is no
1364   *          such rule defined in the server schema.
1365   */
1366  public DITStructureRuleDefinition getDITStructureRuleByID(final int ruleID)
1367  {
1368    return dsrMapByID.get(ruleID);
1369  }
1370
1371
1372
1373  /**
1374   * Retrieves the DIT content rule with the specified name from the server
1375   * schema.
1376   *
1377   * @param  ruleName  The name of the DIT structure rule to retrieve.  It must
1378   *                   not be {@code null}.
1379   *
1380   * @return  The requested DIT structure rule, or {@code null} if there is no
1381   *          such rule defined in the server schema.
1382   */
1383  public DITStructureRuleDefinition getDITStructureRuleByName(
1384                                         final String ruleName)
1385  {
1386    ensureNotNull(ruleName);
1387
1388    return dsrMapByName.get(toLowerCase(ruleName));
1389  }
1390
1391
1392
1393  /**
1394   * Retrieves the DIT content rule associated with the specified name form from
1395   * the server schema.
1396   *
1397   * @param  nameForm  The name or OID of the name form for which to retrieve
1398   *                   the associated DIT structure rule.
1399   *
1400   * @return  The requested DIT structure rule, or {@code null} if there is no
1401   *          such rule defined in the server schema.
1402   */
1403  public DITStructureRuleDefinition getDITStructureRuleByNameForm(
1404                                         final String nameForm)
1405  {
1406    ensureNotNull(nameForm);
1407
1408    return dsrMapByNameForm.get(toLowerCase(nameForm));
1409  }
1410
1411
1412
1413  /**
1414   * Retrieves the set of matching rule definitions contained in the server
1415   * schema.
1416   *
1417   * @return  The set of matching rule definitions contained in the server
1418   *          schema.
1419   */
1420  public Set<MatchingRuleDefinition> getMatchingRules()
1421  {
1422    return mrSet;
1423  }
1424
1425
1426
1427  /**
1428   * Retrieves the matching rule with the specified name or OID from the server
1429   * schema.
1430   *
1431   * @param  name  The name or OID of the matching rule to retrieve.  It must
1432   *               not be {@code null}.
1433   *
1434   * @return  The requested matching rule, or {@code null} if there is no
1435   *          such rule defined in the server schema.
1436   */
1437  public MatchingRuleDefinition getMatchingRule(final String name)
1438  {
1439    ensureNotNull(name);
1440
1441    return mrMap.get(toLowerCase(name));
1442  }
1443
1444
1445
1446  /**
1447   * Retrieves the set of matching rule use definitions contained in the server
1448   * schema.
1449   *
1450   * @return  The set of matching rule use definitions contained in the server
1451   *          schema.
1452   */
1453  public Set<MatchingRuleUseDefinition> getMatchingRuleUses()
1454  {
1455    return mruSet;
1456  }
1457
1458
1459
1460  /**
1461   * Retrieves the matching rule use with the specified name or OID from the
1462   * server schema.
1463   *
1464   * @param  name  The name or OID of the matching rule use to retrieve.  It
1465   *               must not be {@code null}.
1466   *
1467   * @return  The requested matching rule, or {@code null} if there is no
1468   *          such matching rule use defined in the server schema.
1469   */
1470  public MatchingRuleUseDefinition getMatchingRuleUse(final String name)
1471  {
1472    ensureNotNull(name);
1473
1474    return mruMap.get(toLowerCase(name));
1475  }
1476
1477
1478
1479  /**
1480   * Retrieves the set of name form definitions contained in the server schema.
1481   *
1482   * @return  The set of name form definitions contained in the server schema.
1483   */
1484  public Set<NameFormDefinition> getNameForms()
1485  {
1486    return nfSet;
1487  }
1488
1489
1490
1491  /**
1492   * Retrieves the name form with the specified name or OID from the server
1493   * schema.
1494   *
1495   * @param  name  The name or OID of the name form to retrieve.  It must not be
1496   *               {@code null}.
1497   *
1498   * @return  The requested name form, or {@code null} if there is no
1499   *          such rule defined in the server schema.
1500   */
1501  public NameFormDefinition getNameFormByName(final String name)
1502  {
1503    ensureNotNull(name);
1504
1505    return nfMapByName.get(toLowerCase(name));
1506  }
1507
1508
1509
1510  /**
1511   * Retrieves the name form associated with the specified structural object
1512   * class from the server schema.
1513   *
1514   * @param  objectClass  The name or OID of the structural object class for
1515   *                      which to retrieve the associated name form.  It must
1516   *                      not be {@code null}.
1517   *
1518   * @return  The requested name form, or {@code null} if there is no
1519   *          such rule defined in the server schema.
1520   */
1521  public NameFormDefinition getNameFormByObjectClass(final String objectClass)
1522  {
1523    ensureNotNull(objectClass);
1524
1525    return nfMapByOC.get(toLowerCase(objectClass));
1526  }
1527
1528
1529
1530  /**
1531   * Retrieves the set of object class definitions contained in the server
1532   * schema.
1533   *
1534   * @return  The set of object class definitions contained in the server
1535   *          schema.
1536   */
1537  public Set<ObjectClassDefinition> getObjectClasses()
1538  {
1539    return ocSet;
1540  }
1541
1542
1543
1544  /**
1545   * Retrieves the set of abstract object class definitions contained in the
1546   * server schema.
1547   *
1548   * @return  The set of abstract object class definitions contained in the
1549   *          server schema.
1550   */
1551  public Set<ObjectClassDefinition> getAbstractObjectClasses()
1552  {
1553    return abstractOCSet;
1554  }
1555
1556
1557
1558  /**
1559   * Retrieves the set of auxiliary object class definitions contained in the
1560   * server schema.
1561   *
1562   * @return  The set of auxiliary object class definitions contained in the
1563   *          server schema.
1564   */
1565  public Set<ObjectClassDefinition> getAuxiliaryObjectClasses()
1566  {
1567    return auxiliaryOCSet;
1568  }
1569
1570
1571
1572  /**
1573   * Retrieves the set of structural object class definitions contained in the
1574   * server schema.
1575   *
1576   * @return  The set of structural object class definitions contained in the
1577   *          server schema.
1578   */
1579  public Set<ObjectClassDefinition> getStructuralObjectClasses()
1580  {
1581    return structuralOCSet;
1582  }
1583
1584
1585
1586  /**
1587   * Retrieves the object class with the specified name or OID from the server
1588   * schema.
1589   *
1590   * @param  name  The name or OID of the object class to retrieve.  It must
1591   *               not be {@code null}.
1592   *
1593   * @return  The requested object class, or {@code null} if there is no such
1594   *          class defined in the server schema.
1595   */
1596  public ObjectClassDefinition getObjectClass(final String name)
1597  {
1598    ensureNotNull(name);
1599
1600    return ocMap.get(toLowerCase(name));
1601  }
1602
1603
1604
1605  /**
1606   * Retrieves a hash code for this schema object.
1607   *
1608   * @return  A hash code for this schema object.
1609   */
1610  @Override()
1611  public int hashCode()
1612  {
1613    int hc;
1614    try
1615    {
1616      hc = schemaEntry.getParsedDN().hashCode();
1617    }
1618    catch (final Exception e)
1619    {
1620      debugException(e);
1621      hc = toLowerCase(schemaEntry.getDN()).hashCode();
1622    }
1623
1624    Attribute a = schemaEntry.getAttribute(ATTR_ATTRIBUTE_SYNTAX);
1625    if (a != null)
1626    {
1627      hc += a.hashCode();
1628    }
1629
1630    a = schemaEntry.getAttribute(ATTR_MATCHING_RULE);
1631    if (a != null)
1632    {
1633      hc += a.hashCode();
1634    }
1635
1636    a = schemaEntry.getAttribute(ATTR_ATTRIBUTE_TYPE);
1637    if (a != null)
1638    {
1639      hc += a.hashCode();
1640    }
1641
1642    a = schemaEntry.getAttribute(ATTR_OBJECT_CLASS);
1643    if (a != null)
1644    {
1645      hc += a.hashCode();
1646    }
1647
1648    a = schemaEntry.getAttribute(ATTR_NAME_FORM);
1649    if (a != null)
1650    {
1651      hc += a.hashCode();
1652    }
1653
1654    a = schemaEntry.getAttribute(ATTR_DIT_CONTENT_RULE);
1655    if (a != null)
1656    {
1657      hc += a.hashCode();
1658    }
1659
1660    a = schemaEntry.getAttribute(ATTR_DIT_STRUCTURE_RULE);
1661    if (a != null)
1662    {
1663      hc += a.hashCode();
1664    }
1665
1666    a = schemaEntry.getAttribute(ATTR_MATCHING_RULE_USE);
1667    if (a != null)
1668    {
1669      hc += a.hashCode();
1670    }
1671
1672    return hc;
1673  }
1674
1675
1676
1677  /**
1678   * Indicates whether the provided object is equal to this schema object.
1679   *
1680   * @param  o  The object for which to make the determination.
1681   *
1682   * @return  {@code true} if the provided object is equal to this schema
1683   *          object, or {@code false} if not.
1684   */
1685  @Override()
1686  public boolean equals(final Object o)
1687  {
1688    if (o == null)
1689    {
1690      return false;
1691    }
1692
1693    if (o == this)
1694    {
1695      return true;
1696    }
1697
1698    if (! (o instanceof Schema))
1699    {
1700      return false;
1701    }
1702
1703    final Schema s = (Schema) o;
1704
1705    try
1706    {
1707      if (! schemaEntry.getParsedDN().equals(s.schemaEntry.getParsedDN()))
1708      {
1709        return false;
1710      }
1711    }
1712    catch (final Exception e)
1713    {
1714      debugException(e);
1715      if (! schemaEntry.getDN().equalsIgnoreCase(s.schemaEntry.getDN()))
1716      {
1717        return false;
1718      }
1719    }
1720
1721    return (asSet.equals(s.asSet) &&
1722         mrSet.equals(s.mrSet) &&
1723         atSet.equals(s.atSet) &&
1724         ocSet.equals(s.ocSet) &&
1725         nfSet.equals(s.nfSet) &&
1726         dcrSet.equals(s.dcrSet) &&
1727         dsrSet.equals(s.dsrSet) &&
1728         mruSet.equals(s.mruSet));
1729  }
1730
1731
1732
1733  /**
1734   * Retrieves a string representation of the associated schema entry.
1735   *
1736   * @return  A string representation of the associated schema entry.
1737   */
1738  @Override()
1739  public String toString()
1740  {
1741    return schemaEntry.toString();
1742  }
1743}