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