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<>();
067        oidToDescendantSet = new HashMap<>();
068    }
069
070
071    /**
072     * {@inheritDoc}
073     */
074    @Override
075    public Map<String, OidNormalizer> getNormalizerMapping()
076    {
077        return Collections.unmodifiableMap( oidNormalizerMap );
078    }
079
080
081    /**
082     * {@inheritDoc}
083     */
084    @Override
085    public boolean hasDescendants( String ancestorId ) throws LdapException
086    {
087        try
088        {
089            String oid = getOidByName( ancestorId );
090            Set<AttributeType> descendants = oidToDescendantSet.get( oid );
091            return ( descendants != null ) && !descendants.isEmpty();
092        }
093        catch ( LdapException ne )
094        {
095            throw new LdapNoSuchAttributeException( ne.getMessage(), ne );
096        }
097    }
098
099
100    /**
101     * {@inheritDoc}
102     */
103    @Override
104    public boolean hasDescendants( AttributeType ancestor ) throws LdapException
105    {
106        String oid = ancestor.getOid();
107        Set<AttributeType> descendants = oidToDescendantSet.get( oid );
108        return ( descendants != null ) && !descendants.isEmpty();
109    }
110
111
112    /**
113     * {@inheritDoc}
114     */
115    @SuppressWarnings("unchecked")
116    @Override
117    public Iterator<AttributeType> descendants( String ancestorId ) throws LdapException
118    {
119        try
120        {
121            String oid = getOidByName( ancestorId );
122            Set<AttributeType> descendants = oidToDescendantSet.get( oid );
123
124            if ( descendants == null )
125            {
126                return Collections.EMPTY_SET.iterator();
127            }
128
129            return descendants.iterator();
130        }
131        catch ( LdapException ne )
132        {
133            throw new LdapNoSuchAttributeException( ne.getMessage(), ne );
134        }
135    }
136
137
138    /**
139     * {@inheritDoc}
140     */
141    @SuppressWarnings("unchecked")
142    @Override
143    public Iterator<AttributeType> descendants( AttributeType ancestor ) throws LdapException
144    {
145        String oid = ancestor.getOid();
146        Set<AttributeType> descendants = oidToDescendantSet.get( oid );
147
148        if ( descendants == null )
149        {
150            return Collections.EMPTY_SET.iterator();
151        }
152
153        return descendants.iterator();
154    }
155
156
157    /**
158     * {@inheritDoc}
159     */
160    @Override
161    public void registerDescendants( AttributeType attributeType, AttributeType ancestor ) throws LdapException
162    {
163        // add this attribute to descendant list of other attributes in superior chain
164        if ( ancestor == null )
165        {
166            return;
167        }
168
169        // Get the ancestor's descendant, if any
170        Set<AttributeType> descendants = oidToDescendantSet.get( ancestor.getOid() );
171
172        // Initialize the descendant Set to store the descendants for the attributeType
173        if ( descendants == null )
174        {
175            descendants = new HashSet<>( 1 );
176            oidToDescendantSet.put( ancestor.getOid(), descendants );
177        }
178
179        // Add the current type as a descendant
180        descendants.add( attributeType );
181    }
182
183
184    /**
185     * {@inheritDoc}
186     */
187    @Override
188    public void unregisterDescendants( AttributeType attributeType, AttributeType ancestor ) throws LdapException
189    {
190        // add this attribute to descendant list of other attributes in superior chain
191        if ( ancestor == null )
192        {
193            return;
194        }
195
196        // Get the ancestor's descendant, if any
197        Set<AttributeType> descendants = oidToDescendantSet.get( ancestor.getOid() );
198
199        if ( descendants != null )
200        {
201            descendants.remove( attributeType );
202
203            if ( descendants.isEmpty() )
204            {
205                oidToDescendantSet.remove( ancestor.getOid() );
206            }
207        }
208    }
209
210
211    /**
212     * {@inheritDoc}
213     */
214    @Override
215    public AttributeType unregister( String numericOid ) throws LdapException
216    {
217        try
218        {
219            AttributeType removed = super.unregister( numericOid );
220
221            removeMappingFor( removed );
222
223            // Deleting an AT which might be used as a superior means we have
224            // to recursively update the descendant map. We also have to remove
225            // the at.oid -> descendant relation
226            oidToDescendantSet.remove( numericOid );
227
228            // Now recurse if needed
229            unregisterDescendants( removed, removed.getSuperior() );
230
231            return removed;
232        }
233        catch ( LdapException ne )
234        {
235            throw new LdapNoSuchAttributeException( ne.getMessage(), ne );
236        }
237    }
238
239
240    /**
241     * {@inheritDoc}
242     */
243    @Override
244    public void addMappingFor( AttributeType attributeType ) throws LdapException
245    {
246        MatchingRule equality = attributeType.getEquality();
247        OidNormalizer oidNormalizer;
248        String oid = attributeType.getOid();
249
250        if ( equality == null )
251        {
252            LOG.debug( "Attribute {} does not have an EQUALITY MatchingRule : using NoopNormalizer", attributeType
253                .getName() );
254            oidNormalizer = new OidNormalizer( oid, new NoOpNormalizer( attributeType.getOid() ) );
255        }
256        else
257        {
258            oidNormalizer = new OidNormalizer( oid, equality.getNormalizer() );
259        }
260
261        oidNormalizerMap.put( oid, oidNormalizer );
262
263        // Also inject the attributeType's short names in the map
264        for ( String name : attributeType.getNames() )
265        {
266            oidNormalizerMap.put( Strings.toLowerCaseAscii( name ), oidNormalizer );
267        }
268    }
269
270
271    /**
272     * Remove the AttributeType normalizer from the OidNormalizer map 
273     */
274    @Override
275    public void removeMappingFor( AttributeType attributeType ) throws LdapException
276    {
277        if ( attributeType == null )
278        {
279            return;
280        }
281
282        oidNormalizerMap.remove( attributeType.getOid() );
283
284        // We also have to remove all the short names for this attribute
285        for ( String name : attributeType.getNames() )
286        {
287            oidNormalizerMap.remove( Strings.toLowerCaseAscii( name ) );
288        }
289    }
290
291
292    /**
293     * {@inheritDoc}
294     */
295    @Override
296    public AttributeType lookup( String oid ) throws LdapException
297    {
298        try
299        {
300            return super.lookup( oid );
301        }
302        catch ( LdapException ne )
303        {
304            throw new LdapNoSuchAttributeException( ne.getMessage(), ne );
305        }
306    }
307
308
309    /**
310     * {@inheritDoc}
311     */
312    @Override
313    public DefaultAttributeTypeRegistry copy()
314    {
315        DefaultAttributeTypeRegistry copy = new DefaultAttributeTypeRegistry();
316
317        // Copy the base data
318        copy.copy( this );
319
320        return copy;
321    }
322
323
324    /**
325     * {@inheritDoc}
326     */
327    @Override
328    public void clear()
329    {
330        // First clear the shared elements
331        super.clear();
332
333        // clear the OidNormalizer map
334        oidNormalizerMap.clear();
335
336        // and clear the descendant
337        for ( Map.Entry<String, Set<AttributeType>> entry : oidToDescendantSet.entrySet() )
338        {
339            Set<AttributeType> descendants = entry.getValue();
340
341            if ( descendants != null )
342            {
343                descendants.clear();
344            }
345        }
346
347        oidToDescendantSet.clear();
348    }
349}