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