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   *    http://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.server.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 javax.naming.NamingException;
31  import javax.naming.directory.NoSuchAttributeException;
32  
33  import org.apache.directory.shared.ldap.constants.SchemaConstants;
34  import org.apache.directory.shared.ldap.schema.AttributeType;
35  import org.apache.directory.shared.ldap.schema.MatchingRule;
36  import org.apache.directory.shared.ldap.schema.NoOpNormalizer;
37  import org.apache.directory.shared.ldap.schema.OidNormalizer;
38  import org.apache.directory.shared.ldap.util.StringTools;
39  
40  import org.slf4j.Logger;
41  import org.slf4j.LoggerFactory;
42  
43  
44  /**
45   * A plain old java object implementation of an AttributeTypeRegistry.
46   *
47   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
48   * @version $Rev: 664295 $
49   */
50  public class DefaultAttributeTypeRegistry implements AttributeTypeRegistry
51  {
52      /** static class logger */
53      private static final Logger LOG = LoggerFactory.getLogger( DefaultAttributeTypeRegistry.class );
54  
55      /** Speedup for DEBUG mode */
56      private static final boolean IS_DEBUG = LOG.isDebugEnabled();
57      
58      /** maps an OID to an AttributeType */
59      private final Map<String,AttributeType> byOid;
60      /** maps OIDs to a Set of descendants for that OID */
61      private final Map<String,Set<AttributeType>> oidToDescendantSet;
62      /** the registry used to resolve names to OIDs */
63      private final OidRegistry oidRegistry;
64      /** cached normalizer mapping */
65      private transient Map<String, OidNormalizer> mapping;
66      
67  
68      // ------------------------------------------------------------------------
69      // C O N S T R U C T O R S
70      // ------------------------------------------------------------------------
71  
72  
73      /**
74       * Creates an empty DefaultAttributeTypeRegistry.
75       *
76       * @param oidRegistry used by this registry for OID to name resolution of
77       * dependencies and to automatically register and unregister it's aliases and OIDs
78       */
79      public DefaultAttributeTypeRegistry( OidRegistry oidRegistry )
80      {
81          this.byOid = new HashMap<String,AttributeType>();
82          this.oidToDescendantSet= new HashMap<String,Set<AttributeType>>();
83          this.oidRegistry = oidRegistry;
84      }
85  
86  
87      // ------------------------------------------------------------------------
88      // Service Methods
89      // ------------------------------------------------------------------------
90  
91      
92      public void register( AttributeType attributeType ) throws NamingException
93      {
94          if ( byOid.containsKey( attributeType.getOid() ) )
95          {
96              throw new NamingException( "attributeType w/ OID " + attributeType.getOid()
97                  + " has already been registered!" );
98          }
99  
100         String[] names = attributeType.getNamesRef();
101         for ( String name : names )
102         {
103             oidRegistry.register( name, attributeType.getOid() );
104         }
105         oidRegistry.register( attributeType.getOid(), attributeType.getOid() );
106 
107         if ( mapping != null )
108         {
109             addMappingFor( attributeType );
110         }
111 
112         registerDescendants( attributeType );
113         byOid.put( attributeType.getOid(), attributeType );
114         
115         if ( IS_DEBUG )
116         {
117             LOG.debug( "registed attributeType: " + attributeType );
118         }
119     }
120 
121 
122     public Set<String> getBinaryAttributes() throws NamingException
123     {
124         Set<String> binaries = new HashSet<String>();
125         Iterator<AttributeType> list = iterator();
126         while ( list.hasNext() )
127         {
128             AttributeType type = list.next();
129 
130             if ( ! type.getSyntax().isHumanReadable() )
131             {
132                 // add the OID for the attributeType
133                 binaries.add( type.getOid() );
134 
135                 // add the lowercased name for the names for the attributeType
136                 String[] names = type.getNamesRef();
137 
138                 for ( String name : names )
139                 {
140                     // @TODO do we really need to lowercase strings here?
141                     binaries.add( StringTools.lowerCaseAscii( StringTools.trim( name ) ) );
142                 }
143             }
144         }
145 
146         return binaries;
147     }
148 
149 
150     public void registerDescendants( AttributeType attributeType ) throws NamingException
151     {
152         // add/create the descendent set for this attribute
153         oidToDescendantSet.put( attributeType.getOid(), new HashSet<AttributeType>( 5 ) );
154         
155         // add this attribute to descendant list of other attributes in superior chain
156         onRegisterAddToAncestorDescendants( attributeType, attributeType.getSuperior() );
157     }
158     
159     
160     /**
161      * Recursively adds a new attributeType to the descendant's list of all ancestors
162      * until top is reached.  Top will not have the new type added.
163      * 
164      * @param newType the new attributeType being added
165      * @param ancestor some anscestor from superior up to and including top
166      * @throws NamingException if there are resolution failures
167      */
168     protected void onRegisterAddToAncestorDescendants( AttributeType newType, AttributeType ancestor ) 
169         throws NamingException
170     {
171         if ( ancestor == null )
172         {
173             return;
174         }
175         
176         if ( ancestor.getName() != null && ancestor.getName().equals( SchemaConstants.TOP_OC ) )
177         {
178             return;
179         }
180         
181         Set<AttributeType> descendants = oidToDescendantSet.get( ancestor.getOid() );
182         if ( descendants == null )
183         {
184             descendants = new HashSet<AttributeType>( 5 );
185             oidToDescendantSet.put( ancestor.getOid(), descendants );
186         }
187         descendants.add( newType );
188         onRegisterAddToAncestorDescendants( newType, ancestor.getSuperior() );
189     }
190     
191 
192     public AttributeType lookup( String id ) throws NamingException
193     {
194         String oid = oidRegistry.getOid( id );
195 
196         if ( !byOid.containsKey( oid ) )
197         {
198             throw new NoSuchAttributeException( "attributeType w/ OID " + oid + " not registered!" );
199         }
200 
201         AttributeType attributeType = byOid.get( oid );
202         
203         if ( IS_DEBUG )
204         {
205             LOG.debug( "lookup with id" + oid + "' of attributeType: " + attributeType );
206         }
207         
208         return attributeType;
209     }
210 
211 
212     public boolean hasAttributeType( String id )
213     {
214         if ( oidRegistry.hasOid( id ) )
215         {
216             try
217             {
218                 return byOid.containsKey( oidRegistry.getOid( id ) );
219             }
220             catch ( NamingException e )
221             {
222                 return false;
223             }
224         }
225 
226         return false;
227     }
228 
229 
230     public String getSchemaName( String id ) throws NamingException
231     {
232         id = oidRegistry.getOid( id );
233         AttributeType at = byOid.get( id );
234         
235         if ( at != null )
236         {
237             return at.getSchema();
238         }
239 
240         throw new NamingException( "OID " + id + " not found in oid to " + "AttributeType map!" );
241     }
242 
243 
244     public Iterator list()
245     {
246         return byOid.values().iterator();
247     }
248 
249 
250     private void removeMappingFor( AttributeType type ) throws NamingException
251     {
252         if ( type == null )
253         {
254             return;
255         }
256         
257         MatchingRule matchingRule = type.getEquality();
258         mapping.remove( type.getOid() );
259         String[] aliases = type.getNamesRef();
260         for ( String aliase : aliases )
261         {
262             mapping.remove( aliase );
263             mapping.remove( aliase.toLowerCase() );
264         }
265     }
266 
267 
268     private void addMappingFor( AttributeType type ) throws NamingException
269     {
270         MatchingRule matchingRule = type.getEquality();
271         OidNormalizer oidNormalizer;
272 
273         if ( matchingRule == null )
274         {
275             LOG.debug( "Attribute " + type.getName() + " does not have normalizer : using NoopNormalizer" );
276             oidNormalizer = new OidNormalizer( type.getOid(), new NoOpNormalizer() );
277         }
278         else
279         {
280             oidNormalizer = new OidNormalizer( type.getOid(), matchingRule.getNormalizer() );
281         }
282 
283         mapping.put( type.getOid(), oidNormalizer );
284         String[] aliases = type.getNamesRef();
285         for ( String aliase : aliases )
286         {
287             mapping.put( aliase, oidNormalizer );
288             mapping.put( aliase.toLowerCase(), oidNormalizer );
289         }
290     }
291 
292     
293     public Map<String,OidNormalizer> getNormalizerMapping() throws NamingException
294     {
295         if ( mapping == null )
296         {
297             mapping = new HashMap<String,OidNormalizer>( byOid.size() << 1 );
298             for ( AttributeType type : byOid.values() )
299             {
300                 addMappingFor( type );
301             }
302         }
303         
304         return Collections.unmodifiableMap( mapping );
305     }
306 
307 
308     public Iterator<AttributeType> descendants( String ancestorId ) throws NamingException
309     {
310         String oid = oidRegistry.getOid( ancestorId );
311         Set<AttributeType> descendants = oidToDescendantSet.get( oid );
312         if ( descendants == null )
313         {
314             //noinspection unchecked
315             return Collections.EMPTY_SET.iterator();
316         }
317         return descendants.iterator();
318     }
319 
320 
321     public boolean hasDescendants( String ancestorId ) throws NamingException
322     {
323         String oid = oidRegistry.getOid( ancestorId );
324         Set descendants = oidToDescendantSet.get( oid );
325         return descendants != null && !descendants.isEmpty();
326     }
327 
328 
329     public Iterator<AttributeType> iterator()
330     {
331         return byOid.values().iterator();
332     }
333     
334     
335     public void unregister( String numericOid ) throws NamingException
336     {
337         if ( ! Character.isDigit( numericOid.charAt( 0 ) ) )
338         {
339             throw new NamingException( "Looks like the arg is not a numeric OID" );
340         }
341 
342         if ( mapping != null )
343         {
344             removeMappingFor( byOid.get( numericOid ));
345         }
346 
347         byOid.remove( numericOid );
348         oidToDescendantSet.remove( numericOid );
349     }
350 }