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