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 legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2006-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2011-2015 ForgeRock AS
026 */
027package org.opends.server.types;
028
029import java.io.*;
030import java.util.ArrayList;
031import java.util.Collections;
032import java.util.HashMap;
033import java.util.LinkedHashSet;
034import java.util.LinkedList;
035import java.util.List;
036import java.util.Map;
037import java.util.Set;
038import java.util.TreeSet;
039import java.util.concurrent.ConcurrentHashMap;
040
041import org.forgerock.i18n.LocalizableMessage;
042import org.forgerock.i18n.slf4j.LocalizedLogger;
043import org.forgerock.opendj.ldap.ByteString;
044import org.forgerock.opendj.ldap.ModificationType;
045import org.forgerock.opendj.ldap.ResultCode;
046import org.forgerock.opendj.ldap.schema.MatchingRule;
047import org.forgerock.opendj.ldap.schema.Syntax;
048import org.opends.server.core.DirectoryServer;
049import org.opends.server.core.SchemaConfigManager;
050import org.opends.server.schema.AttributeTypeSyntax;
051import org.opends.server.schema.DITContentRuleSyntax;
052import org.opends.server.schema.DITStructureRuleSyntax;
053import org.opends.server.schema.MatchingRuleUseSyntax;
054import org.opends.server.schema.NameFormSyntax;
055import org.opends.server.schema.ObjectClassSyntax;
056import org.opends.server.util.StaticUtils;
057
058import static org.opends.messages.BackendMessages.*;
059import static org.opends.messages.CoreMessages.*;
060import static org.opends.server.config.ConfigConstants.*;
061import static org.opends.server.types.CommonSchemaElements.*;
062import static org.opends.server.util.CollectionUtils.*;
063import static org.opends.server.util.ServerConstants.*;
064import static org.opends.server.util.StaticUtils.*;
065
066/**
067 * This class defines a data structure that holds information about
068 * the components of the Directory Server schema.  It includes the
069 * following kinds of elements:
070 *
071 * <UL>
072 *   <LI>Attribute type definitions</LI>
073 *   <LI>Objectclass definitions</LI>
074 *   <LI>Attribute syntax definitions</LI>
075 *   <LI>Matching rule definitions</LI>
076 *   <LI>Matching rule use definitions</LI>
077 *   <LI>DIT content rule definitions</LI>
078 *   <LI>DIT structure rule definitions</LI>
079 *   <LI>Name form definitions</LI>
080 * </UL>
081 */
082@org.opends.server.types.PublicAPI(
083     stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
084     mayInstantiate=false,
085     mayExtend=false,
086     mayInvoke=true)
087public final class Schema
088{
089  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
090
091  /**
092   * The set of subordinate attribute types registered within the server schema.
093   */
094  private ConcurrentHashMap<AttributeType,List<AttributeType>>
095               subordinateTypes;
096
097  /**
098   * The set of attribute type definitions for this schema, mapped between the
099   * lowercase names and OID for the definition and the attribute type itself.
100   */
101  private ConcurrentHashMap<String,AttributeType> attributeTypes;
102
103  /**
104   * The set of objectclass definitions for this schema, mapped between the
105   * lowercase names and OID for the definition and the objectclass itself.
106   */
107  private ConcurrentHashMap<String,ObjectClass> objectClasses;
108
109  /**
110   * The set of attribute syntaxes for this schema, mapped between the OID for
111   * the syntax and the syntax itself.
112   */
113  private ConcurrentHashMap<String,Syntax> syntaxes;
114
115  /**
116   * The default attribute syntax to use for attributes with no defined syntax.
117   */
118  private Syntax defaultSyntax;
119
120  /**
121   * The entire set of matching rules for this schema, mapped between the
122   * lowercase names and OID for the definition and the matching rule itself.
123   */
124  private ConcurrentHashMap<String,MatchingRule> matchingRules;
125
126
127
128  /**
129   * The set of matching rule uses for this schema, mapped between the matching
130   * rule for the definition and the matching rule use itself.
131   */
132  private ConcurrentHashMap<MatchingRule,MatchingRuleUse>
133               matchingRuleUses;
134
135  /**
136   * The set of DIT content rules for this schema, mapped between the structural
137   * objectclass for the definition and the DIT content rule itself.
138   */
139  private ConcurrentHashMap<ObjectClass,DITContentRule>
140               ditContentRules;
141
142  /**
143   * The set of DIT structure rules for this schema, mapped between the name
144   * form for the definition and the DIT structure rule itself.
145   */
146  private ConcurrentHashMap<Integer,DITStructureRule>
147               ditStructureRulesByID;
148
149  /**
150   * The set of DIT structure rules for this schema, mapped between the name
151   * form for the definition and the DIT structure rule itself.
152   */
153  private ConcurrentHashMap<NameForm,DITStructureRule>
154               ditStructureRulesByNameForm;
155
156  /**
157   * The set of name forms for this schema, mapped between the structural
158   * objectclass for the definition and the list of name forms.
159   */
160  private ConcurrentHashMap<ObjectClass,List<NameForm>>
161          nameFormsByOC;
162
163  /**
164   * The set of name forms for this schema, mapped between the names/OID and the
165   * name form itself.
166   */
167  private ConcurrentHashMap<String,NameForm> nameFormsByName;
168
169  /**
170   * The set of ldap syntax descriptions for this schema, mapped the OID and the
171   * ldap syntax description itself.
172   */
173  private ConcurrentHashMap<String,LDAPSyntaxDescription>
174          ldapSyntaxDescriptions;
175
176  /** The oldest modification timestamp for any schema configuration file. */
177  private long oldestModificationTime;
178
179  /** The youngest modification timestamp for any schema configuration file. */
180  private long youngestModificationTime;
181
182  /**
183   * A set of extra attributes that are not used directly by the schema but may
184   * be used by other component to store information in the schema.
185   * <p>
186   * ex : Replication uses this to store its state and GenerationID.
187   */
188  private Map<String, Attribute> extraAttributes = new HashMap<>();
189
190
191  /** Creates a new schema structure with all elements initialized but empty. */
192  public Schema()
193  {
194    attributeTypes = new ConcurrentHashMap<>();
195    objectClasses = new ConcurrentHashMap<>();
196    syntaxes = new ConcurrentHashMap<>();
197    matchingRules = new ConcurrentHashMap<>();
198    matchingRuleUses = new ConcurrentHashMap<>();
199    ditContentRules = new ConcurrentHashMap<>();
200    ditStructureRulesByID = new ConcurrentHashMap<>();
201    ditStructureRulesByNameForm = new ConcurrentHashMap<>();
202    nameFormsByOC = new ConcurrentHashMap<>();
203    nameFormsByName = new ConcurrentHashMap<>();
204    ldapSyntaxDescriptions = new ConcurrentHashMap<>();
205    subordinateTypes = new ConcurrentHashMap<>();
206
207    oldestModificationTime    = System.currentTimeMillis();
208    youngestModificationTime  = oldestModificationTime;
209  }
210
211
212
213  /**
214   * Retrieves the attribute type definitions for this schema, as a
215   * mapping between the lowercase names and OIDs for the attribute
216   * type and the attribute type itself.  Each attribute type may be
217   * associated with multiple keys (once for the OID and again for
218   * each name).  The contents of the returned mapping must not be
219   * altered.
220   *
221   * @return  The attribute type definitions for this schema.
222   */
223  public ConcurrentHashMap<String,AttributeType> getAttributeTypes()
224  {
225    return attributeTypes;
226  }
227
228
229
230  /**
231   * Indicates whether this schema definition includes an attribute
232   * type with the provided name or OID.
233   *
234   * @param  lowerName  The name or OID for which to make the
235   *                    determination, formatted in all lowercase
236   *                    characters.
237   *
238   * @return  {@code true} if this schema contains an attribute type
239   *          with the provided name or OID, or {@code false} if not.
240   */
241  public boolean hasAttributeType(String lowerName)
242  {
243    return attributeTypes.containsKey(lowerName);
244  }
245
246
247
248  /**
249   * Retrieves the attribute type definition with the specified name
250   * or OID.
251   *
252   * @param  lowerName  The name or OID of the attribute type to
253   *                    retrieve, formatted in all lowercase
254   *                    characters.
255   *
256   * @return  The requested attribute type, or <CODE>null</CODE> if no
257   *          type is registered with the provided name or OID.
258   */
259  public AttributeType getAttributeType(String lowerName)
260  {
261    return attributeTypes.get(lowerName);
262  }
263
264
265
266  /**
267   * Registers the provided attribute type definition with this
268   * schema.
269   *
270   * @param  attributeType      The attribute type to register with
271   *                            this schema.
272   * @param  overwriteExisting  Indicates whether to overwrite an
273   *                            existing mapping if there are any
274   *                            conflicts (i.e., another attribute
275   *                            type with the same OID or name).
276   *
277   * @throws  DirectoryException  If a conflict is encountered and the
278   *                              <CODE>overwriteExisting</CODE> flag
279   *                              is set to <CODE>false</CODE>
280   */
281  public void registerAttributeType(AttributeType attributeType,
282                                    boolean overwriteExisting)
283         throws DirectoryException
284  {
285    synchronized (attributeTypes)
286    {
287      if (! overwriteExisting)
288      {
289        String oid = toLowerCase(attributeType.getOID());
290        if (attributeTypes.containsKey(oid))
291        {
292          AttributeType conflictingType = attributeTypes.get(oid);
293
294          LocalizableMessage message = ERR_SCHEMA_CONFLICTING_ATTRIBUTE_OID.
295              get(attributeType.getNameOrOID(), oid,
296                  conflictingType.getNameOrOID());
297          throw new DirectoryException(
298                         ResultCode.CONSTRAINT_VIOLATION, message);
299        }
300
301        for (String name : attributeType.getNormalizedNames())
302        {
303          if (attributeTypes.containsKey(name))
304          {
305            AttributeType conflictingType = attributeTypes.get(name);
306
307            LocalizableMessage message = ERR_SCHEMA_CONFLICTING_ATTRIBUTE_NAME.
308                get(attributeType.getNameOrOID(), name,
309                    conflictingType.getNameOrOID());
310            throw new DirectoryException(
311                           ResultCode.CONSTRAINT_VIOLATION, message);
312          }
313        }
314      }
315
316      AttributeType old = attributeTypes.put(
317          toLowerCase(attributeType.getOID()), attributeType);
318      if (old != null && old != attributeType)
319      {
320        // Mark the old attribute type as stale so that caches (such as
321        // compressed schema) can detect changes.
322        old.setDirty();
323      }
324
325      for (String name : attributeType.getNormalizedNames())
326      {
327        attributeTypes.put(name, attributeType);
328      }
329
330      AttributeType superiorType = attributeType.getSuperiorType();
331      if (superiorType != null)
332      {
333        registerSubordinateType(attributeType, superiorType);
334      }
335    }
336  }
337
338  /**
339   * Deregisters the provided attribute type definition with this
340   * schema.
341   *
342   * @param  attributeType  The attribute type to deregister with this
343   *                        schema.
344   */
345  public void deregisterAttributeType(AttributeType attributeType)
346  {
347    synchronized (attributeTypes)
348    {
349      if (attributeTypes.remove(toLowerCase(attributeType.getOID()),
350          attributeType))
351      {
352        // Mark the old attribute type as stale so that caches (such as
353        // compressed schema) can detect changes.
354        attributeType.setDirty();
355      }
356
357      for (String name : attributeType.getNormalizedNames())
358      {
359        attributeTypes.remove(name, attributeType);
360      }
361
362      AttributeType superiorType = attributeType.getSuperiorType();
363      if (superiorType != null)
364      {
365        deregisterSubordinateType(attributeType, superiorType);
366      }
367    }
368  }
369
370
371  /**
372   * Registers the provided attribute type as a subtype of the given
373   * superior attribute type, recursively following any additional
374   * elements in the superior chain.
375   *
376   * @param  attributeType  The attribute type to be registered as a
377   *                        subtype for the given superior type.
378   * @param  superiorType   The superior type for which to register
379   *                        the given attribute type as a subtype.
380   */
381  private void registerSubordinateType(AttributeType attributeType,
382                                       AttributeType superiorType)
383  {
384    List<AttributeType> subTypes = subordinateTypes.get(superiorType);
385    if (subTypes == null)
386    {
387      superiorType.setMayHaveSubordinateTypes();
388      subordinateTypes.put(superiorType, newLinkedList(attributeType));
389    }
390    else if (! subTypes.contains(attributeType))
391    {
392      superiorType.setMayHaveSubordinateTypes();
393      subTypes.add(attributeType);
394
395      AttributeType higherSuperior = superiorType.getSuperiorType();
396      if (higherSuperior != null)
397      {
398        registerSubordinateType(attributeType, higherSuperior);
399      }
400    }
401  }
402
403
404
405  /**
406   * Deregisters the provided attribute type as a subtype of the given
407   * superior attribute type, recursively following any additional
408   * elements in the superior chain.
409   *
410   * @param  attributeType  The attribute type to be deregistered as a
411   *                        subtype for the given superior type.
412   * @param  superiorType   The superior type for which to deregister
413   *                        the given attribute type as a subtype.
414   */
415  private void deregisterSubordinateType(AttributeType attributeType,
416                                         AttributeType superiorType)
417  {
418    List<AttributeType> subTypes = subordinateTypes.get(superiorType);
419    if (subTypes != null && subTypes.remove(attributeType))
420    {
421      AttributeType higherSuperior = superiorType.getSuperiorType();
422      if (higherSuperior != null)
423      {
424        deregisterSubordinateType(attributeType, higherSuperior);
425      }
426    }
427  }
428
429
430
431  /**
432   * Retrieves the set of subtypes registered for the given attribute
433   * type.
434   *
435   * @param  attributeType  The attribute type for which to retrieve
436   *                        the set of registered subtypes.
437   *
438   * @return  The set of subtypes registered for the given attribute
439   *          type, or an empty set if there are no subtypes
440   *          registered for the attribute type.
441   */
442  public List<AttributeType> getSubTypes(AttributeType attributeType)
443  {
444    List<AttributeType> subTypes = subordinateTypes.get(attributeType);
445    if (subTypes == null)
446    {
447      return Collections.emptyList();
448    }
449    else
450    {
451      return subTypes;
452    }
453  }
454
455
456
457  /**
458   * Retrieves the objectclass definitions for this schema, as a
459   * mapping between the lowercase names and OIDs for the objectclass
460   * and the objectclass itself.  Each objectclass may be associated
461   * with multiple keys (once for the OID and again for each name).
462   * The contents of the returned mapping must not be altered.
463   *
464   * @return  The objectclass definitions for this schema.
465   */
466  public ConcurrentHashMap<String,ObjectClass> getObjectClasses()
467  {
468    return objectClasses;
469  }
470
471
472
473  /**
474   * Indicates whether this schema definition includes an objectclass
475   * with the provided name or OID.
476   *
477   * @param  lowerName  The name or OID for which to make the
478   *                    determination, formatted in all lowercase
479   *                    characters.
480   *
481   * @return  {@code true} if this schema contains an objectclass with
482   *          the provided name or OID, or {@code false} if not.
483   */
484  public boolean hasObjectClass(String lowerName)
485  {
486    return objectClasses.containsKey(lowerName);
487  }
488
489
490
491  /**
492   * Retrieves the objectclass definition with the specified name or
493   * OID.
494   *
495   * @param  lowerName  The name or OID of the objectclass to
496   *                    retrieve, formatted in all lowercase
497   *                    characters.
498   *
499   * @return  The requested objectclass, or <CODE>null</CODE> if no
500   *          class is registered with the provided name or OID.
501   */
502  public ObjectClass getObjectClass(String lowerName)
503  {
504    return objectClasses.get(lowerName);
505  }
506
507
508
509  /**
510   * Registers the provided objectclass definition with this schema.
511   *
512   * @param  objectClass        The objectclass to register with this
513   *                            schema.
514   * @param  overwriteExisting  Indicates whether to overwrite an
515   *                            existing mapping if there are any
516   *                            conflicts (i.e., another objectclass
517   *                            with the same OID or name).
518   *
519   * @throws  DirectoryException  If a conflict is encountered and the
520   *                              <CODE>overwriteExisting</CODE> flag
521   *                              is set to <CODE>false</CODE>.
522   */
523  public void registerObjectClass(ObjectClass objectClass,
524                                  boolean overwriteExisting)
525         throws DirectoryException
526  {
527    synchronized (objectClasses)
528    {
529      if (! overwriteExisting)
530      {
531        String oid = toLowerCase(objectClass.getOID());
532        if (objectClasses.containsKey(oid))
533        {
534          ObjectClass conflictingClass = objectClasses.get(oid);
535
536          LocalizableMessage message = ERR_SCHEMA_CONFLICTING_OBJECTCLASS_OID.
537              get(objectClass.getNameOrOID(), oid,
538                  conflictingClass.getNameOrOID());
539          throw new DirectoryException(
540                       ResultCode.CONSTRAINT_VIOLATION, message);
541        }
542
543        for (String name : objectClass.getNormalizedNames())
544        {
545          if (objectClasses.containsKey(name))
546          {
547            ObjectClass conflictingClass = objectClasses.get(name);
548
549            LocalizableMessage message = ERR_SCHEMA_CONFLICTING_OBJECTCLASS_NAME.
550                get(objectClass.getNameOrOID(), name,
551                    conflictingClass.getNameOrOID());
552            throw new DirectoryException(
553                           ResultCode.CONSTRAINT_VIOLATION, message);
554          }
555        }
556      }
557
558      ObjectClass old = objectClasses.put(toLowerCase(objectClass.getOID()),
559          objectClass);
560      if (old != null && old != objectClass)
561      {
562        // Mark the old object class as stale so that caches (such as compressed
563        // schema) can detect changes.
564        old.setDirty();
565      }
566
567      for (String name : objectClass.getNormalizedNames())
568      {
569        objectClasses.put(name, objectClass);
570      }
571    }
572  }
573
574
575
576  /**
577   * Deregisters the provided objectclass definition with this schema.
578   *
579   * @param  objectClass  The objectclass to deregister with this
580   *                      schema.
581   */
582  public void deregisterObjectClass(ObjectClass objectClass)
583  {
584    synchronized (objectClasses)
585    {
586      if (objectClasses.remove(toLowerCase(objectClass.getOID()), objectClass))
587      {
588        // Mark the old object class as stale so that caches (such as
589        // compressed schema) can detect changes.
590        objectClass.setDirty();
591      }
592
593      for (String name : objectClass.getNormalizedNames())
594      {
595        objectClasses.remove(name, objectClass);
596      }
597    }
598  }
599
600
601
602  /**
603   * Retrieves the attribute syntax definitions for this schema, as a
604   * mapping between the OID for the syntax and the syntax itself.
605   * Each syntax should only be present once, since its only key is
606   * its OID.  The contents of the returned mapping must not be
607   * altered.
608   *
609   * @return  The attribute syntax definitions for this schema.
610   */
611  public ConcurrentHashMap<String,Syntax> getSyntaxes()
612  {
613    return syntaxes;
614  }
615
616
617
618  /**
619   * Indicates whether this schema definition includes an attribute
620   * syntax with the provided name or OID.
621   *
622   * @param  lowerName  The name or OID for which to make the
623   *                    determination, formatted in all lowercase
624   *                    characters.
625   *
626   * @return  {@code true} if this schema contains an attribute syntax
627   *          with the provided name or OID, or {@code false} if not.
628   */
629  public boolean hasSyntax(String lowerName)
630  {
631    return syntaxes.containsKey(lowerName);
632  }
633
634  /**
635   * Retrieves the requested attribute syntax.
636   *
637   * @param oid
638   *          The OID of the syntax to retrieve.
639   * @param allowDefault
640   *          Indicates whether to return the default attribute syntax if the
641   *          requested syntax is unknown.
642   * @return The requested attribute syntax, the default syntax if the requested
643   *         syntax is unknown and the caller has indicated that the default is
644   *         acceptable, or <CODE>null</CODE> otherwise.
645   */
646  public Syntax getSyntax(String oid, boolean allowDefault)
647  {
648    Syntax syntax = getSyntax(oid);
649    if (syntax == null && allowDefault)
650    {
651      return getDefaultSyntax();
652    }
653
654    return syntax;
655  }
656
657  /**
658   * Retrieves the attribute syntax definition with the OID.
659   *
660   * @param  lowerName  The OID of the attribute syntax to retrieve,
661   *                    formatted in all lowercase characters.
662   *
663   * @return  The requested attribute syntax, or <CODE>null</CODE> if
664   *          no syntax is registered with the provided OID.
665   */
666  public Syntax getSyntax(String lowerName)
667  {
668    return syntaxes.get(lowerName);
669  }
670
671  /**
672   * Retrieves the default attribute syntax that should be used for attributes
673   * that are not defined in the server schema.
674   *
675   * @return  The default attribute syntax that should be used for attributes
676   *          that are not defined in the server schema.
677   */
678  public Syntax getDefaultSyntax()
679  {
680    return defaultSyntax;
681  }
682
683
684
685  /**
686   * Registers the defaut syntax for this schema.
687   *
688   * @param defaultSyntax
689   *            The defautl syntax to use.
690   */
691  public void registerDefaultSyntax(
692      Syntax defaultSyntax)
693  {
694    this.defaultSyntax = defaultSyntax;
695  }
696
697
698
699  /**
700   * Registers the provided attribute syntax definition with this
701   * schema.
702   *
703   * @param  syntax             The attribute syntax to register with
704   *                            this schema.
705   * @param  overwriteExisting  Indicates whether to overwrite an
706   *                            existing mapping if there are any
707   *                            conflicts (i.e., another attribute
708   *                            syntax with the same OID).
709   *
710   * @throws  DirectoryException  If a conflict is encountered and the
711   *                              <CODE>overwriteExisting</CODE> flag
712   *                              is set to <CODE>false</CODE>
713   */
714  public void registerSyntax(Syntax syntax,
715                             boolean overwriteExisting)
716         throws DirectoryException
717  {
718    synchronized (syntaxes)
719    {
720      if (! overwriteExisting)
721      {
722        String oid = toLowerCase(syntax.getOID());
723        if (syntaxes.containsKey(oid))
724        {
725          Syntax conflictingSyntax = syntaxes.get(oid);
726
727          LocalizableMessage message = ERR_SCHEMA_CONFLICTING_SYNTAX_OID.
728              get(syntax.getName(), oid,
729                  conflictingSyntax.getName());
730          throw new DirectoryException(
731                         ResultCode.CONSTRAINT_VIOLATION, message);
732        }
733      }
734
735      syntaxes.put(toLowerCase(syntax.getOID()), syntax);
736    }
737  }
738
739
740
741  /**
742   * Deregisters the provided attribute syntax definition with this
743   * schema.
744   *
745   * @param  syntax  The attribute syntax to deregister with this
746   *                 schema.
747   */
748  public void deregisterSyntax(Syntax syntax)
749  {
750    synchronized (syntaxes)
751    {
752      syntaxes.remove(toLowerCase(syntax.getOID()), syntax);
753    }
754  }
755
756
757
758  /**
759   * Retrieves the ldap syntax definitions for this schema, as a
760   * mapping between the OID for the syntax and the ldap syntax
761   * definition itself. Each ldap syntax should only be present once,
762   * since its only key is its OID.  The contents of the returned
763   * mapping must not be altered.
764   *
765   * @return  The ldap syntax definitions for this schema.
766   */
767  public ConcurrentHashMap<String,LDAPSyntaxDescription>
768          getLdapSyntaxDescriptions()
769  {
770    return ldapSyntaxDescriptions;
771  }
772
773
774
775  /**
776   * Indicates whether this schema definition includes an ldap
777   * syntax description with the provided name or OID.
778   *
779   * @param  lowerName  The OID for which to make the
780   *                    determination, formatted in all lowercase
781   *                    characters.
782   *
783   * @return  {@code true} if this schema contains an ldap syntax
784   *          with the provided name or OID, or {@code false} if not.
785   */
786  public boolean hasLdapSyntaxDescription(String lowerName)
787  {
788    return ldapSyntaxDescriptions.containsKey(lowerName);
789  }
790
791
792
793  /**
794   * Retrieves the ldap syntax definition with the OID.
795   *
796   * @param  lowerName  The OID of the ldap syntax to retrieve,
797   *                    formatted in all lowercase characters.
798   *
799   * @return  The requested ldap syntax, or <CODE>null</CODE> if
800   *          no syntax is registered with the provided OID.
801   */
802  public LDAPSyntaxDescription getLdapSyntaxDescription(
803          String lowerName)
804  {
805    return ldapSyntaxDescriptions.get(lowerName);
806  }
807
808
809
810  /**
811   * Registers the provided ldap syntax description with this
812   * schema.
813   *
814   * @param  syntax    The ldap syntax description to register
815   *                              with this schema.
816   * @param  overwriteExisting  Indicates whether to overwrite an
817   *                            existing mapping if there are any
818   *                            conflicts (i.e., another ldap
819   *                            syntax with the same OID).
820   *
821   * @throws  DirectoryException  If a conflict is encountered and
822   *                              <CODE>overwriteExisting</CODE> flag
823   *                              is set to <CODE>false</CODE>
824   */
825  public void registerLdapSyntaxDescription(
826                            LDAPSyntaxDescription syntax,
827                             boolean overwriteExisting)
828         throws DirectoryException
829  {
830    /**
831     * ldapsyntaxes is part real and part virtual. For any
832     * ldapsyntaxes attribute this is real, an LDAPSyntaxDescription
833     * object is created and stored with the schema. Also, the
834     * associated LDAPSyntaxDescriptionSyntax is added into the
835     * virtual syntax set to make this available through virtual
836     * ldapsyntaxes attribute.
837     */
838    synchronized (ldapSyntaxDescriptions)
839    {
840      String oid = toLowerCase(syntax.getSyntax().getOID());
841      if (! overwriteExisting && ldapSyntaxDescriptions.containsKey(oid))
842      {
843         throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
844            ERR_SCHEMA_MODIFY_MULTIPLE_CONFLICTS_FOR_ADD_LDAP_SYNTAX.get(oid));
845      }
846
847      ldapSyntaxDescriptions.put(oid, syntax);
848
849      //Register the attribute syntax with the schema. It will ensure
850      // syntax is available along with the other virtual values for
851      // ldapsyntaxes.
852      registerSyntax(syntax.getSyntax(), overwriteExisting);
853    }
854  }
855
856
857
858  /**
859   * Deregisters the provided ldap syntax description with this
860   * schema.
861   *
862   * @param  syntax  The ldap syntax to deregister with this
863   *                 schema.
864   */
865  public void deregisterLdapSyntaxDescription(
866          LDAPSyntaxDescription syntax)
867  {
868    synchronized (ldapSyntaxDescriptions)
869    {
870      //Remove the real value.
871      ldapSyntaxDescriptions.remove(
872       toLowerCase(syntax.getSyntax().getOID()),
873       syntax);
874
875      try
876      {
877        //Get rid of this from the virtual ldapsyntaxes.
878        deregisterSyntax(syntax.getSyntax());
879      }
880      catch (Exception e)
881      {
882        deregisterSyntax(syntax.getSyntax());
883      }
884    }
885  }
886
887
888
889  /**
890   * Retrieves the entire set of matching rule definitions for this
891   * schema, as a mapping between the lowercase names and OIDs for the
892   * matching rule and the matching rule itself.  Each matching rule
893   * may be associated with multiple keys (once for the OID and again
894   * for each name).  This should be a superset of the sets of
895   * approximate, equality, ordering, and substring matching rules.
896   * The contents of the returned mapping must not be altered.
897   *
898   * @return  The matching rule definitions for this schema.
899   */
900  public ConcurrentHashMap<String,MatchingRule> getMatchingRules()
901  {
902    return matchingRules;
903  }
904
905
906
907  /**
908   * Indicates whether this schema definition includes a matching rule
909   * with the provided name or OID.
910   *
911   * @param  lowerName  The name or OID for which to make the
912   *                    determination, formatted in all lowercase
913   *                    characters.
914   *
915   * @return  {@code true} if this schema contains a matching rule
916   *          with the provided name or OID, or {@code false} if not.
917   */
918  public boolean hasMatchingRule(String lowerName)
919  {
920    return matchingRules.containsKey(lowerName);
921  }
922
923
924
925  /**
926   * Retrieves the matching rule definition with the specified name or
927   * OID.
928   *
929   * @param  lowerName  The name or OID of the matching rule to
930   *                    retrieve, formatted in all lowercase
931   *                    characters.
932   *
933   * @return  The requested matching rule, or <CODE>null</CODE> if no
934   *          rule is registered with the provided name or OID.
935   */
936  public MatchingRule getMatchingRule(String lowerName)
937  {
938    return matchingRules.get(lowerName);
939  }
940
941
942
943  /**
944   * Registers the provided matching rule definition with this schema.
945   *
946   * @param  matchingRule       The matching rule to register with
947   *                            this schema.
948   * @param  overwriteExisting  Indicates whether to overwrite an
949   *                            existing mapping if there are any
950   *                            conflicts (i.e.,
951   *                            another matching rule with the same
952   *                            OID or name).
953   *
954   * @throws  DirectoryException  If a conflict is encountered and the
955   *                              <CODE>overwriteExisting</CODE> flag
956   *                              is set to <CODE>false</CODE>
957   */
958  public void registerMatchingRule(MatchingRule matchingRule, boolean overwriteExisting)
959         throws DirectoryException
960  {
961    synchronized (matchingRules)
962    {
963      if (!overwriteExisting)
964      {
965        String oid = toLowerCase(matchingRule.getOID());
966        if (matchingRules.containsKey(oid))
967        {
968          MatchingRule conflictingRule = matchingRules.get(oid);
969
970          LocalizableMessage message =
971              ERR_SCHEMA_CONFLICTING_MR_OID.get(matchingRule.getNameOrOID(),
972                  oid, conflictingRule.getNameOrOID());
973          throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
974        }
975
976        for (String name : matchingRule.getNames())
977        {
978          if (name != null)
979          {
980            name = toLowerCase(name);
981            if (matchingRules.containsKey(name))
982            {
983              MatchingRule conflictingRule = matchingRules.get(name);
984
985              LocalizableMessage message =
986                  ERR_SCHEMA_CONFLICTING_MR_NAME.get(matchingRule.getOID(),
987                      name, conflictingRule.getOID());
988              throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
989                  message);
990            }
991          }
992        }
993      }
994      matchingRules.put(toLowerCase(matchingRule.getOID()), matchingRule);
995
996      for (String name : matchingRule.getNames())
997      {
998        if (name != null)
999        {
1000          matchingRules.put(toLowerCase(name), matchingRule);
1001        }
1002      }
1003    }
1004  }
1005
1006
1007  /**
1008   * Deregisters the provided matching rule definition with this
1009   * schema.
1010   *
1011   * @param  matchingRule  The matching rule to deregister with this
1012   *                       schema.
1013   */
1014  public void deregisterMatchingRule(MatchingRule matchingRule)
1015  {
1016    synchronized (matchingRules)
1017    {
1018      matchingRules.remove(toLowerCase(matchingRule.getOID()), matchingRule);
1019
1020      for (String name : matchingRule.getNames())
1021      {
1022        if (name != null)
1023        {
1024          matchingRules.remove(toLowerCase(name), matchingRule);
1025        }
1026      }
1027    }
1028  }
1029
1030
1031  /**
1032   * Retrieves the matching rule use definitions for this schema, as a
1033   * mapping between the matching rule for the matching rule use
1034   * definition and the matching rule use itself.  Each matching rule
1035   * use should only be present once, since its only key is its
1036   * matching rule.  The contents of the returned mapping must not be
1037   * altered.
1038   *
1039   * @return  The matching rule use definitions for this schema.
1040   */
1041  public ConcurrentHashMap<MatchingRule,MatchingRuleUse>
1042              getMatchingRuleUses()
1043  {
1044    return matchingRuleUses;
1045  }
1046
1047
1048
1049  /**
1050   * Indicates whether this schema definition includes a matching rule
1051   * use for the provided matching rule.
1052   *
1053   * @param  matchingRule  The matching rule for which to make the
1054   *                       determination.
1055   *
1056   * @return  {@code true} if this schema contains a matching rule use
1057   *          for the provided matching rule, or {@code false} if not.
1058   */
1059  public boolean hasMatchingRuleUse(MatchingRule matchingRule)
1060  {
1061    return matchingRuleUses.containsKey(matchingRule);
1062  }
1063
1064
1065
1066  /**
1067   * Retrieves the matching rule use definition for the specified
1068   * matching rule.
1069   *
1070   * @param  matchingRule  The matching rule for which to retrieve the
1071   *                       matching rule use definition.
1072   *
1073   * @return  The matching rule use definition, or <CODE>null</CODE>
1074   *          if none exists for the specified matching rule.
1075   */
1076  public MatchingRuleUse getMatchingRuleUse(MatchingRule matchingRule)
1077  {
1078    return matchingRuleUses.get(matchingRule);
1079  }
1080
1081
1082
1083  /**
1084   * Registers the provided matching rule use definition with this
1085   * schema.
1086   *
1087   * @param  matchingRuleUse    The matching rule use definition to
1088   *                            register.
1089   * @param  overwriteExisting  Indicates whether to overwrite an
1090   *                            existing mapping if there are any
1091   *                            conflicts (i.e., another matching rule
1092   *                            use with the same matching rule).
1093   *
1094   * @throws  DirectoryException  If a conflict is encountered and the
1095   *                              <CODE>overwriteExisting</CODE> flag
1096   *                              is set to <CODE>false</CODE>
1097   */
1098  public void registerMatchingRuleUse(MatchingRuleUse matchingRuleUse,
1099                                      boolean overwriteExisting)
1100         throws DirectoryException
1101  {
1102    synchronized (matchingRuleUses)
1103    {
1104      MatchingRule matchingRule = matchingRuleUse.getMatchingRule();
1105
1106      if (!overwriteExisting && matchingRuleUses.containsKey(matchingRule))
1107      {
1108        MatchingRuleUse conflictingUse = matchingRuleUses.get(matchingRule);
1109
1110        LocalizableMessage message = ERR_SCHEMA_CONFLICTING_MATCHING_RULE_USE.
1111            get(matchingRuleUse.getNameOrOID(),
1112                matchingRule.getNameOrOID(),
1113                conflictingUse.getNameOrOID());
1114        throw new DirectoryException(
1115                       ResultCode.CONSTRAINT_VIOLATION, message);
1116      }
1117
1118      matchingRuleUses.put(matchingRule, matchingRuleUse);
1119    }
1120  }
1121
1122
1123
1124  /**
1125   * Deregisters the provided matching rule use definition with this
1126   * schema.
1127   *
1128   * @param  matchingRuleUse  The matching rule use to deregister with
1129   *                          this schema.
1130   */
1131  public void deregisterMatchingRuleUse(
1132                   MatchingRuleUse matchingRuleUse)
1133  {
1134    synchronized (matchingRuleUses)
1135    {
1136      matchingRuleUses.remove(matchingRuleUse.getMatchingRule(),
1137                              matchingRuleUse);
1138    }
1139  }
1140
1141
1142
1143  /**
1144   * Retrieves the DIT content rule definitions for this schema, as a
1145   * mapping between the objectclass for the rule and the DIT content
1146   * rule itself.  Each DIT content rule should only be present once,
1147   * since its only key is its objectclass.  The contents of the
1148   * returned mapping must not be altered.
1149   *
1150   * @return  The DIT content rule definitions for this schema.
1151   */
1152  public ConcurrentHashMap<ObjectClass,DITContentRule>
1153              getDITContentRules()
1154  {
1155    return ditContentRules;
1156  }
1157
1158
1159
1160  /**
1161   * Indicates whether this schema definition includes a DIT content
1162   * rule for the provided objectclass.
1163   *
1164   * @param  objectClass  The objectclass for which to make the
1165   *                      determination.
1166   *
1167   * @return  {@code true} if this schema contains a DIT content rule
1168   *          for the provided objectclass, or {@code false} if not.
1169   */
1170  public boolean hasDITContentRule(ObjectClass objectClass)
1171  {
1172    return ditContentRules.containsKey(objectClass);
1173  }
1174
1175
1176
1177  /**
1178   * Retrieves the DIT content rule definition for the specified
1179   * objectclass.
1180   *
1181   * @param  objectClass  The objectclass for the DIT content rule to
1182   *                      retrieve.
1183   *
1184   * @return  The requested DIT content rule, or <CODE>null</CODE> if
1185   *          no DIT content rule is registered with the provided
1186   *          objectclass.
1187   */
1188  public DITContentRule getDITContentRule(ObjectClass objectClass)
1189  {
1190    return ditContentRules.get(objectClass);
1191  }
1192
1193
1194
1195  /**
1196   * Registers the provided DIT content rule definition with this
1197   * schema.
1198   *
1199   * @param  ditContentRule     The DIT content rule to register.
1200   * @param  overwriteExisting  Indicates whether to overwrite an
1201   *                            existing mapping if there are any
1202   *                            conflicts (i.e., another DIT content
1203   *                            rule with the same objectclass).
1204   *
1205   * @throws  DirectoryException  If a conflict is encountered and the
1206   *                              <CODE>overwriteExisting</CODE> flag
1207   *                              is set to <CODE>false</CODE>
1208   */
1209  public void registerDITContentRule(DITContentRule ditContentRule,
1210                                     boolean overwriteExisting)
1211         throws DirectoryException
1212  {
1213    synchronized (ditContentRules)
1214    {
1215      ObjectClass objectClass = ditContentRule.getStructuralClass();
1216
1217      if (! overwriteExisting && ditContentRules.containsKey(objectClass))
1218      {
1219        DITContentRule conflictingRule =
1220                            ditContentRules.get(objectClass);
1221
1222        LocalizableMessage message = ERR_SCHEMA_CONFLICTING_DIT_CONTENT_RULE.
1223            get(ditContentRule.getNameOrOID(),
1224                objectClass.getNameOrOID(),
1225                conflictingRule.getNameOrOID());
1226        throw new DirectoryException(
1227                       ResultCode.CONSTRAINT_VIOLATION, message);
1228      }
1229
1230      ditContentRules.put(objectClass, ditContentRule);
1231    }
1232  }
1233
1234
1235
1236  /**
1237   * Deregisters the provided DIT content rule definition with this
1238   * schema.
1239   *
1240   * @param  ditContentRule  The DIT content rule to deregister with
1241   *                         this schema.
1242   */
1243  public void deregisterDITContentRule(DITContentRule ditContentRule)
1244  {
1245    synchronized (ditContentRules)
1246    {
1247      ditContentRules.remove(ditContentRule.getStructuralClass(),
1248                             ditContentRule);
1249    }
1250  }
1251
1252
1253
1254  /**
1255   * Retrieves the DIT structure rule definitions for this schema, as
1256   * a mapping between the rule ID for the rule and the DIT structure
1257   * rule itself.  Each DIT structure rule should only be present
1258   * once, since its only key is its rule ID.  The contents of the
1259   * returned mapping must not be altered.
1260   *
1261   * @return  The DIT structure rule definitions for this schema.
1262   */
1263  public ConcurrentHashMap<Integer,DITStructureRule>
1264              getDITStructureRulesByID()
1265  {
1266    return ditStructureRulesByID;
1267  }
1268
1269
1270
1271  /**
1272   * Retrieves the DIT structure rule definitions for this schema, as
1273   * a mapping between the name form for the rule and the DIT
1274   * structure rule itself.  Each DIT structure rule should only be
1275   * present once, since its only key is its name form.  The contents
1276   * of the returned mapping must not be altered.
1277   *
1278   * @return  The DIT structure rule definitions for this schema.
1279   */
1280  public ConcurrentHashMap<NameForm,DITStructureRule>
1281              getDITStructureRulesByNameForm()
1282  {
1283    return ditStructureRulesByNameForm;
1284  }
1285
1286
1287
1288  /**
1289   * Indicates whether this schema definition includes a DIT structure
1290   * rule with the provided rule ID.
1291   *
1292   * @param  ruleID  The rule ID for which to make the determination.
1293   *
1294   * @return  {@code true} if this schema contains a DIT structure
1295   *          rule with the provided rule ID, or {@code false} if not.
1296   */
1297  public boolean hasDITStructureRule(int ruleID)
1298  {
1299    return ditStructureRulesByID.containsKey(ruleID);
1300  }
1301
1302
1303
1304  /**
1305   * Indicates whether this schema definition includes a DIT structure
1306   * rule for the provided name form.
1307   *
1308   * @param  nameForm  The name form for which to make the
1309   *                   determination.
1310   *
1311   * @return  {@code true} if this schema contains a DIT structure
1312   *          rule for the provided name form, or {@code false} if
1313   *          not.
1314   */
1315  public boolean hasDITStructureRule(NameForm nameForm)
1316  {
1317    return ditStructureRulesByNameForm.containsKey(nameForm);
1318  }
1319
1320
1321
1322  /**
1323   * Retrieves the DIT structure rule definition with the provided
1324   * rule ID.
1325   *
1326   * @param  ruleID  The rule ID for the DIT structure rule to
1327   *                 retrieve.
1328   *
1329   * @return  The requested DIT structure rule, or <CODE>null</CODE>
1330   *          if no DIT structure rule is registered with the provided
1331   *          rule ID.
1332   */
1333  public DITStructureRule getDITStructureRule(int ruleID)
1334  {
1335    return ditStructureRulesByID.get(ruleID);
1336  }
1337
1338
1339
1340  /**
1341   * Retrieves the DIT structure rule definition for the provided name
1342   * form.
1343   *
1344   * @param  nameForm  The name form for the DIT structure rule to
1345   *                   retrieve.
1346   *
1347   * @return  The requested DIT structure rule, or <CODE>null</CODE>
1348   *          if no DIT structure rule is registered with the provided
1349   *          name form.
1350   */
1351  public DITStructureRule getDITStructureRule(NameForm nameForm)
1352  {
1353    return ditStructureRulesByNameForm.get(nameForm);
1354  }
1355
1356
1357
1358  /**
1359   * Registers the provided DIT structure rule definition with this
1360   * schema.
1361   *
1362   * @param  ditStructureRule   The DIT structure rule to register.
1363   * @param  overwriteExisting  Indicates whether to overwrite an
1364   *                            existing mapping if there are any
1365   *                            conflicts (i.e., another DIT structure
1366   *                            rule with the same name form).
1367   *
1368   * @throws  DirectoryException  If a conflict is encountered and the
1369   *                              <CODE>overwriteExisting</CODE> flag
1370   *                              is set to <CODE>false</CODE>
1371   */
1372  public void registerDITStructureRule(
1373                   DITStructureRule ditStructureRule,
1374                   boolean overwriteExisting)
1375         throws DirectoryException
1376  {
1377    synchronized (ditStructureRulesByNameForm)
1378    {
1379      NameForm nameForm = ditStructureRule.getNameForm();
1380      int      ruleID   = ditStructureRule.getRuleID();
1381
1382      if (! overwriteExisting)
1383      {
1384        if (ditStructureRulesByNameForm.containsKey(nameForm))
1385        {
1386          DITStructureRule conflictingRule =
1387               ditStructureRulesByNameForm.get(nameForm);
1388
1389          LocalizableMessage message =
1390              ERR_SCHEMA_CONFLICTING_DIT_STRUCTURE_RULE_NAME_FORM.
1391                get(ditStructureRule.getNameOrRuleID(),
1392                    nameForm.getNameOrOID(),
1393                    conflictingRule.getNameOrRuleID());
1394          throw new DirectoryException(
1395                         ResultCode.CONSTRAINT_VIOLATION, message);
1396        }
1397
1398        if (ditStructureRulesByID.containsKey(ruleID))
1399        {
1400          DITStructureRule conflictingRule =
1401               ditStructureRulesByID.get(ruleID);
1402
1403          LocalizableMessage message =
1404              ERR_SCHEMA_CONFLICTING_DIT_STRUCTURE_RULE_ID.
1405                get(ditStructureRule.getNameOrRuleID(), ruleID,
1406                    conflictingRule.getNameOrRuleID());
1407          throw new DirectoryException(
1408                         ResultCode.CONSTRAINT_VIOLATION, message);
1409        }
1410      }
1411
1412      ditStructureRulesByNameForm.put(nameForm, ditStructureRule);
1413      ditStructureRulesByID.put(ruleID, ditStructureRule);
1414    }
1415  }
1416
1417
1418
1419  /**
1420   * Deregisters the provided DIT structure rule definition with this
1421   * schema.
1422   *
1423   * @param  ditStructureRule  The DIT structure rule to deregister
1424   *                           with this schema.
1425   */
1426  public void deregisterDITStructureRule(
1427                   DITStructureRule ditStructureRule)
1428  {
1429    synchronized (ditStructureRulesByNameForm)
1430    {
1431      ditStructureRulesByNameForm.remove(
1432           ditStructureRule.getNameForm(), ditStructureRule);
1433      ditStructureRulesByID.remove(ditStructureRule.getRuleID(),
1434                                   ditStructureRule);
1435    }
1436  }
1437
1438
1439
1440  /**
1441   * Retrieves the name form definitions for this schema, as a mapping
1442   * between the objectclass for the name forms and the name forms
1443   * themselves.
1444   *
1445   * @return  The name form definitions for this schema.
1446   */
1447  public ConcurrentHashMap<ObjectClass,List<NameForm>>
1448              getNameFormsByObjectClass()
1449  {
1450    return nameFormsByOC;
1451  }
1452
1453
1454
1455  /**
1456   * Retrieves the name form definitions for this schema, as a mapping
1457   * between the names/OID for the name form and the name form itself.
1458   * Each name form may be present multiple times with different names
1459   * and its OID.  The contents of the returned mapping must not be
1460   * altered.
1461   *
1462   * @return  The name form definitions for this schema.
1463   */
1464  public ConcurrentHashMap<String,NameForm> getNameFormsByNameOrOID()
1465  {
1466    return nameFormsByName;
1467  }
1468
1469
1470
1471  /**
1472   * Indicates whether this schema definition includes a name form for
1473   * the specified objectclass.
1474   *
1475   * @param  objectClass  The objectclass for which to make the
1476   *                      determination.
1477   *
1478   * @return  {@code true} if this schema contains a name form for the
1479   *          provided objectclass, or {@code false} if not.
1480   */
1481  public boolean hasNameForm(ObjectClass objectClass)
1482  {
1483    return nameFormsByOC.containsKey(objectClass);
1484  }
1485
1486
1487
1488  /**
1489   * Indicates whether this schema definition includes a name form
1490   * with the specified name or OID.
1491   *
1492   * @param  lowerName  The name or OID for which to make the
1493   *                    determination, formatted in all lowercase
1494   *                    characters.
1495   *
1496   * @return  {@code true} if this schema contains a name form with
1497   *          the provided name or OID, or {@code false} if not.
1498   */
1499  public boolean hasNameForm(String lowerName)
1500  {
1501    return nameFormsByName.containsKey(lowerName);
1502  }
1503
1504
1505
1506  /**
1507   * Retrieves the name forms definition for the specified
1508   * objectclass.
1509   *
1510   * @param  objectClass  The objectclass for the name form to
1511   *                      retrieve.
1512   *
1513   * @return  The requested name forms, or <CODE>null</CODE> if no
1514   *           name forms are registered with the provided
1515   *           objectClass.
1516   */
1517  public List<NameForm> getNameForm(ObjectClass objectClass)
1518  {
1519    return nameFormsByOC.get(objectClass);
1520  }
1521
1522
1523
1524  /**
1525   * Retrieves the name form definition with the provided name or OID.
1526   *
1527   * @param  lowerName  The name or OID of the name form to retrieve,
1528   *                    formatted in all lowercase characters.
1529   *
1530   * @return  The requested name form, or <CODE>null</CODE> if no name
1531   *          form is registered with the provided name or OID.
1532   */
1533  public NameForm getNameForm(String lowerName)
1534  {
1535    return nameFormsByName.get(lowerName);
1536  }
1537
1538
1539
1540  /**
1541   * Registers the provided name form definition with this schema.
1542   *
1543   * @param  nameForm           The name form definition to register.
1544   * @param  overwriteExisting  Indicates whether to overwrite an
1545   *                            existing mapping if there are any
1546   *                            conflicts (i.e., another name form
1547   *                            with the same objectclass).
1548   *
1549   * @throws  DirectoryException  If a conflict is encountered and the
1550   *                              <CODE>overwriteExisting</CODE> flag
1551   *                              is set to <CODE>false</CODE>
1552   */
1553  public void registerNameForm(NameForm nameForm,
1554                               boolean overwriteExisting)
1555         throws DirectoryException
1556  {
1557    synchronized (nameFormsByOC)
1558    {
1559      ObjectClass objectClass = nameForm.getStructuralClass();
1560      List<NameForm> mappedForms = nameFormsByOC.get(objectClass);
1561      if (! overwriteExisting)
1562      {
1563        if(mappedForms !=null)
1564        {
1565          //Iterate over the forms to make sure we aren't adding a
1566          //duplicate.
1567          for(NameForm nf : mappedForms)
1568          {
1569            if(nf.equals(nameForm))
1570            {
1571              LocalizableMessage message = ERR_SCHEMA_CONFLICTING_NAME_FORM_OC.
1572                get(nameForm.getNameOrOID(),
1573                    objectClass.getNameOrOID(),
1574                    nf.getNameOrOID());
1575              throw new DirectoryException(
1576                           ResultCode.CONSTRAINT_VIOLATION, message);
1577            }
1578          }
1579        }
1580
1581        String oid = toLowerCase(nameForm.getOID());
1582        if (nameFormsByName.containsKey(oid))
1583        {
1584          NameForm conflictingNameForm = nameFormsByName.get(oid);
1585
1586          LocalizableMessage message = ERR_SCHEMA_CONFLICTING_NAME_FORM_OID.
1587              get(nameForm.getNameOrOID(), oid,
1588                  conflictingNameForm.getNameOrOID());
1589          throw new DirectoryException(
1590                         ResultCode.CONSTRAINT_VIOLATION, message);
1591        }
1592
1593        for (String name : nameForm.getNames().keySet())
1594        {
1595          if (nameFormsByName.containsKey(name))
1596          {
1597            NameForm conflictingNameForm = nameFormsByName.get(name);
1598
1599            LocalizableMessage message = ERR_SCHEMA_CONFLICTING_NAME_FORM_NAME.
1600                get(nameForm.getNameOrOID(), oid,
1601                    conflictingNameForm.getNameOrOID());
1602            throw new DirectoryException(
1603                           ResultCode.CONSTRAINT_VIOLATION, message);
1604          }
1605        }
1606      }
1607
1608      if(mappedForms == null)
1609      {
1610        mappedForms = new ArrayList<>();
1611      }
1612
1613      mappedForms.add(nameForm);
1614      nameFormsByOC.put(objectClass, mappedForms);
1615      nameFormsByName.put(toLowerCase(nameForm.getOID()), nameForm);
1616
1617      for (String name : nameForm.getNames().keySet())
1618      {
1619        nameFormsByName.put(name, nameForm);
1620      }
1621    }
1622  }
1623
1624
1625
1626  /**
1627   * Deregisters the provided name form definition with this schema.
1628   *
1629   * @param  nameForm  The name form definition to deregister.
1630   */
1631  public void deregisterNameForm(NameForm nameForm)
1632  {
1633    synchronized (nameFormsByOC)
1634    {
1635      List<NameForm> mappedForms = nameFormsByOC.get(
1636              nameForm.getStructuralClass());
1637      if(mappedForms != null)
1638      {
1639        mappedForms.remove(nameForm);
1640        if(mappedForms.isEmpty())
1641        {
1642          nameFormsByOC.remove(nameForm.getStructuralClass());
1643        }
1644      }
1645      nameFormsByOC.remove(nameForm.getStructuralClass());
1646      nameFormsByName.remove(toLowerCase(nameForm.getOID()),
1647                             nameForm);
1648
1649      for (String name : nameForm.getNames().keySet())
1650      {
1651        nameFormsByName.remove(name, nameForm);
1652      }
1653    }
1654  }
1655
1656
1657
1658  /**
1659   * Retrieves the modification timestamp for the file in the schema
1660   * configuration directory with the oldest last modified time.
1661   *
1662   * @return  The modification timestamp for the file in the schema
1663   *          configuration directory with the oldest last modified
1664   *          time.
1665   */
1666  public long getOldestModificationTime()
1667  {
1668    return oldestModificationTime;
1669  }
1670
1671
1672
1673  /**
1674   * Sets the modification timestamp for the oldest file in the schema
1675   * configuration directory.
1676   *
1677   * @param  oldestModificationTime  The modification timestamp for
1678   *                                 the oldest file in the schema
1679   *                                 configuration directory.
1680   */
1681  public void setOldestModificationTime(long oldestModificationTime)
1682  {
1683    this.oldestModificationTime = oldestModificationTime;
1684  }
1685
1686
1687
1688  /**
1689   * Retrieves the modification timestamp for the file in the schema
1690   * configuration directory with the youngest last modified time.
1691   *
1692   * @return  The modification timestamp for the file in the schema
1693   *          configuration directory with the youngest last modified
1694   *          time.
1695   */
1696  public long getYoungestModificationTime()
1697  {
1698    return youngestModificationTime;
1699  }
1700
1701
1702
1703  /**
1704   * Sets the modification timestamp for the youngest file in the
1705   * schema configuration directory.
1706   *
1707   * @param  youngestModificationTime  The modification timestamp for
1708   *                                   the youngest file in the schema
1709   *                                   configuration directory.
1710   */
1711  public void setYoungestModificationTime(
1712                   long youngestModificationTime)
1713  {
1714    this.youngestModificationTime = youngestModificationTime;
1715  }
1716
1717
1718
1719  /**
1720   * Recursively rebuilds all schema elements that are dependent upon
1721   * the provided element.  This must be invoked whenever an existing
1722   * schema element is modified in order to ensure that any elements
1723   * that depend on it should also be recreated to reflect the change.
1724   * <BR><BR>
1725   * The following conditions create dependencies between schema
1726   * elements:
1727   * <UL>
1728   *   <LI>If an attribute type references a superior attribute type,
1729   *       then it is dependent upon that superior attribute
1730   *       type.</LI>
1731   *   <LI>If an objectclass requires or allows an attribute type,
1732   *       then it is dependent upon that attribute type.</LI>
1733   *   <LI>If a name form requires or allows an attribute type in the
1734   *       RDN, then it is dependent upon that attribute type.</LI>
1735   *   <LI>If a DIT content rule requires, allows, or forbids the use
1736   *       of an attribute type, then it is dependent upon that
1737   *       attribute type.</LI>
1738   *   <LI>If a matching rule use references an attribute type, then
1739   *       it is dependent upon that attribute type.</LI>
1740   *   <LI>If an objectclass references a superior objectclass, then
1741   *       it is dependent upon that superior objectclass.</LI>
1742   *   <LI>If a name form references a structural objectclass, then it
1743   *       is dependent upon that objectclass.</LI>
1744   *   <LI>If a DIT content rule references a structural or auxiliary
1745   *       objectclass, then it is dependent upon that
1746   *       objectclass.</LI>
1747   *   <LI>If a DIT structure rule references a name form, then it is
1748   *       dependent upon that name form.</LI>
1749   *   <LI>If a DIT structure rule references a superior DIT structure
1750   *       rule, then it is dependent upon that superior DIT structure
1751   *       rule.</LI>
1752   * </UL>
1753   *
1754   * @param  element  The element for which to recursively rebuild all
1755   *                  dependent elements.
1756   *
1757   * @throws  DirectoryException  If a problem occurs while rebuilding
1758   *                              any of the schema elements.
1759   */
1760  public void rebuildDependentElements(SchemaFileElement element)
1761         throws DirectoryException
1762  {
1763    try
1764    {
1765      rebuildDependentElements(element, 0);
1766    }
1767    catch (DirectoryException de)
1768    {
1769      // If we got an error as a result of a circular reference, then
1770      // we want to make sure that the schema element we call out is
1771      // the one that is at the root of the problem.
1772      if (StaticUtils.hasDescriptor(de.getMessageObject(),
1773          ERR_SCHEMA_CIRCULAR_DEPENDENCY_REFERENCE))
1774      {
1775        LocalizableMessage message =
1776            ERR_SCHEMA_CIRCULAR_DEPENDENCY_REFERENCE.get(element);
1777        throw new DirectoryException(de.getResultCode(), message,
1778                                     de);
1779      }
1780
1781
1782      // It wasn't a circular reference error, so just re-throw the
1783      // exception.
1784      throw de;
1785    }
1786  }
1787
1788
1789
1790  /**
1791   * Recursively rebuilds all schema elements that are dependent upon
1792   * the provided element, increasing the depth for each level of
1793   * recursion to protect against errors due to circular references.
1794   *
1795   * @param  element  The element for which to recursively rebuild all
1796   *                  dependent elements.
1797   * @param  depth    The current recursion depth.
1798   *
1799   * @throws  DirectoryException  If a problem occurs while rebuilding
1800   *                              any of the schema elements.
1801   */
1802  private void rebuildDependentElements(SchemaFileElement element,
1803                                        int depth)
1804          throws DirectoryException
1805  {
1806    if (depth > 20)
1807    {
1808      // FIXME -- Is this an appropriate maximum depth for detecting
1809      // circular references?
1810      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
1811          ERR_SCHEMA_CIRCULAR_DEPENDENCY_REFERENCE.get(element));
1812    }
1813
1814
1815    // Figure out what type of element we're dealing with and make the
1816    // appropriate determinations for that element.
1817    if (element instanceof AttributeType)
1818    {
1819      AttributeType t = (AttributeType) element;
1820
1821      for (AttributeType at : attributeTypes.values())
1822      {
1823        if (at.getSuperiorType() != null && at.getSuperiorType().equals(t))
1824        {
1825          AttributeType newAT = recreateFromDefinition(at);
1826          deregisterAttributeType(at);
1827          registerAttributeType(newAT, true);
1828          rebuildDependentElements(at, depth+1);
1829        }
1830      }
1831
1832      for (ObjectClass oc : objectClasses.values())
1833      {
1834        if (oc.getRequiredAttributes().contains(t) ||
1835            oc.getOptionalAttributes().contains(t))
1836        {
1837          ObjectClass newOC = recreateFromDefinition(oc);
1838          deregisterObjectClass(oc);
1839          registerObjectClass(newOC, true);
1840          rebuildDependentElements(oc, depth+1);
1841        }
1842      }
1843
1844      for (List<NameForm> mappedForms : nameFormsByOC.values())
1845      {
1846        for(NameForm nf : mappedForms)
1847        {
1848          if (nf.getRequiredAttributes().contains(t) ||
1849              nf.getOptionalAttributes().contains(t))
1850          {
1851            NameForm newNF = recreateFromDefinition(nf);
1852            deregisterNameForm(nf);
1853            registerNameForm(newNF, true);
1854            rebuildDependentElements(nf, depth+1);
1855          }
1856        }
1857      }
1858
1859      for (DITContentRule dcr : ditContentRules.values())
1860      {
1861        if (dcr.getRequiredAttributes().contains(t) ||
1862            dcr.getOptionalAttributes().contains(t) ||
1863            dcr.getProhibitedAttributes().contains(t))
1864        {
1865          DITContentRule newDCR = recreateFromDefinition(dcr);
1866          deregisterDITContentRule(dcr);
1867          registerDITContentRule(newDCR, true);
1868          rebuildDependentElements(dcr, depth+1);
1869        }
1870      }
1871
1872      for (MatchingRuleUse mru : matchingRuleUses.values())
1873      {
1874        if (mru.getAttributes().contains(t))
1875        {
1876          MatchingRuleUse newMRU = recreateFromDefinition(mru);
1877          deregisterMatchingRuleUse(mru);
1878          registerMatchingRuleUse(newMRU, true);
1879          rebuildDependentElements(mru, depth+1);
1880        }
1881      }
1882    }
1883    else if (element instanceof ObjectClass)
1884    {
1885      ObjectClass c = (ObjectClass) element;
1886
1887      for (ObjectClass oc : objectClasses.values())
1888      {
1889        if (oc.getSuperiorClasses().contains(c))
1890        {
1891          ObjectClass newOC = recreateFromDefinition(oc);
1892          deregisterObjectClass(oc);
1893          registerObjectClass(newOC, true);
1894          rebuildDependentElements(oc, depth+1);
1895        }
1896      }
1897
1898      List<NameForm> mappedForms = nameFormsByOC.get(c);
1899      if(mappedForms != null)
1900      {
1901        for(NameForm nf : mappedForms)
1902        {
1903          if (nf != null)
1904          {
1905            NameForm newNF = recreateFromDefinition(nf);
1906            deregisterNameForm(nf);
1907            registerNameForm(newNF, true);
1908            rebuildDependentElements(nf, depth+1);
1909          }
1910        }
1911      }
1912
1913      for (DITContentRule dcr : ditContentRules.values())
1914      {
1915        if (dcr.getStructuralClass().equals(c) ||
1916            dcr.getAuxiliaryClasses().contains(c))
1917        {
1918          DITContentRule newDCR = recreateFromDefinition(dcr);
1919          deregisterDITContentRule(dcr);
1920          registerDITContentRule(newDCR, true);
1921          rebuildDependentElements(dcr, depth+1);
1922        }
1923      }
1924    }
1925    else if (element instanceof NameForm)
1926    {
1927      NameForm n = (NameForm) element;
1928      DITStructureRule dsr = ditStructureRulesByNameForm.get(n);
1929      if (dsr != null)
1930      {
1931        DITStructureRule newDSR = recreateFromDefinition(dsr);
1932        deregisterDITStructureRule(dsr);
1933        registerDITStructureRule(newDSR, true);
1934        rebuildDependentElements(dsr, depth+1);
1935      }
1936    }
1937    else if (element instanceof DITStructureRule)
1938    {
1939      DITStructureRule d = (DITStructureRule) element;
1940      for (DITStructureRule dsr : ditStructureRulesByID.values())
1941      {
1942        if (dsr.getSuperiorRules().contains(d))
1943        {
1944          DITStructureRule newDSR = recreateFromDefinition(dsr);
1945          deregisterDITStructureRule(dsr);
1946          registerDITStructureRule(newDSR, true);
1947          rebuildDependentElements(dsr, depth+1);
1948        }
1949      }
1950    }
1951  }
1952
1953  private AttributeType recreateFromDefinition(AttributeType attrType)
1954      throws DirectoryException
1955  {
1956    ByteString value = ByteString.valueOf(attrType.toString());
1957    AttributeType copy =
1958        AttributeTypeSyntax.decodeAttributeType(value, this, false);
1959    setSchemaFile(copy, getSchemaFile(attrType));
1960    if (attrType.mayHaveSubordinateTypes())
1961    {
1962      copy.setMayHaveSubordinateTypes();
1963    }
1964    return copy;
1965  }
1966
1967  private DITContentRule recreateFromDefinition(DITContentRule dcr)
1968      throws DirectoryException
1969  {
1970    ByteString value = ByteString.valueOf(dcr.toString());
1971    DITContentRule copy =
1972        DITContentRuleSyntax.decodeDITContentRule(value, this, false);
1973    setSchemaFile(copy, getSchemaFile(dcr));
1974    return copy;
1975  }
1976
1977  private DITStructureRule recreateFromDefinition(DITStructureRule dsr)
1978      throws DirectoryException
1979  {
1980    ByteString value = ByteString.valueOf(dsr.toString());
1981    DITStructureRule copy =
1982        DITStructureRuleSyntax.decodeDITStructureRule(value, this, false);
1983    setSchemaFile(copy, getSchemaFile(dsr));
1984    return copy;
1985  }
1986
1987  private MatchingRuleUse recreateFromDefinition(MatchingRuleUse mru)
1988      throws DirectoryException
1989  {
1990    ByteString value = ByteString.valueOf(mru.toString());
1991    MatchingRuleUse copy =
1992        MatchingRuleUseSyntax.decodeMatchingRuleUse(value, this, false);
1993    setSchemaFile(copy, getSchemaFile(mru));
1994    return copy;
1995  }
1996
1997  private NameForm recreateFromDefinition(NameForm nf)
1998      throws DirectoryException
1999  {
2000    ByteString value = ByteString.valueOf(nf.toString());
2001    NameForm copy = NameFormSyntax.decodeNameForm(value, this, false);
2002    setSchemaFile(copy, getSchemaFile(nf));
2003    return copy;
2004  }
2005
2006  private ObjectClass recreateFromDefinition(ObjectClass oc)
2007      throws DirectoryException
2008  {
2009    ByteString value = ByteString.valueOf(oc.toString());
2010    ObjectClass copy = ObjectClassSyntax.decodeObjectClass(value, this, false);
2011    setSchemaFile(copy, getSchemaFile(oc));
2012    return copy;
2013  }
2014
2015  /**
2016   * Creates a new <CODE>Schema</CODE> object that is a duplicate of
2017   * this one.  It elements may be added and removed from the
2018   * duplicate without impacting this version.
2019   *
2020   * @return  A new <CODE>Schema</CODE> object that is a duplicate of
2021   *          this one.
2022   */
2023  public Schema duplicate()
2024  {
2025    Schema dupSchema = new Schema();
2026
2027    dupSchema.attributeTypes.putAll(attributeTypes);
2028    dupSchema.subordinateTypes.putAll(subordinateTypes);
2029    dupSchema.objectClasses.putAll(objectClasses);
2030    dupSchema.syntaxes.putAll(syntaxes);
2031    dupSchema.matchingRules.putAll(matchingRules);
2032    dupSchema.matchingRuleUses.putAll(matchingRuleUses);
2033    dupSchema.ditContentRules.putAll(ditContentRules);
2034    dupSchema.ditStructureRulesByID.putAll(ditStructureRulesByID);
2035    dupSchema.ditStructureRulesByNameForm.putAll(
2036         ditStructureRulesByNameForm);
2037    dupSchema.nameFormsByOC.putAll(nameFormsByOC);
2038    dupSchema.nameFormsByName.putAll(nameFormsByName);
2039    dupSchema.ldapSyntaxDescriptions.putAll(ldapSyntaxDescriptions);
2040    dupSchema.oldestModificationTime   = oldestModificationTime;
2041    dupSchema.youngestModificationTime = youngestModificationTime;
2042    if (extraAttributes != null)
2043    {
2044      dupSchema.extraAttributes = new HashMap<>(extraAttributes);
2045    }
2046
2047    return dupSchema;
2048  }
2049
2050
2051  /**
2052   * Get the extraAttributes stored in this schema.
2053   *
2054   * @return  The extraAttributes stored in this schema.
2055   */
2056  public Map<String, Attribute> getExtraAttributes()
2057  {
2058    return extraAttributes;
2059  }
2060
2061
2062  /**
2063   * Add a new extra Attribute for this schema.
2064   *
2065   * @param  name     The identifier of the extra Attribute.
2066   *
2067   * @param  attr     The extra attribute that must be added to
2068   *                  this Schema.
2069   */
2070  public void addExtraAttribute(String name, Attribute attr)
2071  {
2072    extraAttributes.put(name, attr);
2073  }
2074
2075
2076  /**
2077   * Writes a single file containing all schema element definitions,
2078   * which can be used on startup to determine whether the schema
2079   * files were edited with the server offline.
2080   */
2081  public static void writeConcatenatedSchema()
2082  {
2083    String concatFilePath = null;
2084    try
2085    {
2086      Set<String> attributeTypes = new LinkedHashSet<>();
2087      Set<String> objectClasses = new LinkedHashSet<>();
2088      Set<String> nameForms = new LinkedHashSet<>();
2089      Set<String> ditContentRules = new LinkedHashSet<>();
2090      Set<String> ditStructureRules = new LinkedHashSet<>();
2091      Set<String> matchingRuleUses = new LinkedHashSet<>();
2092      Set<String> ldapSyntaxes = new LinkedHashSet<>();
2093      genConcatenatedSchema(attributeTypes, objectClasses, nameForms,
2094                            ditContentRules, ditStructureRules,
2095                            matchingRuleUses,ldapSyntaxes);
2096
2097
2098      File configFile = new File(DirectoryServer.getConfigFile());
2099      File configDirectory  = configFile.getParentFile();
2100      File upgradeDirectory = new File(configDirectory, "upgrade");
2101      upgradeDirectory.mkdir();
2102      File concatFile       = new File(upgradeDirectory,
2103                                       SCHEMA_CONCAT_FILE_NAME);
2104      concatFilePath = concatFile.getAbsolutePath();
2105
2106      File tempFile = new File(concatFilePath + ".tmp");
2107      BufferedWriter writer =
2108           new BufferedWriter(new FileWriter(tempFile, false));
2109      writer.write("dn: " + DirectoryServer.getSchemaDN());
2110      writer.newLine();
2111      writer.write("objectClass: top");
2112      writer.newLine();
2113      writer.write("objectClass: ldapSubentry");
2114      writer.newLine();
2115      writer.write("objectClass: subschema");
2116      writer.newLine();
2117
2118      for (String line : attributeTypes)
2119      {
2120        writer.write(ATTR_ATTRIBUTE_TYPES);
2121        writer.write(": ");
2122        writer.write(line);
2123        writer.newLine();
2124      }
2125
2126      for (String line : objectClasses)
2127      {
2128        writer.write(ATTR_OBJECTCLASSES);
2129        writer.write(": ");
2130        writer.write(line);
2131        writer.newLine();
2132      }
2133
2134      for (String line : nameForms)
2135      {
2136        writer.write(ATTR_NAME_FORMS);
2137        writer.write(": ");
2138        writer.write(line);
2139        writer.newLine();
2140      }
2141
2142      for (String line : ditContentRules)
2143      {
2144        writer.write(ATTR_DIT_CONTENT_RULES);
2145        writer.write(": ");
2146        writer.write(line);
2147        writer.newLine();
2148      }
2149
2150      for (String line : ditStructureRules)
2151      {
2152        writer.write(ATTR_DIT_STRUCTURE_RULES);
2153        writer.write(": ");
2154        writer.write(line);
2155        writer.newLine();
2156      }
2157
2158      for (String line : matchingRuleUses)
2159      {
2160        writer.write(ATTR_MATCHING_RULE_USE);
2161        writer.write(": ");
2162        writer.write(line);
2163        writer.newLine();
2164      }
2165
2166
2167      for (String line : ldapSyntaxes)
2168      {
2169        writer.write(ATTR_LDAP_SYNTAXES);
2170        writer.write(": ");
2171        writer.write(line);
2172        writer.newLine();
2173      }
2174
2175      writer.close();
2176
2177      if (concatFile.exists())
2178      {
2179        concatFile.delete();
2180      }
2181      tempFile.renameTo(concatFile);
2182    }
2183    catch (Exception e)
2184    {
2185      logger.traceException(e);
2186
2187      // This is definitely not ideal, but it's not the end of the
2188      // world.  The worst that should happen is that the schema
2189      // changes could potentially be sent to the other servers again
2190      // when this server is restarted, which shouldn't hurt anything.
2191      // Still, we should log a warning message.
2192      logger.error(ERR_SCHEMA_CANNOT_WRITE_CONCAT_SCHEMA_FILE, concatFilePath, getExceptionMessage(e));
2193    }
2194  }
2195
2196
2197
2198  /**
2199   * Reads the files contained in the schema directory and generates a
2200   * concatenated view of their contents in the provided sets.
2201   *
2202   * @param  attributeTypes     The set into which to place the
2203   *                            attribute types read from the schema
2204   *                            files.
2205   * @param  objectClasses      The set into which to place the object
2206   *                            classes read from the schema files.
2207   * @param  nameForms          The set into which to place the name
2208   *                            forms read from the schema files.
2209   * @param  ditContentRules    The set into which to place the DIT
2210   *                            content rules read from the schema
2211   *                            files.
2212   * @param  ditStructureRules  The set into which to place the DIT
2213   *                            structure rules read from the schema
2214   *                            files.
2215   * @param  matchingRuleUses   The set into which to place the
2216   *                            matching rule uses read from the
2217   *                            schema files.
2218   * @param ldapSyntaxes The set into which to place the
2219   *                            ldap syntaxes read from the
2220   *                            schema files.
2221   *
2222   * @throws  IOException  If a problem occurs while reading the
2223   *                       schema file elements.
2224   */
2225  public static void genConcatenatedSchema(
2226                          Set<String> attributeTypes,
2227                          Set<String> objectClasses,
2228                          Set<String> nameForms,
2229                          Set<String> ditContentRules,
2230                          Set<String> ditStructureRules,
2231                          Set<String> matchingRuleUses,
2232                          Set<String> ldapSyntaxes)
2233          throws IOException
2234  {
2235    // Get a sorted list of the files in the schema directory.
2236    TreeSet<File> schemaFiles = new TreeSet<>();
2237    String schemaDirectory =
2238      SchemaConfigManager.getSchemaDirectoryPath();
2239
2240    final FilenameFilter filter = new SchemaConfigManager.SchemaFileFilter();
2241    for (File f : new File(schemaDirectory).listFiles(filter))
2242    {
2243      if (f.isFile())
2244      {
2245        schemaFiles.add(f);
2246      }
2247    }
2248
2249
2250    // Open each of the files in order and read the elements that they
2251    // contain, appending them to the appropriate lists.
2252    for (File f : schemaFiles)
2253    {
2254      // Read the contents of the file into a list with one schema
2255      // element per list element.
2256      LinkedList<StringBuilder> lines = new LinkedList<>();
2257      BufferedReader reader = new BufferedReader(new FileReader(f));
2258
2259      while (true)
2260      {
2261        String line = reader.readLine();
2262        if (line == null)
2263        {
2264          break;
2265        }
2266        else if (line.startsWith("#") || line.length() == 0)
2267        {
2268          continue;
2269        }
2270        else if (line.startsWith(" "))
2271        {
2272          lines.getLast().append(line.substring(1));
2273        }
2274        else
2275        {
2276          lines.add(new StringBuilder(line));
2277        }
2278      }
2279
2280      reader.close();
2281
2282
2283      // Iterate through each line in the list.  Find the colon and
2284      // get the attribute name at the beginning.  If it's something
2285      // that we don't recognize, then skip it.  Otherwise, add the
2286      // X-SCHEMA-FILE extension and add it to the appropriate schema
2287      // element list.
2288      for (StringBuilder buffer : lines)
2289      {
2290        // Get the line and add the X-SCHEMA-FILE extension to the end
2291        // of it.  All of them should end with " )" but some might
2292        // have the parenthesis crammed up against the last character
2293        // so deal with that as well.
2294        String line = buffer.toString().trim();
2295        if (line.endsWith(" )"))
2296        {
2297         line = line.substring(0, line.length()-1) +
2298                SCHEMA_PROPERTY_FILENAME + " '" + f.getName() + "' )";
2299        }
2300        else if (line.endsWith(")"))
2301        {
2302         line = line.substring(0, line.length()-1) + " " +
2303                SCHEMA_PROPERTY_FILENAME + " '" + f.getName() + "' )";
2304        }
2305        else
2306        {
2307          continue;
2308        }
2309
2310        parseSchemaLine(line, attributeTypes, objectClasses,
2311            nameForms, ditContentRules, ditStructureRules, matchingRuleUses,
2312            ldapSyntaxes);
2313      }
2314    }
2315  }
2316
2317
2318
2319  /**
2320   * Reads data from the specified concatenated schema file into the
2321   * provided sets.
2322   *
2323   * @param  concatSchemaFile   The path to the concatenated schema
2324   *                            file to be read.
2325   * @param  attributeTypes     The set into which to place the
2326   *                            attribute types read from the
2327   *                            concatenated schema file.
2328   * @param  objectClasses      The set into which to place the object
2329   *                            classes read from the concatenated
2330   *                            schema file.
2331   * @param  nameForms          The set into which to place the name
2332   *                            forms read from the concatenated
2333   *                            schema file.
2334   * @param  ditContentRules    The set into which to place the DIT
2335   *                            content rules read from the
2336   *                            concatenated schema file.
2337   * @param  ditStructureRules  The set into which to place the DIT
2338   *                            structure rules read from the
2339   *                            concatenated schema file.
2340   * @param  matchingRuleUses   The set into which to place the
2341   *                            matching rule uses read from the
2342   *                            concatenated schema file.
2343   * @param ldapSyntaxes The set into which to place the
2344   *                            ldap syntaxes read from the
2345   *                            concatenated schema file.
2346   *
2347   * @throws  IOException  If a problem occurs while reading the
2348   *                       schema file elements.
2349   */
2350  public static void readConcatenatedSchema(String concatSchemaFile,
2351                          Set<String> attributeTypes,
2352                          Set<String> objectClasses,
2353                          Set<String> nameForms,
2354                          Set<String> ditContentRules,
2355                          Set<String> ditStructureRules,
2356                          Set<String> matchingRuleUses,
2357                          Set<String> ldapSyntaxes)
2358          throws IOException
2359  {
2360    BufferedReader reader =
2361         new BufferedReader(new FileReader(concatSchemaFile));
2362    while (true)
2363    {
2364      String line = reader.readLine();
2365      if (line == null)
2366      {
2367        break;
2368      }
2369      parseSchemaLine(line, attributeTypes, objectClasses,
2370          nameForms, ditContentRules, ditStructureRules, matchingRuleUses,
2371          ldapSyntaxes);
2372    }
2373
2374    reader.close();
2375  }
2376
2377  /**
2378   * Parse a line of a schema file into the provided sets.
2379   *
2380   * @param line                The current line of schema.
2381   * @param  attributeTypes     The set into which to place the
2382   *                            attribute type if the line represents
2383   *                            one.
2384   * @param  objectClasses      The set into which to place the object
2385   *                            class if the line represents one.
2386   * @param  nameForms          The set into which to place the name
2387   *                            form if the line represents one.
2388   * @param  ditContentRules    The set into which to place the DIT
2389   *                            content rule if the line represents one.
2390   * @param  ditStructureRules  The set into which to place the DIT
2391   *                            structure rule if the line represents one.
2392   * @param  matchingRuleUses   The set into which to place the
2393   *                            matching rule use if the line represents
2394   *                            one.
2395   * @param ldapSyntaxes        The set into which to place the ldap
2396   *                            syntax if the line represents one.
2397   */
2398
2399  private static void parseSchemaLine(String line,
2400                               Set<String> attributeTypes,
2401                               Set<String> objectClasses,
2402                               Set<String> nameForms,
2403                               Set<String> ditContentRules,
2404                               Set<String> ditStructureRules,
2405                               Set<String> matchingRuleUses,
2406                               Set<String> ldapSyntaxes)
2407  {
2408    String value;
2409    String lowerLine = toLowerCase(line);
2410    if (lowerLine.startsWith(ATTR_ATTRIBUTE_TYPES_LC))
2411    {
2412      value =
2413          line.substring(ATTR_ATTRIBUTE_TYPES.length()+1).trim();
2414      attributeTypes.add(value);
2415    }
2416    else if (lowerLine.startsWith(ATTR_OBJECTCLASSES_LC))
2417    {
2418      value = line.substring(ATTR_OBJECTCLASSES.length()+1).trim();
2419      objectClasses.add(value);
2420    }
2421    else if (lowerLine.startsWith(ATTR_NAME_FORMS_LC))
2422    {
2423      value = line.substring(ATTR_NAME_FORMS.length()+1).trim();
2424      nameForms.add(value);
2425    }
2426    else if (lowerLine.startsWith(ATTR_DIT_CONTENT_RULES_LC))
2427    {
2428      value = line.substring(
2429          ATTR_DIT_CONTENT_RULES.length()+1).trim();
2430      ditContentRules.add(value);
2431    }
2432    else if (lowerLine.startsWith(ATTR_DIT_STRUCTURE_RULES_LC))
2433    {
2434      value = line.substring(
2435          ATTR_DIT_STRUCTURE_RULES.length()+1).trim();
2436      ditStructureRules.add(value);
2437    }
2438    else if (lowerLine.startsWith(ATTR_MATCHING_RULE_USE_LC))
2439    {
2440      value = line.substring(
2441          ATTR_MATCHING_RULE_USE.length()+1).trim();
2442      matchingRuleUses.add(value);
2443    }
2444    else if (lowerLine.startsWith(ATTR_LDAP_SYNTAXES_LC))
2445    {
2446      value = line.substring(
2447          ATTR_LDAP_SYNTAXES.length()+1).trim();
2448      ldapSyntaxes.add(value);
2449    }
2450  }
2451
2452  /**
2453   * Compares the provided sets of schema element definitions and
2454   * writes any differences found into the given list of
2455   * modifications.
2456   *
2457   * @param  oldElements  The set of elements of the specified type
2458   *                      read from the previous concatenated schema
2459   *                      files.
2460   * @param  newElements  The set of elements of the specified type
2461   *                      read from the server's current schema.
2462   * @param  elementType  The attribute type associated with the
2463   *                      schema element being compared.
2464   * @param  mods         The list of modifications into which any
2465   *                      identified differences should be written.
2466   */
2467  public static void compareConcatenatedSchema(
2468                          Set<String> oldElements,
2469                          Set<String> newElements,
2470                          AttributeType elementType,
2471                          List<Modification> mods)
2472  {
2473    AttributeBuilder builder = new AttributeBuilder(elementType);
2474    for (String s : oldElements)
2475    {
2476      if (!newElements.contains(s))
2477      {
2478        builder.add(s);
2479      }
2480    }
2481
2482    if (!builder.isEmpty())
2483    {
2484      mods.add(new Modification(ModificationType.DELETE,
2485                                builder.toAttribute()));
2486    }
2487
2488    builder.setAttributeType(elementType);
2489    for (String s : newElements)
2490    {
2491      if (!oldElements.contains(s))
2492      {
2493        builder.add(s);
2494      }
2495    }
2496
2497    if (!builder.isEmpty())
2498    {
2499      mods.add(new Modification(ModificationType.ADD,
2500                                builder.toAttribute()));
2501    }
2502  }
2503
2504
2505
2506  /**
2507   * Destroys the structures maintained by the schema so that they are
2508   * no longer usable. This should only be called at the end of the
2509   * server shutdown process, and it can help detect inappropriate
2510   * cached references.
2511   */
2512  @org.opends.server.types.PublicAPI(
2513       stability=org.opends.server.types.StabilityLevel.PRIVATE,
2514       mayInstantiate=false,
2515       mayExtend=false,
2516       mayInvoke=true)
2517  public synchronized void destroy()
2518  {
2519    if (attributeTypes != null)
2520    {
2521      attributeTypes.clear();
2522      attributeTypes = null;
2523    }
2524
2525    if (ditContentRules != null)
2526    {
2527      ditContentRules.clear();
2528      ditContentRules = null;
2529    }
2530
2531    if (ditStructureRulesByID != null)
2532    {
2533      ditStructureRulesByID.clear();
2534      ditStructureRulesByID = null;
2535    }
2536
2537    if (ditStructureRulesByNameForm != null)
2538    {
2539      ditStructureRulesByNameForm.clear();
2540      ditStructureRulesByNameForm = null;
2541    }
2542
2543    if (matchingRules != null)
2544    {
2545      matchingRules.clear();
2546      matchingRules = null;
2547    }
2548
2549    if (matchingRuleUses != null)
2550    {
2551      matchingRuleUses.clear();
2552      matchingRuleUses = null;
2553    }
2554
2555    if (nameFormsByName != null)
2556    {
2557      nameFormsByName.clear();
2558      nameFormsByName = null;
2559    }
2560
2561    if (nameFormsByOC != null)
2562    {
2563      nameFormsByOC.clear();
2564      nameFormsByOC = null;
2565    }
2566
2567    if (objectClasses != null)
2568    {
2569      objectClasses.clear();
2570      objectClasses = null;
2571    }
2572
2573    if (subordinateTypes != null)
2574    {
2575      subordinateTypes.clear();
2576      subordinateTypes = null;
2577    }
2578
2579    if (extraAttributes != null)
2580    {
2581      extraAttributes.clear();
2582      extraAttributes = null;
2583    }
2584
2585    if (syntaxes != null)
2586    {
2587      syntaxes.clear();
2588      syntaxes = null;
2589    }
2590
2591    if(ldapSyntaxDescriptions != null)
2592    {
2593      ldapSyntaxDescriptions.clear();
2594      ldapSyntaxDescriptions = null;
2595    }
2596
2597  }
2598}
2599