001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.shared.ldap.schema.registries;
021    
022    
023    import java.util.Collections;
024    import java.util.HashMap;
025    import java.util.HashSet;
026    import java.util.Iterator;
027    import java.util.Map;
028    import java.util.Set;
029    
030    import org.apache.directory.shared.ldap.exception.LdapException;
031    import org.apache.directory.shared.ldap.exception.LdapNoSuchAttributeException;
032    import org.apache.directory.shared.ldap.schema.AttributeType;
033    import org.apache.directory.shared.ldap.schema.MatchingRule;
034    import org.apache.directory.shared.ldap.schema.SchemaObjectType;
035    import org.apache.directory.shared.ldap.schema.normalizers.NoOpNormalizer;
036    import org.apache.directory.shared.ldap.schema.normalizers.OidNormalizer;
037    import org.slf4j.Logger;
038    import org.slf4j.LoggerFactory;
039    
040    
041    /**
042     * An AttributeType registry service default implementation.
043     *
044     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
045     * @version $Rev: 828111 $
046     */
047    public class DefaultAttributeTypeRegistry extends DefaultSchemaObjectRegistry<AttributeType> implements
048        AttributeTypeRegistry
049    {
050        /** static class logger */
051        private static final Logger LOG = LoggerFactory.getLogger( DefaultAttributeTypeRegistry.class );
052    
053        /** Speedup for DEBUG mode */
054        private static final boolean IS_DEBUG = LOG.isDebugEnabled();
055    
056        /** cached Oid/normalizer mapping */
057        private transient Map<String, OidNormalizer> oidNormalizerMap;
058    
059        /** maps OIDs to a Set of descendants for that OID */
060        private Map<String, Set<AttributeType>> oidToDescendantSet;
061    
062    
063        /**
064         * Creates a new default AttributeTypeRegistry instance.
065         */
066        public DefaultAttributeTypeRegistry()
067        {
068            super( SchemaObjectType.ATTRIBUTE_TYPE, new OidRegistry() );
069            oidNormalizerMap = new HashMap<String, OidNormalizer>();
070            oidToDescendantSet = new HashMap<String, Set<AttributeType>>();
071        }
072    
073    
074        /**
075         * {@inheritDoc}
076         */
077        public Map<String, OidNormalizer> getNormalizerMapping()
078        {
079            return Collections.unmodifiableMap( oidNormalizerMap );
080        }
081    
082    
083        /**
084         * {@inheritDoc}
085         */
086        public boolean hasDescendants( String ancestorId ) throws LdapException
087        {
088            try
089            {
090                String oid = getOidByName( ancestorId );
091                Set<AttributeType> descendants = oidToDescendantSet.get( oid );
092                return ( descendants != null ) && !descendants.isEmpty();
093            }
094            catch ( LdapException ne )
095            {
096                throw new LdapNoSuchAttributeException( ne.getMessage() );
097            }
098        }
099    
100    
101        /**
102         * {@inheritDoc}
103         */
104        @SuppressWarnings("unchecked")
105        public Iterator<AttributeType> descendants( String ancestorId ) throws LdapException
106        {
107            try
108            {
109                String oid = getOidByName( ancestorId );
110                Set<AttributeType> descendants = oidToDescendantSet.get( oid );
111    
112                if ( descendants == null )
113                {
114                    return Collections.EMPTY_SET.iterator();
115                }
116    
117                return descendants.iterator();
118            }
119            catch ( LdapException ne )
120            {
121                throw new LdapNoSuchAttributeException( ne.getMessage() );
122            }
123        }
124    
125    
126        /**
127         * {@inheritDoc}
128         */
129        public void registerDescendants( AttributeType attributeType, AttributeType ancestor ) throws LdapException
130        {
131            // add this attribute to descendant list of other attributes in superior chain
132            if ( ancestor == null )
133            {
134                return;
135            }
136    
137            // Get the ancestor's descendant, if any
138            Set<AttributeType> descendants = oidToDescendantSet.get( ancestor.getOid() );
139    
140            // Initialize the descendant Set to store the descendants for the attributeType
141            if ( descendants == null )
142            {
143                descendants = new HashSet<AttributeType>( 1 );
144                oidToDescendantSet.put( ancestor.getOid(), descendants );
145            }
146    
147            // Add the current type as a descendant
148            descendants.add( attributeType );
149    
150            /*
151            try
152            {
153                // And recurse until we reach the top of the hierarchy
154                registerDescendants( attributeType, ancestor.getSuperior() );
155            }
156            catch ( LdapException ne )
157            {
158                throw new NoSuchAttributeException( ne.getMessage() );
159            }
160            */
161        }
162    
163    
164        /**
165         * {@inheritDoc}
166         */
167        public void unregisterDescendants( AttributeType attributeType, AttributeType ancestor ) throws LdapException
168        {
169            // add this attribute to descendant list of other attributes in superior chain
170            if ( ancestor == null )
171            {
172                return;
173            }
174    
175            // Get the ancestor's descendant, if any
176            Set<AttributeType> descendants = oidToDescendantSet.get( ancestor.getOid() );
177    
178            if ( descendants != null )
179            {
180                descendants.remove( attributeType );
181    
182                if ( descendants.size() == 0 )
183                {
184                    oidToDescendantSet.remove( descendants );
185                }
186            }
187    
188            /*
189            try
190            {
191                // And recurse until we reach the top of the hierarchy
192                unregisterDescendants( attributeType, ancestor.getSuperior() );
193            }
194            catch ( LdapException ne )
195            {
196                throw new NoSuchAttributeException( ne.getMessage() );
197            }
198            */
199        }
200    
201    
202        /**
203         * {@inheritDoc}
204         */
205        public AttributeType unregister( String numericOid ) throws LdapException
206        {
207            try
208            {
209                AttributeType removed = super.unregister( numericOid );
210    
211                removeMappingFor( removed );
212    
213                // Deleting an AT which might be used as a superior means we have
214                // to recursively update the descendant map. We also have to remove
215                // the at.oid -> descendant relation
216                oidToDescendantSet.remove( numericOid );
217    
218                // Now recurse if needed
219                unregisterDescendants( removed, removed.getSuperior() );
220    
221                return removed;
222            }
223            catch ( LdapException ne )
224            {
225                throw new LdapNoSuchAttributeException( ne.getMessage() );
226            }
227        }
228    
229    
230        /**
231         * {@inheritDoc}
232         */
233        public void addMappingFor( AttributeType attributeType ) throws LdapException
234        {
235            MatchingRule equality = attributeType.getEquality();
236            OidNormalizer oidNormalizer;
237            String oid = attributeType.getOid();
238    
239            if ( equality == null )
240            {
241                LOG.debug( "Attribute {} does not have an EQUALITY MatchingRule : using NoopNormalizer", attributeType
242                    .getName() );
243                oidNormalizer = new OidNormalizer( oid, new NoOpNormalizer( attributeType.getOid() ) );
244            }
245            else
246            {
247                oidNormalizer = new OidNormalizer( oid, equality.getNormalizer() );
248            }
249    
250            oidNormalizerMap.put( oid, oidNormalizer );
251    
252            // Also inject the attributeType's short names in the map
253            for ( String name : attributeType.getNames() )
254            {
255                oidNormalizerMap.put( name.toLowerCase(), oidNormalizer );
256            }
257        }
258    
259    
260        /**
261         * Remove the AttributeType normalizer from the OidNormalizer map 
262         */
263        public void removeMappingFor( AttributeType attributeType ) throws LdapException
264        {
265            if ( attributeType == null )
266            {
267                return;
268            }
269    
270            oidNormalizerMap.remove( attributeType.getOid() );
271    
272            // We also have to remove all the short names for this attribute
273            for ( String name : attributeType.getNames() )
274            {
275                oidNormalizerMap.remove( name.toLowerCase() );
276            }
277        }
278    
279    
280        /**
281         * {@inheritDoc}
282         */
283        public AttributeType lookup( String oid ) throws LdapException
284        {
285            try
286            {
287                return super.lookup( oid );
288            }
289            catch ( LdapException ne )
290            {
291                throw new LdapNoSuchAttributeException( ne.getMessage() );
292            }
293        }
294    
295    
296        /**
297         * {@inheritDoc}
298         */
299        public AttributeTypeRegistry copy()
300        {
301            DefaultAttributeTypeRegistry copy = new DefaultAttributeTypeRegistry();
302    
303            // Copy the base data
304            copy.copy( this );
305    
306            return copy;
307        }
308    
309    
310        /**
311         * {@inheritDoc}
312         */
313        public void clear()
314        {
315            // First clear the shared elements
316            super.clear();
317    
318            // clear the OidNormalizer map
319            oidNormalizerMap.clear();
320    
321            // and clear the descendant
322            for ( String oid : oidToDescendantSet.keySet() )
323            {
324                Set<AttributeType> descendants = oidToDescendantSet.get( oid );
325    
326                if ( descendants != null )
327                {
328                    descendants.clear();
329                }
330            }
331    
332            oidToDescendantSet.clear();
333        }
334    }