View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    https://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.api.ldap.model.schema.registries;
21  
22  
23  import java.util.Collections;
24  import java.util.HashMap;
25  import java.util.HashSet;
26  import java.util.Iterator;
27  import java.util.Map;
28  import java.util.Set;
29  
30  import org.apache.directory.api.i18n.I18n;
31  import org.apache.directory.api.ldap.model.exception.LdapException;
32  import org.apache.directory.api.ldap.model.exception.LdapNoSuchAttributeException;
33  import org.apache.directory.api.ldap.model.exception.LdapSchemaException;
34  import org.apache.directory.api.ldap.model.exception.LdapSchemaExceptionCodes;
35  import org.apache.directory.api.ldap.model.schema.AttributeType;
36  import org.apache.directory.api.ldap.model.schema.MatchingRule;
37  import org.apache.directory.api.ldap.model.schema.SchemaObjectType;
38  import org.apache.directory.api.ldap.model.schema.normalizers.NoOpNormalizer;
39  import org.apache.directory.api.ldap.model.schema.normalizers.OidNormalizer;
40  import org.apache.directory.api.util.Strings;
41  import org.slf4j.Logger;
42  import org.slf4j.LoggerFactory;
43  
44  
45  /**
46   * An AttributeType registry service default implementation.
47   *
48   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
49   */
50  public class DefaultAttributeTypeRegistry extends DefaultSchemaObjectRegistry<AttributeType> implements
51      AttributeTypeRegistry
52  {
53      /** static class logger */
54      private static final Logger LOG = LoggerFactory.getLogger( DefaultAttributeTypeRegistry.class );
55  
56      /** cached Oid/normalizer mapping */
57      private Map<String, OidNormalizer> oidNormalizerMap;
58  
59      /** maps OIDs to a Set of descendants for that OID */
60      private Map<String, Set<AttributeType>> oidToDescendantSet;
61  
62  
63      /**
64       * Creates a new default AttributeTypeRegistry instance.
65       */
66      public DefaultAttributeTypeRegistry()
67      {
68          super( SchemaObjectType.ATTRIBUTE_TYPE, new OidRegistry<AttributeType>() );
69          oidNormalizerMap = new HashMap<>();
70          oidToDescendantSet = new HashMap<>();
71      }
72  
73  
74      /**
75       * {@inheritDoc}
76       */
77      @Override
78      public Map<String, OidNormalizer> getNormalizerMapping()
79      {
80          return Collections.unmodifiableMap( oidNormalizerMap );
81      }
82  
83  
84      /**
85       * {@inheritDoc}
86       */
87      @Override
88      public boolean hasDescendants( String ancestorId ) throws LdapException
89      {
90          try
91          {
92              String oid = getOidByName( ancestorId );
93              Set<AttributeType> descendants = oidToDescendantSet.get( oid );
94              return ( descendants != null ) && !descendants.isEmpty();
95          }
96          catch ( LdapException ne )
97          {
98              throw new LdapNoSuchAttributeException( ne.getMessage(), ne );
99          }
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 }