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