View Javadoc

1   /*
2    *   Copyright 2004 The Apache Software Foundation
3    *
4    *   Licensed under the Apache License, Version 2.0 (the "License");
5    *   you may not use this file except in compliance with the License.
6    *   You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *   Unless required by applicable law or agreed to in writing, software
11   *   distributed under the License is distributed on an "AS IS" BASIS,
12   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *   See the License for the specific language governing permissions and
14   *   limitations under the License.
15   *
16   */
17  package org.apache.ldap.server.partition.impl.btree;
18  
19  
20  import java.math.BigInteger;
21  import java.util.Comparator;
22  
23  import javax.naming.NamingEnumeration;
24  import javax.naming.NamingException;
25  import javax.naming.directory.Attribute;
26  import javax.naming.directory.Attributes;
27  
28  import org.apache.ldap.common.NotImplementedException;
29  import org.apache.ldap.common.filter.ExprNode;
30  import org.apache.ldap.common.filter.LeafNode;
31  import org.apache.ldap.common.filter.PresenceNode;
32  import org.apache.ldap.common.filter.ScopeNode;
33  import org.apache.ldap.common.filter.SimpleNode;
34  import org.apache.ldap.common.schema.AttributeType;
35  import org.apache.ldap.common.schema.MatchingRule;
36  import org.apache.ldap.common.schema.Normalizer;
37  import org.apache.ldap.server.schema.AttributeTypeRegistry;
38  import org.apache.ldap.server.schema.OidRegistry;
39  
40  
41  /***
42   * Evaluates LeafNode assertions on candidates using a database.
43   * 
44   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
45   * @version $Rev: 264732 $
46   */
47  public class LeafEvaluator implements Evaluator
48  {
49      /*** equality matching type constant */
50      private static final int EQUALITY_MATCH = 0;
51      /*** ordering matching type constant */
52      private static final int ORDERING_MATCH = 1;
53      /*** substring matching type constant */
54      private static final int SUBSTRING_MATCH = 3;
55  
56  
57      /*** Database used to evaluate leaf with */
58      private BTreeContextPartition db;
59      /*** Oid Registry used to translate attributeIds to OIDs */
60      private OidRegistry oidRegistry;
61      /*** AttributeType registry needed for normalizing and comparing values */
62      private AttributeTypeRegistry attributeTypeRegistry;
63      /*** Substring node evaluator we depend on */
64      private SubstringEvaluator substringEvaluator;
65      /*** ScopeNode evaluator we depend on */
66      private ScopeEvaluator scopeEvaluator;
67  
68  
69      /***
70       * Creates a leaf expression node evaluator.
71       *
72       * @param db
73       * @param scopeEvaluator
74       * @param substringEvaluator
75       */
76      public LeafEvaluator( BTreeContextPartition db, OidRegistry oidRegistry,
77                            AttributeTypeRegistry attributeTypeRegistry,
78                            ScopeEvaluator scopeEvaluator,
79                            SubstringEvaluator substringEvaluator )
80      {
81          this.db = db;
82          this.oidRegistry = oidRegistry;
83          this.attributeTypeRegistry = attributeTypeRegistry;
84          this.scopeEvaluator = scopeEvaluator;
85          this.substringEvaluator = substringEvaluator;
86      }
87  
88  
89      public ScopeEvaluator getScopeEvaluator()
90      {
91          return scopeEvaluator;
92      }
93  
94  
95      public SubstringEvaluator getSubstringEvaluator()
96      {
97          return substringEvaluator;
98      }
99  
100 
101     /***
102      * @see org.apache.ldap.server.partition.impl.btree.Evaluator#evaluate(ExprNode, IndexRecord)
103      */
104     public boolean evaluate( ExprNode node, IndexRecord record ) throws NamingException
105     {
106         if ( node instanceof ScopeNode )
107         {
108             return scopeEvaluator.evaluate( node, record );
109         }
110         
111         switch( ( ( LeafNode ) node ).getAssertionType() ) 
112         {
113         case( LeafNode.APPROXIMATE ):
114             return evalEquality( ( SimpleNode ) node, record );
115         case( LeafNode.EQUALITY ):
116             return evalEquality( ( SimpleNode ) node, record );
117         case( LeafNode.EXTENSIBLE ):
118             throw new NotImplementedException();
119         case( LeafNode.GREATEREQ ):
120             return evalGreater( ( SimpleNode ) node, record, true );
121         case( LeafNode.LESSEQ ):
122             return evalGreater( ( SimpleNode ) node, record, false );
123         case( LeafNode.PRESENCE ):
124             String attrId = ( ( PresenceNode ) node ).getAttribute();
125             return evalPresence( attrId, record ); 
126         case( LeafNode.SUBSTRING ):
127             return substringEvaluator.evaluate( node, record );
128         default:
129             throw new NamingException( "Unrecognized leaf node type: "
130                 + ( ( LeafNode ) node ).getAssertionType() );
131         }
132     }
133     
134     
135     /***
136      * Evaluates a simple greater than or less than attribute value assertion on
137      * a perspective candidate.
138      * 
139      * @param node the greater than or less than node to evaluate
140      * @param record the IndexRecord of the perspective candidate
141      * @param isGreater true if it is a greater than or equal to comparison,
142      *      false if it is a less than or equal to comparison.
143      * @return the ava evaluation on the perspective candidate
144      * @throws NamingException if there is a database access failure
145      */
146     private boolean evalGreater( SimpleNode node, IndexRecord record,
147         boolean isGreater ) throws NamingException
148     {
149         String attrId = node.getAttribute();
150         BigInteger id = record.getEntryId();
151         
152         if ( db.hasUserIndexOn( attrId ) )
153         {
154             Index idx = db.getUserIndex( attrId );
155             
156             if ( isGreater )
157             {
158                 return idx.hasValue( node.getValue(), id, true ); 
159             }
160             
161             return idx.hasValue( node.getValue(), id, false );
162         }
163         
164         // resusitate entry if need be
165         if ( null == record.getAttributes() )
166         {
167             record.setAttributes( db.lookup( id ) );
168         }
169         
170         // get the attribute associated with the node 
171         Attribute attr = record.getAttributes().get( attrId );
172 
173         // If we do not have the attribute just return false
174         if ( null == attr )
175         {
176             return false;
177         }
178         
179         /*
180          * We need to iterate through all values and for each value we normalize
181          * and use the comparator to determine if a match exists.
182          */
183         Normalizer normalizer = getNormalizer( attrId );
184         Comparator comparator = getComparator( attrId );
185         Object filterValue = normalizer.normalize( node.getValue() );
186         NamingEnumeration list = attr.getAll();
187         
188         /*
189          * Cheaper to not check isGreater in one loop - better to separate
190          * out into two loops which you choose to execute based on isGreater
191          */
192         if ( isGreater )
193         {
194             while ( list.hasMore() )
195             {
196                 Object value = normalizer.normalize( list.next() );
197             
198                 // Found a value that is greater than or equal to the ava value
199                 if ( 0 >= comparator.compare( value, filterValue ) )
200                 {
201                     return true;
202                 }
203             }
204         }
205         else 
206         {    
207             while ( list.hasMore() )
208             {
209                 Object value = normalizer.normalize( list.next() );
210             
211                 // Found a value that is less than or equal to the ava value
212                 if ( 0 <= comparator.compare( value, filterValue ) )
213                 {
214                     return true;
215                 }
216             }
217         }
218         
219         // no match so return false
220         return false;
221     }
222 
223     
224     /***
225      * Evaluates a simple presence attribute value assertion on a perspective
226      * candidate.
227      * 
228      * @param attrId the name of the attribute tested for presence 
229      * @param rec the IndexRecord of the perspective candidate
230      * @return the ava evaluation on the perspective candidate
231      * @throws NamingException if there is a database access failure
232      */
233     private boolean evalPresence( String attrId, IndexRecord rec )
234         throws NamingException
235     {
236         if ( db.hasUserIndexOn( attrId ) )
237         {
238             Index idx = db.getExistanceIndex();
239             return idx.hasValue( attrId, rec.getEntryId() );
240         }
241         
242         // resusitate entry if need be
243         if ( null == rec.getAttributes() )
244         {
245             rec.setAttributes( db.lookup( rec.getEntryId() ) );
246         }
247         
248         // get the attribute associated with the node 
249         Attributes attrs = rec.getAttributes();
250 
251         if ( attrs == null )
252         {
253             return false;
254         }
255         
256         return null != attrs.get( attrId );
257     }
258 
259 
260     /***
261      * Evaluates a simple equality attribute value assertion on a perspective
262      * candidate.
263      *
264      * @param node the equality node to evaluate
265      * @param rec the IndexRecord of the perspective candidate
266      * @return the ava evaluation on the perspective candidate
267      * @throws NamingException if there is a database access failure
268      */
269     private boolean evalEquality( SimpleNode node, IndexRecord rec )
270         throws NamingException
271     {
272         if ( db.hasUserIndexOn( node.getAttribute() ) )
273         {
274             Index idx = db.getUserIndex( node.getAttribute() );
275             return idx.hasValue( node.getValue(), rec.getEntryId() );
276         }
277 
278         Normalizer normalizer = getNormalizer( node.getAttribute() );
279         Comparator comparator = getComparator( node.getAttribute() );
280 
281         /*
282          * Get the attribute and if it is not set in rec then resusitate it
283          * from the master table and set it in rec for use later if at all.
284          * Before iterating through all values for a match check to see if the
285          * AVA value is contained or the normalized form of the AVA value is 
286          * contained.
287          */
288         
289         // resusitate entry if need be
290         if ( null == rec.getAttributes() )
291         {
292             rec.setAttributes( db.lookup( rec.getEntryId() ) );
293         }
294         
295         // get the attribute associated with the node 
296         Attribute attr = rec.getAttributes().get( node.getAttribute() );
297         
298         // If we do not have the attribute just return false
299         if ( null == attr )
300         {
301             return false;
302         }
303         
304         // check if AVA value exists in attribute
305         if ( attr.contains( node.getValue() ) )
306         {
307             return true;
308         }
309 
310         // get the normalized AVA filter value
311         Object filterValue = normalizer.normalize( node.getValue() );
312 
313         // check if the normalized value is present
314         if ( attr.contains( filterValue ) )
315         {
316             return true;
317         }
318         
319         /*
320          * We need to now iterate through all values because we could not get
321          * a lookup to work.  For each value we normalize and use the comparator
322          * to determine if a match exists.
323          */
324         NamingEnumeration list = attr.getAll();
325         while ( list.hasMore() )
326         {
327             Object value = normalizer.normalize( list.next() );
328             
329             if ( 0 == comparator.compare( value, filterValue ) )
330             {
331                 return true;
332             }
333         }
334         
335         // no match so return false
336         return false;
337     }
338 
339 
340     /***
341      * Gets the comparator for equality matching.
342      *
343      * @param attrId the attribute identifier
344      * @return the comparator for equality matching
345      * @throws NamingException if there is a failure
346      */
347     private Comparator getComparator( String attrId ) throws NamingException
348     {
349         MatchingRule mrule = getMatchingRule( attrId, EQUALITY_MATCH );
350         return mrule.getComparator();
351     }
352 
353 
354     /***
355      * Gets the normalizer for equality matching.
356      *
357      * @param attrId the attribute identifier
358      * @return the normalizer for equality matching
359      * @throws NamingException if there is a failure
360      */
361     private Normalizer getNormalizer( String attrId ) throws NamingException
362     {
363         MatchingRule mrule = getMatchingRule( attrId, EQUALITY_MATCH );
364         return mrule.getNormalizer();
365     }
366 
367 
368     /***
369      * Gets the matching rule for an attributeType.
370      *
371      * @param attrId the attribute identifier
372      * @return the matching rule
373      * @throws NamingException if there is a failure
374      */
375     private MatchingRule getMatchingRule( String attrId, int matchType )
376         throws NamingException
377     {
378         MatchingRule mrule = null;
379         String oid = oidRegistry.getOid( attrId );
380         AttributeType type = attributeTypeRegistry.lookup( oid );
381 
382         switch( matchType )
383         {
384             case( EQUALITY_MATCH ):
385                 mrule = type.getEquality();
386                 break;
387             case( SUBSTRING_MATCH ):
388                 mrule = type.getSubstr();
389                 break;
390             case( ORDERING_MATCH ):
391                 mrule = type.getOrdering();
392                 break;
393             default:
394                 throw new NamingException( "Unknown match type: " + matchType );
395         }
396 
397         return mrule;
398     }
399 }