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.List;
028import java.util.Map;
029import java.util.Set;
030
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.schema.ObjectClass;
034import org.apache.directory.api.ldap.model.schema.SchemaObject;
035import org.apache.directory.api.ldap.model.schema.SchemaObjectType;
036
037
038/**
039 * An ObjectClass registry's service default implementation.
040 *
041 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
042 */
043public class DefaultObjectClassRegistry extends DefaultSchemaObjectRegistry<ObjectClass>
044    implements ObjectClassRegistry
045{
046    /** maps OIDs to a Set of descendants for that OID */
047    private Map<String, Set<ObjectClass>> oidToDescendants;
048
049
050    /**
051     * Creates a new default ObjectClassRegistry instance.
052     */
053    public DefaultObjectClassRegistry()
054    {
055        super( SchemaObjectType.OBJECT_CLASS, new OidRegistry<ObjectClass>() );
056        oidToDescendants = new HashMap<>();
057    }
058
059
060    /**
061     * {@inheritDoc}
062     */
063    @Override
064    public boolean hasDescendants( String ancestorId ) throws LdapException
065    {
066        try
067        {
068            String oid = getOidByName( ancestorId );
069            Set<ObjectClass> descendants = oidToDescendants.get( oid );
070            return ( descendants != null ) && !descendants.isEmpty();
071        }
072        catch ( LdapException ne )
073        {
074            throw new LdapNoSuchAttributeException( ne.getMessage(), ne );
075        }
076    }
077
078
079    /**
080     * {@inheritDoc}
081     */
082    @SuppressWarnings("unchecked")
083    @Override
084    public Iterator<ObjectClass> descendants( String ancestorId ) throws LdapException
085    {
086        try
087        {
088            String oid = getOidByName( ancestorId );
089            Set<ObjectClass> descendants = oidToDescendants.get( oid );
090
091            if ( descendants == null )
092            {
093                return Collections.EMPTY_SET.iterator();
094            }
095
096            return descendants.iterator();
097        }
098        catch ( LdapException ne )
099        {
100            throw new LdapNoSuchAttributeException( ne.getMessage(), ne );
101        }
102    }
103
104
105    /**
106     * {@inheritDoc}
107     */
108    @Override
109    public void registerDescendants( ObjectClass objectClass, List<ObjectClass> ancestors )
110        throws LdapException
111    {
112        // add this attribute to descendant list of other attributes in superior chain
113        if ( ( ancestors == null ) || ancestors.isEmpty() )
114        {
115            return;
116        }
117
118        for ( ObjectClass ancestor : ancestors )
119        {
120            // Get the ancestor's descendant, if any
121            Set<ObjectClass> descendants = oidToDescendants.get( ancestor.getOid() );
122
123            // Initialize the descendant Set to store the descendants for the attributeType
124            if ( descendants == null )
125            {
126                descendants = new HashSet<>( 1 );
127                oidToDescendants.put( ancestor.getOid(), descendants );
128            }
129
130            // Add the current ObjectClass as a descendant
131            descendants.add( objectClass );
132
133            try
134            {
135                // And recurse until we reach the top of the hierarchy
136                registerDescendants( objectClass, ancestor.getSuperiors() );
137            }
138            catch ( LdapException ne )
139            {
140                throw new LdapNoSuchAttributeException( ne.getMessage(), ne );
141            }
142        }
143    }
144
145
146    /**
147     * {@inheritDoc}
148     */
149    @Override
150    public void unregisterDescendants( ObjectClass attributeType, List<ObjectClass> ancestors )
151        throws LdapException
152    {
153        // add this attribute to descendant list of other attributes in superior chain
154        if ( ( ancestors == null ) || ancestors.isEmpty() )
155        {
156            return;
157        }
158
159        for ( ObjectClass ancestor : ancestors )
160        {
161            // Get the ancestor's descendant, if any
162            Set<ObjectClass> descendants = oidToDescendants.get( ancestor.getOid() );
163
164            if ( descendants != null )
165            {
166                descendants.remove( attributeType );
167
168                if ( descendants.isEmpty() )
169                {
170                    oidToDescendants.remove( ancestor.getOid() );
171                }
172            }
173
174            try
175            {
176                // And recurse until we reach the top of the hierarchy
177                unregisterDescendants( attributeType, ancestor.getSuperiors() );
178            }
179            catch ( LdapException ne )
180            {
181                throw new LdapNoSuchAttributeException( ne.getMessage(), ne );
182            }
183        }
184    }
185
186
187    /**
188     * {@inheritDoc}
189     */
190    @Override
191    public ObjectClass unregister( String numericOid ) throws LdapException
192    {
193        try
194        {
195            ObjectClass removed = super.unregister( numericOid );
196
197            // Deleting an ObjectClass which might be used as a superior means we have
198            // to recursively update the descendant map. We also have to remove
199            // the at.oid -> descendant relation
200            oidToDescendants.remove( numericOid );
201
202            // Now recurse if needed
203            unregisterDescendants( removed, removed.getSuperiors() );
204
205            return removed;
206        }
207        catch ( LdapException ne )
208        {
209            throw new LdapNoSuchAttributeException( ne.getMessage(), ne );
210        }
211    }
212
213
214    /**
215     * {@inheritDoc}
216     */
217    @Override
218    public DefaultObjectClassRegistry copy()
219    {
220        DefaultObjectClassRegistry copy = new DefaultObjectClassRegistry();
221
222        // Copy the base data
223        copy.copy( this );
224
225        return copy;
226    }
227
228
229    /**
230     * {@inheritDoc}
231     */
232    @Override
233    public void clear()
234    {
235        // Clear the contained SchemaObjects
236        for ( SchemaObject objectClass : oidRegistry )
237        {
238            objectClass.clear();
239        }
240
241        // First clear the shared elements
242        super.clear();
243
244        // and clear the descendant
245        for ( Map.Entry<String, Set<ObjectClass>> entry : oidToDescendants.entrySet() )
246        {
247            Set<ObjectClass> descendants = entry.getValue();
248
249            if ( descendants != null )
250            {
251                descendants.clear();
252            }
253        }
254
255        oidToDescendants.clear();
256    }
257}