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