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