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