001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.types;
028    
029    
030    
031    import java.io.BufferedReader;
032    import java.io.BufferedWriter;
033    import java.io.File;
034    import java.io.FileReader;
035    import java.io.FileWriter;
036    import java.io.IOException;
037    import java.util.Collections;
038    import java.util.HashMap;
039    import java.util.LinkedHashSet;
040    import java.util.LinkedList;
041    import java.util.List;
042    import java.util.Map;
043    import java.util.TreeSet;
044    import java.util.concurrent.ConcurrentHashMap;
045    
046    import org.opends.messages.Message;
047    import org.opends.server.api.ApproximateMatchingRule;
048    import org.opends.server.api.AttributeSyntax;
049    import org.opends.server.api.EqualityMatchingRule;
050    import org.opends.server.api.MatchingRule;
051    import org.opends.server.api.OrderingMatchingRule;
052    import org.opends.server.api.SubstringMatchingRule;
053    import org.opends.server.core.DirectoryServer;
054    import org.opends.server.core.SchemaConfigManager;
055    import org.opends.server.loggers.debug.DebugTracer;
056    import org.opends.server.protocols.asn1.ASN1OctetString;
057    import org.opends.server.schema.CaseIgnoreEqualityMatchingRule;
058    
059    import static org.opends.messages.BackendMessages.*;
060    import static org.opends.messages.CoreMessages.*;
061    import static org.opends.server.config.ConfigConstants.*;
062    import static org.opends.server.loggers.debug.DebugLogger.*;
063    import static org.opends.server.loggers.ErrorLogger.*;
064    import static org.opends.server.util.ServerConstants.*;
065    import static org.opends.server.util.StaticUtils.*;
066    
067    
068    
069    /**
070     * This class defines a data structure that holds information about
071     * the components of the Directory Server schema.  It includes the
072     * following kinds of elements:
073     *
074     * <UL>
075     *   <LI>Attribute type definitions</LI>
076     *   <LI>Objectclass definitions</LI>
077     *   <LI>Attribute syntax definitions</LI>
078     *   <LI>Matching rule definitions</LI>
079     *   <LI>Matching rule use definitions</LI>
080     *   <LI>DIT content rule definitions</LI>
081     *   <LI>DIT structure rule definitions</LI>
082     *   <LI>Name form definitions</LI>
083     * </UL>
084     */
085    @org.opends.server.types.PublicAPI(
086         stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
087         mayInstantiate=false,
088         mayExtend=false,
089         mayInvoke=true)
090    public final class Schema
091    {
092      /**
093       * The tracer object for the debug logger.
094       */
095      private static final DebugTracer TRACER = getTracer();
096    
097    
098    
099    
100      // The matching rule that will be used to normalize schema element
101      // definitions.
102      private EqualityMatchingRule normalizationMatchingRule;
103    
104      // The set of subordinate attribute types registered within the
105      // server schema.
106      private ConcurrentHashMap<AttributeType,List<AttributeType>>
107                   subordinateTypes;
108    
109      // The set of attribute type definitions for this schema, mapped
110      // between the lowercase names and OID for the definition and the
111      // attribute type itself.
112      private ConcurrentHashMap<String,AttributeType> attributeTypes;
113    
114      // The set of objectclass definitions for this schema, mapped
115      // between the lowercase names and OID for the definition and the
116      // objectclass itself.
117      private ConcurrentHashMap<String,ObjectClass> objectClasses;
118    
119      // The set of attribute syntaxes for this schema, mapped between the
120      // OID for the syntax and the syntax itself.
121      private ConcurrentHashMap<String,AttributeSyntax> syntaxes;
122    
123      // The entire set of matching rules for this schema, mapped between
124      // the lowercase names and OID for the definition and the matching
125      // rule itself.
126      private ConcurrentHashMap<String,MatchingRule> matchingRules;
127    
128      // The set of approximate matching rules for this schema, mapped
129      // between the lowercase names and OID for the definition and the
130      // matching rule itself.
131      private ConcurrentHashMap<String,ApproximateMatchingRule>
132                   approximateMatchingRules;
133    
134      // The set of equality matching rules for this schema, mapped
135      // between the lowercase names and OID for the definition and the
136      // matching rule itself.
137      private ConcurrentHashMap<String,EqualityMatchingRule>
138                   equalityMatchingRules;
139    
140      // The set of ordering matching rules for this schema, mapped
141      // between the lowercase names and OID for the definition and the
142      // matching rule itself.
143      private ConcurrentHashMap<String,OrderingMatchingRule>
144                   orderingMatchingRules;
145    
146      // The set of substring matching rules for this schema, mapped
147      // between the lowercase names and OID for the definition and the
148      // matching rule itself.
149      private ConcurrentHashMap<String,SubstringMatchingRule>
150                   substringMatchingRules;
151    
152      // The set of matching rule uses for this schema, mapped between the
153      // matching rule for the definition and the matching rule use
154      // itself.
155      private ConcurrentHashMap<MatchingRule,MatchingRuleUse>
156                   matchingRuleUses;
157    
158      // The set of DIT content rules for this schema, mapped between the
159      // structural objectclass for the definition and the DIT content
160      // rule itself.
161      private ConcurrentHashMap<ObjectClass,DITContentRule>
162                   ditContentRules;
163    
164      // The set of DIT structure rules for this schema, mapped between
165      // the name form for the definition and the DIT structure rule
166      // itself.
167      private ConcurrentHashMap<Integer,DITStructureRule>
168                   ditStructureRulesByID;
169    
170      // The set of DIT structure rules for this schema, mapped between
171      // the name form for the definition and the DIT structure rule
172      // itself.
173      private ConcurrentHashMap<NameForm,DITStructureRule>
174                   ditStructureRulesByNameForm;
175    
176      // The set of name forms for this schema, mapped between the
177      // structural objectclass for the definition and the name form
178      // itself.
179      private ConcurrentHashMap<ObjectClass,NameForm> nameFormsByOC;
180    
181      // The set of name forms for this schema, mapped between the
182      // names/OID and the name form itself.
183      private ConcurrentHashMap<String,NameForm> nameFormsByName;
184    
185      // The set of pre-encoded attribute syntax representations.
186      private LinkedHashSet<AttributeValue> syntaxSet;
187    
188      // The set of pre-encoded attribute type representations.
189      private LinkedHashSet<AttributeValue> attributeTypeSet;
190    
191      // The set of pre-encoded DIT content rule representations.
192      private LinkedHashSet<AttributeValue> ditContentRuleSet;
193    
194      // The set of pre-encoded DIT structure rule representations.
195      private LinkedHashSet<AttributeValue> ditStructureRuleSet;
196    
197      // The set of pre-encoded matching rule representations.
198      private LinkedHashSet<AttributeValue> matchingRuleSet;
199    
200      // The set of pre-encoded matching rule use representations.
201      private LinkedHashSet<AttributeValue> matchingRuleUseSet;
202    
203      // The set of pre-encoded name form representations.
204      private LinkedHashSet<AttributeValue> nameFormSet;
205    
206      // The set of pre-encoded objectclass representations.
207      private LinkedHashSet<AttributeValue> objectClassSet;
208    
209      // The oldest modification timestamp for any schema configuration
210      // file.
211      private long oldestModificationTime;
212    
213      // The youngest modification timestamp for any schema configuration
214      // file.
215      private long youngestModificationTime;
216    
217      // A set of extra attributes that are not used directly by
218      // the schema but may be used by other component to store
219      // information in the schema.
220      // ex : Replication uses this to store its state and GenerationID.
221    
222      private Map<String, Attribute> extraAttributes =
223        new HashMap<String, Attribute>();
224    
225    
226    
227      /**
228       * Creates a new schema structure with all elements initialized but
229       * empty.
230       */
231      public Schema()
232      {
233        attributeTypes = new ConcurrentHashMap<String,AttributeType>();
234        objectClasses = new ConcurrentHashMap<String,ObjectClass>();
235        syntaxes = new ConcurrentHashMap<String,AttributeSyntax>();
236        matchingRules = new ConcurrentHashMap<String,MatchingRule>();
237        approximateMatchingRules =
238             new ConcurrentHashMap<String,ApproximateMatchingRule>();
239        equalityMatchingRules =
240             new ConcurrentHashMap<String,EqualityMatchingRule>();
241        orderingMatchingRules =
242             new ConcurrentHashMap<String,OrderingMatchingRule>();
243        substringMatchingRules =
244             new ConcurrentHashMap<String,SubstringMatchingRule>();
245        matchingRuleUses =
246             new ConcurrentHashMap<MatchingRule,MatchingRuleUse>();
247        ditContentRules =
248             new ConcurrentHashMap<ObjectClass,DITContentRule>();
249        ditStructureRulesByID =
250             new ConcurrentHashMap<Integer,DITStructureRule>();
251        ditStructureRulesByNameForm =
252             new ConcurrentHashMap<NameForm,DITStructureRule>();
253        nameFormsByOC = new ConcurrentHashMap<ObjectClass,NameForm>();
254        nameFormsByName = new ConcurrentHashMap<String,NameForm>();
255        subordinateTypes =
256             new ConcurrentHashMap<AttributeType,List<AttributeType>>();
257    
258    
259        syntaxSet           = new LinkedHashSet<AttributeValue>();
260        attributeTypeSet    = new LinkedHashSet<AttributeValue>();
261        ditContentRuleSet   = new LinkedHashSet<AttributeValue>();
262        ditStructureRuleSet = new LinkedHashSet<AttributeValue>();
263        matchingRuleSet     = new LinkedHashSet<AttributeValue>();
264        matchingRuleUseSet  = new LinkedHashSet<AttributeValue>();
265        nameFormSet         = new LinkedHashSet<AttributeValue>();
266        objectClassSet      = new LinkedHashSet<AttributeValue>();
267    
268        normalizationMatchingRule = new CaseIgnoreEqualityMatchingRule();
269        oldestModificationTime    = System.currentTimeMillis();
270        youngestModificationTime  = oldestModificationTime;
271      }
272    
273    
274    
275      /**
276       * Retrieves the attribute type definitions for this schema, as a
277       * mapping between the lowercase names and OIDs for the attribute
278       * type and the attribute type itself.  Each attribute type may be
279       * associated with multiple keys (once for the OID and again for
280       * each name).  The contents of the returned mapping must not be
281       * altered.
282       *
283       * @return  The attribute type definitions for this schema.
284       */
285      public ConcurrentHashMap<String,AttributeType> getAttributeTypes()
286      {
287        return attributeTypes;
288      }
289    
290    
291    
292      /**
293       * Retrieves the set of defined attribute types for this schema.
294       *
295       * @return  The set of defined attribute types for this schema.
296       */
297      public LinkedHashSet<AttributeValue> getAttributeTypeSet()
298      {
299        return attributeTypeSet;
300      }
301    
302    
303    
304      /**
305       * Indicates whether this schema definition includes an attribute
306       * type with the provided name or OID.
307       *
308       * @param  lowerName  The name or OID for which to make the
309       *                    determination, formatted in all lowercase
310       *                    characters.
311       *
312       * @return  {@code true} if this schema contains an attribute type
313       *          with the provided name or OID, or {@code false} if not.
314       */
315      public boolean hasAttributeType(String lowerName)
316      {
317        return attributeTypes.containsKey(lowerName);
318      }
319    
320    
321    
322      /**
323       * Retrieves the attribute type definition with the specified name
324       * or OID.
325       *
326       * @param  lowerName  The name or OID of the attribute type to
327       *                    retrieve, formatted in all lowercase
328       *                    characters.
329       *
330       * @return  The requested attribute type, or <CODE>null</CODE> if no
331       *          type is registered with the provided name or OID.
332       */
333      public AttributeType getAttributeType(String lowerName)
334      {
335        return attributeTypes.get(lowerName);
336      }
337    
338    
339    
340      /**
341       * Registers the provided attribute type definition with this
342       * schema.
343       *
344       * @param  attributeType      The attribute type to register with
345       *                            this schema.
346       * @param  overwriteExisting  Indicates whether to overwrite an
347       *                            existing mapping if there are any
348       *                            conflicts (i.e., another attribute
349       *                            type with the same OID or name).
350       *
351       * @throws  DirectoryException  If a conflict is encountered and the
352       *                              <CODE>overwriteExisting</CODE> flag
353       *                              is set to <CODE>false</CODE>
354       */
355      public void registerAttributeType(AttributeType attributeType,
356                                        boolean overwriteExisting)
357             throws DirectoryException
358      {
359        synchronized (attributeTypes)
360        {
361          if (! overwriteExisting)
362          {
363            String oid = toLowerCase(attributeType.getOID());
364            if (attributeTypes.containsKey(oid))
365            {
366              AttributeType conflictingType = attributeTypes.get(oid);
367    
368              Message message = ERR_SCHEMA_CONFLICTING_ATTRIBUTE_OID.
369                  get(attributeType.getNameOrOID(), oid,
370                      conflictingType.getNameOrOID());
371              throw new DirectoryException(
372                             ResultCode.CONSTRAINT_VIOLATION, message);
373            }
374    
375            for (String name : attributeType.getNormalizedNames())
376            {
377              if (attributeTypes.containsKey(name))
378              {
379                AttributeType conflictingType = attributeTypes.get(name);
380    
381                Message message = ERR_SCHEMA_CONFLICTING_ATTRIBUTE_NAME.
382                    get(attributeType.getNameOrOID(), name,
383                        conflictingType.getNameOrOID());
384                throw new DirectoryException(
385                               ResultCode.CONSTRAINT_VIOLATION, message);
386              }
387            }
388          }
389    
390          attributeTypes.put(toLowerCase(attributeType.getOID()),
391                             attributeType);
392    
393          for (String name : attributeType.getNormalizedNames())
394          {
395            attributeTypes.put(name, attributeType);
396          }
397    
398          AttributeType superiorType = attributeType.getSuperiorType();
399          if (superiorType != null)
400          {
401            registerSubordinateType(attributeType, superiorType);
402          }
403    
404          // We'll use an attribute value including the normalized value
405          // rather than the attribute type because otherwise it would use
406          // a very expensive matching rule (OID first component match)
407          // that would kill performance.
408          String valueString = attributeType.getDefinition();
409          ASN1OctetString rawValue = new ASN1OctetString(valueString);
410          ByteString normValue = normalizationMatchingRule.normalizeValue(
411                                      new ASN1OctetString(valueString));
412          attributeTypeSet.add(new AttributeValue(rawValue, normValue));
413        }
414      }
415    
416    
417    
418      /**
419       * Deregisters the provided attribute type definition with this
420       * schema.
421       *
422       * @param  attributeType  The attribute type to deregister with this
423       *                        schema.
424       */
425      public void deregisterAttributeType(AttributeType attributeType)
426      {
427        synchronized (attributeTypes)
428        {
429          attributeTypes.remove(toLowerCase(attributeType.getOID()),
430                                attributeType);
431    
432          for (String name : attributeType.getNormalizedNames())
433          {
434            attributeTypes.remove(name, attributeType);
435          }
436    
437          AttributeType superiorType = attributeType.getSuperiorType();
438          if (superiorType != null)
439          {
440            deregisterSubordinateType(attributeType, superiorType);
441          }
442    
443          // We'll use an attribute value including the normalized value
444          // rather than the attribute type because otherwise it would use
445          // a very expensive matching rule (OID first component match)
446          // that would kill performance.
447          try
448          {
449            String valueString = attributeType.getDefinition();
450            ASN1OctetString rawValue = new ASN1OctetString(valueString);
451            ByteString normValue =
452                 normalizationMatchingRule.normalizeValue(
453                      new ASN1OctetString(valueString));
454            attributeTypeSet.remove(new AttributeValue(rawValue,
455                                                       normValue));
456          }
457          catch (Exception e)
458          {
459            String valueString = attributeType.getDefinition();
460            ASN1OctetString rawValue = new ASN1OctetString(valueString);
461            ASN1OctetString normValue =
462                 new ASN1OctetString(toLowerCase(valueString));
463            attributeTypeSet.remove(new AttributeValue(rawValue,
464                                                       normValue));
465          }
466        }
467      }
468    
469    
470    
471      /**
472       * Registers the provided attribute type as a subtype of the given
473       * superior attribute type, recursively following any additional
474       * elements in the superior chain.
475       *
476       * @param  attributeType  The attribute type to be registered as a
477       *                        subtype for the given superior type.
478       * @param  superiorType   The superior type for which to register
479       *                        the given attribute type as a subtype.
480       */
481      private void registerSubordinateType(AttributeType attributeType,
482                                           AttributeType superiorType)
483      {
484        List<AttributeType> subTypes = subordinateTypes.get(superiorType);
485        if (subTypes == null)
486        {
487          superiorType.setMayHaveSubordinateTypes();
488          subTypes = new LinkedList<AttributeType>();
489          subTypes.add(attributeType);
490          subordinateTypes.put(superiorType, subTypes);
491        }
492        else if (! subTypes.contains(attributeType))
493        {
494          superiorType.setMayHaveSubordinateTypes();
495          subTypes.add(attributeType);
496    
497          AttributeType higherSuperior = superiorType.getSuperiorType();
498          if (higherSuperior != null)
499          {
500            registerSubordinateType(attributeType, higherSuperior);
501          }
502        }
503      }
504    
505    
506    
507      /**
508       * Deregisters the provided attribute type as a subtype of the given
509       * superior attribute type, recursively following any additional
510       * elements in the superior chain.
511       *
512       * @param  attributeType  The attribute type to be deregistered as a
513       *                        subtype for the given superior type.
514       * @param  superiorType   The superior type for which to deregister
515       *                        the given attribute type as a subtype.
516       */
517      private void deregisterSubordinateType(AttributeType attributeType,
518                                             AttributeType superiorType)
519      {
520        List<AttributeType> subTypes = subordinateTypes.get(superiorType);
521        if (subTypes != null)
522        {
523          if (subTypes.remove(attributeType))
524          {
525            AttributeType higherSuperior = superiorType.getSuperiorType();
526            if (higherSuperior != null)
527            {
528              deregisterSubordinateType(attributeType, higherSuperior);
529            }
530          }
531        }
532      }
533    
534    
535    
536      /**
537       * Retrieves the set of subtypes registered for the given attribute
538       * type.
539       *
540       * @param  attributeType  The attribute type for which to retrieve
541       *                        the set of registered subtypes.
542       *
543       * @return  The set of subtypes registered for the given attribute
544       *          type, or an empty set if there are no subtypes
545       *          registered for the attribute type.
546       */
547      public Iterable<AttributeType>
548                  getSubTypes(AttributeType attributeType)
549      {
550        List<AttributeType> subTypes =
551             subordinateTypes.get(attributeType);
552        if (subTypes == null)
553        {
554          return Collections.<AttributeType>emptyList();
555        }
556        else
557        {
558          return subTypes;
559        }
560      }
561    
562    
563    
564      /**
565       * Retrieves the objectclass definitions for this schema, as a
566       * mapping between the lowercase names and OIDs for the objectclass
567       * and the objectclass itself.  Each objectclass may be associated
568       * with multiple keys (once for the OID and again for each name).
569       * The contents of the returned mapping must not be altered.
570       *
571       * @return  The objectclass definitions for this schema.
572       */
573      public ConcurrentHashMap<String,ObjectClass> getObjectClasses()
574      {
575        return objectClasses;
576      }
577    
578    
579    
580      /**
581       * Retrieves the set of defined objectclasses for this schema.
582       *
583       * @return  The set of defined objectclasses for this schema.
584       */
585      public LinkedHashSet<AttributeValue> getObjectClassSet()
586      {
587        return objectClassSet;
588      }
589    
590    
591    
592      /**
593       * Indicates whether this schema definition includes an objectclass
594       * with the provided name or OID.
595       *
596       * @param  lowerName  The name or OID for which to make the
597       *                    determination, formatted in all lowercase
598       *                    characters.
599       *
600       * @return  {@code true} if this schema contains an objectclass with
601       *          the provided name or OID, or {@code false} if not.
602       */
603      public boolean hasObjectClass(String lowerName)
604      {
605        return objectClasses.containsKey(lowerName);
606      }
607    
608    
609    
610      /**
611       * Retrieves the objectclass definition with the specified name or
612       * OID.
613       *
614       * @param  lowerName  The name or OID of the objectclass to
615       *                    retrieve, formatted in all lowercase
616       *                    characters.
617       *
618       * @return  The requested objectclass, or <CODE>null</CODE> if no
619       *          class is registered with the provided name or OID.
620       */
621      public ObjectClass getObjectClass(String lowerName)
622      {
623        return objectClasses.get(lowerName);
624      }
625    
626    
627    
628      /**
629       * Registers the provided objectclass definition with this schema.
630       *
631       * @param  objectClass        The objectclass to register with this
632       *                            schema.
633       * @param  overwriteExisting  Indicates whether to overwrite an
634       *                            existing mapping if there are any
635       *                            conflicts (i.e., another objectclass
636       *                            with the same OID or name).
637       *
638       * @throws  DirectoryException  If a conflict is encountered and the
639       *                              <CODE>overwriteExisting</CODE> flag
640       *                              is set to <CODE>false</CODE>.
641       */
642      public void registerObjectClass(ObjectClass objectClass,
643                                      boolean overwriteExisting)
644             throws DirectoryException
645      {
646        synchronized (objectClasses)
647        {
648          if (! overwriteExisting)
649          {
650            String oid = toLowerCase(objectClass.getOID());
651            if (objectClasses.containsKey(oid))
652            {
653              ObjectClass conflictingClass = objectClasses.get(oid);
654    
655              Message message = ERR_SCHEMA_CONFLICTING_OBJECTCLASS_OID.
656                  get(objectClass.getNameOrOID(), oid,
657                      conflictingClass.getNameOrOID());
658              throw new DirectoryException(
659                           ResultCode.CONSTRAINT_VIOLATION, message);
660            }
661    
662            for (String name : objectClass.getNormalizedNames())
663            {
664              if (objectClasses.containsKey(name))
665              {
666                ObjectClass conflictingClass = objectClasses.get(name);
667    
668                Message message = ERR_SCHEMA_CONFLICTING_OBJECTCLASS_NAME.
669                    get(objectClass.getNameOrOID(), name,
670                        conflictingClass.getNameOrOID());
671                throw new DirectoryException(
672                               ResultCode.CONSTRAINT_VIOLATION, message);
673              }
674            }
675          }
676    
677          objectClasses.put(toLowerCase(objectClass.getOID()),
678                            objectClass);
679    
680          for (String name : objectClass.getNormalizedNames())
681          {
682            objectClasses.put(name, objectClass);
683          }
684    
685          // We'll use an attribute value including the normalized value
686          // rather than the attribute type because otherwise it would use
687          // a very expensive matching rule (OID first component match)
688          // that would kill performance.
689          String valueString = objectClass.getDefinition();
690          ASN1OctetString rawValue = new ASN1OctetString(valueString);
691          ByteString normValue = normalizationMatchingRule.normalizeValue(
692                                      new ASN1OctetString(valueString));
693          objectClassSet.add(new AttributeValue(rawValue, normValue));
694        }
695      }
696    
697    
698    
699      /**
700       * Deregisters the provided objectclass definition with this schema.
701       *
702       * @param  objectClass  The objectclass to deregister with this
703       *                      schema.
704       */
705      public void deregisterObjectClass(ObjectClass objectClass)
706      {
707        synchronized (objectClasses)
708        {
709          objectClasses.remove(toLowerCase(objectClass.getOID()),
710                               objectClass);
711    
712          for (String name : objectClass.getNormalizedNames())
713          {
714            objectClasses.remove(name, objectClass);
715          }
716    
717    
718          // We'll use an attribute value including the normalized value
719          // rather than the attribute type because otherwise it would use
720          // a very expensive matching rule (OID first component match)
721          // that would kill performance.
722          try
723          {
724            String valueString = objectClass.getDefinition();
725            ASN1OctetString rawValue = new ASN1OctetString(valueString);
726            ByteString normValue =
727                 normalizationMatchingRule.normalizeValue(
728                      new ASN1OctetString(valueString));
729            objectClassSet.remove(new AttributeValue(rawValue,
730                                                     normValue));
731          }
732          catch (Exception e)
733          {
734            String valueString = objectClass.getDefinition();
735            ASN1OctetString rawValue = new ASN1OctetString(valueString);
736            ASN1OctetString normValue =
737                 new ASN1OctetString(toLowerCase(valueString));
738            objectClassSet.remove(new AttributeValue(rawValue,
739                                                     normValue));
740          }
741        }
742      }
743    
744    
745    
746      /**
747       * Retrieves the attribute syntax definitions for this schema, as a
748       * mapping between the OID for the syntax and the syntax itself.
749       * Each syntax should only be present once, since its only key is
750       * its OID.  The contents of the returned mapping must not be
751       * altered.
752       *
753       * @return  The attribute syntax definitions for this schema.
754       */
755      public ConcurrentHashMap<String,AttributeSyntax> getSyntaxes()
756      {
757        return syntaxes;
758      }
759    
760    
761    
762      /**
763       * Retrieves the set of defined attribute syntaxes for this schema.
764       *
765       * @return  The set of defined attribute syntaxes for this schema.
766       */
767      public LinkedHashSet<AttributeValue> getSyntaxSet()
768      {
769        return syntaxSet;
770      }
771    
772    
773    
774      /**
775       * Indicates whether this schema definition includes an attribute
776       * syntax with the provided name or OID.
777       *
778       * @param  lowerName  The name or OID for which to make the
779       *                    determination, formatted in all lowercase
780       *                    characters.
781       *
782       * @return  {@code true} if this schema contains an attribute syntax
783       *          with the provided name or OID, or {@code false} if not.
784       */
785      public boolean hasSyntax(String lowerName)
786      {
787        return syntaxes.containsKey(lowerName);
788      }
789    
790    
791    
792      /**
793       * Retrieves the attribute syntax definition with the OID.
794       *
795       * @param  lowerName  The OID of the attribute syntax to retrieve,
796       *                    formatted in all lowercase characters.
797       *
798       * @return  The requested attribute syntax, or <CODE>null</CODE> if
799       *          no syntax is registered with the provided OID.
800       */
801      public AttributeSyntax getSyntax(String lowerName)
802      {
803        return syntaxes.get(lowerName);
804      }
805    
806    
807    
808      /**
809       * Registers the provided attribute syntax definition with this
810       * schema.
811       *
812       * @param  syntax             The attribute syntax to register with
813       *                            this schema.
814       * @param  overwriteExisting  Indicates whether to overwrite an
815       *                            existing mapping if there are any
816       *                            conflicts (i.e., another attribute
817       *                            syntax with the same OID).
818       *
819       * @throws  DirectoryException  If a conflict is encountered and the
820       *                              <CODE>overwriteExisting</CODE> flag
821       *                              is set to <CODE>false</CODE>
822       */
823      public void registerSyntax(AttributeSyntax syntax,
824                                 boolean overwriteExisting)
825             throws DirectoryException
826      {
827        synchronized (syntaxes)
828        {
829          if (! overwriteExisting)
830          {
831            String oid = toLowerCase(syntax.getOID());
832            if (syntaxes.containsKey(oid))
833            {
834              AttributeSyntax conflictingSyntax = syntaxes.get(oid);
835    
836              Message message = ERR_SCHEMA_CONFLICTING_SYNTAX_OID.
837                  get(syntax.getSyntaxName(), oid,
838                      conflictingSyntax.getSyntaxName());
839              throw new DirectoryException(
840                             ResultCode.CONSTRAINT_VIOLATION, message);
841            }
842          }
843    
844          syntaxes.put(toLowerCase(syntax.getOID()), syntax);
845    
846          // We'll use an attribute value including the normalized value
847          // rather than the attribute type because otherwise it would use
848          // a very expensive matching rule (OID first component match)
849          // that would kill performance.
850          String valueString = syntax.toString();
851          ASN1OctetString rawValue = new ASN1OctetString(valueString);
852          ByteString normValue = normalizationMatchingRule.normalizeValue(
853                                      new ASN1OctetString(valueString));
854          syntaxSet.add(new AttributeValue(rawValue, normValue));
855        }
856      }
857    
858    
859    
860      /**
861       * Deregisters the provided attribute syntax definition with this
862       * schema.
863       *
864       * @param  syntax  The attribute syntax to deregister with this
865       *                 schema.
866       */
867      public void deregisterSyntax(AttributeSyntax syntax)
868      {
869        synchronized (syntaxes)
870        {
871          syntaxes.remove(toLowerCase(syntax.getOID()), syntax);
872    
873          // We'll use an attribute value including the normalized value
874          // rather than the attribute type because otherwise it would use
875          // a very expensive matching rule (OID first component match)
876          // that would kill performance.
877          try
878          {
879            String valueString = syntax.toString();
880            ASN1OctetString rawValue = new ASN1OctetString(valueString);
881            ByteString normValue =
882                 normalizationMatchingRule.normalizeValue(
883                      new ASN1OctetString(valueString));
884            syntaxSet.remove(new AttributeValue(rawValue, normValue));
885          }
886          catch (Exception e)
887          {
888            String valueString = syntax.toString();
889            ASN1OctetString rawValue = new ASN1OctetString(valueString);
890            ASN1OctetString normValue =
891                 new ASN1OctetString(toLowerCase(valueString));
892            syntaxSet.remove(new AttributeValue(rawValue, normValue));
893          }
894        }
895      }
896    
897    
898    
899      /**
900       * Retrieves the entire set of matching rule definitions for this
901       * schema, as a mapping between the lowercase names and OIDs for the
902       * matching rule and the matching rule itself.  Each matching rule
903       * may be associated with multiple keys (once for the OID and again
904       * for each name).  This should be a superset of the sets of
905       * approximate, equality, ordering, and substring matching rules.
906       * The contents of the returned mapping must not be altered.
907       *
908       * @return  The matching rule definitions for this schema.
909       */
910      public ConcurrentHashMap<String,MatchingRule> getMatchingRules()
911      {
912        return matchingRules;
913      }
914    
915    
916    
917      /**
918       * Retrieves the set of defined matching rules for this schema.
919       *
920       * @return  The set of defined matching rules for this schema.
921       */
922      public LinkedHashSet<AttributeValue> getMatchingRuleSet()
923      {
924        return matchingRuleSet;
925      }
926    
927    
928    
929      /**
930       * Indicates whether this schema definition includes a matching rule
931       * with the provided name or OID.
932       *
933       * @param  lowerName  The name or OID for which to make the
934       *                    determination, formatted in all lowercase
935       *                    characters.
936       *
937       * @return  {@code true} if this schema contains a matching rule
938       *          with the provided name or OID, or {@code false} if not.
939       */
940      public boolean hasMatchingRule(String lowerName)
941      {
942        return matchingRules.containsKey(lowerName);
943      }
944    
945    
946    
947      /**
948       * Retrieves the matching rule definition with the specified name or
949       * OID.
950       *
951       * @param  lowerName  The name or OID of the matching rule to
952       *                    retrieve, formatted in all lowercase
953       *                    characters.
954       *
955       * @return  The requested matching rule, or <CODE>null</CODE> if no
956       *          rule is registered with the provided name or OID.
957       */
958      public MatchingRule getMatchingRule(String lowerName)
959      {
960        return matchingRules.get(lowerName);
961      }
962    
963    
964    
965      /**
966       * Registers the provided matching rule definition with this schema.
967       *
968       * @param  matchingRule       The matching rule to register with
969       *                            this schema.
970       * @param  overwriteExisting  Indicates whether to overwrite an
971       *                            existing mapping if there are any
972       *                            conflicts (i.e.,
973       *                            another matching rule with the same
974       *                            OID or name).
975       *
976       * @throws  DirectoryException  If a conflict is encountered and the
977       *                              <CODE>overwriteExisting</CODE> flag
978       *                              is set to <CODE>false</CODE>
979       */
980      public void registerMatchingRule(MatchingRule matchingRule,
981                                       boolean overwriteExisting)
982             throws DirectoryException
983      {
984        if (matchingRule instanceof ApproximateMatchingRule)
985        {
986          registerApproximateMatchingRule(
987               (ApproximateMatchingRule) matchingRule, overwriteExisting);
988        }
989        else if (matchingRule instanceof EqualityMatchingRule)
990        {
991          registerEqualityMatchingRule(
992               (EqualityMatchingRule) matchingRule, overwriteExisting);
993        }
994        else if (matchingRule instanceof OrderingMatchingRule)
995        {
996          registerOrderingMatchingRule(
997               (OrderingMatchingRule) matchingRule, overwriteExisting);
998        }
999        else if (matchingRule instanceof SubstringMatchingRule)
1000        {
1001          registerSubstringMatchingRule(
1002               (SubstringMatchingRule) matchingRule, overwriteExisting);
1003        }
1004        else
1005        {
1006          synchronized (matchingRules)
1007          {
1008            if (! overwriteExisting)
1009            {
1010              String oid = toLowerCase(matchingRule.getOID());
1011              if (matchingRules.containsKey(oid))
1012              {
1013                MatchingRule conflictingRule = matchingRules.get(oid);
1014    
1015                Message message = ERR_SCHEMA_CONFLICTING_MR_OID.
1016                    get(matchingRule.getNameOrOID(), oid,
1017                        conflictingRule.getNameOrOID());
1018                throw new DirectoryException(
1019                               ResultCode.CONSTRAINT_VIOLATION, message);
1020              }
1021    
1022              String name = matchingRule.getName();
1023              if (name != null)
1024              {
1025                name = toLowerCase(name);
1026                if (matchingRules.containsKey(name))
1027                {
1028                  MatchingRule conflictingRule = matchingRules.get(name);
1029    
1030                  Message message = ERR_SCHEMA_CONFLICTING_MR_NAME.
1031                      get(matchingRule.getOID(), name,
1032                          conflictingRule.getOID());
1033                  throw new DirectoryException(
1034                                ResultCode.CONSTRAINT_VIOLATION, message);
1035                }
1036              }
1037            }
1038    
1039            matchingRules.put(toLowerCase(matchingRule.getOID()),
1040                              matchingRule);
1041    
1042            String name = matchingRule.getName();
1043            if (name != null)
1044            {
1045              matchingRules.put(toLowerCase(name), matchingRule);
1046            }
1047    
1048            // We'll use an attribute value including the normalized value
1049            // rather than the attribute type because otherwise it would
1050            // use a very expensive matching rule (OID first component
1051            // match) that would kill performance.
1052            String valueString = matchingRule.toString();
1053            ASN1OctetString rawValue  = new ASN1OctetString(valueString);
1054            ByteString normValue =
1055                 normalizationMatchingRule.normalizeValue(
1056                      new ASN1OctetString(valueString));
1057            matchingRuleSet.add(new AttributeValue(rawValue, normValue));
1058          }
1059        }
1060      }
1061    
1062    
1063    
1064      /**
1065       * Deregisters the provided matching rule definition with this
1066       * schema.
1067       *
1068       * @param  matchingRule  The matching rule to deregister with this
1069       *                       schema.
1070       */
1071      public void deregisterMatchingRule(MatchingRule matchingRule)
1072      {
1073        if (matchingRule instanceof ApproximateMatchingRule)
1074        {
1075          deregisterApproximateMatchingRule(
1076               (ApproximateMatchingRule) matchingRule);
1077        }
1078        else if (matchingRule instanceof EqualityMatchingRule)
1079        {
1080          deregisterEqualityMatchingRule(
1081               (EqualityMatchingRule) matchingRule);
1082        }
1083        else if (matchingRule instanceof OrderingMatchingRule)
1084        {
1085          deregisterOrderingMatchingRule(
1086               (OrderingMatchingRule) matchingRule);
1087        }
1088        else if (matchingRule instanceof SubstringMatchingRule)
1089        {
1090          deregisterSubstringMatchingRule(
1091               (SubstringMatchingRule) matchingRule);
1092        }
1093        else
1094        {
1095          synchronized (matchingRules)
1096          {
1097            matchingRules.remove(toLowerCase(matchingRule.getOID()),
1098                                 matchingRule);
1099    
1100            String name = matchingRule.getName();
1101            if (name != null)
1102            {
1103              matchingRules.remove(toLowerCase(name), matchingRule);
1104            }
1105    
1106    
1107            // We'll use an attribute value including the normalized value
1108            // rather than the attribute type because otherwise it would
1109            // use a very expensive matching rule (OID first component
1110            // match) that would kill performance.
1111            try
1112            {
1113              String valueString = matchingRule.toString();
1114              ASN1OctetString rawValue = new ASN1OctetString(valueString);
1115              ByteString normValue =
1116                   normalizationMatchingRule.normalizeValue(
1117                        new ASN1OctetString(valueString));
1118              matchingRuleSet.remove(new AttributeValue(rawValue,
1119                                                        normValue));
1120            }
1121            catch (Exception e)
1122            {
1123              String valueString = matchingRule.toString();
1124              ASN1OctetString rawValue = new ASN1OctetString(valueString);
1125              ASN1OctetString normValue =
1126                   new ASN1OctetString(toLowerCase(valueString));
1127              matchingRuleSet.remove(new AttributeValue(rawValue,
1128                                                        normValue));
1129            }
1130          }
1131        }
1132      }
1133    
1134    
1135    
1136      /**
1137       * Retrieves the approximate matching rule definitions for this
1138       * schema, as a mapping between the lowercase names and OIDs for the
1139       * matching rule and the matching rule itself.  Each matching rule
1140       * may be associated with multiple keys (once for the OID and again
1141       * for each name).  The contents of the returned mapping must not be
1142       * altered.
1143       *
1144       * @return  The approximate matching rule definitions for this
1145       *          schema.
1146       */
1147      public ConcurrentHashMap<String,ApproximateMatchingRule>
1148                  getApproximateMatchingRules()
1149      {
1150        return approximateMatchingRules;
1151      }
1152    
1153    
1154    
1155      /**
1156       * Retrieves the approximate matching rule definition with the
1157       * specified name or OID.
1158       *
1159       * @param  lowerName  The name or OID of the matching rule to
1160       *                    retrieve, formatted in all lowercase
1161       *                    characters.
1162       *
1163       * @return  The requested matching rule, or <CODE>null</CODE> if no
1164       *          approximate matching rule is registered with the
1165       *          provided name or OID.
1166       */
1167      public ApproximateMatchingRule getApproximateMatchingRule(
1168                                          String lowerName)
1169      {
1170        return approximateMatchingRules.get(lowerName);
1171      }
1172    
1173    
1174    
1175      /**
1176       * Registers the provided approximate matching rule with this
1177       * schema.
1178       *
1179       * @param  matchingRule       The approximate matching rule to
1180       *                            register.
1181       * @param  overwriteExisting  Indicates whether to overwrite an
1182       *                            existing mapping if there are any
1183       *                            conflicts (i.e., another matching rule
1184       *                            with the same OID or name).
1185       *
1186       * @throws  DirectoryException  If a conflict is encountered and the
1187       *                              <CODE>overwriteExisting</CODE> flag
1188       *                              is set to <CODE>false</CODE>
1189       */
1190      public void registerApproximateMatchingRule(
1191                       ApproximateMatchingRule matchingRule,
1192                       boolean overwriteExisting)
1193             throws DirectoryException
1194      {
1195        synchronized (matchingRules)
1196        {
1197          if (! overwriteExisting)
1198          {
1199            String oid = toLowerCase(matchingRule.getOID());
1200            if (matchingRules.containsKey(oid))
1201            {
1202              MatchingRule conflictingRule = matchingRules.get(oid);
1203    
1204              Message message = ERR_SCHEMA_CONFLICTING_MR_OID.
1205                  get(matchingRule.getNameOrOID(), oid,
1206                      conflictingRule.getNameOrOID());
1207              throw new DirectoryException(
1208                             ResultCode.CONSTRAINT_VIOLATION, message);
1209            }
1210    
1211            String name = matchingRule.getName();
1212            if (name != null)
1213            {
1214              name = toLowerCase(name);
1215              if (matchingRules.containsKey(name))
1216              {
1217                MatchingRule conflictingRule = matchingRules.get(name);
1218    
1219                Message message = ERR_SCHEMA_CONFLICTING_MR_NAME.
1220                    get(matchingRule.getOID(), name,
1221                        conflictingRule.getOID());
1222                throw new DirectoryException(
1223                               ResultCode.CONSTRAINT_VIOLATION, message);
1224              }
1225            }
1226          }
1227    
1228          String oid = toLowerCase(matchingRule.getOID());
1229          approximateMatchingRules.put(oid, matchingRule);
1230          matchingRules.put(oid, matchingRule);
1231    
1232          String name = matchingRule.getName();
1233          if (name != null)
1234          {
1235            name = toLowerCase(name);
1236            approximateMatchingRules.put(name, matchingRule);
1237            matchingRules.put(name, matchingRule);
1238          }
1239    
1240          // We'll use an attribute value including the normalized value
1241          // rather than the attribute type because otherwise it would use
1242          // a very expensive matching rule (OID first component match)
1243          // that would kill performance.
1244          String valueString = matchingRule.toString();
1245          ASN1OctetString rawValue  = new ASN1OctetString(valueString);
1246          ByteString normValue = normalizationMatchingRule.normalizeValue(
1247                                      new ASN1OctetString(valueString));
1248          matchingRuleSet.add(new AttributeValue(rawValue, normValue));
1249        }
1250      }
1251    
1252    
1253    
1254      /**
1255       * Deregisters the provided approximate matching rule definition
1256       * with this schema.
1257       *
1258       * @param  matchingRule  The approximate matching rule to deregister
1259       *                       with this schema.
1260       */
1261      public void deregisterApproximateMatchingRule(
1262                       ApproximateMatchingRule matchingRule)
1263      {
1264        synchronized (matchingRules)
1265        {
1266          String oid = matchingRule.getOID();
1267          approximateMatchingRules.remove(oid, matchingRule);
1268          matchingRules.remove(oid, matchingRule);
1269    
1270          String name = matchingRule.getName();
1271          if (name != null)
1272          {
1273            name = toLowerCase(name);
1274            approximateMatchingRules.remove(name, matchingRule);
1275            matchingRules.remove(name, matchingRule);
1276          }
1277    
1278          // We'll use an attribute value including the normalized value
1279          // rather than the attribute type because otherwise it would use
1280          // a very expensive matching rule (OID first component match)
1281          // that would kill performance.
1282          try
1283          {
1284            String valueString = matchingRule.toString();
1285            ASN1OctetString rawValue = new ASN1OctetString(valueString);
1286            ByteString normValue =
1287                 normalizationMatchingRule.normalizeValue(
1288                      new ASN1OctetString(valueString));
1289            matchingRuleSet.remove(new AttributeValue(rawValue,
1290                                                      normValue));
1291          }
1292          catch (Exception e)
1293          {
1294            String valueString = matchingRule.toString();
1295            ASN1OctetString rawValue = new ASN1OctetString(valueString);
1296            ASN1OctetString normValue =
1297                 new ASN1OctetString(toLowerCase(valueString));
1298            matchingRuleSet.remove(new AttributeValue(rawValue,
1299                                                      normValue));
1300          }
1301        }
1302      }
1303    
1304    
1305    
1306      /**
1307       * Retrieves the equality matching rule definitions for this schema,
1308       * as a mapping between the lowercase names and OIDs for the
1309       * matching rule and the matching rule itself.  Each matching rule
1310       * may be associated with multiple keys (once for the OID and again
1311       * for each name).  The contents of the returned mapping must not be
1312       * altered.
1313       *
1314       * @return  The equality matching rule definitions for this schema.
1315       */
1316      public ConcurrentHashMap<String,EqualityMatchingRule>
1317                  getEqualityMatchingRules()
1318      {
1319        return equalityMatchingRules;
1320      }
1321    
1322    
1323    
1324      /**
1325       * Retrieves the equality matching rule definition with the
1326       * specified name or OID.
1327       *
1328       * @param  lowerName  The name or OID of the matching rule to
1329       *                    retrieve, formatted in all lowercase
1330       *                    characters.
1331       *
1332       * @return  The requested matching rule, or <CODE>null</CODE> if no
1333       *          equality matching rule is registered with the provided
1334       *          name or OID.
1335       */
1336      public EqualityMatchingRule getEqualityMatchingRule(
1337                                       String lowerName)
1338      {
1339        return equalityMatchingRules.get(lowerName);
1340      }
1341    
1342    
1343    
1344      /**
1345       * Registers the provided equality matching rule with this schema.
1346       *
1347       * @param  matchingRule       The equality matching rule to
1348       *                            register.
1349       * @param  overwriteExisting  Indicates whether to overwrite an
1350       *                            existing mapping if there are any
1351       *                            conflicts (i.e., another matching rule
1352       *                            with the same OID or name).
1353       *
1354       * @throws  DirectoryException  If a conflict is encountered and the
1355       *                              <CODE>overwriteExisting</CODE> flag
1356       *                              is set to <CODE>false</CODE>
1357       */
1358      public void registerEqualityMatchingRule(
1359                       EqualityMatchingRule matchingRule,
1360                       boolean overwriteExisting)
1361             throws DirectoryException
1362      {
1363        synchronized (matchingRules)
1364        {
1365          if (! overwriteExisting)
1366          {
1367            String oid = toLowerCase(matchingRule.getOID());
1368            if (matchingRules.containsKey(oid))
1369            {
1370              MatchingRule conflictingRule = matchingRules.get(oid);
1371    
1372              Message message = ERR_SCHEMA_CONFLICTING_MR_OID.
1373                  get(matchingRule.getNameOrOID(), oid,
1374                      conflictingRule.getNameOrOID());
1375              throw new DirectoryException(
1376                             ResultCode.CONSTRAINT_VIOLATION, message);
1377            }
1378    
1379            String name = matchingRule.getName();
1380            if (name != null)
1381            {
1382              name = toLowerCase(name);
1383              if (matchingRules.containsKey(name))
1384              {
1385                MatchingRule conflictingRule = matchingRules.get(name);
1386    
1387                Message message = ERR_SCHEMA_CONFLICTING_MR_NAME.
1388                    get(matchingRule.getOID(), name,
1389                        conflictingRule.getOID());
1390                throw new DirectoryException(
1391                               ResultCode.CONSTRAINT_VIOLATION, message);
1392              }
1393            }
1394          }
1395    
1396          String oid = toLowerCase(matchingRule.getOID());
1397          equalityMatchingRules.put(oid, matchingRule);
1398          matchingRules.put(oid, matchingRule);
1399    
1400          String name = matchingRule.getName();
1401          if (name != null)
1402          {
1403            name = toLowerCase(name);
1404            equalityMatchingRules.put(name, matchingRule);
1405            matchingRules.put(name, matchingRule);
1406          }
1407    
1408          // We'll use an attribute value including the normalized value
1409          // rather than the attribute type because otherwise it would use
1410          // a very expensive matching rule (OID first component match)
1411          // that would kill performance.
1412          String valueString = matchingRule.toString();
1413          ASN1OctetString rawValue  = new ASN1OctetString(valueString);
1414          ByteString normValue = normalizationMatchingRule.normalizeValue(
1415                                      new ASN1OctetString(valueString));
1416          matchingRuleSet.add(new AttributeValue(rawValue, normValue));
1417        }
1418      }
1419    
1420    
1421    
1422      /**
1423       * Deregisters the provided equality matching rule definition with
1424       * this schema.
1425       *
1426       * @param  matchingRule  The equality matching rule to deregister
1427       *                       with this schema.
1428       */
1429      public void deregisterEqualityMatchingRule(
1430                       EqualityMatchingRule matchingRule)
1431      {
1432        synchronized (matchingRules)
1433        {
1434          String oid = matchingRule.getOID();
1435          equalityMatchingRules.remove(oid, matchingRule);
1436          matchingRules.remove(oid, matchingRule);
1437    
1438          String name = matchingRule.getName();
1439          if (name != null)
1440          {
1441            name = toLowerCase(name);
1442            equalityMatchingRules.remove(name, matchingRule);
1443            matchingRules.remove(name, matchingRule);
1444          }
1445    
1446    
1447          // We'll use an attribute value including the normalized value
1448          // rather than the attribute type because otherwise it would use
1449          // a very expensive matching rule (OID first component match)
1450          // that would kill performance.
1451          try
1452          {
1453            String valueString = matchingRule.toString();
1454            ASN1OctetString rawValue = new ASN1OctetString(valueString);
1455            ByteString normValue =
1456                 normalizationMatchingRule.normalizeValue(
1457                      new ASN1OctetString(valueString));
1458            matchingRuleSet.remove(new AttributeValue(rawValue,
1459                                                      normValue));
1460          }
1461          catch (Exception e)
1462          {
1463            String valueString = matchingRule.toString();
1464            ASN1OctetString rawValue = new ASN1OctetString(valueString);
1465            ASN1OctetString normValue =
1466                 new ASN1OctetString(toLowerCase(valueString));
1467            matchingRuleSet.remove(new AttributeValue(rawValue,
1468                                                      normValue));
1469          }
1470        }
1471      }
1472    
1473    
1474    
1475      /**
1476       * Retrieves the ordering matching rule definitions for this schema,
1477       * as a mapping between the lowercase names and OIDs for the
1478       * matching rule and the matching rule itself.  Each matching rule
1479       * may be associated with multiple keys (once for the OID and again
1480       * for each name).  The contents of the returned mapping must not be
1481       * altered.
1482       *
1483       * @return  The ordering matching rule definitions for this schema.
1484       */
1485      public ConcurrentHashMap<String,OrderingMatchingRule>
1486                  getOrderingMatchingRules()
1487      {
1488        return orderingMatchingRules;
1489      }
1490    
1491    
1492    
1493      /**
1494       * Retrieves the ordering matching rule definition with the
1495       * specified name or OID.
1496       *
1497       * @param  lowerName  The name or OID of the matching rule to
1498       *                    retrieve, formatted in all lowercase
1499       *                    characters.
1500       *
1501       * @return  The requested matching rule, or <CODE>null</CODE> if no
1502       *          ordering matching rule is registered with the provided
1503       *          name or OID.
1504       */
1505      public OrderingMatchingRule getOrderingMatchingRule(
1506                                       String lowerName)
1507      {
1508        return orderingMatchingRules.get(lowerName);
1509      }
1510    
1511    
1512    
1513      /**
1514       * Registers the provided ordering matching rule with this schema.
1515       *
1516       * @param  matchingRule       The ordering matching rule to
1517       *                            register.
1518       * @param  overwriteExisting  Indicates whether to overwrite an
1519       *                            existing mapping if there are any
1520       *                            conflicts (i.e., another matching rule
1521       *                            with the same OID or name).
1522       *
1523       * @throws  DirectoryException  If a conflict is encountered and the
1524       *                              <CODE>overwriteExisting</CODE> flag
1525       *                              is set to <CODE>false</CODE>
1526       */
1527      public void registerOrderingMatchingRule(
1528                       OrderingMatchingRule matchingRule,
1529                       boolean overwriteExisting)
1530             throws DirectoryException
1531      {
1532        synchronized (matchingRules)
1533        {
1534          if (! overwriteExisting)
1535          {
1536            String oid = toLowerCase(matchingRule.getOID());
1537            if (matchingRules.containsKey(oid))
1538            {
1539              MatchingRule conflictingRule = matchingRules.get(oid);
1540    
1541              Message message = ERR_SCHEMA_CONFLICTING_MR_OID.
1542                  get(matchingRule.getNameOrOID(), oid,
1543                      conflictingRule.getNameOrOID());
1544              throw new DirectoryException(
1545                             ResultCode.CONSTRAINT_VIOLATION, message);
1546            }
1547    
1548            String name = matchingRule.getName();
1549            if (name != null)
1550            {
1551              name = toLowerCase(name);
1552              if (matchingRules.containsKey(name))
1553              {
1554                MatchingRule conflictingRule = matchingRules.get(name);
1555    
1556                Message message = ERR_SCHEMA_CONFLICTING_MR_NAME.
1557                    get(matchingRule.getOID(), name,
1558                        conflictingRule.getOID());
1559                throw new DirectoryException(
1560                               ResultCode.CONSTRAINT_VIOLATION, message);
1561              }
1562            }
1563          }
1564    
1565          String oid = toLowerCase(matchingRule.getOID());
1566          orderingMatchingRules.put(oid, matchingRule);
1567          matchingRules.put(oid, matchingRule);
1568    
1569          String name = matchingRule.getName();
1570          if (name != null)
1571          {
1572            name = toLowerCase(name);
1573            orderingMatchingRules.put(name, matchingRule);
1574            matchingRules.put(name, matchingRule);
1575          }
1576    
1577          // We'll use an attribute value including the normalized value
1578          // rather than the attribute type because otherwise it would use
1579          // a very expensive matching rule (OID first component match)
1580          // that would kill performance.
1581          String valueString = matchingRule.toString();
1582          ASN1OctetString rawValue  = new ASN1OctetString(valueString);
1583          ByteString normValue = normalizationMatchingRule.normalizeValue(
1584                                      new ASN1OctetString(valueString));
1585          matchingRuleSet.add(new AttributeValue(rawValue, normValue));
1586        }
1587      }
1588    
1589    
1590    
1591      /**
1592       * Deregisters the provided ordering matching rule definition with
1593       * this schema.
1594       *
1595       * @param  matchingRule  The ordering matching rule to deregister
1596       *                       with this schema.
1597       */
1598      public void deregisterOrderingMatchingRule(
1599                       OrderingMatchingRule matchingRule)
1600      {
1601        synchronized (matchingRules)
1602        {
1603          String oid = matchingRule.getOID();
1604          orderingMatchingRules.remove(oid, matchingRule);
1605          matchingRules.remove(oid, matchingRule);
1606    
1607          String name = matchingRule.getName();
1608          if (name != null)
1609          {
1610            name = toLowerCase(name);
1611            orderingMatchingRules.remove(name, matchingRule);
1612            matchingRules.remove(name, matchingRule);
1613          }
1614    
1615          // We'll use an attribute value including the normalized value
1616          // rather than the attribute type because otherwise it would use
1617          // a very expensive matching rule (OID first component match)
1618          // that would kill performance.
1619          try
1620          {
1621            String valueString = matchingRule.toString();
1622            ASN1OctetString rawValue = new ASN1OctetString(valueString);
1623            ByteString normValue =
1624                 normalizationMatchingRule.normalizeValue(
1625                      new ASN1OctetString(valueString));
1626            matchingRuleSet.remove(new AttributeValue(rawValue,
1627                                                      normValue));
1628          }
1629          catch (Exception e)
1630          {
1631            String valueString = matchingRule.toString();
1632            ASN1OctetString rawValue = new ASN1OctetString(valueString);
1633            ASN1OctetString normValue =
1634                 new ASN1OctetString(toLowerCase(valueString));
1635            matchingRuleSet.remove(new AttributeValue(rawValue,
1636                                                      normValue));
1637          }
1638        }
1639      }
1640    
1641    
1642    
1643      /**
1644       * Retrieves the substring matching rule definitions for this
1645       * schema, as a mapping between the lowercase names and OIDs for the
1646       * matching rule and the matching rule itself.  Each matching rule
1647       * may be associated with multiple keys (once for the OID and again
1648       * for each name).  The contents of the returned mapping must not be
1649       * altered.
1650       *
1651       * @return  The substring matching rule definitions for this schema.
1652       */
1653      public ConcurrentHashMap<String,SubstringMatchingRule>
1654                  getSubstringMatchingRules()
1655      {
1656        return substringMatchingRules;
1657      }
1658    
1659    
1660    
1661      /**
1662       * Retrieves the substring matching rule definition with the
1663       * specified name or OID.
1664       *
1665       * @param  lowerName  The name or OID of the matching rule to
1666       *                    retrieve, formatted in all lowercase
1667       *                    characters.
1668       *
1669       * @return  The requested matching rule, or <CODE>null</CODE> if no
1670       *          substring matching rule is registered with the provided
1671       *          name or OID.
1672       */
1673      public SubstringMatchingRule getSubstringMatchingRule(
1674                                        String lowerName)
1675      {
1676        return substringMatchingRules.get(lowerName);
1677      }
1678    
1679    
1680    
1681      /**
1682       * Registers the provided substring matching rule with this schema.
1683       *
1684       * @param  matchingRule       The substring matching rule to
1685       *                            register.
1686       * @param  overwriteExisting  Indicates whether to overwrite an
1687       *                            existing mapping if there are any
1688       *                            conflicts (i.e., another matching rule
1689       *                            with the same OID or name).
1690       *
1691       * @throws  DirectoryException  If a conflict is encountered and the
1692       *                              <CODE>overwriteExisting</CODE> flag
1693       *                              is set to <CODE>false</CODE>
1694       */
1695      public void registerSubstringMatchingRule(
1696                       SubstringMatchingRule matchingRule,
1697                       boolean overwriteExisting)
1698             throws DirectoryException
1699      {
1700        synchronized (matchingRules)
1701        {
1702          if (! overwriteExisting)
1703          {
1704            String oid = toLowerCase(matchingRule.getOID());
1705            if (matchingRules.containsKey(oid))
1706            {
1707              MatchingRule conflictingRule = matchingRules.get(oid);
1708    
1709              Message message = ERR_SCHEMA_CONFLICTING_MR_OID.
1710                  get(matchingRule.getNameOrOID(), oid,
1711                      conflictingRule.getNameOrOID());
1712              throw new DirectoryException(
1713                             ResultCode.CONSTRAINT_VIOLATION, message);
1714            }
1715    
1716            String name = matchingRule.getName();
1717            if (name != null)
1718            {
1719              name = toLowerCase(name);
1720              if (matchingRules.containsKey(name))
1721              {
1722                MatchingRule conflictingRule = matchingRules.get(name);
1723    
1724                Message message = ERR_SCHEMA_CONFLICTING_MR_NAME.
1725                    get(matchingRule.getOID(), name,
1726                        conflictingRule.getOID());
1727                throw new DirectoryException(
1728                               ResultCode.CONSTRAINT_VIOLATION, message);
1729              }
1730            }
1731          }
1732    
1733          String oid = toLowerCase(matchingRule.getOID());
1734          substringMatchingRules.put(oid, matchingRule);
1735          matchingRules.put(oid, matchingRule);
1736    
1737          String name = matchingRule.getName();
1738          if (name != null)
1739          {
1740            name = toLowerCase(name);
1741            substringMatchingRules.put(name, matchingRule);
1742            matchingRules.put(name, matchingRule);
1743          }
1744    
1745          // We'll use an attribute value including the normalized value
1746          // rather than the attribute type because otherwise it would use
1747          // a very expensive matching rule (OID first component match)
1748          // that would kill performance.
1749          String valueString = matchingRule.toString();
1750          ASN1OctetString rawValue  = new ASN1OctetString(valueString);
1751          ByteString normValue = normalizationMatchingRule.normalizeValue(
1752                                      new ASN1OctetString(valueString));
1753          matchingRuleSet.add(new AttributeValue(rawValue, normValue));
1754        }
1755      }
1756    
1757    
1758    
1759      /**
1760       * Deregisters the provided substring matching rule definition with
1761       * this schema.
1762       *
1763       * @param  matchingRule  The substring matching rule to deregister
1764       *                       with this schema.
1765       */
1766      public void deregisterSubstringMatchingRule(
1767                       SubstringMatchingRule matchingRule)
1768      {
1769        synchronized (matchingRules)
1770        {
1771          String oid = matchingRule.getOID();
1772          substringMatchingRules.remove(oid, matchingRule);
1773          matchingRules.remove(oid, matchingRule);
1774    
1775          String name = matchingRule.getName();
1776          if (name != null)
1777          {
1778            name = toLowerCase(name);
1779            substringMatchingRules.remove(name, matchingRule);
1780            matchingRules.remove(name, matchingRule);
1781          }
1782    
1783          // We'll use an attribute value including the normalized value
1784          // rather than the attribute type because otherwise it would use
1785          // a very expensive matching rule (OID first component match)
1786          // that would kill performance.
1787          try
1788          {
1789            String valueString = matchingRule.toString();
1790            ASN1OctetString rawValue = new ASN1OctetString(valueString);
1791            ByteString normValue =
1792                 normalizationMatchingRule.normalizeValue(
1793                      new ASN1OctetString(valueString));
1794            matchingRuleSet.remove(new AttributeValue(rawValue,
1795                                                      normValue));
1796          }
1797          catch (Exception e)
1798          {
1799            String valueString = matchingRule.toString();
1800            ASN1OctetString rawValue = new ASN1OctetString(valueString);
1801            ASN1OctetString normValue =
1802                 new ASN1OctetString(toLowerCase(valueString));
1803            matchingRuleSet.remove(new AttributeValue(rawValue,
1804                                                      normValue));
1805          }
1806        }
1807      }
1808    
1809    
1810    
1811      /**
1812       * Retrieves the matching rule use definitions for this schema, as a
1813       * mapping between the matching rule for the matching rule use
1814       * definition and the matching rule use itself.  Each matching rule
1815       * use should only be present once, since its only key is its
1816       * matching rule.  The contents of the returned mapping must not be
1817       * altered.
1818       *
1819       * @return  The matching rule use definitions for this schema.
1820       */
1821      public ConcurrentHashMap<MatchingRule,MatchingRuleUse>
1822                  getMatchingRuleUses()
1823      {
1824        return matchingRuleUses;
1825      }
1826    
1827    
1828    
1829      /**
1830       * Retrieves the set of defined matching rule uses for this schema.
1831       *
1832       * @return  The set of defined matching rule uses for this schema.
1833       */
1834      public LinkedHashSet<AttributeValue> getMatchingRuleUseSet()
1835      {
1836        return matchingRuleUseSet;
1837      }
1838    
1839    
1840    
1841      /**
1842       * Indicates whether this schema definition includes a matching rule
1843       * use for the provided matching rule.
1844       *
1845       * @param  matchingRule  The matching rule for which to make the
1846       *                       determination.
1847       *
1848       * @return  {@code true} if this schema contains a matching rule use
1849       *          for the provided matching rule, or {@code false} if not.
1850       */
1851      public boolean hasMatchingRuleUse(MatchingRule matchingRule)
1852      {
1853        return matchingRuleUses.containsKey(matchingRule);
1854      }
1855    
1856    
1857    
1858      /**
1859       * Retrieves the matching rule use definition for the specified
1860       * matching rule.
1861       *
1862       * @param  matchingRule  The matching rule for which to retrieve the
1863       *                       matching rule use definition.
1864       *
1865       * @return  The matching rule use definition, or <CODE>null</CODE>
1866       *          if none exists for the specified matching rule.
1867       */
1868      public MatchingRuleUse getMatchingRuleUse(MatchingRule matchingRule)
1869      {
1870        return matchingRuleUses.get(matchingRule);
1871      }
1872    
1873    
1874    
1875      /**
1876       * Registers the provided matching rule use definition with this
1877       * schema.
1878       *
1879       * @param  matchingRuleUse    The matching rule use definition to
1880       *                            register.
1881       * @param  overwriteExisting  Indicates whether to overwrite an
1882       *                            existing mapping if there are any
1883       *                            conflicts (i.e., another matching rule
1884       *                            use with the same matching rule).
1885       *
1886       * @throws  DirectoryException  If a conflict is encountered and the
1887       *                              <CODE>overwriteExisting</CODE> flag
1888       *                              is set to <CODE>false</CODE>
1889       */
1890      public void registerMatchingRuleUse(MatchingRuleUse matchingRuleUse,
1891                                          boolean overwriteExisting)
1892             throws DirectoryException
1893      {
1894        synchronized (matchingRuleUses)
1895        {
1896          MatchingRule matchingRule = matchingRuleUse.getMatchingRule();
1897    
1898          if (! overwriteExisting)
1899          {
1900            if (matchingRuleUses.containsKey(matchingRule))
1901            {
1902              MatchingRuleUse conflictingUse =
1903                                   matchingRuleUses.get(matchingRule);
1904    
1905              Message message = ERR_SCHEMA_CONFLICTING_MATCHING_RULE_USE.
1906                  get(matchingRuleUse.getName(),
1907                      matchingRule.getNameOrOID(),
1908                      conflictingUse.getName());
1909              throw new DirectoryException(
1910                             ResultCode.CONSTRAINT_VIOLATION, message);
1911            }
1912          }
1913    
1914          matchingRuleUses.put(matchingRule, matchingRuleUse);
1915    
1916          // We'll use an attribute value including the normalized value
1917          // rather than the attribute type because otherwise it would use
1918          // a very expensive matching rule (OID first component match)
1919          // that would kill performance.
1920          String valueString = matchingRuleUse.getDefinition();
1921          ASN1OctetString rawValue  = new ASN1OctetString(valueString);
1922          ByteString normValue = normalizationMatchingRule.normalizeValue(
1923                                      new ASN1OctetString(valueString));
1924          matchingRuleUseSet.add(new AttributeValue(rawValue, normValue));
1925        }
1926      }
1927    
1928    
1929    
1930      /**
1931       * Deregisters the provided matching rule use definition with this
1932       * schema.
1933       *
1934       * @param  matchingRuleUse  The matching rule use to deregister with
1935       *                          this schema.
1936       */
1937      public void deregisterMatchingRuleUse(
1938                       MatchingRuleUse matchingRuleUse)
1939      {
1940        synchronized (matchingRuleUses)
1941        {
1942          matchingRuleUses.remove(matchingRuleUse.getMatchingRule(),
1943                                  matchingRuleUse);
1944    
1945          // We'll use an attribute value including the normalized value
1946          // rather than the attribute type because otherwise it would use
1947          // a very expensive matching rule (OID first component match)
1948          // that would kill performance.
1949          try
1950          {
1951            String valueString = matchingRuleUse.getDefinition();
1952            ASN1OctetString rawValue = new ASN1OctetString(valueString);
1953            ByteString normValue =
1954                 normalizationMatchingRule.normalizeValue(
1955                      new ASN1OctetString(valueString));
1956            matchingRuleUseSet.remove(new AttributeValue(rawValue,
1957                                                         normValue));
1958          }
1959          catch (Exception e)
1960          {
1961            String valueString = matchingRuleUse.getDefinition();
1962            ASN1OctetString rawValue = new ASN1OctetString(valueString);
1963            ASN1OctetString normValue =
1964                 new ASN1OctetString(toLowerCase(valueString));
1965            matchingRuleUseSet.remove(new AttributeValue(rawValue,
1966                                                         normValue));
1967          }
1968        }
1969      }
1970    
1971    
1972    
1973      /**
1974       * Retrieves the DIT content rule definitions for this schema, as a
1975       * mapping between the objectclass for the rule and the DIT content
1976       * rule itself.  Each DIT content rule should only be present once,
1977       * since its only key is its objectclass.  The contents of the
1978       * returned mapping must not be altered.
1979       *
1980       * @return  The DIT content rule definitions for this schema.
1981       */
1982      public ConcurrentHashMap<ObjectClass,DITContentRule>
1983                  getDITContentRules()
1984      {
1985        return ditContentRules;
1986      }
1987    
1988    
1989    
1990      /**
1991       * Retrieves the set of defined DIT content rules for this schema.
1992       *
1993       * @return  The set of defined DIT content rules for this schema.
1994       */
1995      public LinkedHashSet<AttributeValue> getDITContentRuleSet()
1996      {
1997        return ditContentRuleSet;
1998      }
1999    
2000    
2001    
2002      /**
2003       * Indicates whether this schema definition includes a DIT content
2004       * rule for the provided objectclass.
2005       *
2006       * @param  objectClass  The objectclass for which to make the
2007       *                      determination.
2008       *
2009       * @return  {@code true} if this schema contains a DIT content rule
2010       *          for the provided objectclass, or {@code false} if not.
2011       */
2012      public boolean hasDITContentRule(ObjectClass objectClass)
2013      {
2014        return ditContentRules.containsKey(objectClass);
2015      }
2016    
2017    
2018    
2019      /**
2020       * Retrieves the DIT content rule definition for the specified
2021       * objectclass.
2022       *
2023       * @param  objectClass  The objectclass for the DIT content rule to
2024       *                      retrieve.
2025       *
2026       * @return  The requested DIT content rule, or <CODE>null</CODE> if
2027       *          no DIT content rule is registered with the provided
2028       *          objectclass.
2029       */
2030      public DITContentRule getDITContentRule(ObjectClass objectClass)
2031      {
2032        return ditContentRules.get(objectClass);
2033      }
2034    
2035    
2036    
2037      /**
2038       * Registers the provided DIT content rule definition with this
2039       * schema.
2040       *
2041       * @param  ditContentRule     The DIT content rule to register.
2042       * @param  overwriteExisting  Indicates whether to overwrite an
2043       *                            existing mapping if there are any
2044       *                            conflicts (i.e., another DIT content
2045       *                            rule with the same objectclass).
2046       *
2047       * @throws  DirectoryException  If a conflict is encountered and the
2048       *                              <CODE>overwriteExisting</CODE> flag
2049       *                              is set to <CODE>false</CODE>
2050       */
2051      public void registerDITContentRule(DITContentRule ditContentRule,
2052                                         boolean overwriteExisting)
2053             throws DirectoryException
2054      {
2055        synchronized (ditContentRules)
2056        {
2057          ObjectClass objectClass = ditContentRule.getStructuralClass();
2058    
2059          if (! overwriteExisting)
2060          {
2061            if (ditContentRules.containsKey(objectClass))
2062            {
2063              DITContentRule conflictingRule =
2064                                  ditContentRules.get(objectClass);
2065    
2066              Message message = ERR_SCHEMA_CONFLICTING_DIT_CONTENT_RULE.
2067                  get(ditContentRule.getName(),
2068                      objectClass.getNameOrOID(),
2069                      conflictingRule.getName());
2070              throw new DirectoryException(
2071                             ResultCode.CONSTRAINT_VIOLATION, message);
2072            }
2073          }
2074    
2075          ditContentRules.put(objectClass, ditContentRule);
2076    
2077          // We'll use an attribute value including the normalized value
2078          // rather than the attribute type because otherwise it would use
2079          // a very expensive matching rule (OID first component match)
2080          // that would kill performance.
2081          String valueString = ditContentRule.getDefinition();
2082          ASN1OctetString rawValue  = new ASN1OctetString(valueString);
2083          ByteString normValue = normalizationMatchingRule.normalizeValue(
2084                                      new ASN1OctetString(valueString));
2085          ditContentRuleSet.add(new AttributeValue(rawValue, normValue));
2086        }
2087      }
2088    
2089    
2090    
2091      /**
2092       * Deregisters the provided DIT content rule definition with this
2093       * schema.
2094       *
2095       * @param  ditContentRule  The DIT content rule to deregister with
2096       *                         this schema.
2097       */
2098      public void deregisterDITContentRule(DITContentRule ditContentRule)
2099      {
2100        synchronized (ditContentRules)
2101        {
2102          ditContentRules.remove(ditContentRule.getStructuralClass(),
2103                                 ditContentRule);
2104    
2105          // We'll use an attribute value including the normalized value
2106          // rather than the attribute type because otherwise it would use
2107          // a very expensive matching rule (OID first component match)
2108          // that would kill performance.
2109          try
2110          {
2111            String valueString = ditContentRule.getDefinition();
2112            ASN1OctetString rawValue = new ASN1OctetString(valueString);
2113            ByteString normValue =
2114                 normalizationMatchingRule.normalizeValue(
2115                      new ASN1OctetString(valueString));
2116            ditContentRuleSet.remove(new AttributeValue(rawValue,
2117                                                        normValue));
2118          }
2119          catch (Exception e)
2120          {
2121            String valueString = ditContentRule.getDefinition();
2122            ASN1OctetString rawValue = new ASN1OctetString(valueString);
2123            ASN1OctetString normValue =
2124                 new ASN1OctetString(toLowerCase(valueString));
2125            ditContentRuleSet.remove(new AttributeValue(rawValue,
2126                                                        normValue));
2127          }
2128        }
2129      }
2130    
2131    
2132    
2133      /**
2134       * Retrieves the set of defined DIT structure rules for this schema.
2135       *
2136       * @return  The set of defined DIT structure rules for this schema.
2137       */
2138      public LinkedHashSet<AttributeValue> getDITStructureRuleSet()
2139      {
2140        return ditStructureRuleSet;
2141      }
2142    
2143    
2144    
2145      /**
2146       * Retrieves the DIT structure rule definitions for this schema, as
2147       * a mapping between the rule ID for the rule and the DIT structure
2148       * rule itself.  Each DIT structure rule should only be present
2149       * once, since its only key is its rule ID.  The contents of the
2150       * returned mapping must not be altered.
2151       *
2152       * @return  The DIT structure rule definitions for this schema.
2153       */
2154      public ConcurrentHashMap<Integer,DITStructureRule>
2155                  getDITStructureRulesByID()
2156      {
2157        return ditStructureRulesByID;
2158      }
2159    
2160    
2161    
2162      /**
2163       * Retrieves the DIT structure rule definitions for this schema, as
2164       * a mapping between the name form for the rule and the DIT
2165       * structure rule itself.  Each DIT structure rule should only be
2166       * present once, since its only key is its name form.  The contents
2167       * of the returned mapping must not be altered.
2168       *
2169       * @return  The DIT structure rule definitions for this schema.
2170       */
2171      public ConcurrentHashMap<NameForm,DITStructureRule>
2172                  getDITStructureRulesByNameForm()
2173      {
2174        return ditStructureRulesByNameForm;
2175      }
2176    
2177    
2178    
2179      /**
2180       * Indicates whether this schema definition includes a DIT structure
2181       * rule with the provided rule ID.
2182       *
2183       * @param  ruleID  The rule ID for which to make the determination.
2184       *
2185       * @return  {@code true} if this schema contains a DIT structure
2186       *          rule with the provided rule ID, or {@code false} if not.
2187       */
2188      public boolean hasDITStructureRule(int ruleID)
2189      {
2190        return ditStructureRulesByID.containsKey(ruleID);
2191      }
2192    
2193    
2194    
2195      /**
2196       * Indicates whether this schema definition includes a DIT structure
2197       * rule for the provided name form.
2198       *
2199       * @param  nameForm  The name form for which to make the
2200       *                   determination.
2201       *
2202       * @return  {@code true} if this schema contains a DIT structure
2203       *          rule for the provided name form, or {@code false} if
2204       *          not.
2205       */
2206      public boolean hasDITStructureRule(NameForm nameForm)
2207      {
2208        return ditStructureRulesByNameForm.containsKey(nameForm);
2209      }
2210    
2211    
2212    
2213      /**
2214       * Retrieves the DIT structure rule definition with the provided
2215       * rule ID.
2216       *
2217       * @param  ruleID  The rule ID for the DIT structure rule to
2218       *                 retrieve.
2219       *
2220       * @return  The requested DIT structure rule, or <CODE>null</CODE>
2221       *          if no DIT structure rule is registered with the provided
2222       *          rule ID.
2223       */
2224      public DITStructureRule getDITStructureRule(int ruleID)
2225      {
2226        return ditStructureRulesByID.get(ruleID);
2227      }
2228    
2229    
2230    
2231      /**
2232       * Retrieves the DIT structure rule definition for the provided name
2233       * form.
2234       *
2235       * @param  nameForm  The name form for the DIT structure rule to
2236       *                   retrieve.
2237       *
2238       * @return  The requested DIT structure rule, or <CODE>null</CODE>
2239       *          if no DIT structure rule is registered with the provided
2240       *          name form.
2241       */
2242      public DITStructureRule getDITStructureRule(NameForm nameForm)
2243      {
2244        return ditStructureRulesByNameForm.get(nameForm);
2245      }
2246    
2247    
2248    
2249      /**
2250       * Registers the provided DIT structure rule definition with this
2251       * schema.
2252       *
2253       * @param  ditStructureRule   The DIT structure rule to register.
2254       * @param  overwriteExisting  Indicates whether to overwrite an
2255       *                            existing mapping if there are any
2256       *                            conflicts (i.e., another DIT structure
2257       *                            rule with the same name form).
2258       *
2259       * @throws  DirectoryException  If a conflict is encountered and the
2260       *                              <CODE>overwriteExisting</CODE> flag
2261       *                              is set to <CODE>false</CODE>
2262       */
2263      public void registerDITStructureRule(
2264                       DITStructureRule ditStructureRule,
2265                       boolean overwriteExisting)
2266             throws DirectoryException
2267      {
2268        synchronized (ditStructureRulesByNameForm)
2269        {
2270          NameForm nameForm = ditStructureRule.getNameForm();
2271          int      ruleID   = ditStructureRule.getRuleID();
2272    
2273          if (! overwriteExisting)
2274          {
2275            if (ditStructureRulesByNameForm.containsKey(nameForm))
2276            {
2277              DITStructureRule conflictingRule =
2278                   ditStructureRulesByNameForm.get(nameForm);
2279    
2280              Message message =
2281                  ERR_SCHEMA_CONFLICTING_DIT_STRUCTURE_RULE_NAME_FORM.
2282                    get(ditStructureRule.getNameOrRuleID(),
2283                        nameForm.getNameOrOID(),
2284                        conflictingRule.getNameOrRuleID());
2285              throw new DirectoryException(
2286                             ResultCode.CONSTRAINT_VIOLATION, message);
2287            }
2288    
2289            if (ditStructureRulesByID.containsKey(ruleID))
2290            {
2291              DITStructureRule conflictingRule =
2292                   ditStructureRulesByID.get(ruleID);
2293    
2294              Message message =
2295                  ERR_SCHEMA_CONFLICTING_DIT_STRUCTURE_RULE_ID.
2296                    get(ditStructureRule.getNameOrRuleID(), ruleID,
2297                        conflictingRule.getNameOrRuleID());
2298              throw new DirectoryException(
2299                             ResultCode.CONSTRAINT_VIOLATION, message);
2300            }
2301          }
2302    
2303          ditStructureRulesByNameForm.put(nameForm, ditStructureRule);
2304          ditStructureRulesByID.put(ruleID, ditStructureRule);
2305    
2306          // We'll use an attribute value including the normalized value
2307          // rather than the attribute type because otherwise it would use
2308          // a very expensive matching rule (OID first component match)
2309          // that would kill performance.
2310          String valueString = ditStructureRule.getDefinition();
2311          ASN1OctetString rawValue = new ASN1OctetString(valueString);
2312          ByteString normValue = normalizationMatchingRule.normalizeValue(
2313                                      new ASN1OctetString(valueString));
2314          ditStructureRuleSet.add(new AttributeValue(rawValue,
2315                                                     normValue));
2316        }
2317      }
2318    
2319    
2320    
2321      /**
2322       * Deregisters the provided DIT structure rule definition with this
2323       * schema.
2324       *
2325       * @param  ditStructureRule  The DIT structure rule to deregister
2326       *                           with this schema.
2327       */
2328      public void deregisterDITStructureRule(
2329                       DITStructureRule ditStructureRule)
2330      {
2331        synchronized (ditStructureRulesByNameForm)
2332        {
2333          ditStructureRulesByNameForm.remove(
2334               ditStructureRule.getNameForm(), ditStructureRule);
2335          ditStructureRulesByID.remove(ditStructureRule.getRuleID(),
2336                                       ditStructureRule);
2337    
2338          // We'll use an attribute value including the normalized value
2339          // rather than the attribute type because otherwise it would use
2340          // a very expensive matching rule (OID first component match)
2341          // that would kill performance.
2342          try
2343          {
2344            String valueString = ditStructureRule.getDefinition();
2345            ASN1OctetString rawValue = new ASN1OctetString(valueString);
2346            ByteString normValue =
2347                 normalizationMatchingRule.normalizeValue(
2348                      new ASN1OctetString(valueString));
2349            ditStructureRuleSet.remove(new AttributeValue(rawValue,
2350                                                          normValue));
2351          }
2352          catch (Exception e)
2353          {
2354            String valueString = ditStructureRule.getDefinition();
2355            ASN1OctetString rawValue = new ASN1OctetString(valueString);
2356            ASN1OctetString normValue =
2357                 new ASN1OctetString(toLowerCase(valueString));
2358            ditStructureRuleSet.remove(new AttributeValue(rawValue,
2359                                                          normValue));
2360          }
2361        }
2362      }
2363    
2364    
2365    
2366      /**
2367       * Retrieves the set of defined name forms for this schema.
2368       *
2369       * @return  The set of defined name forms for this schema.
2370       */
2371      public LinkedHashSet<AttributeValue> getNameFormSet()
2372      {
2373        return nameFormSet;
2374      }
2375    
2376    
2377    
2378      /**
2379       * Retrieves the name form definitions for this schema, as a mapping
2380       * between the objectclass for the name form and the name form
2381       * itself.  Each name form should only be present once, since its
2382       * only key is its objectclass.  The contents of the returned
2383       * mapping must not be altered.
2384       *
2385       * @return  The name form definitions for this schema.
2386       */
2387      public ConcurrentHashMap<ObjectClass,NameForm>
2388                  getNameFormsByObjectClass()
2389      {
2390        return nameFormsByOC;
2391      }
2392    
2393    
2394    
2395      /**
2396       * Retrieves the name form definitions for this schema, as a mapping
2397       * between the names/OID for the name form and the name form itself.
2398       * Each name form may be present multiple times with different names
2399       * and its OID.  The contents of the returned mapping must not be
2400       * altered.
2401       *
2402       * @return  The name form definitions for this schema.
2403       */
2404      public ConcurrentHashMap<String,NameForm> getNameFormsByNameOrOID()
2405      {
2406        return nameFormsByName;
2407      }
2408    
2409    
2410    
2411      /**
2412       * Indicates whether this schema definition includes a name form for
2413       * the specified objectclass.
2414       *
2415       * @param  objectClass  The objectclass for which to make the
2416       *                      determination.
2417       *
2418       * @return  {@code true} if this schema contains a name form for the
2419       *          provided objectclass, or {@code false} if not.
2420       */
2421      public boolean hasNameForm(ObjectClass objectClass)
2422      {
2423        return nameFormsByOC.containsKey(objectClass);
2424      }
2425    
2426    
2427    
2428      /**
2429       * Indicates whether this schema definition includes a name form
2430       * with the specified name or OID.
2431       *
2432       * @param  lowerName  The name or OID for which to make the
2433       *                    determination, formatted in all lowercase
2434       *                    characters.
2435       *
2436       * @return  {@code true} if this schema contains a name form with
2437       *          the provided name or OID, or {@code false} if not.
2438       */
2439      public boolean hasNameForm(String lowerName)
2440      {
2441        return nameFormsByName.containsKey(lowerName);
2442      }
2443    
2444    
2445    
2446      /**
2447       * Retrieves the name form definition for the specified objectclass.
2448       *
2449       * @param  objectClass  The objectclass for the name form to
2450       *                      retrieve.
2451       *
2452       * @return  The requested name form, or <CODE>null</CODE> if no name
2453       *          form is registered with the provided objectClass.
2454       */
2455      public NameForm getNameForm(ObjectClass objectClass)
2456      {
2457        return nameFormsByOC.get(objectClass);
2458      }
2459    
2460    
2461    
2462      /**
2463       * Retrieves the name form definition with the provided name or OID.
2464       *
2465       * @param  lowerName  The name or OID of the name form to retrieve,
2466       *                    formatted in all lowercase characters.
2467       *
2468       * @return  The requested name form, or <CODE>null</CODE> if no name
2469       *          form is registered with the provided name or OID.
2470       */
2471      public NameForm getNameForm(String lowerName)
2472      {
2473        return nameFormsByName.get(lowerName);
2474      }
2475    
2476    
2477    
2478      /**
2479       * Registers the provided name form definition with this schema.
2480       *
2481       * @param  nameForm           The name form definition to register.
2482       * @param  overwriteExisting  Indicates whether to overwrite an
2483       *                            existing mapping if there are any
2484       *                            conflicts (i.e., another name form
2485       *                            with the same objectclass).
2486       *
2487       * @throws  DirectoryException  If a conflict is encountered and the
2488       *                              <CODE>overwriteExisting</CODE> flag
2489       *                              is set to <CODE>false</CODE>
2490       */
2491      public void registerNameForm(NameForm nameForm,
2492                                   boolean overwriteExisting)
2493             throws DirectoryException
2494      {
2495        synchronized (nameFormsByOC)
2496        {
2497          ObjectClass objectClass = nameForm.getStructuralClass();
2498    
2499          if (! overwriteExisting)
2500          {
2501            if (nameFormsByOC.containsKey(objectClass))
2502            {
2503              NameForm conflictingNameForm =
2504                   nameFormsByOC.get(objectClass);
2505    
2506              Message message = ERR_SCHEMA_CONFLICTING_NAME_FORM_OC.
2507                  get(nameForm.getNameOrOID(), objectClass.getNameOrOID(),
2508                      conflictingNameForm.getNameOrOID());
2509              throw new DirectoryException(
2510                             ResultCode.CONSTRAINT_VIOLATION, message);
2511            }
2512    
2513            String oid = toLowerCase(nameForm.getOID());
2514            if (nameFormsByName.containsKey(oid))
2515            {
2516              NameForm conflictingNameForm = nameFormsByName.get(oid);
2517    
2518              Message message = ERR_SCHEMA_CONFLICTING_NAME_FORM_OID.
2519                  get(nameForm.getNameOrOID(), oid,
2520                      conflictingNameForm.getNameOrOID());
2521              throw new DirectoryException(
2522                             ResultCode.CONSTRAINT_VIOLATION, message);
2523            }
2524    
2525            for (String name : nameForm.getNames().keySet())
2526            {
2527              if (nameFormsByName.containsKey(name))
2528              {
2529                NameForm conflictingNameForm = nameFormsByName.get(name);
2530    
2531                Message message = ERR_SCHEMA_CONFLICTING_NAME_FORM_NAME.
2532                    get(nameForm.getNameOrOID(), oid,
2533                        conflictingNameForm.getNameOrOID());
2534                throw new DirectoryException(
2535                               ResultCode.CONSTRAINT_VIOLATION, message);
2536              }
2537            }
2538          }
2539    
2540          nameFormsByOC.put(objectClass, nameForm);
2541          nameFormsByName.put(toLowerCase(nameForm.getOID()), nameForm);
2542    
2543          for (String name : nameForm.getNames().keySet())
2544          {
2545            nameFormsByName.put(name, nameForm);
2546          }
2547    
2548          // We'll use an attribute value including the normalized value
2549          // rather than the attribute type because otherwise it would use
2550          // a very expensive matching rule (OID first component match)
2551          // that would kill performance.
2552          String valueString = nameForm.getDefinition();
2553          ASN1OctetString rawValue  = new ASN1OctetString(valueString);
2554          ByteString normValue = normalizationMatchingRule.normalizeValue(
2555                                      new ASN1OctetString(valueString));
2556          nameFormSet.add(new AttributeValue(rawValue, normValue));
2557        }
2558      }
2559    
2560    
2561    
2562      /**
2563       * Deregisters the provided name form definition with this schema.
2564       *
2565       * @param  nameForm  The name form definition to deregister.
2566       */
2567      public void deregisterNameForm(NameForm nameForm)
2568      {
2569        synchronized (nameFormsByOC)
2570        {
2571          nameFormsByOC.remove(nameForm.getStructuralClass(), nameForm);
2572          nameFormsByName.remove(toLowerCase(nameForm.getOID()),
2573                                 nameForm);
2574    
2575          for (String name : nameForm.getNames().keySet())
2576          {
2577            nameFormsByName.remove(name, nameForm);
2578          }
2579    
2580          // We'll use an attribute value including the normalized value
2581          // rather than the attribute type because otherwise it would use
2582          // a very expensive matching rule (OID first component match)
2583          // that would kill performance.
2584          try
2585          {
2586            String valueString = nameForm.getDefinition();
2587            ASN1OctetString rawValue = new ASN1OctetString(valueString);
2588            ByteString normValue =
2589                 normalizationMatchingRule.normalizeValue(
2590                      new ASN1OctetString(valueString));
2591            nameFormSet.remove(new AttributeValue(rawValue, normValue));
2592          }
2593          catch (Exception e)
2594          {
2595            String valueString = nameForm.getDefinition();
2596            ASN1OctetString rawValue = new ASN1OctetString(valueString);
2597            ASN1OctetString normValue =
2598                 new ASN1OctetString(toLowerCase(valueString));
2599            nameFormSet.remove(new AttributeValue(rawValue, normValue));
2600          }
2601        }
2602      }
2603    
2604    
2605    
2606      /**
2607       * Retrieves the modification timestamp for the file in the schema
2608       * configuration directory with the oldest last modified time.
2609       *
2610       * @return  The modification timestamp for the file in the schema
2611       *          configuration directory with the oldest last modified
2612       *          time.
2613       */
2614      public long getOldestModificationTime()
2615      {
2616        return oldestModificationTime;
2617      }
2618    
2619    
2620    
2621      /**
2622       * Sets the modification timestamp for the oldest file in the schema
2623       * configuration directory.
2624       *
2625       * @param  oldestModificationTime  The modification timestamp for
2626       *                                 the oldest file in the schema
2627       *                                 configuration directory.
2628       */
2629      public void setOldestModificationTime(long oldestModificationTime)
2630      {
2631        this.oldestModificationTime = oldestModificationTime;
2632      }
2633    
2634    
2635    
2636      /**
2637       * Retrieves the modification timestamp for the file in the schema
2638       * configuration directory with the youngest last modified time.
2639       *
2640       * @return  The modification timestamp for the file in the schema
2641       *          configuration directory with the youngest last modified
2642       *          time.
2643       */
2644      public long getYoungestModificationTime()
2645      {
2646        return youngestModificationTime;
2647      }
2648    
2649    
2650    
2651      /**
2652       * Sets the modification timestamp for the youngest file in the
2653       * schema configuration directory.
2654       *
2655       * @param  youngestModificationTime  The modification timestamp for
2656       *                                   the youngest file in the schema
2657       *                                   configuration directory.
2658       */
2659      public void setYoungestModificationTime(
2660                       long youngestModificationTime)
2661      {
2662        this.youngestModificationTime = youngestModificationTime;
2663      }
2664    
2665    
2666    
2667      /**
2668       * Recursively rebuilds all schema elements that are dependent upon
2669       * the provided element.  This must be invoked whenever an existing
2670       * schema element is modified in order to ensure that any elements
2671       * that depend on it should also be recreated to reflect the change.
2672       * <BR><BR>
2673       * The following conditions create dependencies between schema
2674       * elements:
2675       * <UL>
2676       *   <LI>If an attribute type references a superior attribute type,
2677       *       then it is dependent upon that superior attribute
2678       *       type.</LI>
2679       *   <LI>If an objectclass requires or allows an attribute type,
2680       *       then it is dependent upon that attribute type.</LI>
2681       *   <LI>If a name form requires or allows an attribute type in the
2682       *       RDN, then it is dependent upon that attribute type.</LI>
2683       *   <LI>If a DIT content rule requires, allows, or forbids the use
2684       *       of an attribute type, then it is dependent upon that
2685       *       attribute type.</LI>
2686       *   <LI>If a matching rule use references an attribute type, then
2687       *       it is dependent upon that attribute type.</LI>
2688       *   <LI>If an objectclass references a superior objectclass, then
2689       *       it is dependent upon that superior objectclass.</LI>
2690       *   <LI>If a name form references a structural objectclass, then it
2691       *       is dependent upon that objectclass.</LI>
2692       *   <LI>If a DIT content rule references a structural or auxiliary
2693       *       objectclass, then it is dependent upon that
2694       *       objectclass.</LI>
2695       *   <LI>If a DIT structure rule references a name form, then it is
2696       *       dependent upon that name form.</LI>
2697       *   <LI>If a DIT structure rule references a superior DIT structure
2698       *       rule, then it is dependent upon that superior DIT structure
2699       *       rule.</LI>
2700       * </UL>
2701       *
2702       * @param  element  The element for which to recursively rebuild all
2703       *                  dependent elements.
2704       *
2705       * @throws  DirectoryException  If a problem occurs while rebuilding
2706       *                              any of the schema elements.
2707       */
2708      public void rebuildDependentElements(SchemaFileElement element)
2709             throws DirectoryException
2710      {
2711        try
2712        {
2713          rebuildDependentElements(element, 0);
2714        }
2715        catch (DirectoryException de)
2716        {
2717          // If we got an error as a result of a circular reference, then
2718          // we want to make sure that the schema element we call out is
2719          // the one that is at the root of the problem.
2720          if (de.getMessageObject().getDescriptor().equals(
2721              ERR_SCHEMA_CIRCULAR_DEPENDENCY_REFERENCE))
2722          {
2723            Message message = ERR_SCHEMA_CIRCULAR_DEPENDENCY_REFERENCE.
2724                get(element.getDefinition());
2725            throw new DirectoryException(de.getResultCode(), message,
2726                                         de);
2727          }
2728    
2729    
2730          // It wasn't a circular reference error, so just re-throw the
2731          // exception.
2732          throw de;
2733        }
2734      }
2735    
2736    
2737    
2738      /**
2739       * Recursively rebuilds all schema elements that are dependent upon
2740       * the provided element, increasing the depth for each level of
2741       * recursion to protect against errors due to circular references.
2742       *
2743       * @param  element  The element for which to recursively rebuild all
2744       *                  dependent elements.
2745       * @param  depth    The current recursion depth.
2746       *
2747       * @throws  DirectoryException  If a problem occurs while rebuilding
2748       *                              any of the schema elements.
2749       */
2750      private void rebuildDependentElements(SchemaFileElement element,
2751                                            int depth)
2752              throws DirectoryException
2753      {
2754        if (depth > 20)
2755        {
2756          // FIXME -- Is this an appropriate maximum depth for detecting
2757          // circular references?
2758          Message message = ERR_SCHEMA_CIRCULAR_DEPENDENCY_REFERENCE.get(
2759              element.getDefinition());
2760          throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
2761                                       message);
2762        }
2763    
2764    
2765        // Figure out what type of element we're dealing with and make the
2766        // appropriate determinations for that element.
2767        if (element instanceof AttributeType)
2768        {
2769          AttributeType t = (AttributeType) element;
2770    
2771          for (AttributeType at : attributeTypes.values())
2772          {
2773            if ((at.getSuperiorType() != null) &&
2774                at.getSuperiorType().equals(t))
2775            {
2776              AttributeType newAT = at.recreateFromDefinition();
2777              deregisterAttributeType(at);
2778              registerAttributeType(newAT, true);
2779              rebuildDependentElements(at, depth+1);
2780            }
2781          }
2782    
2783          for (ObjectClass oc : objectClasses.values())
2784          {
2785            if (oc.getRequiredAttributes().contains(t) ||
2786                oc.getOptionalAttributes().contains(t))
2787            {
2788              ObjectClass newOC = oc.recreateFromDefinition();
2789              deregisterObjectClass(oc);
2790              registerObjectClass(newOC, true);
2791              rebuildDependentElements(oc, depth+1);
2792            }
2793          }
2794    
2795          for (NameForm nf : nameFormsByOC.values())
2796          {
2797            if (nf.getRequiredAttributes().contains(t) ||
2798                nf.getOptionalAttributes().contains(t))
2799            {
2800              NameForm newNF = nf.recreateFromDefinition();
2801              deregisterNameForm(nf);
2802              registerNameForm(newNF, true);
2803              rebuildDependentElements(nf, depth+1);
2804            }
2805          }
2806    
2807          for (DITContentRule dcr : ditContentRules.values())
2808          {
2809            if (dcr.getRequiredAttributes().contains(t) ||
2810                dcr.getOptionalAttributes().contains(t) ||
2811                dcr.getProhibitedAttributes().contains(t))
2812            {
2813              DITContentRule newDCR = dcr.recreateFromDefinition();
2814              deregisterDITContentRule(dcr);
2815              registerDITContentRule(newDCR, true);
2816              rebuildDependentElements(dcr, depth+1);
2817            }
2818          }
2819    
2820          for (MatchingRuleUse mru : matchingRuleUses.values())
2821          {
2822            if (mru.getAttributes().contains(t))
2823            {
2824              MatchingRuleUse newMRU = mru.recreateFromDefinition();
2825              deregisterMatchingRuleUse(mru);
2826              registerMatchingRuleUse(newMRU, true);
2827              rebuildDependentElements(mru, depth+1);
2828            }
2829          }
2830        }
2831        else if (element instanceof ObjectClass)
2832        {
2833          ObjectClass c = (ObjectClass) element;
2834    
2835          for (ObjectClass oc : objectClasses.values())
2836          {
2837            if ((oc.getSuperiorClass() != null) &&
2838                oc.getSuperiorClass().equals(c))
2839            {
2840              ObjectClass newOC = oc.recreateFromDefinition();
2841              deregisterObjectClass(oc);
2842              registerObjectClass(newOC, true);
2843              rebuildDependentElements(oc, depth+1);
2844            }
2845          }
2846    
2847          NameForm nf = nameFormsByOC.get(c);
2848          if (nf != null)
2849          {
2850            NameForm newNF = nf.recreateFromDefinition();
2851            deregisterNameForm(nf);
2852            registerNameForm(newNF, true);
2853            rebuildDependentElements(nf, depth+1);
2854          }
2855    
2856          for (DITContentRule dcr : ditContentRules.values())
2857          {
2858            if (dcr.getStructuralClass().equals(c) ||
2859                dcr.getAuxiliaryClasses().contains(c))
2860            {
2861              DITContentRule newDCR = dcr.recreateFromDefinition();
2862              deregisterDITContentRule(dcr);
2863              registerDITContentRule(newDCR, true);
2864              rebuildDependentElements(dcr, depth+1);
2865            }
2866          }
2867        }
2868        else if (element instanceof NameForm)
2869        {
2870          NameForm n = (NameForm) element;
2871          DITStructureRule dsr = ditStructureRulesByNameForm.get(n);
2872          if (dsr != null)
2873          {
2874            DITStructureRule newDSR = dsr.recreateFromDefinition();
2875            deregisterDITStructureRule(dsr);
2876            registerDITStructureRule(newDSR, true);
2877            rebuildDependentElements(dsr, depth+1);
2878          }
2879        }
2880        else if (element instanceof DITStructureRule)
2881        {
2882          DITStructureRule d = (DITStructureRule) element;
2883          for (DITStructureRule dsr : ditStructureRulesByID.values())
2884          {
2885            if (dsr.getSuperiorRules().contains(d))
2886            {
2887              DITStructureRule newDSR = dsr.recreateFromDefinition();
2888              deregisterDITStructureRule(dsr);
2889              registerDITStructureRule(newDSR, true);
2890              rebuildDependentElements(dsr, depth+1);
2891            }
2892          }
2893        }
2894      }
2895    
2896    
2897    
2898      /**
2899       * Creates a new <CODE>Schema</CODE> object that is a duplicate of
2900       * this one.  It elements may be added and removed from the
2901       * duplicate without impacting this version.
2902       *
2903       * @return  A new <CODE>Schema</CODE> object that is a duplicate of
2904       *          this one.
2905       */
2906      public Schema duplicate()
2907      {
2908        Schema dupSchema = new Schema();
2909    
2910        dupSchema.attributeTypes.putAll(attributeTypes);
2911        dupSchema.subordinateTypes.putAll(subordinateTypes);
2912        dupSchema.objectClasses.putAll(objectClasses);
2913        dupSchema.syntaxes.putAll(syntaxes);
2914        dupSchema.matchingRules.putAll(matchingRules);
2915        dupSchema.approximateMatchingRules.putAll(
2916             approximateMatchingRules);
2917        dupSchema.equalityMatchingRules.putAll(equalityMatchingRules);
2918        dupSchema.orderingMatchingRules.putAll(orderingMatchingRules);
2919        dupSchema.substringMatchingRules.putAll(substringMatchingRules);
2920        dupSchema.matchingRuleUses.putAll(matchingRuleUses);
2921        dupSchema.ditContentRules.putAll(ditContentRules);
2922        dupSchema.ditStructureRulesByID.putAll(ditStructureRulesByID);
2923        dupSchema.ditStructureRulesByNameForm.putAll(
2924             ditStructureRulesByNameForm);
2925        dupSchema.nameFormsByOC.putAll(nameFormsByOC);
2926        dupSchema.nameFormsByName.putAll(nameFormsByName);
2927        dupSchema.syntaxSet.addAll(syntaxSet);
2928        dupSchema.attributeTypeSet.addAll(attributeTypeSet);
2929        dupSchema.ditContentRuleSet.addAll(ditContentRuleSet);
2930        dupSchema.ditStructureRuleSet.addAll(ditStructureRuleSet);
2931        dupSchema.matchingRuleSet.addAll(matchingRuleSet);
2932        dupSchema.matchingRuleUseSet.addAll(matchingRuleUseSet);
2933        dupSchema.nameFormSet.addAll(nameFormSet);
2934        dupSchema.objectClassSet.addAll(objectClassSet);
2935        dupSchema.oldestModificationTime   = oldestModificationTime;
2936        dupSchema.youngestModificationTime = youngestModificationTime;
2937        if (extraAttributes != null)
2938        {
2939          dupSchema.extraAttributes =
2940            new HashMap<String, Attribute>(extraAttributes);
2941        }
2942    
2943        return dupSchema;
2944      }
2945    
2946    
2947      /**
2948       * Get the extraAttributes stored in this schema.
2949       *
2950       * @return  The extraAttributes stored in this schema.
2951       */
2952      public Map<String, Attribute> getExtraAttributes()
2953      {
2954        return extraAttributes;
2955      }
2956    
2957    
2958      /**
2959       * Add a new extra Attribute for this schema.
2960       *
2961       * @param  name     The identifier of the extra Attribute.
2962       *
2963       * @param  attr     The extra attribute that must be added to
2964       *                  this Schema.
2965       */
2966      public void addExtraAttribute(String name, Attribute attr)
2967      {
2968        extraAttributes.put(name, attr);
2969      }
2970    
2971    
2972      /**
2973       * Writes a single file containing all schema element definitions,
2974       * which can be used on startup to determine whether the schema
2975       * files were edited with the server offline.
2976       */
2977      public static void writeConcatenatedSchema()
2978      {
2979        String concatFilePath = null;
2980        try
2981        {
2982          LinkedHashSet<String> attributeTypes =
2983               new LinkedHashSet<String>();
2984          LinkedHashSet<String> objectClasses =
2985               new LinkedHashSet<String>();
2986          LinkedHashSet<String> nameForms = new LinkedHashSet<String>();
2987          LinkedHashSet<String> ditContentRules =
2988               new LinkedHashSet<String>();
2989          LinkedHashSet<String> ditStructureRules =
2990               new LinkedHashSet<String>();
2991          LinkedHashSet<String> matchingRuleUses =
2992               new LinkedHashSet<String>();
2993          genConcatenatedSchema(attributeTypes, objectClasses, nameForms,
2994                                ditContentRules, ditStructureRules,
2995                                matchingRuleUses);
2996    
2997    
2998          File configFile = new File(DirectoryServer.getConfigFile());
2999          File configDirectory  = configFile.getParentFile();
3000          File upgradeDirectory = new File(configDirectory, "upgrade");
3001          upgradeDirectory.mkdir();
3002          File concatFile       = new File(upgradeDirectory,
3003                                           SCHEMA_CONCAT_FILE_NAME);
3004          concatFilePath = concatFile.getAbsolutePath();
3005    
3006          File tempFile = new File(concatFilePath + ".tmp");
3007          BufferedWriter writer =
3008               new BufferedWriter(new FileWriter(tempFile, false));
3009          writer.write("dn: " + DirectoryServer.getSchemaDN().toString());
3010          writer.newLine();
3011          writer.write("objectClass: top");
3012          writer.newLine();
3013          writer.write("objectClass: ldapSubentry");
3014          writer.newLine();
3015          writer.write("objectClass: subschema");
3016          writer.newLine();
3017    
3018          for (String line : attributeTypes)
3019          {
3020            writer.write(ATTR_ATTRIBUTE_TYPES);
3021            writer.write(": ");
3022            writer.write(line);
3023            writer.newLine();
3024          }
3025    
3026          for (String line : objectClasses)
3027          {
3028            writer.write(ATTR_OBJECTCLASSES);
3029            writer.write(": ");
3030            writer.write(line);
3031            writer.newLine();
3032          }
3033    
3034          for (String line : nameForms)
3035          {
3036            writer.write(ATTR_NAME_FORMS);
3037            writer.write(": ");
3038            writer.write(line);
3039            writer.newLine();
3040          }
3041    
3042          for (String line : ditContentRules)
3043          {
3044            writer.write(ATTR_DIT_CONTENT_RULES);
3045            writer.write(": ");
3046            writer.write(line);
3047            writer.newLine();
3048          }
3049    
3050          for (String line : ditStructureRules)
3051          {
3052            writer.write(ATTR_DIT_STRUCTURE_RULES);
3053            writer.write(": ");
3054            writer.write(line);
3055            writer.newLine();
3056          }
3057    
3058          for (String line : matchingRuleUses)
3059          {
3060            writer.write(ATTR_MATCHING_RULE_USE);
3061            writer.write(": ");
3062            writer.write(line);
3063            writer.newLine();
3064          }
3065    
3066          writer.close();
3067    
3068          if (concatFile.exists())
3069          {
3070            concatFile.delete();
3071          }
3072          tempFile.renameTo(concatFile);
3073        }
3074        catch (Exception e)
3075        {
3076          if (debugEnabled())
3077          {
3078            TRACER.debugCaught(DebugLogLevel.ERROR, e);
3079          }
3080    
3081          // This is definitely not ideal, but it's not the end of the
3082          // world.  The worst that should happen is that the schema
3083          // changes could potentially be sent to the other servers again
3084          // when this server is restarted, which shouldn't hurt anything.
3085          // Still, we should log a warning message.
3086          logError(ERR_SCHEMA_CANNOT_WRITE_CONCAT_SCHEMA_FILE.get(
3087              String.valueOf(concatFilePath), getExceptionMessage(e)));
3088        }
3089      }
3090    
3091    
3092    
3093      /**
3094       * Reads the files contained in the schema directory and generates a
3095       * concatenated view of their contents in the provided sets.
3096       *
3097       * @param  attributeTypes     The set into which to place the
3098       *                            attribute types read from the schema
3099       *                            files.
3100       * @param  objectClasses      The set into which to place the object
3101       *                            classes read from the schema files.
3102       * @param  nameForms          The set into which to place the name
3103       *                            forms read from the schema files.
3104       * @param  ditContentRules    The set into which to place the DIT
3105       *                            content rules read from the schema
3106       *                            files.
3107       * @param  ditStructureRules  The set into which to place the DIT
3108       *                            structure rules read from the schema
3109       *                            files.
3110       * @param  matchingRuleUses   The set into which to place the
3111       *                            matching rule uses read from the
3112       *                            schema files.
3113       *
3114       * @throws  IOException  If a problem occurs while reading the
3115       *                       schema file elements.
3116       */
3117      public static void genConcatenatedSchema(
3118                              LinkedHashSet<String> attributeTypes,
3119                              LinkedHashSet<String> objectClasses,
3120                              LinkedHashSet<String> nameForms,
3121                              LinkedHashSet<String> ditContentRules,
3122                              LinkedHashSet<String> ditStructureRules,
3123                              LinkedHashSet<String> matchingRuleUses)
3124              throws IOException
3125      {
3126        // Get a sorted list of the files in the schema directory.
3127        String schemaDirectory =
3128                    SchemaConfigManager.getSchemaDirectoryPath();
3129        TreeSet<String> schemaFileNames = new TreeSet<String>();
3130        for (File f : new File(schemaDirectory).listFiles())
3131        {
3132          if (f.isFile())
3133          {
3134            schemaFileNames.add(f.getName());
3135          }
3136        }
3137    
3138    
3139        // Open each of the files in order and read the elements that they
3140        // contain, appending them to the appropriate lists.
3141        for (String name : schemaFileNames)
3142        {
3143          // Read the contents of the file into a list with one schema
3144          // element per list element.
3145          LinkedList<StringBuilder> lines =
3146               new LinkedList<StringBuilder>();
3147          BufferedReader reader =
3148               new BufferedReader(new FileReader(
3149                        new File(schemaDirectory, name)));
3150    
3151          while (true)
3152          {
3153            String line = reader.readLine();
3154            if (line == null)
3155            {
3156              break;
3157            }
3158            else if (line.startsWith("#") || (line.length() == 0))
3159            {
3160              continue;
3161            }
3162            else if (line.startsWith(" "))
3163            {
3164              lines.getLast().append(line.substring(1));
3165            }
3166            else
3167            {
3168              lines.add(new StringBuilder(line));
3169            }
3170          }
3171    
3172          reader.close();
3173    
3174    
3175          // Iterate through each line in the list.  Find the colon and
3176          // get the attribute name at the beginning.  If it's someting
3177          // that we don't recognize, then skip it.  Otherwise, add the
3178          // X-SCHEMA-FILE extension and add it to the appropriate schema
3179          // element list.
3180          for (StringBuilder buffer : lines)
3181          {
3182            // Get the line and add the X-SCHEMA-FILE extension to the end
3183            // of it.  All of them should end with " )" but some might
3184            // have the parenthesis crammed up against the last character
3185            // so deal with that as well.
3186            String line = buffer.toString().trim();
3187            if (line.endsWith(" )"))
3188            {
3189             line = line.substring(0, line.length()-1) +
3190                    SCHEMA_PROPERTY_FILENAME + " '" + name + "' )";
3191            }
3192            else if (line.endsWith(")"))
3193            {
3194             line = line.substring(0, line.length()-1) + " " +
3195                    SCHEMA_PROPERTY_FILENAME + " '" + name + "' )";
3196            }
3197            else
3198            {
3199              continue;
3200            }
3201    
3202            String value;
3203            String lowerLine = toLowerCase(line);
3204            if (lowerLine.startsWith(ATTR_ATTRIBUTE_TYPES_LC))
3205            {
3206              value = line.substring(
3207                           ATTR_ATTRIBUTE_TYPES.length()+1).trim();
3208              attributeTypes.add(value);
3209            }
3210            else if (lowerLine.startsWith(ATTR_OBJECTCLASSES_LC))
3211            {
3212              value = line.substring(
3213                           ATTR_OBJECTCLASSES.length()+1).trim();
3214              objectClasses.add(value);
3215            }
3216            else if (lowerLine.startsWith(ATTR_NAME_FORMS_LC))
3217            {
3218              value = line.substring(ATTR_NAME_FORMS.length()+1).trim();
3219              nameForms.add(value);
3220            }
3221            else if (lowerLine.startsWith(ATTR_DIT_CONTENT_RULES_LC))
3222            {
3223              value = line.substring(
3224                         ATTR_DIT_CONTENT_RULES.length()+1).trim();
3225              ditContentRules.add(value);
3226            }
3227            else if (lowerLine.startsWith(ATTR_DIT_STRUCTURE_RULES_LC))
3228            {
3229              value = line.substring(
3230                         ATTR_DIT_STRUCTURE_RULES.length()+1).trim();
3231              ditStructureRules.add(value);
3232            }
3233            else if (lowerLine.startsWith(ATTR_MATCHING_RULE_USE_LC))
3234            {
3235              value = line.substring(
3236                         ATTR_MATCHING_RULE_USE.length()+1).trim();
3237              matchingRuleUses.add(value);
3238            }
3239          }
3240        }
3241      }
3242    
3243    
3244    
3245      /**
3246       * Reads data from the specified concatenated schema file into the
3247       * provided sets.
3248       *
3249       * @param  concatSchemaFile   The path to the concatenated schema
3250       *                            file to be read.
3251       * @param  attributeTypes     The set into which to place the
3252       *                            attribute types read from the
3253       *                            concatenated schema file.
3254       * @param  objectClasses      The set into which to place the object
3255       *                            classes read from the concatenated
3256       *                            schema file.
3257       * @param  nameForms          The set into which to place the name
3258       *                            forms read from the concatenated
3259       *                            schema file.
3260       * @param  ditContentRules    The set into which to place the DIT
3261       *                            content rules read from the
3262       *                            concatenated schema file.
3263       * @param  ditStructureRules  The set into which to place the DIT
3264       *                            structure rules read from the
3265       *                            concatenated schema file.
3266       * @param  matchingRuleUses   The set into which to place the
3267       *                            matching rule uses read from the
3268       *                            concatenated schema file.
3269       *
3270       * @throws  IOException  If a problem occurs while reading the
3271       *                       schema file elements.
3272       */
3273      public static void readConcatenatedSchema(String concatSchemaFile,
3274                              LinkedHashSet<String> attributeTypes,
3275                              LinkedHashSet<String> objectClasses,
3276                              LinkedHashSet<String> nameForms,
3277                              LinkedHashSet<String> ditContentRules,
3278                              LinkedHashSet<String> ditStructureRules,
3279                              LinkedHashSet<String> matchingRuleUses)
3280              throws IOException
3281      {
3282        BufferedReader reader =
3283             new BufferedReader(new FileReader(concatSchemaFile));
3284        while (true)
3285        {
3286          String line = reader.readLine();
3287          if (line == null)
3288          {
3289            break;
3290          }
3291    
3292          String value;
3293          String lowerLine = toLowerCase(line);
3294          if (lowerLine.startsWith(ATTR_ATTRIBUTE_TYPES_LC))
3295          {
3296            value =
3297                 line.substring(ATTR_ATTRIBUTE_TYPES.length()+1).trim();
3298            attributeTypes.add(value);
3299          }
3300          else if (lowerLine.startsWith(ATTR_OBJECTCLASSES_LC))
3301          {
3302            value = line.substring(ATTR_OBJECTCLASSES.length()+1).trim();
3303            objectClasses.add(value);
3304          }
3305          else if (lowerLine.startsWith(ATTR_NAME_FORMS_LC))
3306          {
3307            value = line.substring(ATTR_NAME_FORMS.length()+1).trim();
3308            nameForms.add(value);
3309          }
3310          else if (lowerLine.startsWith(ATTR_DIT_CONTENT_RULES_LC))
3311          {
3312            value = line.substring(
3313                         ATTR_DIT_CONTENT_RULES.length()+1).trim();
3314            ditContentRules.add(value);
3315          }
3316          else if (lowerLine.startsWith(ATTR_DIT_STRUCTURE_RULES_LC))
3317          {
3318            value = line.substring(
3319                         ATTR_DIT_STRUCTURE_RULES.length()+1).trim();
3320            ditStructureRules.add(value);
3321          }
3322          else if (lowerLine.startsWith(ATTR_MATCHING_RULE_USE_LC))
3323          {
3324            value = line.substring(
3325                         ATTR_MATCHING_RULE_USE.length()+1).trim();
3326            matchingRuleUses.add(value);
3327          }
3328        }
3329    
3330        reader.close();
3331      }
3332    
3333    
3334    
3335      /**
3336       * Compares the provided sets of schema element definitions and
3337       * writes any differences found into the given list of
3338       * modifications.
3339       *
3340       * @param  oldElements  The set of elements of the specified type
3341       *                      read from the previous concatenated schema
3342       *                      files.
3343       * @param  newElements  The set of elements of the specified type
3344       *                      read from the server's current schema.
3345       * @param  elementType  The attribute type associated with the
3346       *                      schema element being compared.
3347       * @param  mods         The list of modifications into which any
3348       *                      identified differences should be written.
3349       */
3350      public static void compareConcatenatedSchema(
3351                              LinkedHashSet<String> oldElements,
3352                              LinkedHashSet<String> newElements,
3353                              AttributeType elementType,
3354                              LinkedList<Modification> mods)
3355      {
3356        AttributeType attributeTypesType =
3357             DirectoryServer.getAttributeType(ATTR_ATTRIBUTE_TYPES_LC,
3358                                              true);
3359    
3360        LinkedHashSet<AttributeValue> values =
3361             new LinkedHashSet<AttributeValue>();
3362        for (String s : oldElements)
3363        {
3364          if (! newElements.contains(s))
3365          {
3366            values.add(new AttributeValue(attributeTypesType, s));
3367          }
3368        }
3369    
3370        if (! values.isEmpty())
3371        {
3372          mods.add(new Modification(ModificationType.DELETE,
3373                            new Attribute(elementType,
3374                                          elementType.getNameOrOID(),
3375                                          values)));
3376        }
3377    
3378    
3379        values.clear();
3380        for (String s : newElements)
3381        {
3382          if (! oldElements.contains(s))
3383          {
3384            values.add(new AttributeValue(attributeTypesType, s));
3385          }
3386        }
3387    
3388        if (! values.isEmpty())
3389        {
3390          mods.add(new Modification(ModificationType.ADD,
3391                            new Attribute(elementType,
3392                                          elementType.getNameOrOID(),
3393                                          values)));
3394        }
3395      }
3396    
3397    
3398    
3399      /**
3400       * Destroys the structures maintained by the schema so that they are
3401       * no longer usable.  This should only be called at the end of the
3402       * server shutdown process, and it can help detect inappropriate
3403       * cached references.
3404       */
3405      @org.opends.server.types.PublicAPI(
3406           stability=org.opends.server.types.StabilityLevel.PRIVATE,
3407           mayInstantiate=false,
3408           mayExtend=false,
3409           mayInvoke=true)
3410      public synchronized void destroy()
3411      {
3412        if (approximateMatchingRules != null)
3413        {
3414          approximateMatchingRules.clear();
3415          approximateMatchingRules = null;
3416        }
3417    
3418        if (attributeTypes != null)
3419        {
3420          attributeTypes.clear();
3421          attributeTypes = null;
3422        }
3423    
3424        if (attributeTypeSet != null)
3425        {
3426          attributeTypeSet.clear();
3427          attributeTypeSet = null;
3428        }
3429    
3430        if (ditContentRules != null)
3431        {
3432          ditContentRules.clear();
3433          ditContentRules = null;
3434        }
3435    
3436        if (ditContentRuleSet != null)
3437        {
3438          ditContentRuleSet.clear();
3439          ditContentRuleSet = null;
3440        }
3441    
3442        if (ditStructureRulesByID != null)
3443        {
3444          ditStructureRulesByID.clear();
3445          ditStructureRulesByID = null;
3446        }
3447    
3448        if (ditStructureRulesByNameForm != null)
3449        {
3450          ditStructureRulesByNameForm.clear();
3451          ditStructureRulesByNameForm = null;
3452        }
3453    
3454        if (ditStructureRuleSet != null)
3455        {
3456          ditStructureRuleSet.clear();
3457          ditStructureRuleSet = null;
3458        }
3459    
3460        if (equalityMatchingRules != null)
3461        {
3462          equalityMatchingRules.clear();
3463          equalityMatchingRules = null;
3464        }
3465    
3466        if (matchingRules != null)
3467        {
3468          matchingRules.clear();
3469          matchingRules = null;
3470        }
3471    
3472        if (matchingRuleSet != null)
3473        {
3474          matchingRuleSet.clear();
3475          matchingRuleSet = null;
3476        }
3477    
3478        if (matchingRuleUses != null)
3479        {
3480          matchingRuleUses.clear();
3481          matchingRuleUses = null;
3482        }
3483    
3484        if (matchingRuleUseSet != null)
3485        {
3486          matchingRuleUseSet.clear();
3487          matchingRuleUseSet = null;
3488        }
3489    
3490        if (nameFormsByName != null)
3491        {
3492          nameFormsByName.clear();
3493          nameFormsByName = null;
3494        }
3495    
3496        if (nameFormsByOC != null)
3497        {
3498          nameFormsByOC.clear();
3499          nameFormsByOC = null;
3500        }
3501    
3502        if (nameFormSet != null)
3503        {
3504          nameFormSet.clear();
3505          nameFormSet = null;
3506        }
3507    
3508        if (objectClasses != null)
3509        {
3510          objectClasses.clear();
3511          objectClasses = null;
3512        }
3513    
3514        if (objectClassSet != null)
3515        {
3516          objectClassSet.clear();
3517          objectClassSet = null;
3518        }
3519    
3520        if (orderingMatchingRules != null)
3521        {
3522          orderingMatchingRules.clear();
3523          orderingMatchingRules = null;
3524        }
3525    
3526        if (subordinateTypes != null)
3527        {
3528          subordinateTypes.clear();
3529          subordinateTypes = null;
3530        }
3531    
3532        if (substringMatchingRules != null)
3533        {
3534          substringMatchingRules.clear();
3535          substringMatchingRules = null;
3536        }
3537    
3538        if (extraAttributes != null)
3539        {
3540          extraAttributes.clear();
3541          extraAttributes = null;
3542        }
3543    
3544        if (syntaxes != null)
3545        {
3546          syntaxes.clear();
3547          syntaxes = null;
3548        }
3549    
3550        if (syntaxSet != null)
3551        {
3552          syntaxSet.clear();
3553          syntaxSet = null;
3554        }
3555      }
3556    }
3557