001/*
002 * Copyright 2007-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2018 Ping Identity Corporation
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 (oc.getObjectClassType(null))
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   * Reads schema information from the provided input stream.  The information
884   * should be in LDIF form, with the definitions represented in the form
885   * described in section 4.1 of RFC 4512.  Only a single entry will be read
886   * from the input stream, and it will be closed at the end of this method.
887   *
888   * @param  inputStream  The input stream from which the schema entry will be
889   *                      read.  It must not be {@code null}, and it will be
890   *                      closed when this method returns.
891   *
892   * @return  The schema read from the provided input stream, or {@code null} if
893   *          the end of the input stream is reached without reading any data.
894   *
895   * @throws  IOException  If a problem is encountered while attempting to read
896   *                       from the provided input stream.
897   *
898   * @throws  LDIFException  If a problem occurs while attempting to parse the
899   *                         data read as LDIF.
900   */
901  public static Schema getSchema(final InputStream inputStream)
902         throws IOException, LDIFException
903  {
904    ensureNotNull(inputStream);
905
906    final LDIFReader ldifReader = new LDIFReader(inputStream);
907
908    try
909    {
910      final Entry e = ldifReader.readEntry();
911      if (e == null)
912      {
913        return null;
914      }
915      else
916      {
917        return new Schema(e);
918      }
919    }
920    finally
921    {
922      ldifReader.close();
923    }
924  }
925
926
927
928  /**
929   * Retrieves a schema object that contains definitions for a number of
930   * standard attribute types and object classes from LDAP-related RFCs and
931   * Internet Drafts.
932   *
933   * @return  A schema object that contains definitions for a number of standard
934   *          attribute types and object classes from LDAP-related RFCs and
935   *          Internet Drafts.
936   *
937   * @throws  LDAPException  If a problem occurs while attempting to obtain or
938   *                         parse the default standard schema definitions.
939   */
940  public static Schema getDefaultStandardSchema()
941         throws LDAPException
942  {
943    final Schema s = DEFAULT_STANDARD_SCHEMA.get();
944    if (s != null)
945    {
946      return s;
947    }
948
949    synchronized (DEFAULT_STANDARD_SCHEMA)
950    {
951      try
952      {
953        final ClassLoader classLoader = Schema.class.getClassLoader();
954        final InputStream inputStream =
955             classLoader.getResourceAsStream(DEFAULT_SCHEMA_RESOURCE_PATH);
956        final LDIFReader ldifReader = new LDIFReader(inputStream);
957        final Entry schemaEntry = ldifReader.readEntry();
958        ldifReader.close();
959
960        final Schema schema = new Schema(schemaEntry);
961        DEFAULT_STANDARD_SCHEMA.set(schema);
962        return schema;
963      }
964      catch (final Exception e)
965      {
966        debugException(e);
967        throw new LDAPException(ResultCode.LOCAL_ERROR,
968             ERR_SCHEMA_CANNOT_LOAD_DEFAULT_DEFINITIONS.get(
969                  getExceptionMessage(e)),
970             e);
971      }
972    }
973  }
974
975
976
977  /**
978   * Retrieves a schema containing all of the elements of each of the provided
979   * schemas.
980   *
981   * @param  schemas  The schemas to be merged.  It must not be {@code null} or
982   *                  empty.
983   *
984   * @return  A merged representation of the provided schemas.
985   */
986  public static Schema mergeSchemas(final Schema... schemas)
987  {
988    if ((schemas == null) || (schemas.length == 0))
989    {
990      return null;
991    }
992    else if (schemas.length == 1)
993    {
994      return schemas[0];
995    }
996
997    final LinkedHashMap<String,String> asMap =
998         new LinkedHashMap<String,String>();
999    final LinkedHashMap<String,String> atMap =
1000         new LinkedHashMap<String,String>();
1001    final LinkedHashMap<String,String> dcrMap =
1002         new LinkedHashMap<String,String>();
1003    final LinkedHashMap<Integer,String> dsrMap =
1004         new LinkedHashMap<Integer,String>();
1005    final LinkedHashMap<String,String> mrMap =
1006         new LinkedHashMap<String,String>();
1007    final LinkedHashMap<String,String> mruMap =
1008         new LinkedHashMap<String,String>();
1009    final LinkedHashMap<String,String> nfMap =
1010         new LinkedHashMap<String,String>();
1011    final LinkedHashMap<String,String> ocMap =
1012         new LinkedHashMap<String,String>();
1013
1014    for (final Schema s : schemas)
1015    {
1016      for (final AttributeSyntaxDefinition as : s.asSet)
1017      {
1018        asMap.put(toLowerCase(as.getOID()), as.toString());
1019      }
1020
1021      for (final AttributeTypeDefinition at : s.atSet)
1022      {
1023        atMap.put(toLowerCase(at.getOID()), at.toString());
1024      }
1025
1026      for (final DITContentRuleDefinition dcr : s.dcrSet)
1027      {
1028        dcrMap.put(toLowerCase(dcr.getOID()), dcr.toString());
1029      }
1030
1031      for (final DITStructureRuleDefinition dsr : s.dsrSet)
1032      {
1033        dsrMap.put(dsr.getRuleID(), dsr.toString());
1034      }
1035
1036      for (final MatchingRuleDefinition mr : s.mrSet)
1037      {
1038        mrMap.put(toLowerCase(mr.getOID()), mr.toString());
1039      }
1040
1041      for (final MatchingRuleUseDefinition mru : s.mruSet)
1042      {
1043        mruMap.put(toLowerCase(mru.getOID()), mru.toString());
1044      }
1045
1046      for (final NameFormDefinition nf : s.nfSet)
1047      {
1048        nfMap.put(toLowerCase(nf.getOID()), nf.toString());
1049      }
1050
1051      for (final ObjectClassDefinition oc : s.ocSet)
1052      {
1053        ocMap.put(toLowerCase(oc.getOID()), oc.toString());
1054      }
1055    }
1056
1057    final Entry e = new Entry(schemas[0].getSchemaEntry().getDN());
1058
1059    final Attribute ocAttr =
1060         schemas[0].getSchemaEntry().getObjectClassAttribute();
1061    if (ocAttr == null)
1062    {
1063      e.addAttribute("objectClass", "top", "ldapSubEntry", "subschema");
1064    }
1065    else
1066    {
1067      e.addAttribute(ocAttr);
1068    }
1069
1070    if (! asMap.isEmpty())
1071    {
1072      final String[] values = new String[asMap.size()];
1073      e.addAttribute(ATTR_ATTRIBUTE_SYNTAX, asMap.values().toArray(values));
1074    }
1075
1076    if (! mrMap.isEmpty())
1077    {
1078      final String[] values = new String[mrMap.size()];
1079      e.addAttribute(ATTR_MATCHING_RULE, mrMap.values().toArray(values));
1080    }
1081
1082    if (! atMap.isEmpty())
1083    {
1084      final String[] values = new String[atMap.size()];
1085      e.addAttribute(ATTR_ATTRIBUTE_TYPE, atMap.values().toArray(values));
1086    }
1087
1088    if (! ocMap.isEmpty())
1089    {
1090      final String[] values = new String[ocMap.size()];
1091      e.addAttribute(ATTR_OBJECT_CLASS, ocMap.values().toArray(values));
1092    }
1093
1094    if (! dcrMap.isEmpty())
1095    {
1096      final String[] values = new String[dcrMap.size()];
1097      e.addAttribute(ATTR_DIT_CONTENT_RULE, dcrMap.values().toArray(values));
1098    }
1099
1100    if (! dsrMap.isEmpty())
1101    {
1102      final String[] values = new String[dsrMap.size()];
1103      e.addAttribute(ATTR_DIT_STRUCTURE_RULE, dsrMap.values().toArray(values));
1104    }
1105
1106    if (! nfMap.isEmpty())
1107    {
1108      final String[] values = new String[nfMap.size()];
1109      e.addAttribute(ATTR_NAME_FORM, nfMap.values().toArray(values));
1110    }
1111
1112    if (! mruMap.isEmpty())
1113    {
1114      final String[] values = new String[mruMap.size()];
1115      e.addAttribute(ATTR_MATCHING_RULE_USE, mruMap.values().toArray(values));
1116    }
1117
1118    return new Schema(e);
1119  }
1120
1121
1122
1123  /**
1124   * Retrieves the entry used to create this schema object.
1125   *
1126   * @return  The entry used to create this schema object.
1127   */
1128  public ReadOnlyEntry getSchemaEntry()
1129  {
1130    return schemaEntry;
1131  }
1132
1133
1134
1135  /**
1136   * Retrieves the value of the subschemaSubentry attribute from the specified
1137   * entry using the provided connection.
1138   *
1139   * @param  connection  The connection to use in order to perform the search.
1140   *                     It must not be {@code null}.
1141   * @param  entryDN     The DN of the entry from which to retrieve the
1142   *                     subschemaSubentry attribute.  It may be {@code null} or
1143   *                     an empty string in order to retrieve the value from the
1144   *                     server's root DSE.
1145   *
1146   * @return  The value of the subschemaSubentry attribute from the specified
1147   *          entry, or {@code null} if it is not available for some reason
1148   *          (e.g., the client does not have permission to read the target
1149   *          entry or the subschemaSubentry attribute).
1150   *
1151   * @throws  LDAPException  If a problem occurs while attempting to retrieve
1152   *                         the specified entry.
1153   */
1154  public static String getSubschemaSubentryDN(final LDAPConnection connection,
1155                                              final String entryDN)
1156         throws LDAPException
1157  {
1158    ensureNotNull(connection);
1159
1160    final Entry e;
1161    if (entryDN == null)
1162    {
1163      e = connection.getEntry("", SUBSCHEMA_SUBENTRY_REQUEST_ATTRS);
1164    }
1165    else
1166    {
1167      e = connection.getEntry(entryDN, SUBSCHEMA_SUBENTRY_REQUEST_ATTRS);
1168    }
1169
1170    if (e == null)
1171    {
1172      return null;
1173    }
1174
1175    return e.getAttributeValue(ATTR_SUBSCHEMA_SUBENTRY);
1176  }
1177
1178
1179
1180  /**
1181   * Retrieves the set of attribute syntax definitions contained in the server
1182   * schema.
1183   *
1184   * @return  The set of attribute syntax definitions contained in the server
1185   *          schema.
1186   */
1187  public Set<AttributeSyntaxDefinition> getAttributeSyntaxes()
1188  {
1189    return asSet;
1190  }
1191
1192
1193
1194  /**
1195   * Retrieves the attribute syntax with the specified OID from the server
1196   * schema.
1197   *
1198   * @param  oid  The OID of the attribute syntax to retrieve.  It must not be
1199   *              {@code null}.  It may optionally include a minimum upper bound
1200   *              (as may appear when the syntax OID is included in an attribute
1201   *              type definition), but if it does then that portion will be
1202   *              ignored when retrieving the attribute syntax.
1203   *
1204   * @return  The requested attribute syntax, or {@code null} if there is no
1205   *          such syntax defined in the server schema.
1206   */
1207  public AttributeSyntaxDefinition getAttributeSyntax(final String oid)
1208  {
1209    ensureNotNull(oid);
1210
1211    final String lowerOID = toLowerCase(oid);
1212    final int    curlyPos = lowerOID.indexOf('{');
1213
1214    if (curlyPos > 0)
1215    {
1216      return asMap.get(lowerOID.substring(0, curlyPos));
1217    }
1218    else
1219    {
1220      return asMap.get(lowerOID);
1221    }
1222  }
1223
1224
1225
1226  /**
1227   * Retrieves the set of attribute type definitions contained in the server
1228   * schema.
1229   *
1230   * @return  The set of attribute type definitions contained in the server
1231   *          schema.
1232   */
1233  public Set<AttributeTypeDefinition> getAttributeTypes()
1234  {
1235    return atSet;
1236  }
1237
1238
1239
1240  /**
1241   * Retrieves the set of operational attribute type definitions (i.e., those
1242   * definitions with a usage of directoryOperation, distributedOperation, or
1243   * dSAOperation) contained in the  server  schema.
1244   *
1245   * @return  The set of operational attribute type definitions contained in the
1246   *          server schema.
1247   */
1248  public Set<AttributeTypeDefinition> getOperationalAttributeTypes()
1249  {
1250    return operationalATSet;
1251  }
1252
1253
1254
1255  /**
1256   * Retrieves the set of user attribute type definitions (i.e., those
1257   * definitions with a usage of userApplications) contained in the  server
1258   * schema.
1259   *
1260   * @return  The set of user attribute type definitions contained in the server
1261   *          schema.
1262   */
1263  public Set<AttributeTypeDefinition> getUserAttributeTypes()
1264  {
1265    return userATSet;
1266  }
1267
1268
1269
1270  /**
1271   * Retrieves the attribute type with the specified name or OID from the server
1272   * schema.
1273   *
1274   * @param  name  The name or OID of the attribute type to retrieve.  It must
1275   *               not be {@code null}.
1276   *
1277   * @return  The requested attribute type, or {@code null} if there is no
1278   *          such attribute type defined in the server schema.
1279   */
1280  public AttributeTypeDefinition getAttributeType(final String name)
1281  {
1282    ensureNotNull(name);
1283
1284    return atMap.get(toLowerCase(name));
1285  }
1286
1287
1288
1289  /**
1290   * Retrieves a list of all subordinate attribute type definitions for the
1291   * provided attribute type definition.
1292   *
1293   * @param  d  The attribute type definition for which to retrieve all
1294   *            subordinate attribute types.  It must not be {@code null}.
1295   *
1296   * @return  A list of all subordinate attribute type definitions for the
1297   *          provided attribute type definition, or an empty list if it does
1298   *          not have any subordinate types or the provided attribute type is
1299   *          not defined in the schema.
1300   */
1301  public List<AttributeTypeDefinition> getSubordinateAttributeTypes(
1302                                            final AttributeTypeDefinition d)
1303  {
1304    ensureNotNull(d);
1305
1306    final List<AttributeTypeDefinition> l = subordinateAttributeTypes.get(d);
1307    if (l == null)
1308    {
1309      return Collections.emptyList();
1310    }
1311    else
1312    {
1313      return Collections.unmodifiableList(l);
1314    }
1315  }
1316
1317
1318
1319  /**
1320   * Retrieves the set of DIT content rule definitions contained in the server
1321   * schema.
1322   *
1323   * @return  The set of DIT content rule definitions contained in the server
1324   *          schema.
1325   */
1326  public Set<DITContentRuleDefinition> getDITContentRules()
1327  {
1328    return dcrSet;
1329  }
1330
1331
1332
1333  /**
1334   * Retrieves the DIT content rule with the specified name or OID from the
1335   * server schema.
1336   *
1337   * @param  name  The name or OID of the DIT content rule to retrieve.  It must
1338   *               not be {@code null}.
1339   *
1340   * @return  The requested DIT content rule, or {@code null} if there is no
1341   *          such rule defined in the server schema.
1342   */
1343  public DITContentRuleDefinition getDITContentRule(final String name)
1344  {
1345    ensureNotNull(name);
1346
1347    return dcrMap.get(toLowerCase(name));
1348  }
1349
1350
1351
1352  /**
1353   * Retrieves the set of DIT structure rule definitions contained in the server
1354   * schema.
1355   *
1356   * @return  The set of DIT structure rule definitions contained in the server
1357   *          schema.
1358   */
1359  public Set<DITStructureRuleDefinition> getDITStructureRules()
1360  {
1361    return dsrSet;
1362  }
1363
1364
1365
1366  /**
1367   * Retrieves the DIT content rule with the specified rule ID from the server
1368   * schema.
1369   *
1370   * @param  ruleID  The rule ID for the DIT structure rule to retrieve.
1371   *
1372   * @return  The requested DIT structure rule, or {@code null} if there is no
1373   *          such rule defined in the server schema.
1374   */
1375  public DITStructureRuleDefinition getDITStructureRuleByID(final int ruleID)
1376  {
1377    return dsrMapByID.get(ruleID);
1378  }
1379
1380
1381
1382  /**
1383   * Retrieves the DIT content rule with the specified name from the server
1384   * schema.
1385   *
1386   * @param  ruleName  The name of the DIT structure rule to retrieve.  It must
1387   *                   not be {@code null}.
1388   *
1389   * @return  The requested DIT structure rule, or {@code null} if there is no
1390   *          such rule defined in the server schema.
1391   */
1392  public DITStructureRuleDefinition getDITStructureRuleByName(
1393                                         final String ruleName)
1394  {
1395    ensureNotNull(ruleName);
1396
1397    return dsrMapByName.get(toLowerCase(ruleName));
1398  }
1399
1400
1401
1402  /**
1403   * Retrieves the DIT content rule associated with the specified name form from
1404   * the server schema.
1405   *
1406   * @param  nameForm  The name or OID of the name form for which to retrieve
1407   *                   the associated DIT structure rule.
1408   *
1409   * @return  The requested DIT structure rule, or {@code null} if there is no
1410   *          such rule defined in the server schema.
1411   */
1412  public DITStructureRuleDefinition getDITStructureRuleByNameForm(
1413                                         final String nameForm)
1414  {
1415    ensureNotNull(nameForm);
1416
1417    return dsrMapByNameForm.get(toLowerCase(nameForm));
1418  }
1419
1420
1421
1422  /**
1423   * Retrieves the set of matching rule definitions contained in the server
1424   * schema.
1425   *
1426   * @return  The set of matching rule definitions contained in the server
1427   *          schema.
1428   */
1429  public Set<MatchingRuleDefinition> getMatchingRules()
1430  {
1431    return mrSet;
1432  }
1433
1434
1435
1436  /**
1437   * Retrieves the matching rule with the specified name or OID from the server
1438   * schema.
1439   *
1440   * @param  name  The name or OID of the matching rule to retrieve.  It must
1441   *               not be {@code null}.
1442   *
1443   * @return  The requested matching rule, or {@code null} if there is no
1444   *          such rule defined in the server schema.
1445   */
1446  public MatchingRuleDefinition getMatchingRule(final String name)
1447  {
1448    ensureNotNull(name);
1449
1450    return mrMap.get(toLowerCase(name));
1451  }
1452
1453
1454
1455  /**
1456   * Retrieves the set of matching rule use definitions contained in the server
1457   * schema.
1458   *
1459   * @return  The set of matching rule use definitions contained in the server
1460   *          schema.
1461   */
1462  public Set<MatchingRuleUseDefinition> getMatchingRuleUses()
1463  {
1464    return mruSet;
1465  }
1466
1467
1468
1469  /**
1470   * Retrieves the matching rule use with the specified name or OID from the
1471   * server schema.
1472   *
1473   * @param  name  The name or OID of the matching rule use to retrieve.  It
1474   *               must not be {@code null}.
1475   *
1476   * @return  The requested matching rule, or {@code null} if there is no
1477   *          such matching rule use defined in the server schema.
1478   */
1479  public MatchingRuleUseDefinition getMatchingRuleUse(final String name)
1480  {
1481    ensureNotNull(name);
1482
1483    return mruMap.get(toLowerCase(name));
1484  }
1485
1486
1487
1488  /**
1489   * Retrieves the set of name form definitions contained in the server schema.
1490   *
1491   * @return  The set of name form definitions contained in the server schema.
1492   */
1493  public Set<NameFormDefinition> getNameForms()
1494  {
1495    return nfSet;
1496  }
1497
1498
1499
1500  /**
1501   * Retrieves the name form with the specified name or OID from the server
1502   * schema.
1503   *
1504   * @param  name  The name or OID of the name form to retrieve.  It must not be
1505   *               {@code null}.
1506   *
1507   * @return  The requested name form, or {@code null} if there is no
1508   *          such rule defined in the server schema.
1509   */
1510  public NameFormDefinition getNameFormByName(final String name)
1511  {
1512    ensureNotNull(name);
1513
1514    return nfMapByName.get(toLowerCase(name));
1515  }
1516
1517
1518
1519  /**
1520   * Retrieves the name form associated with the specified structural object
1521   * class from the server schema.
1522   *
1523   * @param  objectClass  The name or OID of the structural object class for
1524   *                      which to retrieve the associated name form.  It must
1525   *                      not be {@code null}.
1526   *
1527   * @return  The requested name form, or {@code null} if there is no
1528   *          such rule defined in the server schema.
1529   */
1530  public NameFormDefinition getNameFormByObjectClass(final String objectClass)
1531  {
1532    ensureNotNull(objectClass);
1533
1534    return nfMapByOC.get(toLowerCase(objectClass));
1535  }
1536
1537
1538
1539  /**
1540   * Retrieves the set of object class definitions contained in the server
1541   * schema.
1542   *
1543   * @return  The set of object class definitions contained in the server
1544   *          schema.
1545   */
1546  public Set<ObjectClassDefinition> getObjectClasses()
1547  {
1548    return ocSet;
1549  }
1550
1551
1552
1553  /**
1554   * Retrieves the set of abstract object class definitions contained in the
1555   * server schema.
1556   *
1557   * @return  The set of abstract object class definitions contained in the
1558   *          server schema.
1559   */
1560  public Set<ObjectClassDefinition> getAbstractObjectClasses()
1561  {
1562    return abstractOCSet;
1563  }
1564
1565
1566
1567  /**
1568   * Retrieves the set of auxiliary object class definitions contained in the
1569   * server schema.
1570   *
1571   * @return  The set of auxiliary object class definitions contained in the
1572   *          server schema.
1573   */
1574  public Set<ObjectClassDefinition> getAuxiliaryObjectClasses()
1575  {
1576    return auxiliaryOCSet;
1577  }
1578
1579
1580
1581  /**
1582   * Retrieves the set of structural object class definitions contained in the
1583   * server schema.
1584   *
1585   * @return  The set of structural object class definitions contained in the
1586   *          server schema.
1587   */
1588  public Set<ObjectClassDefinition> getStructuralObjectClasses()
1589  {
1590    return structuralOCSet;
1591  }
1592
1593
1594
1595  /**
1596   * Retrieves the object class with the specified name or OID from the server
1597   * schema.
1598   *
1599   * @param  name  The name or OID of the object class to retrieve.  It must
1600   *               not be {@code null}.
1601   *
1602   * @return  The requested object class, or {@code null} if there is no such
1603   *          class defined in the server schema.
1604   */
1605  public ObjectClassDefinition getObjectClass(final String name)
1606  {
1607    ensureNotNull(name);
1608
1609    return ocMap.get(toLowerCase(name));
1610  }
1611
1612
1613
1614  /**
1615   * Retrieves a hash code for this schema object.
1616   *
1617   * @return  A hash code for this schema object.
1618   */
1619  @Override()
1620  public int hashCode()
1621  {
1622    int hc;
1623    try
1624    {
1625      hc = schemaEntry.getParsedDN().hashCode();
1626    }
1627    catch (final Exception e)
1628    {
1629      debugException(e);
1630      hc = toLowerCase(schemaEntry.getDN()).hashCode();
1631    }
1632
1633    Attribute a = schemaEntry.getAttribute(ATTR_ATTRIBUTE_SYNTAX);
1634    if (a != null)
1635    {
1636      hc += a.hashCode();
1637    }
1638
1639    a = schemaEntry.getAttribute(ATTR_MATCHING_RULE);
1640    if (a != null)
1641    {
1642      hc += a.hashCode();
1643    }
1644
1645    a = schemaEntry.getAttribute(ATTR_ATTRIBUTE_TYPE);
1646    if (a != null)
1647    {
1648      hc += a.hashCode();
1649    }
1650
1651    a = schemaEntry.getAttribute(ATTR_OBJECT_CLASS);
1652    if (a != null)
1653    {
1654      hc += a.hashCode();
1655    }
1656
1657    a = schemaEntry.getAttribute(ATTR_NAME_FORM);
1658    if (a != null)
1659    {
1660      hc += a.hashCode();
1661    }
1662
1663    a = schemaEntry.getAttribute(ATTR_DIT_CONTENT_RULE);
1664    if (a != null)
1665    {
1666      hc += a.hashCode();
1667    }
1668
1669    a = schemaEntry.getAttribute(ATTR_DIT_STRUCTURE_RULE);
1670    if (a != null)
1671    {
1672      hc += a.hashCode();
1673    }
1674
1675    a = schemaEntry.getAttribute(ATTR_MATCHING_RULE_USE);
1676    if (a != null)
1677    {
1678      hc += a.hashCode();
1679    }
1680
1681    return hc;
1682  }
1683
1684
1685
1686  /**
1687   * Indicates whether the provided object is equal to this schema object.
1688   *
1689   * @param  o  The object for which to make the determination.
1690   *
1691   * @return  {@code true} if the provided object is equal to this schema
1692   *          object, or {@code false} if not.
1693   */
1694  @Override()
1695  public boolean equals(final Object o)
1696  {
1697    if (o == null)
1698    {
1699      return false;
1700    }
1701
1702    if (o == this)
1703    {
1704      return true;
1705    }
1706
1707    if (! (o instanceof Schema))
1708    {
1709      return false;
1710    }
1711
1712    final Schema s = (Schema) o;
1713
1714    try
1715    {
1716      if (! schemaEntry.getParsedDN().equals(s.schemaEntry.getParsedDN()))
1717      {
1718        return false;
1719      }
1720    }
1721    catch (final Exception e)
1722    {
1723      debugException(e);
1724      if (! schemaEntry.getDN().equalsIgnoreCase(s.schemaEntry.getDN()))
1725      {
1726        return false;
1727      }
1728    }
1729
1730    return (asSet.equals(s.asSet) &&
1731         mrSet.equals(s.mrSet) &&
1732         atSet.equals(s.atSet) &&
1733         ocSet.equals(s.ocSet) &&
1734         nfSet.equals(s.nfSet) &&
1735         dcrSet.equals(s.dcrSet) &&
1736         dsrSet.equals(s.dsrSet) &&
1737         mruSet.equals(s.mruSet));
1738  }
1739
1740
1741
1742  /**
1743   * Retrieves a string representation of the associated schema entry.
1744   *
1745   * @return  A string representation of the associated schema entry.
1746   */
1747  @Override()
1748  public String toString()
1749  {
1750    return schemaEntry.toString();
1751  }
1752}