001    /*
002     * Copyright 2007-2017 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2017 UnboundID Corp.
007     *
008     * This program is free software; you can redistribute it and/or modify
009     * it under the terms of the GNU General Public License (GPLv2 only)
010     * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011     * as published by the Free Software Foundation.
012     *
013     * This program is distributed in the hope that it will be useful,
014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program; if not, see <http://www.gnu.org/licenses>.
020     */
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       * Reads schema information from the provided input stream.  The information
884       * should be in LDIF form, with the definitions represented in the form
885       * described in section 4.1 of RFC 4512.  Only a single entry will be read
886       * from the input stream, and it will be closed at the end of this method.
887       *
888       * @param  inputStream  The input stream from which the schema entry will be
889       *                      read.  It must not be {@code null}, and it will be
890       *                      closed when this method returns.
891       *
892       * @return  The schema read from the provided input stream, or {@code null} if
893       *          the end of the input stream is reached without reading any data.
894       *
895       * @throws  IOException  If a problem is encountered while attempting to read
896       *                       from the provided input stream.
897       *
898       * @throws  LDIFException  If a problem occurs while attempting to parse the
899       *                         data read as LDIF.
900       */
901      public static Schema getSchema(final InputStream inputStream)
902             throws IOException, LDIFException
903      {
904        ensureNotNull(inputStream);
905    
906        final LDIFReader ldifReader = new LDIFReader(inputStream);
907    
908        try
909        {
910          final Entry e = ldifReader.readEntry();
911          if (e == null)
912          {
913            return null;
914          }
915          else
916          {
917            return new Schema(e);
918          }
919        }
920        finally
921        {
922          ldifReader.close();
923        }
924      }
925    
926    
927    
928      /**
929       * Retrieves a schema object that contains definitions for a number of
930       * standard attribute types and object classes from LDAP-related RFCs and
931       * Internet Drafts.
932       *
933       * @return  A schema object that contains definitions for a number of standard
934       *          attribute types and object classes from LDAP-related RFCs and
935       *          Internet Drafts.
936       *
937       * @throws  LDAPException  If a problem occurs while attempting to obtain or
938       *                         parse the default standard schema definitions.
939       */
940      public static Schema getDefaultStandardSchema()
941             throws LDAPException
942      {
943        final Schema s = DEFAULT_STANDARD_SCHEMA.get();
944        if (s != null)
945        {
946          return s;
947        }
948    
949        synchronized (DEFAULT_STANDARD_SCHEMA)
950        {
951          try
952          {
953            final ClassLoader classLoader = Schema.class.getClassLoader();
954            final InputStream inputStream =
955                 classLoader.getResourceAsStream(DEFAULT_SCHEMA_RESOURCE_PATH);
956            final LDIFReader ldifReader = new LDIFReader(inputStream);
957            final Entry schemaEntry = ldifReader.readEntry();
958            ldifReader.close();
959    
960            final Schema schema = new Schema(schemaEntry);
961            DEFAULT_STANDARD_SCHEMA.set(schema);
962            return schema;
963          }
964          catch (final Exception e)
965          {
966            debugException(e);
967            throw new LDAPException(ResultCode.LOCAL_ERROR,
968                 ERR_SCHEMA_CANNOT_LOAD_DEFAULT_DEFINITIONS.get(
969                      getExceptionMessage(e)),
970                 e);
971          }
972        }
973      }
974    
975    
976    
977      /**
978       * Retrieves a schema containing all of the elements of each of the provided
979       * schemas.
980       *
981       * @param  schemas  The schemas to be merged.  It must not be {@code null} or
982       *                  empty.
983       *
984       * @return  A merged representation of the provided schemas.
985       */
986      public static Schema mergeSchemas(final Schema... schemas)
987      {
988        if ((schemas == null) || (schemas.length == 0))
989        {
990          return null;
991        }
992        else if (schemas.length == 1)
993        {
994          return schemas[0];
995        }
996    
997        final LinkedHashMap<String,String> asMap =
998             new LinkedHashMap<String,String>();
999        final LinkedHashMap<String,String> atMap =
1000             new LinkedHashMap<String,String>();
1001        final LinkedHashMap<String,String> dcrMap =
1002             new LinkedHashMap<String,String>();
1003        final LinkedHashMap<Integer,String> dsrMap =
1004             new LinkedHashMap<Integer,String>();
1005        final LinkedHashMap<String,String> mrMap =
1006             new LinkedHashMap<String,String>();
1007        final LinkedHashMap<String,String> mruMap =
1008             new LinkedHashMap<String,String>();
1009        final LinkedHashMap<String,String> nfMap =
1010             new LinkedHashMap<String,String>();
1011        final LinkedHashMap<String,String> ocMap =
1012             new LinkedHashMap<String,String>();
1013    
1014        for (final Schema s : schemas)
1015        {
1016          for (final AttributeSyntaxDefinition as : s.asSet)
1017          {
1018            asMap.put(toLowerCase(as.getOID()), as.toString());
1019          }
1020    
1021          for (final AttributeTypeDefinition at : s.atSet)
1022          {
1023            atMap.put(toLowerCase(at.getOID()), at.toString());
1024          }
1025    
1026          for (final DITContentRuleDefinition dcr : s.dcrSet)
1027          {
1028            dcrMap.put(toLowerCase(dcr.getOID()), dcr.toString());
1029          }
1030    
1031          for (final DITStructureRuleDefinition dsr : s.dsrSet)
1032          {
1033            dsrMap.put(dsr.getRuleID(), dsr.toString());
1034          }
1035    
1036          for (final MatchingRuleDefinition mr : s.mrSet)
1037          {
1038            mrMap.put(toLowerCase(mr.getOID()), mr.toString());
1039          }
1040    
1041          for (final MatchingRuleUseDefinition mru : s.mruSet)
1042          {
1043            mruMap.put(toLowerCase(mru.getOID()), mru.toString());
1044          }
1045    
1046          for (final NameFormDefinition nf : s.nfSet)
1047          {
1048            nfMap.put(toLowerCase(nf.getOID()), nf.toString());
1049          }
1050    
1051          for (final ObjectClassDefinition oc : s.ocSet)
1052          {
1053            ocMap.put(toLowerCase(oc.getOID()), oc.toString());
1054          }
1055        }
1056    
1057        final Entry e = new Entry(schemas[0].getSchemaEntry().getDN());
1058    
1059        final Attribute ocAttr =
1060             schemas[0].getSchemaEntry().getObjectClassAttribute();
1061        if (ocAttr == null)
1062        {
1063          e.addAttribute("objectClass", "top", "ldapSubEntry", "subschema");
1064        }
1065        else
1066        {
1067          e.addAttribute(ocAttr);
1068        }
1069    
1070        if (! asMap.isEmpty())
1071        {
1072          final String[] values = new String[asMap.size()];
1073          e.addAttribute(ATTR_ATTRIBUTE_SYNTAX, asMap.values().toArray(values));
1074        }
1075    
1076        if (! mrMap.isEmpty())
1077        {
1078          final String[] values = new String[mrMap.size()];
1079          e.addAttribute(ATTR_MATCHING_RULE, mrMap.values().toArray(values));
1080        }
1081    
1082        if (! atMap.isEmpty())
1083        {
1084          final String[] values = new String[atMap.size()];
1085          e.addAttribute(ATTR_ATTRIBUTE_TYPE, atMap.values().toArray(values));
1086        }
1087    
1088        if (! ocMap.isEmpty())
1089        {
1090          final String[] values = new String[ocMap.size()];
1091          e.addAttribute(ATTR_OBJECT_CLASS, ocMap.values().toArray(values));
1092        }
1093    
1094        if (! dcrMap.isEmpty())
1095        {
1096          final String[] values = new String[dcrMap.size()];
1097          e.addAttribute(ATTR_DIT_CONTENT_RULE, dcrMap.values().toArray(values));
1098        }
1099    
1100        if (! dsrMap.isEmpty())
1101        {
1102          final String[] values = new String[dsrMap.size()];
1103          e.addAttribute(ATTR_DIT_STRUCTURE_RULE, dsrMap.values().toArray(values));
1104        }
1105    
1106        if (! nfMap.isEmpty())
1107        {
1108          final String[] values = new String[nfMap.size()];
1109          e.addAttribute(ATTR_NAME_FORM, nfMap.values().toArray(values));
1110        }
1111    
1112        if (! mruMap.isEmpty())
1113        {
1114          final String[] values = new String[mruMap.size()];
1115          e.addAttribute(ATTR_MATCHING_RULE_USE, mruMap.values().toArray(values));
1116        }
1117    
1118        return new Schema(e);
1119      }
1120    
1121    
1122    
1123      /**
1124       * Retrieves the entry used to create this schema object.
1125       *
1126       * @return  The entry used to create this schema object.
1127       */
1128      public ReadOnlyEntry getSchemaEntry()
1129      {
1130        return schemaEntry;
1131      }
1132    
1133    
1134    
1135      /**
1136       * Retrieves the object class type for the specified object class, recursively
1137       * checking its parents as needed.
1138       *
1139       * @param  oc  The object class definition for which to make the
1140       *             determination.
1141       * @param  m   The map of defined object classes.
1142       *
1143       * @return  The object class type for the object class.
1144       */
1145      private static ObjectClassType getOCType(final ObjectClassDefinition oc,
1146                                          final Map<String,ObjectClassDefinition> m)
1147      {
1148        ObjectClassType t = oc.getObjectClassType();
1149        if (t != null)
1150        {
1151          return t;
1152        }
1153    
1154        for (final String s : oc.getSuperiorClasses())
1155        {
1156          final ObjectClassDefinition d = m.get(toLowerCase(s));
1157          if (d != null)
1158          {
1159            t = getOCType(d, m);
1160            if (t != null)
1161            {
1162              return t;
1163            }
1164          }
1165        }
1166    
1167        return ObjectClassType.STRUCTURAL;
1168      }
1169    
1170    
1171    
1172      /**
1173       * Retrieves the value of the subschemaSubentry attribute from the specified
1174       * entry using the provided connection.
1175       *
1176       * @param  connection  The connection to use in order to perform the search.
1177       *                     It must not be {@code null}.
1178       * @param  entryDN     The DN of the entry from which to retrieve the
1179       *                     subschemaSubentry attribute.  It may be {@code null} or
1180       *                     an empty string in order to retrieve the value from the
1181       *                     server's root DSE.
1182       *
1183       * @return  The value of the subschemaSubentry attribute from the specified
1184       *          entry, or {@code null} if it is not available for some reason
1185       *          (e.g., the client does not have permission to read the target
1186       *          entry or the subschemaSubentry attribute).
1187       *
1188       * @throws  LDAPException  If a problem occurs while attempting to retrieve
1189       *                         the specified entry.
1190       */
1191      public static String getSubschemaSubentryDN(final LDAPConnection connection,
1192                                                  final String entryDN)
1193             throws LDAPException
1194      {
1195        ensureNotNull(connection);
1196    
1197        final Entry e;
1198        if (entryDN == null)
1199        {
1200          e = connection.getEntry("", SUBSCHEMA_SUBENTRY_REQUEST_ATTRS);
1201        }
1202        else
1203        {
1204          e = connection.getEntry(entryDN, SUBSCHEMA_SUBENTRY_REQUEST_ATTRS);
1205        }
1206    
1207        if (e == null)
1208        {
1209          return null;
1210        }
1211    
1212        return e.getAttributeValue(ATTR_SUBSCHEMA_SUBENTRY);
1213      }
1214    
1215    
1216    
1217      /**
1218       * Retrieves the set of attribute syntax definitions contained in the server
1219       * schema.
1220       *
1221       * @return  The set of attribute syntax definitions contained in the server
1222       *          schema.
1223       */
1224      public Set<AttributeSyntaxDefinition> getAttributeSyntaxes()
1225      {
1226        return asSet;
1227      }
1228    
1229    
1230    
1231      /**
1232       * Retrieves the attribute syntax with the specified OID from the server
1233       * schema.
1234       *
1235       * @param  oid  The OID of the attribute syntax to retrieve.  It must not be
1236       *              {@code null}.  It may optionally include a minimum upper bound
1237       *              (as may appear when the syntax OID is included in an attribute
1238       *              type definition), but if it does then that portion will be
1239       *              ignored when retrieving the attribute syntax.
1240       *
1241       * @return  The requested attribute syntax, or {@code null} if there is no
1242       *          such syntax defined in the server schema.
1243       */
1244      public AttributeSyntaxDefinition getAttributeSyntax(final String oid)
1245      {
1246        ensureNotNull(oid);
1247    
1248        final String lowerOID = toLowerCase(oid);
1249        final int    curlyPos = lowerOID.indexOf('{');
1250    
1251        if (curlyPos > 0)
1252        {
1253          return asMap.get(lowerOID.substring(0, curlyPos));
1254        }
1255        else
1256        {
1257          return asMap.get(lowerOID);
1258        }
1259      }
1260    
1261    
1262    
1263      /**
1264       * Retrieves the set of attribute type definitions contained in the server
1265       * schema.
1266       *
1267       * @return  The set of attribute type definitions contained in the server
1268       *          schema.
1269       */
1270      public Set<AttributeTypeDefinition> getAttributeTypes()
1271      {
1272        return atSet;
1273      }
1274    
1275    
1276    
1277      /**
1278       * Retrieves the set of operational attribute type definitions (i.e., those
1279       * definitions with a usage of directoryOperation, distributedOperation, or
1280       * dSAOperation) contained in the  server  schema.
1281       *
1282       * @return  The set of operational attribute type definitions contained in the
1283       *          server schema.
1284       */
1285      public Set<AttributeTypeDefinition> getOperationalAttributeTypes()
1286      {
1287        return operationalATSet;
1288      }
1289    
1290    
1291    
1292      /**
1293       * Retrieves the set of user attribute type definitions (i.e., those
1294       * definitions with a usage of userApplications) contained in the  server
1295       * schema.
1296       *
1297       * @return  The set of user attribute type definitions contained in the server
1298       *          schema.
1299       */
1300      public Set<AttributeTypeDefinition> getUserAttributeTypes()
1301      {
1302        return userATSet;
1303      }
1304    
1305    
1306    
1307      /**
1308       * Retrieves the attribute type with the specified name or OID from the server
1309       * schema.
1310       *
1311       * @param  name  The name or OID of the attribute type to retrieve.  It must
1312       *               not be {@code null}.
1313       *
1314       * @return  The requested attribute type, or {@code null} if there is no
1315       *          such attribute type defined in the server schema.
1316       */
1317      public AttributeTypeDefinition getAttributeType(final String name)
1318      {
1319        ensureNotNull(name);
1320    
1321        return atMap.get(toLowerCase(name));
1322      }
1323    
1324    
1325    
1326      /**
1327       * Retrieves a list of all subordinate attribute type definitions for the
1328       * provided attribute type definition.
1329       *
1330       * @param  d  The attribute type definition for which to retrieve all
1331       *            subordinate attribute types.  It must not be {@code null}.
1332       *
1333       * @return  A list of all subordinate attribute type definitions for the
1334       *          provided attribute type definition, or an empty list if it does
1335       *          not have any subordinate types or the provided attribute type is
1336       *          not defined in the schema.
1337       */
1338      public List<AttributeTypeDefinition> getSubordinateAttributeTypes(
1339                                                final AttributeTypeDefinition d)
1340      {
1341        ensureNotNull(d);
1342    
1343        final List<AttributeTypeDefinition> l = subordinateAttributeTypes.get(d);
1344        if (l == null)
1345        {
1346          return Collections.emptyList();
1347        }
1348        else
1349        {
1350          return Collections.unmodifiableList(l);
1351        }
1352      }
1353    
1354    
1355    
1356      /**
1357       * Retrieves the set of DIT content rule definitions contained in the server
1358       * schema.
1359       *
1360       * @return  The set of DIT content rule definitions contained in the server
1361       *          schema.
1362       */
1363      public Set<DITContentRuleDefinition> getDITContentRules()
1364      {
1365        return dcrSet;
1366      }
1367    
1368    
1369    
1370      /**
1371       * Retrieves the DIT content rule with the specified name or OID from the
1372       * server schema.
1373       *
1374       * @param  name  The name or OID of the DIT content rule to retrieve.  It must
1375       *               not be {@code null}.
1376       *
1377       * @return  The requested DIT content rule, or {@code null} if there is no
1378       *          such rule defined in the server schema.
1379       */
1380      public DITContentRuleDefinition getDITContentRule(final String name)
1381      {
1382        ensureNotNull(name);
1383    
1384        return dcrMap.get(toLowerCase(name));
1385      }
1386    
1387    
1388    
1389      /**
1390       * Retrieves the set of DIT structure rule definitions contained in the server
1391       * schema.
1392       *
1393       * @return  The set of DIT structure rule definitions contained in the server
1394       *          schema.
1395       */
1396      public Set<DITStructureRuleDefinition> getDITStructureRules()
1397      {
1398        return dsrSet;
1399      }
1400    
1401    
1402    
1403      /**
1404       * Retrieves the DIT content rule with the specified rule ID from the server
1405       * schema.
1406       *
1407       * @param  ruleID  The rule ID for the DIT structure rule to retrieve.
1408       *
1409       * @return  The requested DIT structure rule, or {@code null} if there is no
1410       *          such rule defined in the server schema.
1411       */
1412      public DITStructureRuleDefinition getDITStructureRuleByID(final int ruleID)
1413      {
1414        return dsrMapByID.get(ruleID);
1415      }
1416    
1417    
1418    
1419      /**
1420       * Retrieves the DIT content rule with the specified name from the server
1421       * schema.
1422       *
1423       * @param  ruleName  The name of the DIT structure rule to retrieve.  It must
1424       *                   not be {@code null}.
1425       *
1426       * @return  The requested DIT structure rule, or {@code null} if there is no
1427       *          such rule defined in the server schema.
1428       */
1429      public DITStructureRuleDefinition getDITStructureRuleByName(
1430                                             final String ruleName)
1431      {
1432        ensureNotNull(ruleName);
1433    
1434        return dsrMapByName.get(toLowerCase(ruleName));
1435      }
1436    
1437    
1438    
1439      /**
1440       * Retrieves the DIT content rule associated with the specified name form from
1441       * the server schema.
1442       *
1443       * @param  nameForm  The name or OID of the name form for which to retrieve
1444       *                   the associated DIT structure rule.
1445       *
1446       * @return  The requested DIT structure rule, or {@code null} if there is no
1447       *          such rule defined in the server schema.
1448       */
1449      public DITStructureRuleDefinition getDITStructureRuleByNameForm(
1450                                             final String nameForm)
1451      {
1452        ensureNotNull(nameForm);
1453    
1454        return dsrMapByNameForm.get(toLowerCase(nameForm));
1455      }
1456    
1457    
1458    
1459      /**
1460       * Retrieves the set of matching rule definitions contained in the server
1461       * schema.
1462       *
1463       * @return  The set of matching rule definitions contained in the server
1464       *          schema.
1465       */
1466      public Set<MatchingRuleDefinition> getMatchingRules()
1467      {
1468        return mrSet;
1469      }
1470    
1471    
1472    
1473      /**
1474       * Retrieves the matching rule with the specified name or OID from the server
1475       * schema.
1476       *
1477       * @param  name  The name or OID of the matching rule to retrieve.  It must
1478       *               not be {@code null}.
1479       *
1480       * @return  The requested matching rule, or {@code null} if there is no
1481       *          such rule defined in the server schema.
1482       */
1483      public MatchingRuleDefinition getMatchingRule(final String name)
1484      {
1485        ensureNotNull(name);
1486    
1487        return mrMap.get(toLowerCase(name));
1488      }
1489    
1490    
1491    
1492      /**
1493       * Retrieves the set of matching rule use definitions contained in the server
1494       * schema.
1495       *
1496       * @return  The set of matching rule use definitions contained in the server
1497       *          schema.
1498       */
1499      public Set<MatchingRuleUseDefinition> getMatchingRuleUses()
1500      {
1501        return mruSet;
1502      }
1503    
1504    
1505    
1506      /**
1507       * Retrieves the matching rule use with the specified name or OID from the
1508       * server schema.
1509       *
1510       * @param  name  The name or OID of the matching rule use to retrieve.  It
1511       *               must not be {@code null}.
1512       *
1513       * @return  The requested matching rule, or {@code null} if there is no
1514       *          such matching rule use defined in the server schema.
1515       */
1516      public MatchingRuleUseDefinition getMatchingRuleUse(final String name)
1517      {
1518        ensureNotNull(name);
1519    
1520        return mruMap.get(toLowerCase(name));
1521      }
1522    
1523    
1524    
1525      /**
1526       * Retrieves the set of name form definitions contained in the server schema.
1527       *
1528       * @return  The set of name form definitions contained in the server schema.
1529       */
1530      public Set<NameFormDefinition> getNameForms()
1531      {
1532        return nfSet;
1533      }
1534    
1535    
1536    
1537      /**
1538       * Retrieves the name form with the specified name or OID from the server
1539       * schema.
1540       *
1541       * @param  name  The name or OID of the name form to retrieve.  It must not be
1542       *               {@code null}.
1543       *
1544       * @return  The requested name form, or {@code null} if there is no
1545       *          such rule defined in the server schema.
1546       */
1547      public NameFormDefinition getNameFormByName(final String name)
1548      {
1549        ensureNotNull(name);
1550    
1551        return nfMapByName.get(toLowerCase(name));
1552      }
1553    
1554    
1555    
1556      /**
1557       * Retrieves the name form associated with the specified structural object
1558       * class from the server schema.
1559       *
1560       * @param  objectClass  The name or OID of the structural object class for
1561       *                      which to retrieve the associated name form.  It must
1562       *                      not be {@code null}.
1563       *
1564       * @return  The requested name form, or {@code null} if there is no
1565       *          such rule defined in the server schema.
1566       */
1567      public NameFormDefinition getNameFormByObjectClass(final String objectClass)
1568      {
1569        ensureNotNull(objectClass);
1570    
1571        return nfMapByOC.get(toLowerCase(objectClass));
1572      }
1573    
1574    
1575    
1576      /**
1577       * Retrieves the set of object class definitions contained in the server
1578       * schema.
1579       *
1580       * @return  The set of object class definitions contained in the server
1581       *          schema.
1582       */
1583      public Set<ObjectClassDefinition> getObjectClasses()
1584      {
1585        return ocSet;
1586      }
1587    
1588    
1589    
1590      /**
1591       * Retrieves the set of abstract object class definitions contained in the
1592       * server schema.
1593       *
1594       * @return  The set of abstract object class definitions contained in the
1595       *          server schema.
1596       */
1597      public Set<ObjectClassDefinition> getAbstractObjectClasses()
1598      {
1599        return abstractOCSet;
1600      }
1601    
1602    
1603    
1604      /**
1605       * Retrieves the set of auxiliary object class definitions contained in the
1606       * server schema.
1607       *
1608       * @return  The set of auxiliary object class definitions contained in the
1609       *          server schema.
1610       */
1611      public Set<ObjectClassDefinition> getAuxiliaryObjectClasses()
1612      {
1613        return auxiliaryOCSet;
1614      }
1615    
1616    
1617    
1618      /**
1619       * Retrieves the set of structural object class definitions contained in the
1620       * server schema.
1621       *
1622       * @return  The set of structural object class definitions contained in the
1623       *          server schema.
1624       */
1625      public Set<ObjectClassDefinition> getStructuralObjectClasses()
1626      {
1627        return structuralOCSet;
1628      }
1629    
1630    
1631    
1632      /**
1633       * Retrieves the object class with the specified name or OID from the server
1634       * schema.
1635       *
1636       * @param  name  The name or OID of the object class to retrieve.  It must
1637       *               not be {@code null}.
1638       *
1639       * @return  The requested object class, or {@code null} if there is no such
1640       *          class defined in the server schema.
1641       */
1642      public ObjectClassDefinition getObjectClass(final String name)
1643      {
1644        ensureNotNull(name);
1645    
1646        return ocMap.get(toLowerCase(name));
1647      }
1648    
1649    
1650    
1651      /**
1652       * Retrieves a hash code for this schema object.
1653       *
1654       * @return  A hash code for this schema object.
1655       */
1656      @Override()
1657      public int hashCode()
1658      {
1659        int hc;
1660        try
1661        {
1662          hc = schemaEntry.getParsedDN().hashCode();
1663        }
1664        catch (final Exception e)
1665        {
1666          debugException(e);
1667          hc = toLowerCase(schemaEntry.getDN()).hashCode();
1668        }
1669    
1670        Attribute a = schemaEntry.getAttribute(ATTR_ATTRIBUTE_SYNTAX);
1671        if (a != null)
1672        {
1673          hc += a.hashCode();
1674        }
1675    
1676        a = schemaEntry.getAttribute(ATTR_MATCHING_RULE);
1677        if (a != null)
1678        {
1679          hc += a.hashCode();
1680        }
1681    
1682        a = schemaEntry.getAttribute(ATTR_ATTRIBUTE_TYPE);
1683        if (a != null)
1684        {
1685          hc += a.hashCode();
1686        }
1687    
1688        a = schemaEntry.getAttribute(ATTR_OBJECT_CLASS);
1689        if (a != null)
1690        {
1691          hc += a.hashCode();
1692        }
1693    
1694        a = schemaEntry.getAttribute(ATTR_NAME_FORM);
1695        if (a != null)
1696        {
1697          hc += a.hashCode();
1698        }
1699    
1700        a = schemaEntry.getAttribute(ATTR_DIT_CONTENT_RULE);
1701        if (a != null)
1702        {
1703          hc += a.hashCode();
1704        }
1705    
1706        a = schemaEntry.getAttribute(ATTR_DIT_STRUCTURE_RULE);
1707        if (a != null)
1708        {
1709          hc += a.hashCode();
1710        }
1711    
1712        a = schemaEntry.getAttribute(ATTR_MATCHING_RULE_USE);
1713        if (a != null)
1714        {
1715          hc += a.hashCode();
1716        }
1717    
1718        return hc;
1719      }
1720    
1721    
1722    
1723      /**
1724       * Indicates whether the provided object is equal to this schema object.
1725       *
1726       * @param  o  The object for which to make the determination.
1727       *
1728       * @return  {@code true} if the provided object is equal to this schema
1729       *          object, or {@code false} if not.
1730       */
1731      @Override()
1732      public boolean equals(final Object o)
1733      {
1734        if (o == null)
1735        {
1736          return false;
1737        }
1738    
1739        if (o == this)
1740        {
1741          return true;
1742        }
1743    
1744        if (! (o instanceof Schema))
1745        {
1746          return false;
1747        }
1748    
1749        final Schema s = (Schema) o;
1750    
1751        try
1752        {
1753          if (! schemaEntry.getParsedDN().equals(s.schemaEntry.getParsedDN()))
1754          {
1755            return false;
1756          }
1757        }
1758        catch (final Exception e)
1759        {
1760          debugException(e);
1761          if (! schemaEntry.getDN().equalsIgnoreCase(s.schemaEntry.getDN()))
1762          {
1763            return false;
1764          }
1765        }
1766    
1767        return (asSet.equals(s.asSet) &&
1768             mrSet.equals(s.mrSet) &&
1769             atSet.equals(s.atSet) &&
1770             ocSet.equals(s.ocSet) &&
1771             nfSet.equals(s.nfSet) &&
1772             dcrSet.equals(s.dcrSet) &&
1773             dsrSet.equals(s.dsrSet) &&
1774             mruSet.equals(s.mruSet));
1775      }
1776    
1777    
1778    
1779      /**
1780       * Retrieves a string representation of the associated schema entry.
1781       *
1782       * @return  A string representation of the associated schema entry.
1783       */
1784      @Override()
1785      public String toString()
1786      {
1787        return schemaEntry.toString();
1788      }
1789    }