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