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 javax.naming.NamingException;
032    
033    import org.apache.directory.shared.i18n.I18n;
034    import org.apache.directory.shared.ldap.constants.MetaSchemaConstants;
035    import org.apache.directory.shared.ldap.exception.LdapOperationNotSupportedException;
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: 919765 $
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 ( NamingException 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 ( NamingException 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 ( NamingException 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 ( NamingException 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 ( NamingException 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 ( NamingException 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 ( NamingException 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 ( NamingException 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 ( NamingException 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 ( NamingException 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 ( NamingException ne )
723            {
724                // Not allowed.
725                String msg = I18n.err( I18n.ERR_04292, schemaObject.getName(), ne.getLocalizedMessage() );
726    
727                Throwable error = new LdapSchemaViolationException( msg, ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
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 ( NamingException ne )
744            {
745                // Not allowed.
746                String msg = I18n.err( I18n.ERR_04293, schemaObject.getName(), ne.getLocalizedMessage() );
747    
748                Throwable error = new LdapSchemaViolationException( msg, ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
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 ( NamingException 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 ( NamingException 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 ( NamingException 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 ( NamingException 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 ( NamingException 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 LdapSchemaViolationException( I18n.err( I18n.ERR_04294, matchingRule.getOid() ), 
1005                            ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
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( I18n.err( I18n.ERR_04295, matchingRule.getOid() ),
1016                            ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
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( I18n.err( I18n.ERR_04296, matchingRule.getOid() ),
1027                            ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
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 ( NamingException 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( I18n.err( I18n.ERR_04297, attributeType.getOid() ),
1072                            ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
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 ( NamingException 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( I18n.err( I18n.ERR_04298, attributeType.getOid() ),
1104                        ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
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 ( NamingException 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 ( NamingException 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 ( NamingException 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 LdapSchemaViolationException( I18n.err( I18n.ERR_04299, objectClass.getOid() ),
1239                            ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
1240                        errors.add( error );
1241                        return;
1242                    }
1243                }
1244            }
1245        }
1246    
1247    
1248        private void resolveRecursive( ObjectClass objectClass, Set<String> processed, List<Throwable> errors )
1249        {
1250            // Process the Superiors, if any
1251            List<String> superiorOids = objectClass.getSuperiorOids();
1252            ObjectClass superior = null;
1253    
1254            for ( String superiorOid : superiorOids )
1255            {
1256                // Check if the Superior is present in the registries
1257                try
1258                {
1259                    superior = objectClassRegistry.lookup( superiorOid );
1260                }
1261                catch ( NamingException ne )
1262                {
1263                    // This OC's superior has not been loaded into the Registries.
1264                    if ( !processed.contains( superiorOid ) )
1265                    {
1266                        errors.add( ne );
1267                    }
1268                }
1269    
1270                // We now have to process the superior, if it hasn't been 
1271                // processed yet.
1272                if ( superior != null )
1273                {
1274                    if ( !processed.contains( superior.getOid() ) )
1275                    {
1276                        resolveRecursive( superior, processed, errors );
1277                        processed.add( objectClass.getOid() );
1278                    }
1279                    else
1280                    {
1281                        // Not allowed : we have a cyle
1282                        Throwable error = new LdapSchemaViolationException( I18n.err( I18n.ERR_04300, objectClass.getOid(), superior) ,
1283                            ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX );
1284                        errors.add( error );
1285                        return;
1286                    }
1287                }
1288            }
1289    
1290            // Process the MAY attributeTypes.  
1291            for ( String mayOid : objectClass.getMayAttributeTypeOids() )
1292            {
1293                // Check if the MAY AttributeType is present in the registries
1294                try
1295                {
1296                    attributeTypeRegistry.lookup( mayOid );
1297                }
1298                catch ( NamingException ne )
1299                {
1300                    // This AT has not been loaded into the Registries.
1301                    errors.add( ne );
1302                }
1303            }
1304    
1305            // Process the MUST attributeTypes.  
1306            for ( String mustOid : objectClass.getMustAttributeTypeOids() )
1307            {
1308                // Check if the MUST AttributeType is present in the registries
1309                try
1310                {
1311                    attributeTypeRegistry.lookup( mustOid );
1312                }
1313                catch ( NamingException ne )
1314                {
1315                    // This AT has not been loaded into the Registries.
1316                    errors.add( ne );
1317                }
1318            }
1319    
1320            // All is done for this ObjectClass, let's apply the registries
1321            try
1322            {
1323                objectClass.addToRegistries( errors, this );
1324            }
1325            catch ( NamingException ne )
1326            {
1327                // Do nothing. We may have a broken OC, 
1328                // but at this point, it doesn't matter.
1329            }
1330        }
1331    
1332    
1333        /**
1334         * Applies the added SchemaObject to the given register
1335         */
1336        public List<Throwable> add( List<Throwable> errors, SchemaObject schemaObject ) throws NamingException
1337        {
1338            // Relax the registries
1339            boolean wasRelaxed = isRelaxed;
1340            setRelaxed();
1341    
1342            // Register the SchemaObject in the registries
1343            register( errors, schemaObject );
1344    
1345            // Associate the SchemaObject with its schema
1346            associateWithSchema( errors, schemaObject );
1347    
1348            // Build the SchemaObject references
1349            buildReference( errors, schemaObject );
1350    
1351            if ( errors.isEmpty() )
1352            {
1353                // Check the registries now
1354                List<Throwable> checkErrors = checkRefInteg();
1355    
1356                errors.addAll( checkErrors );
1357            }
1358    
1359            // Get back to Strict mode
1360            if ( !wasRelaxed )
1361            {
1362                setStrict();
1363            }
1364    
1365            // return the errors
1366            return errors;
1367        }
1368    
1369    
1370        /**
1371         * Remove the given SchemaObject from the registries
1372         */
1373        public List<Throwable> delete( List<Throwable> errors, SchemaObject schemaObject ) throws NamingException
1374        {
1375            // Relax the registries
1376            boolean wasRelaxed = isRelaxed;
1377            setRelaxed();
1378    
1379            // Remove the SchemaObject from the registries
1380            SchemaObject removed = unregister( errors, schemaObject );
1381    
1382            // Remove the SchemaObject from its schema
1383            dissociateFromSchema( errors, removed );
1384    
1385            // Unlink the SchemaObject references
1386            removeReference( errors, removed );
1387    
1388            if ( errors.isEmpty() )
1389            {
1390                // Check the registries now
1391                List<Throwable> checkErrors = checkRefInteg();
1392    
1393                errors.addAll( checkErrors );
1394            }
1395    
1396            // Restore the previous registries state
1397            if ( !wasRelaxed )
1398            {
1399                setStrict();
1400            }
1401    
1402            // return the errors
1403            return errors;
1404        }
1405    
1406    
1407        /**
1408         * Merely adds the schema to the set of loaded schemas.  Does not
1409         * actually do any work to add schema objects to registries.
1410         * 
1411         * {@inheritDoc}
1412         */
1413        public void schemaLoaded( Schema schema )
1414        {
1415            this.loadedSchemas.put( StringTools.toLowerCase( schema.getSchemaName() ), schema );
1416        }
1417    
1418    
1419        /**
1420         * Merely removes the schema from the set of loaded schemas.  Does not
1421         * actually do any work to remove schema objects from registries.
1422         * 
1423         * {@inheritDoc}
1424         */
1425        public void schemaUnloaded( Schema schema )
1426        {
1427            this.loadedSchemas.remove( StringTools.toLowerCase( schema.getSchemaName() ) );
1428        }
1429    
1430    
1431        /**
1432         * Gets an unmodifiable Map of schema names to loaded Schema objects. 
1433         * 
1434         * @return the map of loaded Schema objects
1435         */
1436        public Map<String, Schema> getLoadedSchemas()
1437        {
1438            return Collections.unmodifiableMap( loadedSchemas );
1439        }
1440    
1441    
1442        /**
1443         * @return Gets a reference to the Map associating a schemaName to
1444         * its contained SchemaObjects
1445         */
1446        public Map<String, Set<SchemaObjectWrapper>> getObjectBySchemaName()
1447        {
1448            return schemaObjects;
1449        }
1450    
1451    
1452        /**
1453         * Retrieve the schema name for a specific SchemaObject, or return "other" if none is found.
1454         */
1455        private String getSchemaName( SchemaObject schemaObject )
1456        {
1457            String schemaName = StringTools.toLowerCase( schemaObject.getSchemaName() );
1458    
1459            if ( loadedSchemas.containsKey( schemaName ) )
1460            {
1461                return schemaName;
1462            }
1463            else
1464            {
1465                return MetaSchemaConstants.SCHEMA_OTHER;
1466            }
1467        }
1468    
1469    
1470        /**
1471         * Tells if the given SchemaObject is present in one schema. The schema
1472         * may be disabled.
1473         *
1474         * @param schemaObject The schemaObject we are looking for
1475         * @return true if the schemaObject is present in a schema
1476         */
1477        public boolean contains( SchemaObject schemaObject )
1478        {
1479            String schemaName = schemaObject.getSchemaName();
1480    
1481            Set<SchemaObjectWrapper> setSchemaObjects = schemaObjects.get( schemaName );
1482    
1483            if ( ( setSchemaObjects == null ) || setSchemaObjects.isEmpty() )
1484            {
1485                return false;
1486            }
1487    
1488            SchemaObjectWrapper wrapper = new SchemaObjectWrapper( schemaObject );
1489    
1490            return setSchemaObjects.contains( wrapper );
1491        }
1492    
1493    
1494        /**
1495         * Create a new schema association with its content
1496         *
1497         * @param schemaName The schema name
1498         */
1499        public Set<SchemaObjectWrapper> addSchema( String schemaName )
1500        {
1501            Set<SchemaObjectWrapper> content = new HashSet<SchemaObjectWrapper>();
1502            schemaObjects.put( schemaName, content );
1503    
1504            return content;
1505        }
1506    
1507    
1508        /**
1509         * Register the given SchemaObject into the associated Registry
1510         */
1511        private void register( List<Throwable> errors, SchemaObject schemaObject ) throws NamingException
1512        {
1513            LOG.debug( "Registering {}:{}", schemaObject.getObjectType(), schemaObject.getOid() );
1514    
1515            // Check that the SchemaObject is not already registered
1516            if ( schemaObject instanceof LoadableSchemaObject )
1517            {
1518                // TODO : Check for existing Loadable SchemaObject
1519            }
1520            else
1521            {
1522                if ( globalOidRegistry.contains( schemaObject.getOid() ) )
1523                {
1524                    // TODO : throw an exception here
1525                    String msg = I18n.err( I18n.ERR_04301, schemaObject.getObjectType(), schemaObject.getOid() );
1526                    LOG.error( msg );
1527                    Throwable error = new LdapOperationNotSupportedException( msg, ResultCodeEnum.UNWILLING_TO_PERFORM );
1528                    errors.add( error );
1529                    return;
1530                }
1531            }
1532    
1533            try
1534            {
1535                // First call the specific registry's register method
1536                switch ( schemaObject.getObjectType() )
1537                {
1538                    case ATTRIBUTE_TYPE:
1539                        attributeTypeRegistry.register( ( AttributeType ) schemaObject );
1540                        break;
1541    
1542                    case COMPARATOR:
1543                        comparatorRegistry.register( ( LdapComparator<?> ) schemaObject );
1544                        break;
1545    
1546                    case DIT_CONTENT_RULE:
1547                        ditContentRuleRegistry.register( ( DITContentRule ) schemaObject );
1548                        break;
1549    
1550                    case DIT_STRUCTURE_RULE:
1551                        ditStructureRuleRegistry.register( ( DITStructureRule ) schemaObject );
1552                        break;
1553    
1554                    case LDAP_SYNTAX:
1555                        ldapSyntaxRegistry.register( ( LdapSyntax ) schemaObject );
1556                        break;
1557    
1558                    case MATCHING_RULE:
1559                        matchingRuleRegistry.register( ( MatchingRule ) schemaObject );
1560                        break;
1561    
1562                    case MATCHING_RULE_USE:
1563                        matchingRuleUseRegistry.register( ( MatchingRuleUse ) schemaObject );
1564                        break;
1565    
1566                    case NAME_FORM:
1567                        nameFormRegistry.register( ( NameForm ) schemaObject );
1568                        break;
1569    
1570                    case NORMALIZER:
1571                        normalizerRegistry.register( ( Normalizer ) schemaObject );
1572                        break;
1573    
1574                    case OBJECT_CLASS:
1575                        objectClassRegistry.register( ( ObjectClass ) schemaObject );
1576                        break;
1577    
1578                    case SYNTAX_CHECKER:
1579                        syntaxCheckerRegistry.register( ( SyntaxChecker ) schemaObject );
1580                        break;
1581                }
1582            }
1583            catch ( Exception e )
1584            {
1585                errors.add( e );
1586            }
1587        }
1588    
1589    
1590        /**
1591         * Store the given SchemaObject in the Map associating SchemaObjetcs to their
1592         * related Schema.
1593         *
1594         * @param schemaObject The schemaObject to register
1595         * @throws NamingException If there is a problem
1596         */
1597        public void associateWithSchema( List<Throwable> errors, SchemaObject schemaObject )
1598        {
1599            LOG.debug( "Registering {}:{}", schemaObject.getObjectType(), schemaObject.getOid() );
1600    
1601            // Check that the SchemaObject is not already registered
1602            if ( !( schemaObject instanceof LoadableSchemaObject ) && globalOidRegistry.contains( schemaObject.getOid() ) )
1603            {
1604                // TODO : throw an exception here
1605                String msg = I18n.err( I18n.ERR_04301, schemaObject.getObjectType(), schemaObject.getOid() );
1606                LOG.error( msg );
1607                Throwable error = new LdapOperationNotSupportedException( msg, ResultCodeEnum.UNWILLING_TO_PERFORM );
1608                errors.add( error );
1609                return;
1610            }
1611    
1612            // Get a normalized form of schema name
1613            String schemaName = getSchemaName( schemaObject );
1614    
1615            // And register the schemaObject within its schema
1616            Set<SchemaObjectWrapper> content = schemaObjects.get( schemaName );
1617    
1618            if ( content == null )
1619            {
1620                content = new HashSet<SchemaObjectWrapper>();
1621                schemaObjects.put( StringTools.toLowerCase( schemaName ), content );
1622            }
1623    
1624            SchemaObjectWrapper schemaObjectWrapper = new SchemaObjectWrapper( schemaObject );
1625    
1626            if ( content.contains( schemaObjectWrapper ) )
1627            {
1628                // Already present !
1629                // What should we do ?
1630                LOG.info( "Registering of {}:{} failed, is already present in the Registries",
1631                    schemaObject.getObjectType(), schemaObject.getOid() );
1632            }
1633            else
1634            {
1635                // Create the association
1636                content.add( schemaObjectWrapper );
1637    
1638                // Update the global OidRegistry if the SchemaObject is not
1639                // an instance of LoadableSchemaObject
1640                if ( !( schemaObject instanceof LoadableSchemaObject ) )
1641                {
1642                    try
1643                    {
1644                        globalOidRegistry.register( schemaObject );
1645                    }
1646                    catch ( NamingException ne )
1647                    {
1648                        errors.add( ne );
1649                        return;
1650                    }
1651                }
1652    
1653                LOG.debug( "registered {} for OID {}", schemaObject.getName(), schemaObject.getOid() );
1654            }
1655        }
1656    
1657    
1658        /**
1659         * Store the given SchemaObject in the Map associating SchemaObjetcs to their
1660         * related Schema.
1661         *
1662         * @param schemaObject The schemaObject to register
1663         * @throws NamingException If there is a problem
1664         */
1665    
1666        public void dissociateFromSchema( List<Throwable> errors, SchemaObject schemaObject ) throws NamingException
1667        {
1668            LOG.debug( "Unregistering {}:{}", schemaObject.getObjectType(), schemaObject.getOid() );
1669    
1670            // Check that the SchemaObject is already registered
1671            if ( !( schemaObject instanceof LoadableSchemaObject ) && !globalOidRegistry.contains( schemaObject.getOid() ) )
1672            {
1673                // TODO : throw an exception here
1674                String msg = I18n.err( I18n.ERR_04302, schemaObject.getObjectType(), schemaObject.getOid() );
1675                LOG.error( msg );
1676                Throwable error = new LdapOperationNotSupportedException( msg, ResultCodeEnum.UNWILLING_TO_PERFORM );
1677                errors.add( error );
1678                return;
1679            }
1680    
1681            // Get a normalized form of schema name
1682            String schemaName = getSchemaName( schemaObject );
1683            String oid = schemaObject.getOid();
1684    
1685            // And unregister the schemaObject from its schema
1686            Set<SchemaObjectWrapper> content = schemaObjects.get( schemaName );
1687    
1688            SchemaObjectWrapper schemaObjectWrapper = new SchemaObjectWrapper( schemaObject );
1689    
1690            if ( !content.contains( schemaObjectWrapper ) )
1691            {
1692                // Not present !
1693                // What should we do ?
1694                LOG.info( "Unregistering of {}:{} failed, is not present in the Registries", schemaObject.getObjectType(),
1695                    schemaObject.getOid() );
1696            }
1697            else
1698            {
1699                // Remove the association
1700                content.remove( schemaObjectWrapper );
1701    
1702                // Update the global OidRegistry if the SchemaObject is not
1703                // an instance of LoadableSchemaObject
1704                if ( !( schemaObject instanceof LoadableSchemaObject ) )
1705                {
1706                    try
1707                    {
1708                        globalOidRegistry.unregister( oid );
1709                    }
1710                    catch ( NamingException ne )
1711                    {
1712                        errors.add( ne );
1713                        return;
1714                    }
1715                }
1716    
1717                LOG.debug( "Unregistered {} for OID {}", schemaObject.getName(), schemaObject.getOid() );
1718            }
1719        }
1720    
1721    
1722        /**
1723         * Unregister a SchemaObject from the registries
1724         *
1725         * @param schemaObject The SchemaObject we want to deregister
1726         * @throws NamingException If the removal failed
1727         */
1728        private SchemaObject unregister( List<Throwable> errors, SchemaObject schemaObject ) throws NamingException
1729        {
1730            LOG.debug( "Unregistering {}:{}", schemaObject.getObjectType(), schemaObject.getOid() );
1731    
1732            // Check that the SchemaObject is present in the registries
1733            if ( schemaObject instanceof LoadableSchemaObject )
1734            {
1735                // TODO : check for an existing Loadable SchemaObject
1736            }
1737            else
1738            {
1739                if ( !globalOidRegistry.contains( schemaObject.getOid() ) )
1740                {
1741                    // TODO : throw an exception here
1742                    String msg = I18n.err( I18n.ERR_04302, schemaObject.getObjectType(), schemaObject.getOid() );
1743                    LOG.error( msg );
1744                    throw new LdapOperationNotSupportedException( msg, ResultCodeEnum.UNWILLING_TO_PERFORM );
1745                }
1746            }
1747    
1748            SchemaObject unregistered = null;
1749    
1750            // First call the specific registry's register method
1751            switch ( schemaObject.getObjectType() )
1752            {
1753                case ATTRIBUTE_TYPE:
1754                    unregistered = attributeTypeRegistry.unregister( ( AttributeType ) schemaObject );
1755                    break;
1756    
1757                case COMPARATOR:
1758                    unregistered = comparatorRegistry.unregister( ( LdapComparator<?> ) schemaObject );
1759                    break;
1760    
1761                case DIT_CONTENT_RULE:
1762                    unregistered = ditContentRuleRegistry.unregister( ( DITContentRule ) schemaObject );
1763                    break;
1764    
1765                case DIT_STRUCTURE_RULE:
1766                    unregistered = ditStructureRuleRegistry.unregister( ( DITStructureRule ) schemaObject );
1767                    break;
1768    
1769                case LDAP_SYNTAX:
1770                    unregistered = ldapSyntaxRegistry.unregister( ( LdapSyntax ) schemaObject );
1771                    break;
1772    
1773                case MATCHING_RULE:
1774                    unregistered = matchingRuleRegistry.unregister( ( MatchingRule ) schemaObject );
1775                    break;
1776    
1777                case MATCHING_RULE_USE:
1778                    unregistered = matchingRuleUseRegistry.unregister( ( MatchingRuleUse ) schemaObject );
1779                    break;
1780    
1781                case NAME_FORM:
1782                    unregistered = nameFormRegistry.unregister( ( NameForm ) schemaObject );
1783                    break;
1784    
1785                case NORMALIZER:
1786                    unregistered = normalizerRegistry.unregister( ( Normalizer ) schemaObject );
1787                    break;
1788    
1789                case OBJECT_CLASS:
1790                    unregistered = objectClassRegistry.unregister( ( ObjectClass ) schemaObject );
1791                    break;
1792    
1793                case SYNTAX_CHECKER:
1794                    unregistered = syntaxCheckerRegistry.unregister( ( SyntaxChecker ) schemaObject );
1795                    break;
1796            }
1797    
1798            return unregistered;
1799        }
1800    
1801    
1802        /**
1803         * Remove the given SchemaObject from the Map associating SchemaObjetcs to their
1804         * related Schema.
1805         *
1806         * @param schemaObject The schemaObject to remove
1807         * @throws NamingException If there is a problem
1808         */
1809        public void dissociateFromSchema( SchemaObject schemaObject ) throws NamingException
1810        {
1811            // And unregister the schemaObject within its schema
1812            Set<SchemaObjectWrapper> content = schemaObjects.get( StringTools.toLowerCase( schemaObject.getSchemaName() ) );
1813    
1814            if ( content != null )
1815            {
1816                SchemaObjectWrapper schemaObjectWrapper = new SchemaObjectWrapper( schemaObject );
1817        
1818                if ( content.contains( schemaObjectWrapper ) )
1819                {
1820                    // remove the schemaObject
1821                    content.remove( schemaObjectWrapper );
1822        
1823                    // Update the global OidRegistry if the SchemaObject is not
1824                    // an instance of LoadableSchemaObject
1825                    if ( !( schemaObject instanceof LoadableSchemaObject ) )
1826                    {
1827                        globalOidRegistry.unregister( schemaObject.getOid() );
1828                    }
1829        
1830                    LOG.debug( "Unregistered {}:{}", schemaObject.getObjectType(), schemaObject.getOid() );
1831                }
1832                else
1833                {
1834                    // Not present !!
1835                    // What should we do ?
1836                    LOG.debug( "Unregistering of {}:{} failed, not found in Registries", schemaObject.getObjectType(),
1837                        schemaObject.getOid() );
1838                }
1839            }
1840        }
1841    
1842    
1843        /**
1844         * Checks if a specific SchemaObject is referenced by any other SchemaObject.
1845         *
1846         * @param schemaObject The SchemaObject we are looking for
1847         * @return true if there is at least one SchemaObjetc referencing the given one
1848         */
1849        public boolean isReferenced( SchemaObject schemaObject )
1850        {
1851            SchemaObjectWrapper wrapper = new SchemaObjectWrapper( schemaObject );
1852    
1853            Set<SchemaObjectWrapper> set = usedBy.get( wrapper );
1854    
1855            boolean referenced = ( set != null ) && ( set.size() != 0 );
1856    
1857            if ( LOG.isDebugEnabled() )
1858            {
1859                if ( referenced )
1860                {
1861                    LOG.debug( "The {}:{} is referenced", schemaObject.getObjectType(), schemaObject.getOid() );
1862                }
1863                else
1864                {
1865                    LOG.debug( "The {}:{} is not referenced", schemaObject.getObjectType(), schemaObject.getOid() );
1866                }
1867            }
1868    
1869            return referenced;
1870        }
1871    
1872    
1873        /**
1874         * Gets the Set of SchemaObjects referencing the given SchemaObject
1875         *
1876         * @param schemaObject The SchemaObject we are looking for
1877         * @return The Set of referencing SchemaObject, or null 
1878         */
1879        public Set<SchemaObjectWrapper> getUsedBy( SchemaObject schemaObject )
1880        {
1881            SchemaObjectWrapper wrapper = new SchemaObjectWrapper( schemaObject );
1882    
1883            return usedBy.get( wrapper );
1884        }
1885    
1886    
1887        /**
1888         * Dump the UsedBy data structure as a String
1889         */
1890        public String dumpUsedBy()
1891        {
1892            StringBuilder sb = new StringBuilder();
1893    
1894            sb.append( "USED BY :\n" );
1895    
1896            for ( SchemaObjectWrapper wrapper : usedBy.keySet() )
1897            {
1898                sb.append( wrapper.get().getObjectType() ).append( '[' ).append( wrapper.get().getOid() ).append( "] : {" );
1899    
1900                boolean isFirst = true;
1901    
1902                for ( SchemaObjectWrapper uses : usedBy.get( wrapper ) )
1903                {
1904                    if ( isFirst )
1905                    {
1906                        isFirst = false;
1907                    }
1908                    else
1909                    {
1910                        sb.append( ", " );
1911                    }
1912    
1913                    sb.append( uses.get().getObjectType() ).append( '[' ).append( wrapper.get().getOid() ).append( "]" );
1914                }
1915    
1916                sb.append( "}\n" );
1917            }
1918    
1919            return sb.toString();
1920        }
1921    
1922    
1923        /**
1924         * Dump the Using data structure as a String
1925         */
1926        public String dumpUsing()
1927        {
1928            StringBuilder sb = new StringBuilder();
1929    
1930            sb.append( "USING :\n" );
1931    
1932            for ( SchemaObjectWrapper wrapper : using.keySet() )
1933            {
1934                sb.append( wrapper.get().getObjectType() ).append( '[' ).append( wrapper.get().getOid() ).append( "] : {" );
1935    
1936                boolean isFirst = true;
1937    
1938                for ( SchemaObjectWrapper uses : using.get( wrapper ) )
1939                {
1940                    if ( isFirst )
1941                    {
1942                        isFirst = false;
1943                    }
1944                    else
1945                    {
1946                        sb.append( ", " );
1947                    }
1948    
1949                    sb.append( uses.get().getObjectType() ).append( '[' ).append( wrapper.get().getOid() ).append( "]" );
1950                }
1951    
1952                sb.append( "}\n" );
1953            }
1954    
1955            return sb.toString();
1956        }
1957    
1958    
1959        /**
1960         * Gets the Set of SchemaObjects referenced by the given SchemaObject
1961         *
1962         * @param schemaObject The SchemaObject we are looking for
1963         * @return The Set of referenced SchemaObject, or null 
1964         */
1965        public Set<SchemaObjectWrapper> getUsing( SchemaObject schemaObject )
1966        {
1967            SchemaObjectWrapper wrapper = new SchemaObjectWrapper( schemaObject );
1968    
1969            return using.get( wrapper );
1970        }
1971    
1972    
1973        /**
1974         * Add an association between a SchemaObject an the SchemaObject it refers
1975         *
1976         * @param reference The base SchemaObject
1977         * @param referee The SchemaObject pointing on the reference
1978         */
1979        private void addUsing( SchemaObject reference, SchemaObject referee )
1980        {
1981            if ( ( reference == null ) || ( referee == null ) )
1982            {
1983                return;
1984            }
1985    
1986            SchemaObjectWrapper wrapper = new SchemaObjectWrapper( reference );
1987    
1988            Set<SchemaObjectWrapper> uses = getUsing( reference );
1989    
1990            if ( uses == null )
1991            {
1992                uses = new HashSet<SchemaObjectWrapper>();
1993            }
1994    
1995            uses.add( new SchemaObjectWrapper( referee ) );
1996    
1997            // Put back the set (this is a concurrentHashMap, it won't be replaced implicitly
1998            using.put( wrapper, uses );
1999        }
2000    
2001    
2002        /**
2003         * Add an association between a SchemaObject an the SchemaObject it refers
2004         *
2005         * @param base The base SchemaObject
2006         * @param referenced The referenced SchemaObject
2007         */
2008        public void addReference( SchemaObject base, SchemaObject referenced )
2009        {
2010            if ( LOG.isDebugEnabled() )
2011            {
2012                LOG.debug( dump( "add", base, referenced ) );
2013            }
2014    
2015            addUsing( base, referenced );
2016            addUsedBy( referenced, base );
2017    
2018            if ( LOG.isDebugEnabled() )
2019            {
2020                LOG.debug( dumpUsedBy() );
2021                LOG.debug( dumpUsing() );
2022            }
2023        }
2024    
2025    
2026        /**
2027         * Add an association between a SchemaObject an the SchemaObject that refers it
2028         *
2029         * @param reference The base SchemaObject
2030         * @param referee The SchemaObject pointing on the reference
2031         */
2032        private void addUsedBy( SchemaObject referee, SchemaObject reference )
2033        {
2034            if ( ( reference == null ) || ( referee == null ) )
2035            {
2036                return;
2037            }
2038    
2039            SchemaObjectWrapper wrapper = new SchemaObjectWrapper( referee );
2040    
2041            Set<SchemaObjectWrapper> uses = getUsedBy( referee );
2042    
2043            if ( uses == null )
2044            {
2045                uses = new HashSet<SchemaObjectWrapper>();
2046            }
2047    
2048            uses.add( new SchemaObjectWrapper( reference ) );
2049    
2050            // Put back the set (this is a concurrentHashMap, it won't be replaced implicitly
2051            usedBy.put( wrapper, uses );
2052        }
2053    
2054    
2055        /**
2056         * Del an association between a SchemaObject an the SchemaObject it refers
2057         *
2058         * @param reference The base SchemaObject
2059         * @param referee The SchemaObject pointing on the reference
2060         */
2061        private void delUsing( SchemaObject reference, SchemaObject referee )
2062        {
2063            if ( ( reference == null ) || ( referee == null ) )
2064            {
2065                return;
2066            }
2067    
2068            Set<SchemaObjectWrapper> uses = getUsing( reference );
2069    
2070            if ( uses == null )
2071            {
2072                return;
2073            }
2074    
2075            uses.remove( new SchemaObjectWrapper( referee ) );
2076    
2077            SchemaObjectWrapper wrapper = new SchemaObjectWrapper( reference );
2078    
2079            if ( uses.size() == 0 )
2080            {
2081                using.remove( wrapper );
2082            }
2083            else
2084            {
2085                using.put( wrapper, uses );
2086            }
2087    
2088            return;
2089        }
2090    
2091    
2092        /**
2093         * Del an association between a SchemaObject an the SchemaObject that refers it
2094         *
2095         * @param reference The base SchemaObject
2096         * @param referee The SchemaObject pointing on the reference
2097         */
2098        private void delUsedBy( SchemaObject referee, SchemaObject reference )
2099        {
2100            if ( ( reference == null ) || ( referee == null ) )
2101            {
2102                return;
2103            }
2104    
2105            Set<SchemaObjectWrapper> uses = getUsedBy( referee );
2106    
2107            if ( uses == null )
2108            {
2109                return;
2110            }
2111    
2112            uses.remove( new SchemaObjectWrapper( reference ) );
2113    
2114            SchemaObjectWrapper wrapper = new SchemaObjectWrapper( referee );
2115    
2116            if ( uses.size() == 0 )
2117            {
2118                usedBy.remove( wrapper );
2119            }
2120            else
2121            {
2122                usedBy.put( wrapper, uses );
2123            }
2124    
2125            return;
2126        }
2127    
2128    
2129        /**
2130         * Delete an association between a SchemaObject an the SchemaObject it refers
2131         *
2132         * @param base The base SchemaObject
2133         * @param referenced The referenced SchemaObject
2134         */
2135        public void delReference( SchemaObject base, SchemaObject referenced )
2136        {
2137            if ( LOG.isDebugEnabled() )
2138            {
2139                LOG.debug( dump( "del", base, referenced ) );
2140            }
2141    
2142            delUsing( base, referenced );
2143            delUsedBy( referenced, base );
2144    
2145            if ( LOG.isDebugEnabled() )
2146            {
2147                LOG.debug( dumpUsedBy() );
2148                LOG.debug( dumpUsing() );
2149            }
2150        }
2151    
2152    
2153        /**
2154         * Dump the reference operation as a String
2155         */
2156        private String dump( String op, SchemaObject reference, SchemaObject referee )
2157        {
2158            return op + " : " + reference.getObjectType() + "[" + reference.getOid() + "]/[" + referee.getObjectType()
2159                + "[" + referee.getOid() + "]";
2160        }
2161    
2162    
2163        private boolean checkReferences( SchemaObject reference, SchemaObject referee, String message )
2164        {
2165            SchemaObjectWrapper referenceWrapper = new SchemaObjectWrapper( reference );
2166            SchemaObjectWrapper refereeWrapper = new SchemaObjectWrapper( referee );
2167    
2168            // Check the references : Syntax -> SyntaxChecker
2169            if ( !using.containsKey( referenceWrapper ) )
2170            {
2171                LOG.debug( "The Syntax {}:{} does not reference any " + message, reference.getObjectType(), reference
2172                    .getOid() );
2173    
2174                return false;
2175            }
2176    
2177            Set<SchemaObjectWrapper> usings = using.get( referenceWrapper );
2178    
2179            if ( !usings.contains( refereeWrapper ) )
2180            {
2181                LOG.debug( "The {}:{} does not reference any " + message, reference.getObjectType(), reference.getOid() );
2182    
2183                return false;
2184            }
2185    
2186            // Check the referees : SyntaxChecker -> Syntax
2187            if ( !usedBy.containsKey( refereeWrapper ) )
2188            {
2189                LOG.debug( "The {}:{} is not referenced by any " + message, referee.getObjectType(), referee.getOid() );
2190    
2191                return false;
2192            }
2193    
2194            Set<SchemaObjectWrapper> used = usedBy.get( refereeWrapper );
2195    
2196            if ( !used.contains( referenceWrapper ) )
2197            {
2198                LOG.debug( "The {}:{} is not referenced by any " + message, referee.getObjectType(), referee.getOid() );
2199    
2200                return false;
2201            }
2202    
2203            return true;
2204        }
2205    
2206    
2207        /**
2208         * Check the registries for invalid relations. This check stops at the first error.
2209         *
2210         * @return true if the Registries is consistent, false otherwise
2211         */
2212        public boolean check()
2213        {
2214            // Check the Syntaxes : check for a SyntaxChecker
2215            LOG.debug( "Checking Syntaxes" );
2216    
2217            for ( LdapSyntax syntax : ldapSyntaxRegistry )
2218            {
2219                // Check that each Syntax has a SyntaxChecker
2220                if ( syntax.getSyntaxChecker() == null )
2221                {
2222                    LOG.debug( "The Syntax {} has no SyntaxChecker", syntax );
2223    
2224                    return false;
2225                }
2226    
2227                if ( !syntaxCheckerRegistry.contains( syntax.getSyntaxChecker().getOid() ) )
2228                {
2229                    LOG.debug( "Cannot find the SyntaxChecker {} for the Syntax {}", syntax.getSyntaxChecker().getOid(),
2230                        syntax );
2231    
2232                    return false;
2233                }
2234    
2235                // Check the references : Syntax -> SyntaxChecker and SyntaxChecker -> Syntax 
2236                if ( !checkReferences( syntax, syntax.getSyntaxChecker(), "SyntaxChecker" ) )
2237                {
2238                    return false;
2239                }
2240            }
2241    
2242            // Check the MatchingRules : check for a Normalizer, a Comparator and a Syntax
2243            LOG.debug( "Checking MatchingRules..." );
2244    
2245            for ( MatchingRule matchingRule : matchingRuleRegistry )
2246            {
2247                // Check that each MatchingRule has a Normalizer
2248                if ( matchingRule.getNormalizer() == null )
2249                {
2250                    LOG.debug( "The MatchingRule {} has no Normalizer", matchingRule );
2251    
2252                    return false;
2253                }
2254    
2255                // Check that each MatchingRule has a Normalizer
2256                if ( !normalizerRegistry.contains( matchingRule.getNormalizer().getOid() ) )
2257                {
2258                    LOG.debug( "Cannot find the Normalizer {} for the MatchingRule {}", matchingRule.getNormalizer()
2259                        .getOid(), matchingRule );
2260    
2261                    return false;
2262                }
2263    
2264                // Check that each MatchingRule has a Comparator
2265                if ( matchingRule.getLdapComparator() == null )
2266                {
2267                    LOG.debug( "The MatchingRule {} has no Comparator", matchingRule );
2268    
2269                    return false;
2270                }
2271    
2272                if ( !comparatorRegistry.contains( matchingRule.getLdapComparator().getOid() ) )
2273                {
2274                    LOG.debug( "Cannot find the Comparator {} for the MatchingRule {}", matchingRule.getLdapComparator()
2275                        .getOid(), matchingRule );
2276    
2277                    return false;
2278                }
2279    
2280                // Check that each MatchingRule has a Syntax
2281                if ( matchingRule.getSyntax() == null )
2282                {
2283                    LOG.debug( "The MatchingRule {} has no Syntax", matchingRule );
2284    
2285                    return false;
2286                }
2287    
2288                if ( !ldapSyntaxRegistry.contains( matchingRule.getSyntax().getOid() ) )
2289                {
2290                    LOG.debug( "Cannot find the Syntax {} for the MatchingRule {}", matchingRule.getSyntax().getOid(),
2291                        matchingRule );
2292    
2293                    return false;
2294                }
2295    
2296                // Check the references : MR -> S and S -> MR 
2297                if ( !checkReferences( matchingRule, matchingRule.getSyntax(), "Syntax" ) )
2298                {
2299                    return false;
2300                }
2301    
2302                // Check the references : MR -> N 
2303                if ( !checkReferences( matchingRule, matchingRule.getNormalizer(), "Normalizer" ) )
2304                {
2305                    return false;
2306                }
2307    
2308                // Check the references : MR -> C and C -> MR 
2309                if ( !checkReferences( matchingRule, matchingRule.getLdapComparator(), "Comparator" ) )
2310                {
2311                    return false;
2312                }
2313            }
2314    
2315            // Check the ObjectClasses : check for MAY, MUST, SUPERIORS
2316            LOG.debug( "Checking ObjectClasses..." );
2317    
2318            for ( ObjectClass objectClass : objectClassRegistry )
2319            {
2320                // Check that each ObjectClass has all the MAY AttributeTypes
2321                if ( objectClass.getMayAttributeTypes() != null )
2322                {
2323                    for ( AttributeType may : objectClass.getMayAttributeTypes() )
2324                    {
2325                        if ( !attributeTypeRegistry.contains( may.getOid() ) )
2326                        {
2327                            LOG.debug( "Cannot find the AttributeType {} for the ObjectClass {} MAY", may, objectClass );
2328    
2329                            return false;
2330                        }
2331    
2332                        // Check the references : OC -> AT  and AT -> OC (MAY) 
2333                        if ( !checkReferences( objectClass, may, "AttributeType" ) )
2334                        {
2335                            return false;
2336                        }
2337                    }
2338                }
2339    
2340                // Check that each ObjectClass has all the MUST AttributeTypes
2341                if ( objectClass.getMustAttributeTypes() != null )
2342                {
2343                    for ( AttributeType must : objectClass.getMustAttributeTypes() )
2344                    {
2345                        if ( !attributeTypeRegistry.contains( must.getOid() ) )
2346                        {
2347                            LOG.debug( "Cannot find the AttributeType {} for the ObjectClass {} MUST", must, objectClass );
2348    
2349                            return false;
2350                        }
2351    
2352                        // Check the references : OC -> AT  and AT -> OC (MUST) 
2353                        if ( !checkReferences( objectClass, must, "AttributeType" ) )
2354                        {
2355                            return false;
2356                        }
2357                    }
2358                }
2359    
2360                // Check that each ObjectClass has all the SUPERIORS ObjectClasses
2361                if ( objectClass.getSuperiors() != null )
2362                {
2363                    for ( ObjectClass superior : objectClass.getSuperiors() )
2364                    {
2365                        if ( !objectClassRegistry.contains( objectClass.getOid() ) )
2366                        {
2367                            LOG.debug( "Cannot find the ObjectClass {} for the ObjectClass {} SUPERIORS", superior,
2368                                objectClass );
2369    
2370                            return false;
2371                        }
2372    
2373                        // Check the references : OC -> OC  and OC -> OC (SUPERIORS) 
2374                        if ( !checkReferences( objectClass, superior, "ObjectClass" ) )
2375                        {
2376                            return false;
2377                        }
2378                    }
2379                }
2380            }
2381    
2382            // Check the AttributeTypes : check for MatchingRules, Syntaxes
2383            LOG.debug( "Checking AttributeTypes..." );
2384    
2385            for ( AttributeType attributeType : attributeTypeRegistry )
2386            {
2387                // Check that each AttributeType has a SYNTAX 
2388                if ( attributeType.getSyntax() == null )
2389                {
2390                    LOG.debug( "The AttributeType {} has no Syntax", attributeType );
2391    
2392                    return false;
2393                }
2394    
2395                if ( !ldapSyntaxRegistry.contains( attributeType.getSyntax().getOid() ) )
2396                {
2397                    LOG.debug( "Cannot find the Syntax {} for the AttributeType {}", attributeType.getSyntax().getOid(),
2398                        attributeType );
2399    
2400                    return false;
2401                }
2402    
2403                // Check the references for AT -> S and S -> AT
2404                if ( !checkReferences( attributeType, attributeType.getSyntax(), "AttributeType" ) )
2405                {
2406                    return false;
2407                }
2408    
2409                // Check the EQUALITY MatchingRule
2410                if ( attributeType.getEquality() != null )
2411                {
2412                    if ( !matchingRuleRegistry.contains( attributeType.getEquality().getOid() ) )
2413                    {
2414                        LOG.debug( "Cannot find the MatchingRule {} for the AttributeType {}", attributeType.getEquality()
2415                            .getOid(), attributeType );
2416    
2417                        return false;
2418                    }
2419    
2420                    // Check the references for AT -> MR and MR -> AT
2421                    if ( !checkReferences( attributeType, attributeType.getEquality(), "AttributeType" ) )
2422                    {
2423                        return false;
2424                    }
2425                }
2426    
2427                // Check the ORDERING MatchingRule
2428                if ( attributeType.getOrdering() != null )
2429                {
2430                    if ( !matchingRuleRegistry.contains( attributeType.getOrdering().getOid() ) )
2431                    {
2432                        LOG.debug( "Cannot find the MatchingRule {} for the AttributeType {}", attributeType.getOrdering()
2433                            .getOid(), attributeType );
2434    
2435                        return false;
2436                    }
2437    
2438                    // Check the references for AT -> MR and MR -> AT
2439                    if ( !checkReferences( attributeType, attributeType.getOrdering(), "AttributeType" ) )
2440                    {
2441                        return false;
2442                    }
2443                }
2444    
2445                // Check the SUBSTR MatchingRule
2446                if ( attributeType.getSubstring() != null )
2447                {
2448                    if ( !matchingRuleRegistry.contains( attributeType.getSubstring().getOid() ) )
2449                    {
2450                        LOG.debug( "Cannot find the MatchingRule {} for the AttributeType {}", attributeType.getSubstring()
2451                            .getOid(), attributeType );
2452    
2453                        return false;
2454                    }
2455    
2456                    // Check the references for AT -> MR and MR -> AT
2457                    if ( !checkReferences( attributeType, attributeType.getSubstring(), "AttributeType" ) )
2458                    {
2459                        return false;
2460                    }
2461                }
2462    
2463                // Check the SUP
2464                if ( attributeType.getSuperior() != null )
2465                {
2466                    AttributeType superior = attributeType.getSuperior();
2467    
2468                    if ( !attributeTypeRegistry.contains( superior.getOid() ) )
2469                    {
2470                        LOG.debug( "Cannot find the AttributeType {} for the AttributeType {} SUPERIOR", superior,
2471                            attributeType );
2472    
2473                        return false;
2474                    }
2475    
2476                    // Check the references : AT -> AT  and AT -> AT (SUPERIOR) 
2477                    if ( !checkReferences( attributeType, superior, "AttributeType" ) )
2478                    {
2479                        return false;
2480                    }
2481                }
2482            }
2483    
2484            return true;
2485        }
2486    
2487    
2488        /**
2489         * Clone the Registries. This is done in two steps :
2490         * - first clone the SchemaObjetc registries
2491         * - second restore the relation between them
2492         */
2493        public Registries clone() throws CloneNotSupportedException
2494        {
2495            // First clone the structure
2496            Registries clone = ( Registries ) super.clone();
2497    
2498            // Now, clone the oidRegistry
2499            clone.globalOidRegistry = globalOidRegistry.copy();
2500    
2501            // We have to clone every SchemaObject registries now
2502            clone.attributeTypeRegistry = attributeTypeRegistry.copy();
2503            clone.comparatorRegistry = comparatorRegistry.copy();
2504            clone.ditContentRuleRegistry = ditContentRuleRegistry.copy();
2505            clone.ditStructureRuleRegistry = ditStructureRuleRegistry.copy();
2506            clone.ldapSyntaxRegistry = ldapSyntaxRegistry.copy();
2507            clone.matchingRuleRegistry = matchingRuleRegistry.copy();
2508            clone.matchingRuleUseRegistry = matchingRuleUseRegistry.copy();
2509            clone.nameFormRegistry = nameFormRegistry.copy();
2510            clone.normalizerRegistry = normalizerRegistry.copy();
2511            clone.objectClassRegistry = objectClassRegistry.copy();
2512            clone.syntaxCheckerRegistry = syntaxCheckerRegistry.copy();
2513    
2514            // Store all the SchemaObjects into the globalOid registry
2515            for ( AttributeType attributeType : clone.attributeTypeRegistry )
2516            {
2517                clone.globalOidRegistry.put( attributeType );
2518            }
2519    
2520            for ( DITContentRule ditContentRule : clone.ditContentRuleRegistry )
2521            {
2522                clone.globalOidRegistry.put( ditContentRule );
2523            }
2524    
2525            for ( DITStructureRule ditStructureRule : clone.ditStructureRuleRegistry )
2526            {
2527                clone.globalOidRegistry.put( ditStructureRule );
2528            }
2529    
2530            for ( MatchingRule matchingRule : clone.matchingRuleRegistry )
2531            {
2532                clone.globalOidRegistry.put( matchingRule );
2533            }
2534    
2535            for ( MatchingRuleUse matchingRuleUse : clone.matchingRuleUseRegistry )
2536            {
2537                clone.globalOidRegistry.put( matchingRuleUse );
2538            }
2539    
2540            for ( NameForm nameForm : clone.nameFormRegistry )
2541            {
2542                clone.globalOidRegistry.put( nameForm );
2543            }
2544    
2545            for ( ObjectClass objectClass : clone.objectClassRegistry )
2546            {
2547                clone.globalOidRegistry.put( objectClass );
2548            }
2549    
2550            for ( LdapSyntax syntax : clone.ldapSyntaxRegistry )
2551            {
2552                clone.globalOidRegistry.put( syntax );
2553            }
2554    
2555            // Clone the schema list
2556            clone.loadedSchemas = new HashMap<String, Schema>();
2557    
2558            for ( String schemaName : loadedSchemas.keySet() )
2559            {
2560                // We don't clone the schemas
2561                clone.loadedSchemas.put( schemaName, loadedSchemas.get( schemaName ) );
2562            }
2563    
2564            // Clone the Using and usedBy structures
2565            // They will be empty
2566            clone.using = new HashMap<SchemaObjectWrapper, Set<SchemaObjectWrapper>>();
2567            clone.usedBy = new HashMap<SchemaObjectWrapper, Set<SchemaObjectWrapper>>();
2568    
2569            // Last, rebuild the using and usedBy references
2570            clone.buildReferences();
2571    
2572            // Now, check the registries. We don't care about errors
2573            clone.checkRefInteg();
2574    
2575            clone.schemaObjects = new HashMap<String, Set<SchemaObjectWrapper>>();
2576    
2577            // Last, not least, clone the SchemaObjects Map, and reference all the copied
2578            // SchemaObjects
2579            for ( String schemaName : schemaObjects.keySet() )
2580            {
2581                Set<SchemaObjectWrapper> objects = new HashSet<SchemaObjectWrapper>();
2582    
2583                for ( SchemaObjectWrapper schemaObjectWrapper : schemaObjects.get( schemaName ) )
2584                {
2585                    SchemaObject original = schemaObjectWrapper.get();
2586    
2587                    try
2588                    {
2589                        if ( ! ( original instanceof LoadableSchemaObject ) )
2590                        {
2591                            SchemaObject copy = clone.globalOidRegistry.getSchemaObject( original.getOid() );
2592                            SchemaObjectWrapper newWrapper = new SchemaObjectWrapper( copy );
2593                            objects.add( newWrapper );
2594                        }
2595                        else
2596                        {
2597                            SchemaObjectWrapper newWrapper = new SchemaObjectWrapper( original );
2598                            objects.add(  newWrapper );
2599                        }
2600                    }
2601                    catch ( NamingException ne )
2602                    {
2603                        int i = 0;
2604                        i++;
2605                        // Nothing to do
2606                    }
2607                }
2608    
2609                clone.schemaObjects.put( schemaName, objects );
2610            }
2611    
2612            return clone;
2613        }
2614    
2615    
2616        /**
2617         * Tells if the Registries is permissive or if it must be checked 
2618         * against inconsistencies.
2619         *
2620         * @return True if SchemaObjects can be added even if they break the consistency 
2621         */
2622        public boolean isRelaxed()
2623        {
2624            return isRelaxed;
2625        }
2626    
2627    
2628        /**
2629         * Tells if the Registries is strict.
2630         *
2631         * @return True if SchemaObjects cannot be added if they break the consistency 
2632         */
2633        public boolean isStrict()
2634        {
2635            return !isRelaxed;
2636        }
2637    
2638    
2639        /**
2640         * Change the Registries to a relaxed mode, where invalid SchemaObjects
2641         * can be registered.
2642         */
2643        public void setRelaxed()
2644        {
2645            isRelaxed = RELAXED;
2646        }
2647    
2648    
2649        /**
2650         * Change the Registries to a strict mode, where invalid SchemaObjects
2651         * cannot be registered.
2652         */
2653        public void setStrict()
2654        {
2655            isRelaxed = STRICT;
2656        }
2657    
2658    
2659        /**
2660         * Tells if the Registries accept disabled elements.
2661         *
2662         * @return True if disabled SchemaObjects can be added 
2663         */
2664        public boolean isDisabledAccepted()
2665        {
2666            return disabledAccepted;
2667        }
2668    
2669    
2670        /**
2671         * Check that we can remove a given SchemaObject without breaking some of its references.
2672         * We will return the list of refereing objects.
2673         *
2674         * @param schemaObject The SchemaObject to remove
2675         * @return The list of SchemaObjects referencing the SchemaObjetc we want to remove
2676         */
2677        public Set<SchemaObjectWrapper> getReferencing( SchemaObject schemaObject )
2678        {
2679            SchemaObjectWrapper schemaObjectWrapper = new SchemaObjectWrapper( schemaObject );
2680    
2681            return usedBy.get( schemaObjectWrapper );
2682        }
2683    
2684    
2685        /**
2686         * Change the Registries behavior regarding disabled SchemaObject element.
2687         *
2688         * @param acceptDisabled If <code>false</code>, then the Registries won't accept
2689         * disabled SchemaObject or enabled SchemaObject from disabled schema 
2690         */
2691        public void setDisabledAccepted( boolean disabledAccepted )
2692        {
2693            this.disabledAccepted = disabledAccepted;
2694        }
2695    
2696    
2697        /**
2698         * Clear the registries from all its elements
2699         *
2700         * @throws NamingException If something goes wrong
2701         */
2702        public void clear() throws NamingException
2703        {
2704            // The AttributeTypeRegistry
2705            if ( attributeTypeRegistry != null )
2706            {
2707                attributeTypeRegistry.clear();
2708            }
2709    
2710            // The ComparatorRegistry
2711            if ( comparatorRegistry != null )
2712            {
2713                comparatorRegistry.clear();
2714            }
2715    
2716            // The DitContentRuleRegistry
2717            if ( ditContentRuleRegistry != null )
2718            {
2719                ditContentRuleRegistry.clear();
2720            }
2721    
2722            // The DitStructureRuleRegistry
2723            if ( ditStructureRuleRegistry != null )
2724            {
2725                ditStructureRuleRegistry.clear();
2726            }
2727    
2728            // The MatchingRuleRegistry
2729            if ( matchingRuleRegistry != null )
2730            {
2731                matchingRuleRegistry.clear();
2732            }
2733    
2734            // The MatchingRuleUseRegistry
2735            if ( matchingRuleUseRegistry != null )
2736            {
2737                matchingRuleUseRegistry.clear();
2738            }
2739    
2740            // The NameFormRegistry
2741            if ( nameFormRegistry != null )
2742            {
2743                nameFormRegistry.clear();
2744            }
2745    
2746            // The NormalizerRegistry
2747            if ( normalizerRegistry != null )
2748            {
2749                normalizerRegistry.clear();
2750            }
2751    
2752            // The ObjectClassRegistry
2753            if ( objectClassRegistry != null )
2754            {
2755                objectClassRegistry.clear();
2756            }
2757    
2758            // The SyntaxRegistry
2759            if ( ldapSyntaxRegistry != null )
2760            {
2761                ldapSyntaxRegistry.clear();
2762            }
2763    
2764            // The SyntaxCheckerRegistry
2765            if ( syntaxCheckerRegistry != null )
2766            {
2767                syntaxCheckerRegistry.clear();
2768            }
2769    
2770            // Clear the schemaObjects map
2771            for ( String schemaName : schemaObjects.keySet() )
2772            {
2773                Set<SchemaObjectWrapper> wrapperSet = schemaObjects.get( schemaName );
2774    
2775                wrapperSet.clear();
2776            }
2777    
2778            schemaObjects.clear();
2779    
2780            // Clear the usedBy map
2781            for ( SchemaObjectWrapper wrapper : usedBy.keySet() )
2782            {
2783                Set<SchemaObjectWrapper> wrapperSet = usedBy.get( wrapper );
2784    
2785                wrapperSet.clear();
2786            }
2787    
2788            usedBy.clear();
2789    
2790            // Clear the using map
2791            for ( SchemaObjectWrapper wrapper : using.keySet() )
2792            {
2793                Set<SchemaObjectWrapper> wrapperSet = using.get( wrapper );
2794    
2795                wrapperSet.clear();
2796            }
2797    
2798            using.clear();
2799    
2800            // Clear the global OID registry
2801            globalOidRegistry.clear();
2802    
2803            // Clear the loadedSchema Map
2804            loadedSchemas.clear();
2805        }
2806    
2807    
2808        /**
2809         * @see Object#toString()
2810         */
2811        public String toString()
2812        {
2813            StringBuilder sb = new StringBuilder();
2814    
2815            sb.append( "Registries [" );
2816    
2817            if ( isRelaxed )
2818            {
2819                sb.append( "RELAXED," );
2820            }
2821            else
2822            {
2823                sb.append( "STRICT," );
2824            }
2825    
2826            if ( disabledAccepted )
2827            {
2828                sb.append( " Disabled accepted] :\n" );
2829            }
2830            else
2831            {
2832                sb.append( " Disabled forbidden] :\n" );
2833            }
2834    
2835            sb.append( "loaded schemas [" );
2836            boolean isFirst = true;
2837    
2838            for ( String schema : loadedSchemas.keySet() )
2839            {
2840                if ( isFirst )
2841                {
2842                    isFirst = false;
2843                }
2844                else
2845                {
2846                    sb.append( ", " );
2847                }
2848    
2849                sb.append( schema );
2850            }
2851    
2852            sb.append( "]\n" );
2853    
2854            sb.append( "AttributeTypes : " ).append( attributeTypeRegistry.size() ).append( "\n" );
2855            sb.append( "Comparators : " ).append( comparatorRegistry.size() ).append( "\n" );
2856            sb.append( "DitContentRules : " ).append( ditContentRuleRegistry.size() ).append( "\n" );
2857            sb.append( "DitStructureRules : " ).append( ditStructureRuleRegistry.size() ).append( "\n" );
2858            sb.append( "MatchingRules : " ).append( matchingRuleRegistry.size() ).append( "\n" );
2859            sb.append( "MatchingRuleUses : " ).append( matchingRuleUseRegistry.size() ).append( "\n" );
2860            sb.append( "NameForms : " ).append( nameFormRegistry.size() ).append( "\n" );
2861            sb.append( "Normalizers : " ).append( normalizerRegistry.size() ).append( "\n" );
2862            sb.append( "ObjectClasses : " ).append( objectClassRegistry.size() ).append( "\n" );
2863            sb.append( "Syntaxes : " ).append( ldapSyntaxRegistry.size() ).append( "\n" );
2864            sb.append( "SyntaxCheckers : " ).append( syntaxCheckerRegistry.size() ).append( "\n" );
2865    
2866            sb.append( "GlobalOidRegistry : " ).append( globalOidRegistry.size() ).append( '\n' );
2867    
2868            return sb.toString();
2869        }
2870    }