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