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