View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    * 
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   * 
19   */
20  package org.apache.directory.api.ldap.model.schema.registries;
21  
22  
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Set;
30  
31  import org.apache.directory.api.i18n.I18n;
32  import org.apache.directory.api.ldap.model.constants.MetaSchemaConstants;
33  import org.apache.directory.api.ldap.model.exception.LdapException;
34  import org.apache.directory.api.ldap.model.exception.LdapProtocolErrorException;
35  import org.apache.directory.api.ldap.model.exception.LdapSchemaException;
36  import org.apache.directory.api.ldap.model.exception.LdapSchemaExceptionCodes;
37  import org.apache.directory.api.ldap.model.exception.LdapSchemaViolationException;
38  import org.apache.directory.api.ldap.model.exception.LdapUnwillingToPerformException;
39  import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
40  import org.apache.directory.api.ldap.model.schema.AttributeType;
41  import org.apache.directory.api.ldap.model.schema.DitContentRule;
42  import org.apache.directory.api.ldap.model.schema.DitStructureRule;
43  import org.apache.directory.api.ldap.model.schema.LdapComparator;
44  import org.apache.directory.api.ldap.model.schema.LdapSyntax;
45  import org.apache.directory.api.ldap.model.schema.LoadableSchemaObject;
46  import org.apache.directory.api.ldap.model.schema.MatchingRule;
47  import org.apache.directory.api.ldap.model.schema.MatchingRuleUse;
48  import org.apache.directory.api.ldap.model.schema.MutableAttributeType;
49  import org.apache.directory.api.ldap.model.schema.MutableMatchingRule;
50  import org.apache.directory.api.ldap.model.schema.NameForm;
51  import org.apache.directory.api.ldap.model.schema.Normalizer;
52  import org.apache.directory.api.ldap.model.schema.ObjectClass;
53  import org.apache.directory.api.ldap.model.schema.SchemaObject;
54  import org.apache.directory.api.ldap.model.schema.SchemaObjectWrapper;
55  import org.apache.directory.api.ldap.model.schema.SyntaxChecker;
56  import org.apache.directory.api.ldap.model.schema.registries.helper.AttributeTypeHelper;
57  import org.apache.directory.api.ldap.model.schema.registries.helper.DitContentRuleHelper;
58  import org.apache.directory.api.ldap.model.schema.registries.helper.DitStructureRuleHelper;
59  import org.apache.directory.api.ldap.model.schema.registries.helper.LdapSyntaxHelper;
60  import org.apache.directory.api.ldap.model.schema.registries.helper.MatchingRuleHelper;
61  import org.apache.directory.api.ldap.model.schema.registries.helper.MatchingRuleUseHelper;
62  import org.apache.directory.api.ldap.model.schema.registries.helper.NameFormHelper;
63  import org.apache.directory.api.ldap.model.schema.registries.helper.ObjectClassHelper;
64  import org.apache.directory.api.util.Strings;
65  import org.slf4j.Logger;
66  import org.slf4j.LoggerFactory;
67  
68  
69  /**
70   * Document this class.
71   *
72   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
73   */
74  public class Registries implements SchemaLoaderListener, Cloneable
75  {
76      /** A logger for this class */
77      private static final Logger LOG = LoggerFactory.getLogger( Registries.class );
78  
79      /**
80       * A String name to Schema object map for the schemas loaded into this
81       * registry. The loaded schemas may be disabled.
82       */
83      protected Map<String, Schema> loadedSchemas = new HashMap<>();
84  
85      /** The AttributeType registry */
86      protected DefaultAttributeTypeRegistry attributeTypeRegistry;
87  
88      /** The ObjectClass registry */
89      protected DefaultObjectClassRegistry objectClassRegistry;
90  
91      /** The LdapSyntax registry */
92      protected DefaultComparatorRegistry comparatorRegistry;
93  
94      /** The DitContentRule registry */
95      protected DefaultDitContentRuleRegistry ditContentRuleRegistry;
96  
97      /** The DitStructureRule registry */
98      protected DefaultDitStructureRuleRegistry ditStructureRuleRegistry;
99  
100     /** The MatchingRule registry */
101     protected DefaultMatchingRuleRegistry matchingRuleRegistry;
102 
103     /** The MatchingRuleUse registry */
104     protected DefaultMatchingRuleUseRegistry matchingRuleUseRegistry;
105 
106     /** The NameForm registry */
107     protected DefaultNameFormRegistry nameFormRegistry;
108 
109     /** The Normalizer registry */
110     protected DefaultNormalizerRegistry normalizerRegistry;
111 
112     /** The global OID registry */
113     protected OidRegistry<SchemaObject> globalOidRegistry;
114 
115     /** The SyntaxChecker registry */
116     protected DefaultSyntaxCheckerRegistry syntaxCheckerRegistry;
117 
118     /** The LdapSyntax registry */
119     protected DefaultLdapSyntaxRegistry ldapSyntaxRegistry;
120 
121     /** A map storing all the schema objects associated with a schema */
122     private Map<String, Set<SchemaObjectWrapper>> schemaObjects;
123 
124     /** A flag indicating that the Registries is relaxed or not */
125     private boolean isRelaxed;
126 
127     /** A flag indicating that disabled SchemaObject are accepted */
128     private boolean disabledAccepted;
129 
130     /** Two flags for RELAXED and STRICT modes */
131     /** The strict mode */
132     public static final boolean STRICT = false;
133     
134     /** The relaxed mode */
135     public static final boolean RELAXED = true;
136 
137     /**
138      *  A map storing a relation between a SchemaObject and all the
139      *  referencing SchemaObjects.
140      */
141     protected Map<SchemaObjectWrapper, Set<SchemaObjectWrapper>> usedBy;
142 
143     /**
144      *  A map storing a relation between a SchemaObject and all the
145      *  SchemaObjects it uses.
146      */
147     protected Map<SchemaObjectWrapper, Set<SchemaObjectWrapper>> using;
148 
149 
150     /**
151      * Creates a new instance of Registries.
152      */
153     public Registries()
154     {
155         globalOidRegistry = new OidRegistry<>();
156         attributeTypeRegistry = new DefaultAttributeTypeRegistry();
157         comparatorRegistry = new DefaultComparatorRegistry();
158         ditContentRuleRegistry = new DefaultDitContentRuleRegistry();
159         ditStructureRuleRegistry = new DefaultDitStructureRuleRegistry();
160         ldapSyntaxRegistry = new DefaultLdapSyntaxRegistry();
161         matchingRuleRegistry = new DefaultMatchingRuleRegistry();
162         matchingRuleUseRegistry = new DefaultMatchingRuleUseRegistry();
163         nameFormRegistry = new DefaultNameFormRegistry();
164         normalizerRegistry = new DefaultNormalizerRegistry();
165         objectClassRegistry = new DefaultObjectClassRegistry();
166         syntaxCheckerRegistry = new DefaultSyntaxCheckerRegistry();
167         schemaObjects = new HashMap<>();
168         usedBy = new HashMap<>();
169         using = new HashMap<>();
170 
171         isRelaxed = STRICT;
172         disabledAccepted = false;
173     }
174 
175 
176     /**
177      * @return The AttributeType registry
178      */
179     public AttributeTypeRegistry getAttributeTypeRegistry()
180     {
181         return attributeTypeRegistry;
182     }
183 
184 
185     /**
186      * @return The Comparator registry
187      */
188     public ComparatorRegistry getComparatorRegistry()
189     {
190         return comparatorRegistry;
191     }
192 
193 
194     /**
195      * @return The DitContentRule registry
196      */
197     public DitContentRuleRegistry getDitContentRuleRegistry()
198     {
199         return ditContentRuleRegistry;
200     }
201 
202 
203     /**
204      * @return The DitStructureRule registry
205      */
206     public DitStructureRuleRegistry getDitStructureRuleRegistry()
207     {
208         return ditStructureRuleRegistry;
209     }
210 
211 
212     /**
213      * @return The MatchingRule registry
214      */
215     public MatchingRuleRegistry getMatchingRuleRegistry()
216     {
217         return matchingRuleRegistry;
218     }
219 
220 
221     /**
222      * @return The MatchingRuleUse registry
223      */
224     public MatchingRuleUseRegistry getMatchingRuleUseRegistry()
225     {
226         return matchingRuleUseRegistry;
227     }
228 
229 
230     /**
231      * @return The NameForm registry
232      */
233     public NameFormRegistry getNameFormRegistry()
234     {
235         return nameFormRegistry;
236     }
237 
238 
239     /**
240      * @return The Normalizer registry
241      */
242     public NormalizerRegistry getNormalizerRegistry()
243     {
244         return normalizerRegistry;
245     }
246 
247 
248     /**
249      * @return The ObjectClass registry
250      */
251     public ObjectClassRegistry getObjectClassRegistry()
252     {
253         return objectClassRegistry;
254     }
255 
256 
257     /**
258      * @return The global Oid registry
259      */
260     public OidRegistry<SchemaObject> getGlobalOidRegistry()
261     {
262         return globalOidRegistry;
263     }
264 
265 
266     /**
267      * @return The SyntaxChecker registry
268      */
269     public SyntaxCheckerRegistry getSyntaxCheckerRegistry()
270     {
271         return syntaxCheckerRegistry;
272     }
273 
274 
275     /**
276      * @return The LdapSyntax registry
277      */
278     public LdapSyntaxRegistry getLdapSyntaxRegistry()
279     {
280         return ldapSyntaxRegistry;
281     }
282 
283 
284     /**
285      * Get an OID from a name. As we have many possible registries, we
286      * have to look in all of them to get the one containing the OID.
287      *
288      * @param name The name we are looking at
289      * @return The associated OID
290      */
291     public String getOid( String name )
292     {
293         // we have many possible Registries to look at.
294         // AttributeType
295         try
296         {
297             AttributeType attributeType = attributeTypeRegistry.lookup( name );
298 
299             if ( attributeType != null )
300             {
301                 return attributeType.getOid();
302             }
303         }
304         catch ( LdapException ne )
305         {
306             // Fall down to the next registry
307         }
308 
309         // ObjectClass
310         try
311         {
312             ObjectClass objectClass = objectClassRegistry.lookup( name );
313 
314             if ( objectClass != null )
315             {
316                 return objectClass.getOid();
317             }
318         }
319         catch ( LdapException ne )
320         {
321             // Fall down to the next registry
322         }
323 
324         // LdapSyntax
325         try
326         {
327             LdapSyntax ldapSyntax = ldapSyntaxRegistry.lookup( name );
328 
329             if ( ldapSyntax != null )
330             {
331                 return ldapSyntax.getOid();
332             }
333         }
334         catch ( LdapException ne )
335         {
336             // Fall down to the next registry
337         }
338 
339         // MatchingRule
340         try
341         {
342             MatchingRule matchingRule = matchingRuleRegistry.lookup( name );
343 
344             if ( matchingRule != null )
345             {
346                 return matchingRule.getOid();
347             }
348         }
349         catch ( LdapException ne )
350         {
351             // Fall down to the next registry
352         }
353 
354         // MatchingRuleUse
355         try
356         {
357             MatchingRuleUse matchingRuleUse = matchingRuleUseRegistry.lookup( name );
358 
359             if ( matchingRuleUse != null )
360             {
361                 return matchingRuleUse.getOid();
362             }
363         }
364         catch ( LdapException ne )
365         {
366             // Fall down to the next registry
367         }
368 
369         // NameForm
370         try
371         {
372             NameForm nameForm = nameFormRegistry.lookup( name );
373 
374             if ( nameForm != null )
375             {
376                 return nameForm.getOid();
377             }
378         }
379         catch ( LdapException ne )
380         {
381             // Fall down to the next registry
382         }
383 
384         // DitContentRule
385         try
386         {
387             DitContentRule ditContentRule = ditContentRuleRegistry.lookup( name );
388 
389             if ( ditContentRule != null )
390             {
391                 return ditContentRule.getOid();
392             }
393         }
394         catch ( LdapException ne )
395         {
396             // Fall down to the next registry
397         }
398 
399         // DitStructureRule
400         try
401         {
402             DitStructureRule ditStructureRule = ditStructureRuleRegistry.lookup( name );
403 
404             if ( ditStructureRule != null )
405             {
406                 return ditStructureRule.getOid();
407             }
408         }
409         catch ( LdapException ne )
410         {
411             // No more registries to look at...
412         }
413 
414         return null;
415     }
416 
417 
418     /**
419      * Gets a schema that has been loaded into these Registries.
420      * 
421      * @param schemaName the name of the schema to lookup
422      * @return the loaded Schema if one corresponding to the name exists
423      */
424     public Schema getLoadedSchema( String schemaName )
425     {
426         return loadedSchemas.get( Strings.toLowerCaseAscii( schemaName ) );
427     }
428 
429 
430     /**
431      * Checks to see if a particular Schema is loaded.
432      *
433      * @param schemaName the name of the Schema to check
434      * @return true if the Schema is loaded, false otherwise
435      */
436     public boolean isSchemaLoaded( String schemaName )
437     {
438         return loadedSchemas.containsKey( Strings.toLowerCaseAscii( schemaName ) );
439     }
440 
441 
442     // ------------------------------------------------------------------------
443     // Code used to sanity check the resolution of entities in registries
444     // ------------------------------------------------------------------------
445     /**
446      * Attempts to resolve the dependent schema objects of all entities that
447      * refer to other objects within the registries.  Null references will be
448      * handed appropriately.
449      * The order in which the SchemaObjects must be :
450      * <ul>
451      *   <li>1) Normalizers, Comparators and SyntaxCheckers (as they depend on nothing)</li>
452      *   <li>2) Syntaxes (depend on SyntaxCheckers)</li>
453      *   <li>3) MatchingRules (depend on Syntaxes, Normalizers and Comparators</li>
454      *   <li>4) AttributeTypes (depend on MatchingRules, Syntaxes and AttributeTypes : in this case, we first handle the superior)</li>
455      *   <li>5) ObjectClasses (depend on AttributeTypes and ObjectClasses)</li>
456      * </ul>
457      * <br><br>
458      * Later, when we will support them :
459      * <ul>
460      *   <li>6) MatchingRuleUses (depend on matchingRules and AttributeTypes)</li>
461      *   <li>7) DitContentRules (depend on ObjectClasses and AttributeTypes)</li>
462      *   <li>8) NameForms (depends on ObjectClasses and AttributeTypes)</li>
463      *   <li>9) DitStructureRules (depends onNameForms and DitStructureRules)</li>
464      * </ul>
465      *
466      * @return a list of exceptions encountered while resolving entities
467      */
468     public List<Throwable> checkRefInteg()
469     {
470         ArrayList<Throwable> errors = new ArrayList<>();
471 
472         // Step 1 :
473         // We start with Normalizers, Comparators and SyntaxCheckers
474         // as they depend on nothing
475         // Check the Normalizers
476         for ( Normalizer normalizer : normalizerRegistry )
477         {
478             resolve( normalizer, errors );
479         }
480 
481         // Check the Comparators
482         for ( LdapComparator<?> comparator : comparatorRegistry )
483         {
484             resolve( comparator, errors );
485         }
486 
487         // Check the SyntaxCheckers
488         for ( SyntaxChecker syntaxChecker : syntaxCheckerRegistry )
489         {
490             resolve( syntaxChecker, errors );
491         }
492 
493         // Step 2 :
494         // Check the LdapSyntaxes
495         for ( LdapSyntax ldapSyntax : ldapSyntaxRegistry )
496         {
497             resolve( ldapSyntax, errors );
498         }
499 
500         // Step 3 :
501         // Check the matchingRules
502         for ( MatchingRule matchingRule : matchingRuleRegistry )
503         {
504             resolve( matchingRule, errors );
505         }
506 
507         // Step 4 :
508         // Check the AttributeTypes
509         for ( AttributeType attributeType : attributeTypeRegistry )
510         {
511             resolve( attributeType, errors );
512         }
513 
514         //  Step 5 :
515         // Check the ObjectClasses
516         for ( ObjectClass objectClass : objectClassRegistry )
517         {
518             resolve( objectClass, errors );
519         }
520 
521         // Step 6-9 aren't yet defined
522         return errors;
523     }
524 
525 
526     /**
527      * Add the SchemaObjectReferences. This method does nothing, it's just
528      * a catch all. The other methods will be called for each specific
529      * schemaObject
530      *
531     public void addCrossReferences( SchemaObject schemaObject )
532     {
533         // Do nothing : it's a catch all method.
534     }
535     
536     
537     /**
538      * Delete the AT references (using and usedBy) :
539      * AT -&gt; MR (for EQUALITY, ORDERING and SUBSTR)
540      * AT -&gt; S
541      * AT -&gt; AT
542      * 
543      * @param attributeType The AttributeType to remove
544      */
545     public void delCrossReferences( AttributeType attributeType )
546     {
547         if ( attributeType.getEquality() != null )
548         {
549             delReference( attributeType, attributeType.getEquality() );
550         }
551 
552         if ( attributeType.getOrdering() != null )
553         {
554             delReference( attributeType, attributeType.getOrdering() );
555         }
556 
557         if ( attributeType.getSubstring() != null )
558         {
559             delReference( attributeType, attributeType.getSubstring() );
560         }
561 
562         if ( attributeType.getSyntax() != null )
563         {
564             delReference( attributeType, attributeType.getSyntax() );
565         }
566 
567         if ( attributeType.getSuperior() != null )
568         {
569             delReference( attributeType, attributeType.getSuperior() );
570         }
571     }
572 
573 
574     /**
575      * Build the AttributeType references. This has to be done recursively, as
576      * an AttributeType may inherit its parent's MatchingRules. The references
577      * to update are :
578      * - EQUALITY MR
579      * - ORDERING MR
580      * - SUBSTRING MR
581      * - SUP AT
582      * - SYNTAX
583      */
584     private void buildAttributeTypeReferences( List<Throwable> errors )
585     {
586         for ( AttributeType attributeType : attributeTypeRegistry )
587         {
588             if ( ( getUsing( attributeType ) == null ) || getUsing( attributeType ).isEmpty() )
589             {
590                 buildReference( errors, attributeType );
591             }
592         }
593     }
594 
595 
596     /**
597      * Build the Comparator references
598      */
599     private void buildComparatorReferences( List<Throwable> errors )
600     {
601         for ( LdapComparator<?> comparator : comparatorRegistry )
602         {
603             buildReference( errors, comparator );
604         }
605     }
606 
607 
608     /**
609      * Build the DitContentRule references
610      */
611     private void buildDitContentRuleReferences( List<Throwable> errors )
612     {
613         // TODO: implement
614     }
615 
616 
617     /**
618      * Build the DitStructureRule references
619      */
620     private void buildDitStructureRuleReferences( List<Throwable> errors )
621     {
622         // TODO: implement
623     }
624 
625 
626     /**
627      * Delete the MR references (using and usedBy) :
628      * MR -&gt; C
629      * MR -&gt; N
630      * MR -&gt; S
631      * 
632      * @param matchingRule The MatchinRule refere ce to delete
633      */
634     public void delCrossReferences( MatchingRule matchingRule )
635     {
636         if ( matchingRule.getLdapComparator() != null )
637         {
638             delReference( matchingRule, matchingRule.getLdapComparator() );
639         }
640 
641         if ( matchingRule.getNormalizer() != null )
642         {
643             delReference( matchingRule, matchingRule.getNormalizer() );
644         }
645 
646         if ( matchingRule.getSyntax() != null )
647         {
648             delReference( matchingRule, matchingRule.getSyntax() );
649         }
650     }
651 
652 
653     /**
654      * Build the SchemaObject references
655      * 
656      * @param errors The List that collect errors
657      * @param schemaObject The SchemaObject to add
658      */
659     public void buildReference( List<Throwable> errors, SchemaObject schemaObject )
660     {
661         try
662         {
663             switch ( schemaObject.getObjectType() )
664             {
665                 case ATTRIBUTE_TYPE:
666                     AttributeTypeHelper.addToRegistries( ( MutableAttributeType ) schemaObject, errors, this );
667                     break;
668 
669                 case DIT_CONTENT_RULE:
670                     DitContentRuleHelper.addToRegistries( ( DitContentRule ) schemaObject, errors, this );
671                     break;
672 
673                 case DIT_STRUCTURE_RULE:
674                     DitStructureRuleHelper.addToRegistries( ( DitStructureRule ) schemaObject, errors, this );
675                     break;
676 
677                 case LDAP_SYNTAX:
678                     LdapSyntaxHelper.addToRegistries( ( LdapSyntax ) schemaObject, errors, this );
679                     break;
680 
681                 case MATCHING_RULE:
682                     MatchingRuleHelper.addToRegistries( ( MutableMatchingRule ) schemaObject, errors, this );
683                     break;
684 
685                 case MATCHING_RULE_USE:
686                     MatchingRuleUseHelper.addToRegistries( ( MatchingRuleUse ) schemaObject, errors, this );
687                     break;
688 
689                 case NAME_FORM:
690                     NameFormHelper.addToRegistries( ( NameForm ) schemaObject, errors, this );
691                     break;
692 
693                 case OBJECT_CLASS:
694                     ObjectClassHelper.addToRegistries( ( ObjectClass ) schemaObject, errors, this );
695                     break;
696 
697                 case SYNTAX_CHECKER:
698                 case NORMALIZER:
699                 case COMPARATOR:
700                     // Those are not registered
701                     break;
702 
703                 default:
704                     throw new IllegalArgumentException( "Unexpected SchemaObjectType: " + schemaObject.getObjectType() );
705             }
706         }
707         catch ( LdapException ne )
708         {
709             // Not allowed.
710             String msg = I18n.err( I18n.ERR_04292, schemaObject.getName(), ne.getLocalizedMessage() );
711 
712             Throwable error = new LdapProtocolErrorException( msg, ne );
713             errors.add( error );
714             LOG.info( msg );
715         }
716     }
717 
718 
719     /**
720      * Unlink the SchemaObject references
721      * 
722      * @param errors The List that collect errors
723      * @param schemaObject The SchemaObject to remove
724      */
725     public void removeReference( List<Throwable> errors, SchemaObject schemaObject )
726     {
727         try
728         {
729             switch ( schemaObject.getObjectType() )
730             {
731                 case ATTRIBUTE_TYPE:
732                     AttributeTypeHelper.removeFromRegistries( ( AttributeType ) schemaObject, errors, this );
733                     break;
734 
735                 case LDAP_SYNTAX:
736                     LdapSyntaxHelper.removeFromRegistries( ( LdapSyntax ) schemaObject, errors, this );
737                     break;
738 
739                 case MATCHING_RULE:
740                     MatchingRuleHelper.removeFromRegistries( ( MatchingRule ) schemaObject, errors, this );
741                     break;
742 
743                 case OBJECT_CLASS:
744                     ObjectClassHelper.removeFromRegistries( ( ObjectClass ) schemaObject, errors, this );
745                     break;
746                     
747                 case DIT_CONTENT_RULE :
748                     // TODO
749                     break;
750                     
751                 case DIT_STRUCTURE_RULE :
752                     // TODO
753                     break;
754                     
755                 case NAME_FORM :
756                     // TODO
757                     break;
758                     
759                 case MATCHING_RULE_USE :
760                     // TODO
761                     break;
762 
763                 case SYNTAX_CHECKER:
764                 case NORMALIZER:
765                 case COMPARATOR:
766                     // Those were not registered
767                     break;
768 
769                 default:
770                     throw new IllegalArgumentException( "Unexpected SchemaObjectType: " + schemaObject.getObjectType() );
771             }
772         }
773         catch ( LdapException ne )
774         {
775             // Not allowed.
776             String msg = I18n.err( I18n.ERR_04293, schemaObject.getName(), ne.getLocalizedMessage() );
777 
778             Throwable error = new LdapSchemaViolationException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, msg, ne );
779             errors.add( error );
780             LOG.info( msg );
781         }
782     }
783 
784 
785     /**
786      * Build the MatchingRule references
787      */
788     private void buildMatchingRuleReferences( List<Throwable> errors )
789     {
790         for ( MatchingRule matchingRule : matchingRuleRegistry )
791         {
792             buildReference( errors, matchingRule );
793         }
794     }
795 
796 
797     /**
798      * Build the MatchingRuleUse references
799      */
800     private void buildMatchingRuleUseReferences( List<Throwable> errors )
801     {
802         for ( MatchingRuleUse matchingRuleUse : matchingRuleUseRegistry )
803         {
804             buildReference( errors, matchingRuleUse );
805         }
806     }
807 
808 
809     /**
810      * Build the NameForm references
811      */
812     private void buildNameFormReferences( List<Throwable> errors )
813     {
814         // TODO: implement
815     }
816 
817 
818     /**
819      * Build the Normalizer references
820      */
821     private void buildNormalizerReferences( List<Throwable> errors )
822     {
823         for ( Normalizer normalizer : normalizerRegistry )
824         {
825             buildReference( errors, normalizer );
826         }
827     }
828 
829 
830     /**
831      * Build the ObjectClasses references
832      */
833     private void buildObjectClassReferences( List<Throwable> errors )
834     {
835         // Remember the OC we have already processed
836         Set<String> done = new HashSet<>();
837 
838         // The ObjectClass
839         for ( ObjectClass objectClass : objectClassRegistry )
840         {
841             if ( done.contains( objectClass.getOid() ) )
842             {
843                 continue;
844             }
845             else
846             {
847                 done.add( objectClass.getOid() );
848             }
849 
850             buildReference( errors, objectClass );
851         }
852     }
853 
854 
855     /**
856      * Build the Syntax references
857      */
858     private void buildLdapSyntaxReferences( List<Throwable> errors )
859     {
860         for ( LdapSyntax syntax : ldapSyntaxRegistry )
861         {
862             buildReference( errors, syntax );
863         }
864     }
865 
866 
867     /**
868      * Build the SyntaxChecker references
869      */
870     private void buildSyntaxCheckerReferences( List<Throwable> errors )
871     {
872         for ( SyntaxChecker syntaxChecker : syntaxCheckerRegistry )
873         {
874             buildReference( errors, syntaxChecker );
875         }
876     }
877 
878 
879     /**
880      * Build the usedBy and using references from the stored elements.
881      * 
882      * @return A list of all the errors we met during the cross reference update
883      */
884     public List<Throwable> buildReferences()
885     {
886         List<Throwable> errors = new ArrayList<>();
887 
888         // The Comparator references
889         buildComparatorReferences( errors );
890 
891         // The Normalizer references
892         buildNormalizerReferences( errors );
893 
894         // The SyntaxChecker references
895         buildSyntaxCheckerReferences( errors );
896 
897         // The Syntax references
898         buildLdapSyntaxReferences( errors );
899 
900         // The MatchingRules references
901         buildMatchingRuleReferences( errors );
902 
903         // The AttributeType references
904         buildAttributeTypeReferences( errors );
905 
906         // The MatchingRuleUse references
907         buildMatchingRuleUseReferences( errors );
908 
909         // The ObjectClasses references
910         buildObjectClassReferences( errors );
911 
912         // The DitContentRules references
913         buildDitContentRuleReferences( errors );
914 
915         // The NameForms references
916         buildNameFormReferences( errors );
917 
918         // The DitStructureRules references
919         buildDitStructureRuleReferences( errors );
920 
921         return errors;
922     }
923 
924 
925     /**
926      * Attempts to resolve the SyntaxChecker associated with a Syntax.
927      *
928      * @param syntax the LdapSyntax to resolve the SyntaxChecker of
929      * @param errors the list of errors to add exceptions to
930      */
931     private void resolve( LdapSyntax syntax, List<Throwable> errors )
932     {
933         // A LdapSyntax must point to a valid SyntaxChecker
934         // or to the OctetString SyntaxChecker
935         try
936         {
937             LdapSyntaxHelper.addToRegistries( syntax, errors, this );
938         }
939         catch ( LdapException e )
940         {
941             errors.add( e );
942         }
943     }
944 
945 
946     /**
947      * Attempts to resolve the Normalizer
948      *
949      * @param normalizer the Normalizer
950      * @param errors the list of errors to add exceptions to
951      */
952     private void resolve( Normalizer normalizer, List<Throwable> errors )
953     {
954         // This is currently doing nothing.
955     }
956 
957 
958     /**
959      * Attempts to resolve the LdapComparator
960      *
961      * @param comparator the LdapComparator
962      * @param errors the list of errors to add exceptions to
963      */
964     private void resolve( LdapComparator<?> comparator, List<Throwable> errors )
965     {
966         // This is currently doing nothing.
967     }
968 
969 
970     /**
971      * Attempts to resolve the SyntaxChecker
972      *
973      * @param normalizer the SyntaxChecker
974      * @param errors the list of errors to add exceptions to
975      */
976     private void resolve( SyntaxChecker syntaxChecker, List<Throwable> errors )
977     {
978         // This is currently doing nothing.
979     }
980 
981 
982     /**
983      * Check if the Comparator, Normalizer and the syntax are
984      * existing for a matchingRule.
985      */
986     private void resolve( MatchingRule matchingRule, List<Throwable> errors )
987     {
988         // Process the Syntax. It can't be null
989         String syntaxOid = matchingRule.getSyntaxOid();
990 
991         if ( syntaxOid != null )
992         {
993             // Check if the Syntax is present in the registries
994             try
995             {
996                 ldapSyntaxRegistry.lookup( syntaxOid );
997             }
998             catch ( LdapException ne )
999             {
1000                 // This MR's syntax has not been loaded into the Registries.
1001                 LdapSchemaException ldapSchemaException = new LdapSchemaException(
1002                     LdapSchemaExceptionCodes.OID_ALREADY_REGISTERED, I18n.err( I18n.ERR_04294, matchingRule.getOid() ),
1003                     ne );
1004                 ldapSchemaException.setSourceObject( matchingRule );
1005                 errors.add( ldapSchemaException );
1006             }
1007         }
1008         else
1009         {
1010             // This is an error.
1011             LdapSchemaException ldapSchemaException = new LdapSchemaException(
1012                 LdapSchemaExceptionCodes.OID_ALREADY_REGISTERED, I18n.err( I18n.ERR_04294, matchingRule.getOid() ) );
1013             ldapSchemaException.setSourceObject( matchingRule );
1014             errors.add( ldapSchemaException );
1015         }
1016 
1017         // Process the Normalizer
1018         Normalizer normalizer = matchingRule.getNormalizer();
1019 
1020         if ( normalizer == null )
1021         {
1022             // Ok, no normalizer, this is an error
1023             Throwable error = new LdapSchemaViolationException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, I18n.err(
1024                 I18n.ERR_04295, matchingRule.getOid() ) );
1025             errors.add( error );
1026         }
1027 
1028         // Process the Comparator
1029         LdapComparator<?> comparator = matchingRule.getLdapComparator();
1030 
1031         if ( comparator == null )
1032         {
1033             // Ok, no comparator, this is an error
1034             Throwable error = new LdapSchemaViolationException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, I18n.err(
1035                 I18n.ERR_04296, matchingRule.getOid() ) );
1036             errors.add( error );
1037         }
1038     }
1039 
1040 
1041     /**
1042      * Check AttributeType referential integrity
1043      */
1044     private void resolveRecursive( AttributeType attributeType, Set<String> processed, List<Throwable> errors )
1045     {
1046         // Process the Superior, if any
1047         String superiorOid = attributeType.getSuperiorOid();
1048 
1049         AttributeType superior = null;
1050 
1051         if ( superiorOid != null )
1052         {
1053             // Check if the Superior is present in the registries
1054             try
1055             {
1056                 superior = attributeTypeRegistry.lookup( superiorOid );
1057             }
1058             catch ( LdapException ne )
1059             {
1060                 // This AT's superior has not been loaded into the Registries.
1061                 if ( !processed.contains( superiorOid ) )
1062                 {
1063                     errors.add( ne );
1064                 }
1065             }
1066 
1067             // We now have to process the superior, if it hasn't been
1068             // processed yet.
1069             if ( superior != null )
1070             {
1071                 if ( !processed.contains( superiorOid ) )
1072                 {
1073                     resolveRecursive( superior, processed, errors );
1074                     processed.add( attributeType.getOid() );
1075                 }
1076                 else
1077                 {
1078                     // Not allowed : we have a cyle
1079                     Throwable error = new LdapSchemaViolationException( ResultCodeEnum.OTHER, I18n.err( I18n.ERR_04297,
1080                         attributeType.getOid() ) );
1081                     errors.add( error );
1082                     return;
1083                 }
1084             }
1085         }
1086 
1087         // Process the Syntax. If it's null, the attributeType must have
1088         // a Superior.
1089         String syntaxOid = attributeType.getSyntaxOid();
1090 
1091         if ( syntaxOid != null )
1092         {
1093             // Check if the Syntax is present in the registries
1094             try
1095             {
1096                 ldapSyntaxRegistry.lookup( syntaxOid );
1097             }
1098             catch ( LdapException ne )
1099             {
1100                 // This AT's syntax has not been loaded into the Registries.
1101                 errors.add( ne );
1102             }
1103         }
1104         else
1105         {
1106             // No Syntax : get it from the AttributeType's superior
1107             if ( superior == null )
1108             {
1109                 // This is an error. if the AT does not have a Syntax,
1110                 // then it must have a superior, which syntax is get from.
1111                 Throwable error = new LdapSchemaViolationException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, I18n.err(
1112                     I18n.ERR_04298, attributeType.getOid() ) );
1113                 errors.add( error );
1114             }
1115         }
1116 
1117         // Process the EQUALITY MatchingRule. It may be null, but if it's not
1118         // it must have been processed before
1119         String equalityOid = attributeType.getEqualityOid();
1120 
1121         if ( equalityOid != null )
1122         {
1123             // Check if the MatchingRule is present in the registries
1124             try
1125             {
1126                 matchingRuleRegistry.lookup( equalityOid );
1127             }
1128             catch ( LdapException ne )
1129             {
1130                 // This AT's EQUALITY matchingRule has not been loaded into the Registries.
1131                 errors.add( ne );
1132             }
1133         }
1134 
1135         // Process the ORDERING MatchingRule. It may be null, but if it's not
1136         // it must have been processed before
1137         String orderingOid = attributeType.getOrderingOid();
1138 
1139         if ( orderingOid != null )
1140         {
1141             // Check if the MatchingRule is present in the registries
1142             try
1143             {
1144                 matchingRuleRegistry.lookup( orderingOid );
1145             }
1146             catch ( LdapException ne )
1147             {
1148                 // This AT's ORDERING matchingRule has not been loaded into the Registries.
1149                 errors.add( ne );
1150             }
1151         }
1152 
1153         // Process the SUBSTR MatchingRule. It may be null, but if it's not
1154         // it must have been processed before
1155         String substringOid = attributeType.getSubstringOid();
1156 
1157         if ( substringOid != null )
1158         {
1159             // Check if the MatchingRule is present in the registries
1160             try
1161             {
1162                 matchingRuleRegistry.lookup( substringOid );
1163             }
1164             catch ( LdapException ne )
1165             {
1166                 // This AT's SUBSTR matchingRule has not been loaded into the Registries.
1167                 errors.add( ne );
1168             }
1169         }
1170     }
1171 
1172 
1173     /**
1174      * Check the inheritance, and the existence of MatchingRules and LdapSyntax
1175      * for an attribute
1176      */
1177     private void resolve( AttributeType attributeType, List<Throwable> errors )
1178     {
1179         // This set is used to avoid having more than one error
1180         // for an AttributeType. It's mandatory when processing
1181         // a Superior, as it may be broken and referenced more than once.
1182         Set<String> processed = new HashSet<>();
1183 
1184         // Store the AttributeType itself in the processed, to avoid cycle
1185         processed.add( attributeType.getOid() );
1186 
1187         // Call the recursive method, as we may have superiors to deal with
1188         resolveRecursive( attributeType, processed, errors );
1189     }
1190 
1191 
1192     private List<AttributeType> getMustRecursive( List<AttributeType> musts, Set<ObjectClass> processed,
1193         ObjectClass objectClass )
1194     {
1195         if ( objectClass != null )
1196         {
1197             if ( processed.contains( objectClass ) )
1198             {
1199                 // We have found a cycle. It has already been reported,
1200                 // don't add a new error, just exit.
1201                 return null;
1202             }
1203 
1204             processed.add( objectClass );
1205 
1206             for ( AttributeType must : objectClass.getMustAttributeTypes() )
1207             {
1208                 musts.add( must );
1209             }
1210 
1211             for ( ObjectClass superior : objectClass.getSuperiors() )
1212             {
1213                 getMustRecursive( musts, processed, superior );
1214             }
1215         }
1216 
1217         return musts;
1218     }
1219 
1220 
1221     private void resolve( ObjectClass objectClass, List<Throwable> errors )
1222     {
1223         // This set is used to avoid having more than one error
1224         // for an ObjectClass. It's mandatory when processing
1225         // the Superiors, as they may be broken and referenced more than once.
1226         Set<String> processed = new HashSet<>();
1227 
1228         // Store the ObjectClass itself in the processed, to avoid cycle
1229         processed.add( objectClass.getOid() );
1230 
1231         // Call the recursive method, as we may have superiors to deal with
1232         resolveRecursive( objectClass, processed, errors );
1233 
1234         // Check that the MAY and MUST AT are consistent (no AT in MAY and in MUST
1235         // in one of its superior
1236         List<AttributeType> musts = getMustRecursive( new ArrayList<AttributeType>(), new HashSet<ObjectClass>(),
1237             objectClass );
1238 
1239         if ( musts != null )
1240         {
1241             for ( AttributeType may : objectClass.getMayAttributeTypes() )
1242             {
1243                 if ( musts.contains( may ) )
1244                 {
1245                     // This is not allowed.
1246                     LdapSchemaException ldapSchemaException = new LdapSchemaException(
1247                         LdapSchemaExceptionCodes.OC_DUPLICATE_AT_IN_MAY_AND_MUST );
1248                     ldapSchemaException.setSourceObject( objectClass );
1249                     ldapSchemaException.setOtherObject( may );
1250                     errors.add( ldapSchemaException );
1251                 }
1252             }
1253         }
1254     }
1255 
1256 
1257     private void resolveRecursive( ObjectClass objectClass, Set<String> processed, List<Throwable> errors )
1258     {
1259         // Process the Superiors, if any
1260         List<String> superiorOids = objectClass.getSuperiorOids();
1261         ObjectClass superior = null;
1262 
1263         for ( String superiorOid : superiorOids )
1264         {
1265             // Check if the Superior is present in the registries
1266             try
1267             {
1268                 superior = objectClassRegistry.lookup( superiorOid );
1269             }
1270             catch ( LdapException ne )
1271             {
1272                 // This OC's superior has not been loaded into the Registries.
1273                 if ( !processed.contains( superiorOid ) )
1274                 {
1275                     LdapSchemaException ldapSchemaException = new LdapSchemaException(
1276                         LdapSchemaExceptionCodes.OC_NONEXISTENT_SUPERIOR, ne );
1277                     ldapSchemaException.setSourceObject( objectClass );
1278                     ldapSchemaException.setRelatedId( superiorOid );
1279                     errors.add( ldapSchemaException );
1280                 }
1281             }
1282 
1283             // We now have to process the superior, if it hasn't been
1284             // processed yet.
1285             if ( superior != null )
1286             {
1287                 if ( !processed.contains( superior.getOid() ) )
1288                 {
1289                     resolveRecursive( superior, processed, errors );
1290                     processed.add( objectClass.getOid() );
1291                 }
1292                 else
1293                 {
1294                     // Not allowed : we have a cyle
1295                     LdapSchemaException ldapSchemaException = new LdapSchemaException(
1296                         LdapSchemaExceptionCodes.OC_CYCLE_CLASS_HIERARCHY );
1297                     ldapSchemaException.setSourceObject( objectClass );
1298                     ldapSchemaException.setOtherObject( superior );
1299                     errors.add( ldapSchemaException );
1300                     return;
1301                 }
1302             }
1303         }
1304 
1305         // Process the MAY attributeTypes.
1306         for ( String mayOid : objectClass.getMayAttributeTypeOids() )
1307         {
1308             // Check if the MAY AttributeType is present in the registries
1309             try
1310             {
1311                 attributeTypeRegistry.lookup( mayOid );
1312             }
1313             catch ( LdapException ne )
1314             {
1315                 // This AT has not been loaded into the Registries.
1316                 errors.add( ne );
1317             }
1318         }
1319 
1320         // Process the MUST attributeTypes.
1321         for ( String mustOid : objectClass.getMustAttributeTypeOids() )
1322         {
1323             // Check if the MUST AttributeType is present in the registries
1324             try
1325             {
1326                 attributeTypeRegistry.lookup( mustOid );
1327             }
1328             catch ( LdapException ne )
1329             {
1330                 // This AT has not been loaded into the Registries.
1331                 errors.add( ne );
1332             }
1333         }
1334 
1335         // All is done for this ObjectClass, let's apply the registries
1336         try
1337         {
1338             ObjectClassHelper.addToRegistries( objectClass, errors, this );
1339         }
1340         catch ( LdapException ne )
1341         {
1342             // Do nothing. We may have a broken OC,
1343             // but at this point, it doesn't matter.
1344         }
1345     }
1346 
1347 
1348     /**
1349      * Applies the added SchemaObject to the given register
1350      *
1351      * @param errors The list of collected errors
1352      * @param schemaObject The SchemaObject to add
1353      * @param check A flag set when we want the schema checks to be done
1354      * @return The list of found errors
1355      * @throws LdapException If we weren't able to add the SchemaObject
1356      */
1357     public List<Throwable> add( List<Throwable> errors, SchemaObject schemaObject, boolean check ) throws LdapException
1358     {
1359         // Relax the registries
1360         boolean wasRelaxed = isRelaxed;
1361         setRelaxed();
1362 
1363         // Register the SchemaObject in the registries
1364         register( errors, schemaObject );
1365 
1366         // Associate the SchemaObject with its schema
1367         associateWithSchema( errors, schemaObject );
1368 
1369         // Build the SchemaObject references
1370         if ( check )
1371         {
1372             buildReference( errors, schemaObject );
1373         }
1374 
1375         // Lock the SchemaObject
1376         schemaObject.lock();
1377 
1378         if ( check && ( errors.isEmpty() ) )
1379         {
1380             // Check the registries now
1381             List<Throwable> checkErrors = checkRefInteg();
1382 
1383             errors.addAll( checkErrors );
1384         }
1385 
1386         // Get back to Strict mode
1387         if ( !wasRelaxed )
1388         {
1389             setStrict();
1390         }
1391 
1392         // return the errors
1393         return errors;
1394     }
1395 
1396 
1397     /**
1398      * Remove the given SchemaObject from the registries
1399      * 
1400      * @param errors The list of collected errors
1401      * @param schemaObject The SchemaObject to delete
1402      * @return The list of errors
1403      * @throws LdapException If the deletion failed
1404      */
1405     public List<Throwable> delete( List<Throwable> errors, SchemaObject schemaObject ) throws LdapException
1406     {
1407         // Relax the registries
1408         boolean wasRelaxed = isRelaxed;
1409         setRelaxed();
1410 
1411         // Remove the SchemaObject from the registries
1412         SchemaObject removed = unregister( errors, schemaObject );
1413 
1414         // Remove the SchemaObject from its schema
1415         dissociateFromSchema( errors, removed );
1416 
1417         // Unlink the SchemaObject references
1418         removeReference( errors, removed );
1419 
1420         if ( errors.isEmpty() )
1421         {
1422             // Check the registries now
1423             List<Throwable> checkErrors = checkRefInteg();
1424 
1425             errors.addAll( checkErrors );
1426         }
1427 
1428         // Restore the previous registries state
1429         if ( !wasRelaxed )
1430         {
1431             setStrict();
1432         }
1433 
1434         // return the errors
1435         return errors;
1436     }
1437 
1438 
1439     /**
1440      * Merely adds the schema to the set of loaded schemas.  Does not
1441      * actually do any work to add schema objects to registries.
1442      * 
1443      * {@inheritDoc}
1444      */
1445     @Override
1446     public void schemaLoaded( Schema schema )
1447     {
1448         this.loadedSchemas.put( Strings.toLowerCaseAscii( schema.getSchemaName() ), schema );
1449     }
1450 
1451 
1452     /**
1453      * Merely removes the schema from the set of loaded schemas.  Does not
1454      * actually do any work to remove schema objects from registries.
1455      * 
1456      * {@inheritDoc}
1457      */
1458     @Override
1459     public void schemaUnloaded( Schema schema )
1460     {
1461         this.loadedSchemas.remove( Strings.toLowerCaseAscii( schema.getSchemaName() ) );
1462     }
1463 
1464 
1465     /**
1466      * Gets an unmodifiable Map of schema names to loaded Schema objects.
1467      * 
1468      * @return the map of loaded Schema objects
1469      */
1470     public Map<String, Schema> getLoadedSchemas()
1471     {
1472         return Collections.unmodifiableMap( loadedSchemas );
1473     }
1474 
1475 
1476     /**
1477      * @return Gets a reference to the Map associating a schemaName to
1478      * its contained SchemaObjects
1479      */
1480     public Map<String, Set<SchemaObjectWrapper>> getObjectBySchemaName()
1481     {
1482         return schemaObjects;
1483     }
1484 
1485 
1486     /**
1487      * Retrieve the schema name for a specific SchemaObject, or return "other" if none is found.
1488      */
1489     private String getSchemaName( SchemaObject schemaObject )
1490     {
1491         String schemaName = Strings.toLowerCaseAscii( schemaObject.getSchemaName() );
1492 
1493         if ( loadedSchemas.containsKey( schemaName ) )
1494         {
1495             return schemaName;
1496         }
1497         else
1498         {
1499             return MetaSchemaConstants.SCHEMA_OTHER;
1500         }
1501     }
1502 
1503 
1504     /**
1505      * Tells if the given SchemaObject is present in one schema. The schema
1506      * may be disabled.
1507      *
1508      * @param schemaObject The schemaObject we are looking for
1509      * @return true if the schemaObject is present in a schema
1510      */
1511     public boolean contains( SchemaObject schemaObject )
1512     {
1513         String schemaName = schemaObject.getSchemaName();
1514 
1515         Set<SchemaObjectWrapper> setSchemaObjects = schemaObjects.get( schemaName );
1516 
1517         if ( ( setSchemaObjects == null ) || setSchemaObjects.isEmpty() )
1518         {
1519             return false;
1520         }
1521 
1522         SchemaObjectWrapper wrapper = new SchemaObjectWrapper( schemaObject );
1523 
1524         return setSchemaObjects.contains( wrapper );
1525     }
1526 
1527 
1528     /**
1529      * Create a new schema association with its content
1530      *
1531      * @param schemaName The schema name
1532      * @return A set containing the associations
1533      */
1534     public Set<SchemaObjectWrapper> addSchema( String schemaName )
1535     {
1536         Set<SchemaObjectWrapper> content = new HashSet<>();
1537         schemaObjects.put( schemaName, content );
1538 
1539         return content;
1540     }
1541 
1542 
1543     /**
1544      * Register the given SchemaObject into the associated Registry
1545      */
1546     private void register( List<Throwable> errors, SchemaObject schemaObject ) throws LdapException
1547     {
1548         LOG.debug( "Registering {}:{}", schemaObject.getObjectType(), schemaObject.getOid() );
1549 
1550         // Check that the SchemaObject is not already registered
1551         if ( !( schemaObject instanceof LoadableSchemaObject ) && globalOidRegistry.contains( schemaObject.getOid() ) )
1552         {
1553             String msg = I18n.err( I18n.ERR_04301, schemaObject.getObjectType(), schemaObject.getOid() );
1554             LOG.error( msg );
1555             Throwable error = new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
1556             errors.add( error );
1557             return;
1558         }
1559 
1560         try
1561         {
1562             // First call the specific registry's register method
1563             switch ( schemaObject.getObjectType() )
1564             {
1565                 case ATTRIBUTE_TYPE:
1566                     attributeTypeRegistry.register( ( AttributeType ) schemaObject );
1567                     break;
1568 
1569                 case COMPARATOR:
1570                     comparatorRegistry.register( ( LdapComparator<?> ) schemaObject );
1571                     break;
1572 
1573                 case DIT_CONTENT_RULE:
1574                     ditContentRuleRegistry.register( ( DitContentRule ) schemaObject );
1575                     break;
1576 
1577                 case DIT_STRUCTURE_RULE:
1578                     ditStructureRuleRegistry.register( ( DitStructureRule ) schemaObject );
1579                     break;
1580 
1581                 case LDAP_SYNTAX:
1582                     ldapSyntaxRegistry.register( ( LdapSyntax ) schemaObject );
1583                     break;
1584 
1585                 case MATCHING_RULE:
1586                     matchingRuleRegistry.register( ( MatchingRule ) schemaObject );
1587                     break;
1588 
1589                 case MATCHING_RULE_USE:
1590                     matchingRuleUseRegistry.register( ( MatchingRuleUse ) schemaObject );
1591                     break;
1592 
1593                 case NAME_FORM:
1594                     nameFormRegistry.register( ( NameForm ) schemaObject );
1595                     break;
1596 
1597                 case NORMALIZER:
1598                     normalizerRegistry.register( ( Normalizer ) schemaObject );
1599                     break;
1600 
1601                 case OBJECT_CLASS:
1602                     objectClassRegistry.register( ( ObjectClass ) schemaObject );
1603                     break;
1604 
1605                 case SYNTAX_CHECKER:
1606                     syntaxCheckerRegistry.register( ( SyntaxChecker ) schemaObject );
1607                     break;
1608 
1609                 default:
1610                     throw new IllegalArgumentException( "Unexpected SchemaObjectType: " + schemaObject.getObjectType() );
1611             }
1612         }
1613         catch ( Exception e )
1614         {
1615             errors.add( e );
1616         }
1617     }
1618 
1619 
1620     /**
1621      * Store the given SchemaObject in the Map associating SchemaObjetcs to their
1622      * related Schema.
1623      *
1624      * @param errors The list of errors we are accumulating while checking the schema
1625      * @param schemaObject The schemaObject to register
1626      */
1627     public void associateWithSchema( List<Throwable> errors, SchemaObject schemaObject )
1628     {
1629         LOG.debug( "Registering {}:{}", schemaObject.getObjectType(), schemaObject.getOid() );
1630 
1631         // Check that the SchemaObject is not already registered
1632         if ( !( schemaObject instanceof LoadableSchemaObject ) && globalOidRegistry.contains( schemaObject.getOid() ) )
1633         {
1634             String msg = I18n.err( I18n.ERR_04301, schemaObject.getObjectType(), schemaObject.getOid() );
1635             LOG.error( msg );
1636             Throwable error = new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
1637             errors.add( error );
1638             
1639             return;
1640         }
1641 
1642         // Get a normalized form of schema name
1643         String schemaName = getSchemaName( schemaObject );
1644 
1645         // And register the schemaObject within its schema
1646         Set<SchemaObjectWrapper> content = schemaObjects.get( schemaName );
1647 
1648         if ( content == null )
1649         {
1650             content = new HashSet<>();
1651             schemaObjects.put( Strings.toLowerCaseAscii( schemaName ), content );
1652         }
1653 
1654         SchemaObjectWrapper schemaObjectWrapper = new SchemaObjectWrapper( schemaObject );
1655 
1656         if ( content.contains( schemaObjectWrapper ) )
1657         {
1658             // Already present !
1659             // What should we do ?
1660             LOG.info( "Registering of {}:{} failed, is already present in the Registries",
1661                 schemaObject.getObjectType(), schemaObject.getOid() );
1662         }
1663         else
1664         {
1665             // Create the association
1666             content.add( schemaObjectWrapper );
1667 
1668             // Update the global OidRegistry if the SchemaObject is not
1669             // an instance of LoadableSchemaObject
1670             if ( !( schemaObject instanceof LoadableSchemaObject ) )
1671             {
1672                 try
1673                 {
1674                     globalOidRegistry.register( schemaObject );
1675                 }
1676                 catch ( LdapException ne )
1677                 {
1678                     errors.add( ne );
1679                     return;
1680                 }
1681             }
1682 
1683             LOG.debug( "registered {} for OID {}", schemaObject.getName(), schemaObject.getOid() );
1684         }
1685     }
1686 
1687 
1688     /**
1689      * Store the given SchemaObject in the Map associating SchemaObjetcs to their
1690      * related Schema.
1691      *
1692      * @param errors The list that collect errors
1693      * @param schemaObject The schemaObject to register
1694      * @throws LdapException If there is a problem
1695      */
1696 
1697     public void dissociateFromSchema( List<Throwable> errors, SchemaObject schemaObject ) throws LdapException
1698     {
1699         LOG.debug( "Unregistering {}:{}", schemaObject.getObjectType(), schemaObject.getOid() );
1700 
1701         // Check that the SchemaObject is already registered
1702         if ( !( schemaObject instanceof LoadableSchemaObject ) && !globalOidRegistry.contains( schemaObject.getOid() ) )
1703         {
1704             String msg = I18n.err( I18n.ERR_04302, schemaObject.getObjectType(), schemaObject.getOid() );
1705             LOG.error( msg );
1706             Throwable error = new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
1707             errors.add( error );
1708             
1709             return;
1710         }
1711 
1712         // Get a normalized form of schema name
1713         String schemaName = getSchemaName( schemaObject );
1714         String oid = schemaObject.getOid();
1715 
1716         // And unregister the schemaObject from its schema
1717         Set<SchemaObjectWrapper> content = schemaObjects.get( schemaName );
1718 
1719         SchemaObjectWrapper schemaObjectWrapper = new SchemaObjectWrapper( schemaObject );
1720 
1721         if ( !content.contains( schemaObjectWrapper ) )
1722         {
1723             // Not present !
1724             // What should we do ?
1725             LOG.info( "Unregistering of {}:{} failed, is not present in the Registries", schemaObject.getObjectType(),
1726                 schemaObject.getOid() );
1727         }
1728         else
1729         {
1730             // Remove the association
1731             content.remove( schemaObjectWrapper );
1732 
1733             // Update the global OidRegistry if the SchemaObject is not
1734             // an instance of LoadableSchemaObject
1735             if ( !( schemaObject instanceof LoadableSchemaObject ) )
1736             {
1737                 try
1738                 {
1739                     globalOidRegistry.unregister( oid );
1740                 }
1741                 catch ( LdapException ne )
1742                 {
1743                     errors.add( ne );
1744                     return;
1745                 }
1746             }
1747 
1748             LOG.debug( "Unregistered {} for OID {}", schemaObject.getName(), schemaObject.getOid() );
1749         }
1750     }
1751 
1752 
1753     /**
1754      * Unregister a SchemaObject from the registries
1755      *
1756      * @param schemaObject The SchemaObject we want to deregister
1757      * @throws LdapException If the removal failed
1758      */
1759     private SchemaObject unregister( List<Throwable> errors, SchemaObject schemaObject ) throws LdapException
1760     {
1761         LOG.debug( "Unregistering {}:{}", schemaObject.getObjectType(), schemaObject.getOid() );
1762 
1763         // Check that the SchemaObject is present in the registries
1764         if ( !( schemaObject instanceof LoadableSchemaObject ) && !globalOidRegistry.contains( schemaObject.getOid() ) )
1765         {
1766             String msg = I18n.err( I18n.ERR_04302, schemaObject.getObjectType(), schemaObject.getOid() );
1767             LOG.error( msg );
1768             throw new LdapUnwillingToPerformException( ResultCodeEnum.UNWILLING_TO_PERFORM, msg );
1769         }
1770 
1771         SchemaObject unregistered;
1772 
1773         // First call the specific registry's register method
1774         switch ( schemaObject.getObjectType() )
1775         {
1776             case ATTRIBUTE_TYPE:
1777                 unregistered = attributeTypeRegistry.unregister( ( AttributeType ) schemaObject );
1778                 break;
1779 
1780             case COMPARATOR:
1781                 unregistered = comparatorRegistry.unregister( ( LdapComparator<?> ) schemaObject );
1782                 break;
1783 
1784             case DIT_CONTENT_RULE:
1785                 unregistered = ditContentRuleRegistry.unregister( ( DitContentRule ) schemaObject );
1786                 break;
1787 
1788             case DIT_STRUCTURE_RULE:
1789                 unregistered = ditStructureRuleRegistry.unregister( ( DitStructureRule ) schemaObject );
1790                 break;
1791 
1792             case LDAP_SYNTAX:
1793                 unregistered = ldapSyntaxRegistry.unregister( ( LdapSyntax ) schemaObject );
1794                 break;
1795 
1796             case MATCHING_RULE:
1797                 unregistered = matchingRuleRegistry.unregister( ( MatchingRule ) schemaObject );
1798                 break;
1799 
1800             case MATCHING_RULE_USE:
1801                 unregistered = matchingRuleUseRegistry.unregister( ( MatchingRuleUse ) schemaObject );
1802                 break;
1803 
1804             case NAME_FORM:
1805                 unregistered = nameFormRegistry.unregister( ( NameForm ) schemaObject );
1806                 break;
1807 
1808             case NORMALIZER:
1809                 unregistered = normalizerRegistry.unregister( ( Normalizer ) schemaObject );
1810                 break;
1811 
1812             case OBJECT_CLASS:
1813                 unregistered = objectClassRegistry.unregister( ( ObjectClass ) schemaObject );
1814                 break;
1815 
1816             case SYNTAX_CHECKER:
1817                 unregistered = syntaxCheckerRegistry.unregister( ( SyntaxChecker ) schemaObject );
1818                 break;
1819 
1820             default:
1821                 throw new IllegalArgumentException( "Unexpected SchemaObjectType: " + schemaObject.getObjectType() );
1822         }
1823 
1824         return unregistered;
1825     }
1826 
1827 
1828     /**
1829      * Remove the given SchemaObject from the Map associating SchemaObjetcs to their
1830      * related Schema.
1831      *
1832      * @param schemaObject The schemaObject to remove
1833      * @throws LdapException If there is a problem
1834      */
1835     public void dissociateFromSchema( SchemaObject schemaObject ) throws LdapException
1836     {
1837         // And unregister the schemaObject within its schema
1838         Set<SchemaObjectWrapper> content = schemaObjects.get( Strings.toLowerCaseAscii( schemaObject.getSchemaName() ) );
1839 
1840         if ( content != null )
1841         {
1842             SchemaObjectWrapper schemaObjectWrapper = new SchemaObjectWrapper( schemaObject );
1843 
1844             if ( content.contains( schemaObjectWrapper ) )
1845             {
1846                 // remove the schemaObject
1847                 content.remove( schemaObjectWrapper );
1848 
1849                 // Update the global OidRegistry if the SchemaObject is not
1850                 // an instance of LoadableSchemaObject
1851                 if ( !( schemaObject instanceof LoadableSchemaObject ) )
1852                 {
1853                     globalOidRegistry.unregister( schemaObject.getOid() );
1854                 }
1855 
1856                 LOG.debug( "Unregistered {}:{}", schemaObject.getObjectType(), schemaObject.getOid() );
1857             }
1858             else
1859             {
1860                 // Not present !!
1861                 // What should we do ?
1862                 LOG.debug( "Unregistering of {}:{} failed, not found in Registries", schemaObject.getObjectType(),
1863                     schemaObject.getOid() );
1864             }
1865         }
1866     }
1867 
1868 
1869     /**
1870      * Checks if a specific SchemaObject is referenced by any other SchemaObject.
1871      *
1872      * @param schemaObject The SchemaObject we are looking for
1873      * @return true if there is at least one SchemaObjetc referencing the given one
1874      */
1875     public boolean isReferenced( SchemaObject schemaObject )
1876     {
1877         SchemaObjectWrapper wrapper = new SchemaObjectWrapper( schemaObject );
1878 
1879         Set<SchemaObjectWrapper> set = usedBy.get( wrapper );
1880 
1881         boolean referenced = ( set != null ) && !set.isEmpty();
1882 
1883         if ( LOG.isDebugEnabled() )
1884         {
1885             if ( referenced )
1886             {
1887                 LOG.debug( "The {}:{} is referenced", schemaObject.getObjectType(), schemaObject.getOid() );
1888             }
1889             else
1890             {
1891                 LOG.debug( "The {}:{} is not referenced", schemaObject.getObjectType(), schemaObject.getOid() );
1892             }
1893         }
1894 
1895         return referenced;
1896     }
1897 
1898 
1899     /**
1900      * Gets the Set of SchemaObjects referencing the given SchemaObject
1901      *
1902      * @param schemaObject The SchemaObject we are looking for
1903      * @return The Set of referencing SchemaObject, or null
1904      */
1905     public Set<SchemaObjectWrapper> getUsedBy( SchemaObject schemaObject )
1906     {
1907         SchemaObjectWrapper wrapper = new SchemaObjectWrapper( schemaObject );
1908 
1909         return usedBy.get( wrapper );
1910     }
1911 
1912 
1913     /**
1914      * Dump the UsedBy data structure as a String
1915      * 
1916      * @return The UsedBy data structure
1917      */
1918     public String dumpUsedBy()
1919     {
1920         StringBuilder sb = new StringBuilder();
1921 
1922         sb.append( "USED BY :\n" );
1923 
1924         try
1925         {
1926             for ( Map.Entry<SchemaObjectWrapper, Set<SchemaObjectWrapper>> entry : usedBy.entrySet() )
1927             {
1928                 SchemaObjectWrapper wrapper = entry.getKey();
1929 
1930                 sb.append( wrapper.get().getObjectType() ).append( '[' ).append( wrapper.get().getOid() )
1931                     .append( "] : {" );
1932 
1933                 boolean isFirst = true;
1934 
1935                 for ( SchemaObjectWrapper uses : entry.getValue() )
1936                 {
1937                     if ( isFirst )
1938                     {
1939                         isFirst = false;
1940                     }
1941                     else
1942                     {
1943                         sb.append( ", " );
1944                     }
1945 
1946                     sb.append( uses.get().getObjectType() ).append( '[' ).append( wrapper.get().getOid() ).append( "]" );
1947                 }
1948 
1949                 sb.append( "}\n" );
1950             }
1951         }
1952         catch ( Exception e )
1953         {
1954             e.printStackTrace();
1955         }
1956 
1957         return sb.toString();
1958     }
1959 
1960 
1961     /**
1962      * Dump the Using data structure as a String
1963      * 
1964      * @return The Using data structure
1965      */
1966     public String dumpUsing()
1967     {
1968         StringBuilder sb = new StringBuilder();
1969 
1970         sb.append( "USING :\n" );
1971 
1972         try
1973         {
1974             for ( Map.Entry<SchemaObjectWrapper, Set<SchemaObjectWrapper>> entry : using.entrySet() )
1975             {
1976                 SchemaObjectWrapper wrapper = entry.getKey();
1977 
1978                 sb.append( wrapper.get().getObjectType() ).append( '[' ).append( wrapper.get().getOid() )
1979                     .append( "] : {" );
1980 
1981                 boolean isFirst = true;
1982 
1983                 for ( SchemaObjectWrapper uses : entry.getValue() )
1984                 {
1985                     if ( isFirst )
1986                     {
1987                         isFirst = false;
1988                     }
1989                     else
1990                     {
1991                         sb.append( ", " );
1992                     }
1993 
1994                     sb.append( uses.get().getObjectType() ).append( '[' ).append( wrapper.get().getOid() ).append( "]" );
1995                 }
1996 
1997                 sb.append( "}\n" );
1998             }
1999         }
2000         catch ( Exception e )
2001         {
2002             e.printStackTrace();
2003         }
2004 
2005         return sb.toString();
2006     }
2007 
2008 
2009     /**
2010      * Gets the Set of SchemaObjects referenced by the given SchemaObject
2011      *
2012      * @param schemaObject The SchemaObject we are looking for
2013      * @return The Set of referenced SchemaObject, or null
2014      */
2015     public Set<SchemaObjectWrapper> getUsing( SchemaObject schemaObject )
2016     {
2017         SchemaObjectWrapper wrapper = new SchemaObjectWrapper( schemaObject );
2018 
2019         return using.get( wrapper );
2020     }
2021 
2022 
2023     /**
2024      * Add an association between a SchemaObject an the SchemaObject it refers
2025      *
2026      * @param reference The base SchemaObject
2027      * @param referee The SchemaObject pointing on the reference
2028      */
2029     private void addUsing( SchemaObject reference, SchemaObject referee )
2030     {
2031         if ( ( reference == null ) || ( referee == null ) )
2032         {
2033             return;
2034         }
2035 
2036         SchemaObjectWrapper wrapper = new SchemaObjectWrapper( reference );
2037 
2038         Set<SchemaObjectWrapper> uses = getUsing( reference );
2039 
2040         if ( uses == null )
2041         {
2042             uses = new HashSet<>();
2043         }
2044 
2045         uses.add( new SchemaObjectWrapper( referee ) );
2046 
2047         // Put back the set (this is a concurrentHashMap, it won't be replaced implicitly
2048         using.put( wrapper, uses );
2049     }
2050 
2051 
2052     /**
2053      * Add an association between a SchemaObject an the SchemaObject it refers
2054      *
2055      * @param base The base SchemaObject
2056      * @param referenced The referenced SchemaObject
2057      */
2058     public void addReference( SchemaObject base, SchemaObject referenced )
2059     {
2060         if ( LOG.isDebugEnabled() )
2061         {
2062             LOG.debug( dump( "add", base, referenced ) );
2063         }
2064 
2065         addUsing( base, referenced );
2066         addUsedBy( referenced, base );
2067 
2068         // do not change to debug mode, this makes the server logs hard to read and useless
2069         // and even prevents the server from starting up
2070         if ( LOG.isTraceEnabled() )
2071         {
2072             LOG.trace( dumpUsedBy() );
2073             LOG.trace( dumpUsing() );
2074         }
2075     }
2076 
2077 
2078     /**
2079      * Add an association between a SchemaObject an the SchemaObject that refers it
2080      *
2081      * @param reference The base SchemaObject
2082      * @param referee The SchemaObject pointing on the reference
2083      */
2084     private void addUsedBy( SchemaObject referee, SchemaObject reference )
2085     {
2086         if ( ( reference == null ) || ( referee == null ) )
2087         {
2088             return;
2089         }
2090 
2091         SchemaObjectWrapper wrapper = new SchemaObjectWrapper( referee );
2092 
2093         Set<SchemaObjectWrapper> uses = getUsedBy( referee );
2094 
2095         if ( uses == null )
2096         {
2097             uses = new HashSet<>();
2098         }
2099 
2100         uses.add( new SchemaObjectWrapper( reference ) );
2101 
2102         // Put back the set (this is a concurrentHashMap, it won't be replaced implicitly
2103         usedBy.put( wrapper, uses );
2104     }
2105 
2106 
2107     /**
2108      * Del an association between a SchemaObject an the SchemaObject it refers
2109      *
2110      * @param reference The base SchemaObject
2111      * @param referee The SchemaObject pointing on the reference
2112      */
2113     private void delUsing( SchemaObject reference, SchemaObject referee )
2114     {
2115         if ( ( reference == null ) || ( referee == null ) )
2116         {
2117             return;
2118         }
2119 
2120         Set<SchemaObjectWrapper> uses = getUsing( reference );
2121 
2122         if ( uses == null )
2123         {
2124             return;
2125         }
2126 
2127         uses.remove( new SchemaObjectWrapper( referee ) );
2128 
2129         SchemaObjectWrapper wrapper = new SchemaObjectWrapper( reference );
2130 
2131         if ( uses.isEmpty() )
2132         {
2133             using.remove( wrapper );
2134         }
2135         else
2136         {
2137             using.put( wrapper, uses );
2138         }
2139     }
2140 
2141 
2142     /**
2143      * Del an association between a SchemaObject an the SchemaObject that refers it
2144      *
2145      * @param reference The base SchemaObject
2146      * @param referee The SchemaObject pointing on the reference
2147      */
2148     private void delUsedBy( SchemaObject referee, SchemaObject reference )
2149     {
2150         if ( ( reference == null ) || ( referee == null ) )
2151         {
2152             return;
2153         }
2154 
2155         Set<SchemaObjectWrapper> uses = getUsedBy( referee );
2156 
2157         if ( uses == null )
2158         {
2159             return;
2160         }
2161 
2162         uses.remove( new SchemaObjectWrapper( reference ) );
2163 
2164         SchemaObjectWrapper wrapper = new SchemaObjectWrapper( referee );
2165 
2166         if ( uses.isEmpty() )
2167         {
2168             usedBy.remove( wrapper );
2169         }
2170         else
2171         {
2172             usedBy.put( wrapper, uses );
2173         }
2174     }
2175 
2176 
2177     /**
2178      * Delete an association between a SchemaObject an the SchemaObject it refers
2179      *
2180      * @param base The base SchemaObject
2181      * @param referenced The referenced SchemaObject
2182      */
2183     public void delReference( SchemaObject base, SchemaObject referenced )
2184     {
2185         if ( LOG.isDebugEnabled() )
2186         {
2187             LOG.debug( dump( "del", base, referenced ) );
2188         }
2189 
2190         delUsing( base, referenced );
2191         delUsedBy( referenced, base );
2192 
2193         if ( LOG.isDebugEnabled() )
2194         {
2195             LOG.debug( dumpUsedBy() );
2196             LOG.debug( dumpUsing() );
2197         }
2198     }
2199 
2200 
2201     /**
2202      * Dump the reference operation as a String
2203      */
2204     private String dump( String op, SchemaObject reference, SchemaObject referee )
2205     {
2206         return op + " : " + reference.getObjectType() + "[" + reference.getOid() + "]/[" + referee.getObjectType()
2207             + "[" + referee.getOid() + "]";
2208     }
2209 
2210 
2211     private boolean checkReferences( SchemaObject reference, SchemaObject referee, String message )
2212     {
2213         SchemaObjectWrapper referenceWrapper = new SchemaObjectWrapper( reference );
2214         SchemaObjectWrapper refereeWrapper = new SchemaObjectWrapper( referee );
2215 
2216         // Check the references : Syntax -> SyntaxChecker
2217         if ( !using.containsKey( referenceWrapper ) )
2218         {
2219             LOG.debug( "The Syntax {}:{} does not reference any " + message, reference.getObjectType(), reference
2220                 .getOid() );
2221 
2222             return false;
2223         }
2224 
2225         Set<SchemaObjectWrapper> usings = using.get( referenceWrapper );
2226 
2227         if ( !usings.contains( refereeWrapper ) )
2228         {
2229             LOG.debug( "The {}:{} does not reference any " + message, reference.getObjectType(), reference.getOid() );
2230 
2231             return false;
2232         }
2233 
2234         // Check the referees : SyntaxChecker -> Syntax
2235         if ( !usedBy.containsKey( refereeWrapper ) )
2236         {
2237             LOG.debug( "The {}:{} is not referenced by any " + message, referee.getObjectType(), referee.getOid() );
2238 
2239             return false;
2240         }
2241 
2242         Set<SchemaObjectWrapper> used = usedBy.get( refereeWrapper );
2243 
2244         if ( !used.contains( referenceWrapper ) )
2245         {
2246             LOG.debug( "The {}:{} is not referenced by any " + message, referee.getObjectType(), referee.getOid() );
2247 
2248             return false;
2249         }
2250 
2251         return true;
2252     }
2253 
2254 
2255     /**
2256      * Check the registries for invalid relations. This check stops at the first error.
2257      *
2258      * @return true if the Registries is consistent, false otherwise
2259      */
2260     public boolean check()
2261     {
2262         // Check the Syntaxes : check for a SyntaxChecker
2263         LOG.debug( "Checking Syntaxes" );
2264 
2265         for ( LdapSyntax syntax : ldapSyntaxRegistry )
2266         {
2267             // Check that each Syntax has a SyntaxChecker
2268             if ( syntax.getSyntaxChecker() == null )
2269             {
2270                 LOG.debug( "The Syntax {} has no SyntaxChecker", syntax );
2271 
2272                 return false;
2273             }
2274 
2275             if ( !syntaxCheckerRegistry.contains( syntax.getSyntaxChecker().getOid() ) )
2276             {
2277                 LOG.debug( "Cannot find the SyntaxChecker {} for the Syntax {}", syntax.getSyntaxChecker().getOid(),
2278                     syntax );
2279 
2280                 return false;
2281             }
2282 
2283             // Check the references : Syntax -> SyntaxChecker and SyntaxChecker -> Syntax
2284             if ( !checkReferences( syntax, syntax.getSyntaxChecker(), "SyntaxChecker" ) )
2285             {
2286                 return false;
2287             }
2288         }
2289 
2290         // Check the MatchingRules : check for a Normalizer, a Comparator and a Syntax
2291         LOG.debug( "Checking MatchingRules..." );
2292 
2293         for ( MatchingRule matchingRule : matchingRuleRegistry )
2294         {
2295             // Check that each MatchingRule has a Normalizer
2296             if ( matchingRule.getNormalizer() == null )
2297             {
2298                 LOG.debug( "The MatchingRule {} has no Normalizer", matchingRule );
2299 
2300                 return false;
2301             }
2302 
2303             // Check that each MatchingRule has a Normalizer
2304             if ( !normalizerRegistry.contains( matchingRule.getNormalizer().getOid() ) )
2305             {
2306                 LOG.debug( "Cannot find the Normalizer {} for the MatchingRule {}", matchingRule.getNormalizer()
2307                     .getOid(), matchingRule );
2308 
2309                 return false;
2310             }
2311 
2312             // Check that each MatchingRule has a Comparator
2313             if ( matchingRule.getLdapComparator() == null )
2314             {
2315                 LOG.debug( "The MatchingRule {} has no Comparator", matchingRule );
2316 
2317                 return false;
2318             }
2319 
2320             if ( !comparatorRegistry.contains( matchingRule.getLdapComparator().getOid() ) )
2321             {
2322                 LOG.debug( "Cannot find the Comparator {} for the MatchingRule {}", matchingRule.getLdapComparator()
2323                     .getOid(), matchingRule );
2324 
2325                 return false;
2326             }
2327 
2328             // Check that each MatchingRule has a Syntax
2329             if ( matchingRule.getSyntax() == null )
2330             {
2331                 LOG.debug( "The MatchingRule {} has no Syntax", matchingRule );
2332 
2333                 return false;
2334             }
2335 
2336             if ( !ldapSyntaxRegistry.contains( matchingRule.getSyntax().getOid() ) )
2337             {
2338                 LOG.debug( "Cannot find the Syntax {} for the MatchingRule {}", matchingRule.getSyntax().getOid(),
2339                     matchingRule );
2340 
2341                 return false;
2342             }
2343 
2344             // Check the references : MR -> S and S -> MR
2345             if ( !checkReferences( matchingRule, matchingRule.getSyntax(), "Syntax" ) )
2346             {
2347                 return false;
2348             }
2349 
2350             // Check the references : MR -> N
2351             if ( !checkReferences( matchingRule, matchingRule.getNormalizer(), "Normalizer" ) )
2352             {
2353                 return false;
2354             }
2355 
2356             // Check the references : MR -> C and C -> MR
2357             if ( !checkReferences( matchingRule, matchingRule.getLdapComparator(), "Comparator" ) )
2358             {
2359                 return false;
2360             }
2361         }
2362 
2363         // Check the ObjectClasses : check for MAY, MUST, SUPERIORS
2364         LOG.debug( "Checking ObjectClasses..." );
2365 
2366         for ( ObjectClass objectClass : objectClassRegistry )
2367         {
2368             // Check that each ObjectClass has all the MAY AttributeTypes
2369             if ( objectClass.getMayAttributeTypes() != null )
2370             {
2371                 for ( AttributeType may : objectClass.getMayAttributeTypes() )
2372                 {
2373                     if ( !attributeTypeRegistry.contains( may.getOid() ) )
2374                     {
2375                         LOG.debug( "Cannot find the AttributeType {} for the ObjectClass {} MAY", may, objectClass );
2376 
2377                         return false;
2378                     }
2379 
2380                     // Check the references : OC -> AT  and AT -> OC (MAY)
2381                     if ( !checkReferences( objectClass, may, "AttributeType" ) )
2382                     {
2383                         return false;
2384                     }
2385                 }
2386             }
2387 
2388             // Check that each ObjectClass has all the MUST AttributeTypes
2389             if ( objectClass.getMustAttributeTypes() != null )
2390             {
2391                 for ( AttributeType must : objectClass.getMustAttributeTypes() )
2392                 {
2393                     if ( !attributeTypeRegistry.contains( must.getOid() ) )
2394                     {
2395                         LOG.debug( "Cannot find the AttributeType {} for the ObjectClass {} MUST", must, objectClass );
2396 
2397                         return false;
2398                     }
2399 
2400                     // Check the references : OC -> AT  and AT -> OC (MUST)
2401                     if ( !checkReferences( objectClass, must, "AttributeType" ) )
2402                     {
2403                         return false;
2404                     }
2405                 }
2406             }
2407 
2408             // Check that each ObjectClass has all the SUPERIORS ObjectClasses
2409             if ( objectClass.getSuperiors() != null )
2410             {
2411                 for ( ObjectClass superior : objectClass.getSuperiors() )
2412                 {
2413                     if ( !objectClassRegistry.contains( objectClass.getOid() ) )
2414                     {
2415                         LOG.debug( "Cannot find the ObjectClass {} for the ObjectClass {} SUPERIORS", superior,
2416                             objectClass );
2417 
2418                         return false;
2419                     }
2420 
2421                     // Check the references : OC -> OC  and OC -> OC (SUPERIORS)
2422                     if ( !checkReferences( objectClass, superior, "ObjectClass" ) )
2423                     {
2424                         return false;
2425                     }
2426                 }
2427             }
2428         }
2429 
2430         // Check the AttributeTypes : check for MatchingRules, Syntaxes
2431         LOG.debug( "Checking AttributeTypes..." );
2432 
2433         for ( AttributeType attributeType : attributeTypeRegistry )
2434         {
2435             // Check that each AttributeType has a SYNTAX
2436             if ( attributeType.getSyntax() == null )
2437             {
2438                 LOG.debug( "The AttributeType {} has no Syntax", attributeType );
2439 
2440                 return false;
2441             }
2442 
2443             if ( !ldapSyntaxRegistry.contains( attributeType.getSyntax().getOid() ) )
2444             {
2445                 LOG.debug( "Cannot find the Syntax {} for the AttributeType {}", attributeType.getSyntax().getOid(),
2446                     attributeType );
2447 
2448                 return false;
2449             }
2450 
2451             // Check the references for AT -> S and S -> AT
2452             if ( !checkReferences( attributeType, attributeType.getSyntax(), "AttributeType" ) )
2453             {
2454                 return false;
2455             }
2456 
2457             // Check the EQUALITY MatchingRule
2458             if ( attributeType.getEquality() != null )
2459             {
2460                 if ( !matchingRuleRegistry.contains( attributeType.getEquality().getOid() ) )
2461                 {
2462                     LOG.debug( "Cannot find the MatchingRule {} for the AttributeType {}", attributeType.getEquality()
2463                         .getOid(), attributeType );
2464 
2465                     return false;
2466                 }
2467 
2468                 // Check the references for AT -> MR and MR -> AT
2469                 if ( !checkReferences( attributeType, attributeType.getEquality(), "AttributeType" ) )
2470                 {
2471                     return false;
2472                 }
2473             }
2474 
2475             // Check the ORDERING MatchingRule
2476             if ( attributeType.getOrdering() != null )
2477             {
2478                 if ( !matchingRuleRegistry.contains( attributeType.getOrdering().getOid() ) )
2479                 {
2480                     LOG.debug( "Cannot find the MatchingRule {} for the AttributeType {}", attributeType.getOrdering()
2481                         .getOid(), attributeType );
2482 
2483                     return false;
2484                 }
2485 
2486                 // Check the references for AT -> MR and MR -> AT
2487                 if ( !checkReferences( attributeType, attributeType.getOrdering(), "AttributeType" ) )
2488                 {
2489                     return false;
2490                 }
2491             }
2492 
2493             // Check the SUBSTR MatchingRule
2494             if ( attributeType.getSubstring() != null )
2495             {
2496                 if ( !matchingRuleRegistry.contains( attributeType.getSubstring().getOid() ) )
2497                 {
2498                     LOG.debug( "Cannot find the MatchingRule {} for the AttributeType {}", attributeType.getSubstring()
2499                         .getOid(), attributeType );
2500 
2501                     return false;
2502                 }
2503 
2504                 // Check the references for AT -> MR and MR -> AT
2505                 if ( !checkReferences( attributeType, attributeType.getSubstring(), "AttributeType" ) )
2506                 {
2507                     return false;
2508                 }
2509             }
2510 
2511             // Check the SUP
2512             if ( attributeType.getSuperior() != null )
2513             {
2514                 AttributeType superior = attributeType.getSuperior();
2515 
2516                 if ( !attributeTypeRegistry.contains( superior.getOid() ) )
2517                 {
2518                     LOG.debug( "Cannot find the AttributeType {} for the AttributeType {} SUPERIOR", superior,
2519                         attributeType );
2520 
2521                     return false;
2522                 }
2523 
2524                 // Check the references : AT -> AT  and AT -> AT (SUPERIOR)
2525                 if ( !checkReferences( attributeType, superior, "AttributeType" ) )
2526                 {
2527                     return false;
2528                 }
2529             }
2530         }
2531 
2532         return true;
2533     }
2534 
2535 
2536     /**
2537      * Clone the Registries. This is done in two steps :
2538      * - first clone the SchemaObjetc registries
2539      * - second restore the relation between them
2540      */
2541     // False positive
2542     @Override
2543     public Registries clone() throws CloneNotSupportedException
2544     {
2545         // First clone the structure
2546         Registries clone = ( Registries ) super.clone();
2547 
2548         // Now, clone the oidRegistry
2549         clone.globalOidRegistry = globalOidRegistry.copy();
2550 
2551         // We have to clone every SchemaObject registries now
2552         clone.attributeTypeRegistry = attributeTypeRegistry.copy();
2553         clone.comparatorRegistry = comparatorRegistry.copy();
2554         clone.ditContentRuleRegistry = ditContentRuleRegistry.copy();
2555         clone.ditStructureRuleRegistry = ditStructureRuleRegistry.copy();
2556         clone.ldapSyntaxRegistry = ldapSyntaxRegistry.copy();
2557         clone.matchingRuleRegistry = matchingRuleRegistry.copy();
2558         clone.matchingRuleUseRegistry = matchingRuleUseRegistry.copy();
2559         clone.nameFormRegistry = nameFormRegistry.copy();
2560         clone.normalizerRegistry = normalizerRegistry.copy();
2561         clone.objectClassRegistry = objectClassRegistry.copy();
2562         clone.syntaxCheckerRegistry = syntaxCheckerRegistry.copy();
2563 
2564         // Store all the SchemaObjects into the globalOid registry
2565         for ( AttributeType attributeType : clone.attributeTypeRegistry )
2566         {
2567             clone.globalOidRegistry.put( attributeType );
2568         }
2569 
2570         for ( DitContentRule ditContentRule : clone.ditContentRuleRegistry )
2571         {
2572             clone.globalOidRegistry.put( ditContentRule );
2573         }
2574 
2575         for ( DitStructureRule ditStructureRule : clone.ditStructureRuleRegistry )
2576         {
2577             clone.globalOidRegistry.put( ditStructureRule );
2578         }
2579 
2580         for ( MatchingRule matchingRule : clone.matchingRuleRegistry )
2581         {
2582             clone.globalOidRegistry.put( matchingRule );
2583         }
2584 
2585         for ( MatchingRuleUse matchingRuleUse : clone.matchingRuleUseRegistry )
2586         {
2587             clone.globalOidRegistry.put( matchingRuleUse );
2588         }
2589 
2590         for ( NameForm nameForm : clone.nameFormRegistry )
2591         {
2592             clone.globalOidRegistry.put( nameForm );
2593         }
2594 
2595         for ( ObjectClass objectClass : clone.objectClassRegistry )
2596         {
2597             clone.globalOidRegistry.put( objectClass );
2598         }
2599 
2600         for ( LdapSyntax syntax : clone.ldapSyntaxRegistry )
2601         {
2602             clone.globalOidRegistry.put( syntax );
2603         }
2604 
2605         // Clone the schema list
2606         clone.loadedSchemas = new HashMap<>();
2607 
2608         for ( Map.Entry<String, Set<SchemaObjectWrapper>> entry : schemaObjects.entrySet() )
2609         {
2610             // We don't clone the schemas
2611             clone.loadedSchemas.put( entry.getKey(), loadedSchemas.get( entry.getKey() ) );
2612         }
2613 
2614         // Clone the Using and usedBy structures
2615         // They will be empty
2616         clone.using = new HashMap<>();
2617         clone.usedBy = new HashMap<>();
2618 
2619         // Last, rebuild the using and usedBy references
2620         clone.buildReferences();
2621 
2622         // Now, check the registries. We don't care about errors
2623         clone.checkRefInteg();
2624 
2625         clone.schemaObjects = new HashMap<>();
2626 
2627         // Last, not least, clone the SchemaObjects Map, and reference all the copied
2628         // SchemaObjects
2629         for ( Map.Entry<String, Set<SchemaObjectWrapper>> entry : schemaObjects.entrySet() )
2630         {
2631             Set<SchemaObjectWrapper> objects = new HashSet<>();
2632 
2633             for ( SchemaObjectWrapper schemaObjectWrapper : entry.getValue() )
2634             {
2635                 SchemaObject original = schemaObjectWrapper.get();
2636 
2637                 try
2638                 {
2639                     if ( !( original instanceof LoadableSchemaObject ) )
2640                     {
2641                         SchemaObject copy = clone.globalOidRegistry.getSchemaObject( original.getOid() );
2642                         SchemaObjectWrapper newWrapper = new SchemaObjectWrapper( copy );
2643                         objects.add( newWrapper );
2644                     }
2645                     else
2646                     {
2647                         SchemaObjectWrapper newWrapper = new SchemaObjectWrapper( original );
2648                         objects.add( newWrapper );
2649                     }
2650                 }
2651                 catch ( LdapException ne )
2652                 {
2653                     // Nothing to do
2654                 }
2655             }
2656 
2657             clone.schemaObjects.put( entry.getKey(), objects );
2658         }
2659 
2660         return clone;
2661     }
2662 
2663 
2664     /**
2665      * Tells if the Registries is permissive or if it must be checked
2666      * against inconsistencies.
2667      *
2668      * @return True if SchemaObjects can be added even if they break the consistency
2669      */
2670     public boolean isRelaxed()
2671     {
2672         return isRelaxed;
2673     }
2674 
2675 
2676     /**
2677      * Tells if the Registries is strict.
2678      *
2679      * @return True if SchemaObjects cannot be added if they break the consistency
2680      */
2681     public boolean isStrict()
2682     {
2683         return !isRelaxed;
2684     }
2685 
2686 
2687     /**
2688      * Change the Registries to a relaxed mode, where invalid SchemaObjects
2689      * can be registered.
2690      */
2691     public void setRelaxed()
2692     {
2693         isRelaxed = RELAXED;
2694         globalOidRegistry.setRelaxed();
2695         attributeTypeRegistry.setRelaxed();
2696         comparatorRegistry.setRelaxed();
2697         ditContentRuleRegistry.setRelaxed();
2698         ditStructureRuleRegistry.setRelaxed();
2699         ldapSyntaxRegistry.setRelaxed();
2700         matchingRuleRegistry.setRelaxed();
2701         matchingRuleUseRegistry.setRelaxed();
2702         nameFormRegistry.setRelaxed();
2703         normalizerRegistry.setRelaxed();
2704         objectClassRegistry.setRelaxed();
2705         syntaxCheckerRegistry.setRelaxed();
2706     }
2707 
2708 
2709     /**
2710      * Change the Registries to a strict mode, where invalid SchemaObjects
2711      * cannot be registered.
2712      */
2713     public void setStrict()
2714     {
2715         isRelaxed = STRICT;
2716         globalOidRegistry.setStrict();
2717         attributeTypeRegistry.setStrict();
2718         comparatorRegistry.setStrict();
2719         ditContentRuleRegistry.setStrict();
2720         ditStructureRuleRegistry.setStrict();
2721         ldapSyntaxRegistry.setStrict();
2722         matchingRuleRegistry.setStrict();
2723         matchingRuleUseRegistry.setStrict();
2724         nameFormRegistry.setStrict();
2725         normalizerRegistry.setStrict();
2726         objectClassRegistry.setStrict();
2727         syntaxCheckerRegistry.setStrict();
2728     }
2729 
2730 
2731     /**
2732      * Tells if the Registries accept disabled elements.
2733      *
2734      * @return True if disabled SchemaObjects can be added
2735      */
2736     public boolean isDisabledAccepted()
2737     {
2738         return disabledAccepted;
2739     }
2740 
2741 
2742     /**
2743      * Check that we can remove a given SchemaObject without breaking some of its references.
2744      * We will return the list of refereing objects.
2745      *
2746      * @param schemaObject The SchemaObject to remove
2747      * @return The list of SchemaObjects referencing the SchemaObjetc we want to remove
2748      */
2749     public Set<SchemaObjectWrapper> getReferencing( SchemaObject schemaObject )
2750     {
2751         SchemaObjectWrapper schemaObjectWrapper = new SchemaObjectWrapper( schemaObject );
2752 
2753         return usedBy.get( schemaObjectWrapper );
2754     }
2755 
2756 
2757     /**
2758      * Change the Registries behavior regarding disabled SchemaObject element.
2759      *
2760      * @param disabledAccepted If <code>false</code>, then the Registries won't accept
2761      * disabled SchemaObject or enabled SchemaObject from disabled schema
2762      */
2763     public void setDisabledAccepted( boolean disabledAccepted )
2764     {
2765         this.disabledAccepted = disabledAccepted;
2766     }
2767 
2768 
2769     /**
2770      * Clear the registries from all its elements
2771      *
2772      * @throws LdapException If something goes wrong
2773      */
2774     public void clear() throws LdapException
2775     {
2776         // The AttributeTypeRegistry
2777         if ( attributeTypeRegistry != null )
2778         {
2779             attributeTypeRegistry.clear();
2780         }
2781 
2782         // The ComparatorRegistry
2783         if ( comparatorRegistry != null )
2784         {
2785             comparatorRegistry.clear();
2786         }
2787 
2788         // The DitContentRuleRegistry
2789         if ( ditContentRuleRegistry != null )
2790         {
2791             ditContentRuleRegistry.clear();
2792         }
2793 
2794         // The DitStructureRuleRegistry
2795         if ( ditStructureRuleRegistry != null )
2796         {
2797             ditStructureRuleRegistry.clear();
2798         }
2799 
2800         // The MatchingRuleRegistry
2801         if ( matchingRuleRegistry != null )
2802         {
2803             matchingRuleRegistry.clear();
2804         }
2805 
2806         // The MatchingRuleUseRegistry
2807         if ( matchingRuleUseRegistry != null )
2808         {
2809             matchingRuleUseRegistry.clear();
2810         }
2811 
2812         // The NameFormRegistry
2813         if ( nameFormRegistry != null )
2814         {
2815             nameFormRegistry.clear();
2816         }
2817 
2818         // The NormalizerRegistry
2819         if ( normalizerRegistry != null )
2820         {
2821             normalizerRegistry.clear();
2822         }
2823 
2824         // The ObjectClassRegistry
2825         if ( objectClassRegistry != null )
2826         {
2827             objectClassRegistry.clear();
2828         }
2829 
2830         // The SyntaxRegistry
2831         if ( ldapSyntaxRegistry != null )
2832         {
2833             ldapSyntaxRegistry.clear();
2834         }
2835 
2836         // The SyntaxCheckerRegistry
2837         if ( syntaxCheckerRegistry != null )
2838         {
2839             syntaxCheckerRegistry.clear();
2840         }
2841 
2842         // Clear the schemaObjects map
2843         for ( Map.Entry<String, Set<SchemaObjectWrapper>> entry : schemaObjects.entrySet() )
2844         {
2845             entry.getValue().clear();
2846         }
2847 
2848         schemaObjects.clear();
2849 
2850         // Clear the usedBy map
2851         for ( Map.Entry<SchemaObjectWrapper, Set<SchemaObjectWrapper>> entry : usedBy.entrySet() )
2852         {
2853             entry.getValue().clear();
2854         }
2855 
2856         usedBy.clear();
2857 
2858         // Clear the using map
2859         for ( Map.Entry<SchemaObjectWrapper, Set<SchemaObjectWrapper>> entry : using.entrySet() )
2860         {
2861             entry.getValue().clear();
2862         }
2863 
2864         using.clear();
2865 
2866         // Clear the global OID registry
2867         globalOidRegistry.clear();
2868 
2869         // Clear the loadedSchema Map
2870         loadedSchemas.clear();
2871     }
2872 
2873 
2874     /**
2875      * @see Object#toString()
2876      */
2877     @Override
2878     public String toString()
2879     {
2880         StringBuilder sb = new StringBuilder();
2881 
2882         sb.append( "Registries [" );
2883 
2884         if ( isRelaxed )
2885         {
2886             sb.append( "RELAXED," );
2887         }
2888         else
2889         {
2890             sb.append( "STRICT," );
2891         }
2892 
2893         if ( disabledAccepted )
2894         {
2895             sb.append( " Disabled accepted] :\n" );
2896         }
2897         else
2898         {
2899             sb.append( " Disabled forbidden] :\n" );
2900         }
2901 
2902         sb.append( "loaded schemas [" );
2903         boolean isFirst = true;
2904 
2905         for ( String schema : loadedSchemas.keySet() )
2906         {
2907             if ( isFirst )
2908             {
2909                 isFirst = false;
2910             }
2911             else
2912             {
2913                 sb.append( ", " );
2914             }
2915 
2916             sb.append( schema );
2917         }
2918 
2919         sb.append( "]\n" );
2920 
2921         sb.append( "AttributeTypes : " ).append( attributeTypeRegistry.size() ).append( "\n" );
2922         sb.append( "Comparators : " ).append( comparatorRegistry.size() ).append( "\n" );
2923         sb.append( "DitContentRules : " ).append( ditContentRuleRegistry.size() ).append( "\n" );
2924         sb.append( "DitStructureRules : " ).append( ditStructureRuleRegistry.size() ).append( "\n" );
2925         sb.append( "MatchingRules : " ).append( matchingRuleRegistry.size() ).append( "\n" );
2926         sb.append( "MatchingRuleUses : " ).append( matchingRuleUseRegistry.size() ).append( "\n" );
2927         sb.append( "NameForms : " ).append( nameFormRegistry.size() ).append( "\n" );
2928         sb.append( "Normalizers : " ).append( normalizerRegistry.size() ).append( "\n" );
2929         sb.append( "ObjectClasses : " ).append( objectClassRegistry.size() ).append( "\n" );
2930         sb.append( "Syntaxes : " ).append( ldapSyntaxRegistry.size() ).append( "\n" );
2931         sb.append( "SyntaxCheckers : " ).append( syntaxCheckerRegistry.size() ).append( "\n" );
2932 
2933         sb.append( "GlobalOidRegistry : " ).append( globalOidRegistry.size() ).append( '\n' );
2934 
2935         return sb.toString();
2936     }
2937 }