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