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