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