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