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