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