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.xdbm.search.impl;
21  
22  
23  import java.util.Iterator;
24  import java.util.regex.Pattern;
25  
26  import org.apache.directory.server.schema.registries.Registries;
27  import org.apache.directory.server.xdbm.Index;
28  import org.apache.directory.server.xdbm.IndexEntry;
29  import org.apache.directory.server.xdbm.Store;
30  import org.apache.directory.server.xdbm.search.Evaluator;
31  import org.apache.directory.server.core.cursor.Cursor;
32  import org.apache.directory.server.core.entry.ServerEntry;
33  import org.apache.directory.server.core.entry.ServerAttribute;
34  import org.apache.directory.shared.ldap.filter.SubstringNode;
35  import org.apache.directory.shared.ldap.schema.AttributeType;
36  import org.apache.directory.shared.ldap.schema.MatchingRule;
37  import org.apache.directory.shared.ldap.schema.Normalizer;
38  import org.apache.directory.shared.ldap.schema.NoOpNormalizer;
39  import org.apache.directory.shared.ldap.entry.Value;
40  
41  
42  /**
43   * Evaluates substring filter assertions on an entry.
44   * 
45   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
46   * @version $Rev: 658798 $
47   */
48  public class SubstringEvaluator implements Evaluator<SubstringNode, ServerEntry>
49  {
50      /** Database used while evaluating candidates */
51      private final Store<ServerEntry> db;
52      
53      /** Oid Registry used to translate attributeIds to OIDs */
54      private final Registries registries;
55  
56      /** The Substring expression */
57      private final SubstringNode node;
58  
59      /** The regular expression generated for the SubstringNode pattern */
60      private final Pattern regex;
61  
62      private final AttributeType type;
63  
64      private final Normalizer normalizer;
65  
66      private final Index<String,ServerEntry> idx;
67  
68  
69      /**
70       * Creates a new SubstringEvaluator for substring expressions.
71       *
72       * @param node the substring expression node
73       * @param db the database this evaluator uses
74       * @param registries the set of registries
75       * @throws Exception if there are failures accessing resources and the db
76       */
77      public SubstringEvaluator( SubstringNode node, Store<ServerEntry> db, Registries registries ) throws Exception
78      {
79          this.db = db;
80          this.node = node;
81          this.registries = registries;
82  
83          String oid = registries.getOidRegistry().getOid( node.getAttribute() );
84          type = registries.getAttributeTypeRegistry().lookup( oid );
85  
86          MatchingRule rule = type.getSubstr();
87  
88          if ( rule == null )
89          {
90              rule = type.getEquality();
91          }
92  
93          if ( rule != null )
94          {
95              normalizer = rule.getNormalizer();
96          }
97          else
98          {
99              normalizer = new NoOpNormalizer();
100         }
101         
102         // compile the regular expression to search for a matching attribute
103         regex = node.getRegex( normalizer );
104 
105         if ( db.hasUserIndexOn( node.getAttribute() ) )
106         {
107             //noinspection unchecked
108             idx = ( Index<String,ServerEntry> ) db.getUserIndex( node.getAttribute() );
109         }
110         else
111         {
112             idx = null;
113         }
114     }
115 
116 
117     public boolean evaluate( IndexEntry<?,ServerEntry> indexEntry ) throws Exception
118     {
119 
120         if ( idx == null )
121         {
122             //noinspection unchecked
123             return evaluateWithoutIndex( ( IndexEntry<String,ServerEntry> ) indexEntry );
124         }
125         else
126         {
127             return evaluateWithIndex( indexEntry );
128         }
129     }
130 
131 
132     public boolean evaluate( Long id ) throws Exception
133     {
134 
135         if ( idx == null )
136         {
137             //noinspection unchecked
138             return evaluateWithoutIndex( id );
139         }
140         else
141         {
142             return evaluateWithIndex( id );
143         }
144     }
145 
146 
147     public boolean evaluate( ServerEntry entry ) throws Exception
148     {
149 
150         if ( idx == null )
151         {
152             //noinspection unchecked
153             return evaluateWithoutIndex( entry );
154         }
155         else
156         {
157             return evaluateWithIndex( entry );
158         }
159     }
160 
161 
162     public Pattern getPattern()
163     {
164         return regex;
165     }
166 
167 
168     public SubstringNode getExpression()
169     {
170         return node;
171     }
172 
173 
174     private boolean evaluateWithIndex( IndexEntry<?,ServerEntry> indexEntry ) throws Exception
175     {
176         /*
177          * Note that this is using the reverse half of the index giving a
178          * considerable performance improvement on this kind of operation.
179          * Otherwise we would have to scan the entire index if there were
180          * no reverse lookups.
181          */
182         Cursor<IndexEntry<String,ServerEntry>> entries = idx.reverseCursor( indexEntry.getId() );
183 
184         // cycle through the attribute values testing for a match
185         while ( entries.next() )
186         {
187             IndexEntry rec = entries.get();
188 
189             // once match is found cleanup and return true
190             if ( regex.matcher( ( String ) rec.getValue() ).matches() )
191             {
192                 entries.close();
193                 return true;
194             }
195         }
196 
197         // we fell through so a match was not found - assertion was false.
198         return false;
199     }
200 
201 
202     @SuppressWarnings( { "UnusedDeclaration" } )
203     private boolean evaluateWithIndex( ServerEntry entry ) throws Exception
204     {
205         throw new UnsupportedOperationException( "This is too inefficient without getId() on ServerEntry" );
206     }
207 
208 
209     private boolean evaluateWithIndex( Long id ) throws Exception
210     {
211         /*
212          * Note that this is using the reverse half of the index giving a
213          * considerable performance improvement on this kind of operation.
214          * Otherwise we would have to scan the entire index if there were
215          * no reverse lookups.
216          */
217         Cursor<IndexEntry<String,ServerEntry>> entries = idx.reverseCursor( id );
218 
219         // cycle through the attribute values testing for a match
220         while ( entries.next() )
221         {
222             IndexEntry rec = entries.get();
223 
224             // once match is found cleanup and return true
225             if ( regex.matcher( ( String ) rec.getValue() ).matches() )
226             {
227                 entries.close();
228                 return true;
229             }
230         }
231 
232         // we fell through so a match was not found - assertion was false.
233         return false;
234     }
235 
236 
237     // TODO - determine if comaparator and index entry should have the Value
238     // wrapper or the raw normalized value
239     private boolean evaluateWithoutIndex( Long id ) throws Exception
240     {
241         return evaluateWithoutIndex ( db.lookup( id ) );
242     }
243 
244 
245     // TODO - determine if comaparator and index entry should have the Value
246     // wrapper or the raw normalized value
247     private boolean evaluateWithoutIndex( ServerEntry entry ) throws Exception
248     {
249         // get the attribute
250         ServerAttribute attr = ( ServerAttribute ) entry.get( type );
251 
252         // if the attribute exists and the pattern matches return true
253         if ( attr != null )
254         {
255             /*
256              * Cycle through the attribute values testing normalized version
257              * obtained from using the substring matching rule's normalizer.
258              * The test uses the comparator obtained from the appropriate
259              * substring matching rule.
260              */
261             for ( Value value : attr )
262             {
263                 value.normalize( normalizer );
264                 String strValue = ( String ) value.getNormalizedValue();
265 
266                 // Once match is found cleanup and return true
267                 if ( regex.matcher( strValue ).matches() )
268                 {
269                     return true;
270                 }
271             }
272 
273             // Fall through as we didn't find any matching value for this attribute.
274             // We will have to check in the potential descendant, if any.
275         }
276 
277         // If we do not have the attribute, loop through the descendant
278         // May be the node Attribute has descendant ?
279         if ( registries.getAttributeTypeRegistry().hasDescendants( node.getAttribute() ) )
280         {
281             // TODO check to see if descendant handling is necessary for the
282             // index so we can match properly even when for example a name
283             // attribute is used instead of more specific commonName
284             Iterator<AttributeType> descendants =
285                 registries.getAttributeTypeRegistry().descendants( node.getAttribute() );
286 
287             while ( descendants.hasNext() )
288             {
289                 AttributeType descendant = descendants.next();
290 
291                 attr = ( ServerAttribute ) entry.get( descendant );
292 
293                 if ( null != attr )
294                 {
295 
296 
297                     /*
298                      * Cycle through the attribute values testing normalized version
299                      * obtained from using the substring matching rule's normalizer.
300                      * The test uses the comparator obtained from the appropriate
301                      * substring matching rule.
302                      */
303                     for ( Value value : attr )
304                     {
305                         value.normalize( normalizer );
306                         String strValue = ( String ) value.getNormalizedValue();
307 
308                         // Once match is found cleanup and return true
309                         if ( regex.matcher( strValue ).matches() )
310                         {
311                             return true;
312                         }
313                     }
314                 }
315             }
316         }
317 
318         // we fell through so a match was not found - assertion was false.
319         return false;
320     }
321 
322 
323     // TODO - determine if comaparator and index entry should have the Value
324     // wrapper or the raw normalized value
325     private boolean evaluateWithoutIndex( IndexEntry<String,ServerEntry> indexEntry ) throws Exception
326     {
327         ServerEntry entry = indexEntry.getObject();
328 
329         // resuscitate the entry if it has not been and set entry in IndexEntry
330         if ( null == entry )
331         {
332             entry = db.lookup( indexEntry.getId() );
333             indexEntry.setObject( entry );
334         }
335 
336         /*
337          * Don't make a call here to evaluateWithoutIndex( ServerEntry ) for
338          * code reuse since we do want to set the value on the indexEntry on
339          * matches.
340          */
341 
342         // get the attribute
343         ServerAttribute attr = ( ServerAttribute ) entry.get( type );
344 
345         // if the attribute exists and the pattern matches return true
346         if ( attr != null )
347         {
348             /*
349              * Cycle through the attribute values testing normalized version
350              * obtained from using the substring matching rule's normalizer.
351              * The test uses the comparator obtained from the appropriate
352              * substring matching rule.
353              */
354             for ( Value value : attr )
355             {
356                 value.normalize( normalizer );
357                 String strValue = ( String ) value.getNormalizedValue();
358 
359                 // Once match is found cleanup and return true
360                 if ( regex.matcher( strValue ).matches() )
361                 {
362                     // before returning we set the normalized value
363                     indexEntry.setValue( strValue );
364                     return true;
365                 }
366             }
367 
368             // Fall through as we didn't find any matching value for this attribute.
369             // We will have to check in the potential descendant, if any.
370         }
371 
372         // If we do not have the attribute, loop through the descendant
373         // May be the node Attribute has descendant ?
374         if ( registries.getAttributeTypeRegistry().hasDescendants( node.getAttribute() ) )
375         {
376             // TODO check to see if descendant handling is necessary for the
377             // index so we can match properly even when for example a name
378             // attribute is used instead of more specific commonName
379             Iterator<AttributeType> descendants =
380                 registries.getAttributeTypeRegistry().descendants( node.getAttribute() );
381 
382             while ( descendants.hasNext() )
383             {
384                 AttributeType descendant = descendants.next();
385 
386                 attr = ( ServerAttribute ) entry.get( descendant );
387 
388                 if ( null != attr )
389                 {
390 
391 
392                     /*
393                      * Cycle through the attribute values testing normalized version
394                      * obtained from using the substring matching rule's normalizer.
395                      * The test uses the comparator obtained from the appropriate
396                      * substring matching rule.
397                      */
398                     for ( Value value : attr )
399                     {
400                         value.normalize( normalizer );
401                         String strValue = ( String ) value.getNormalizedValue();
402 
403                         // Once match is found cleanup and return true
404                         if ( regex.matcher( strValue ).matches() )
405                         {
406                             // before returning we set the normalized value
407                             indexEntry.setValue( strValue );
408                             return true;
409                         }
410                     }
411                 }
412             }
413         }
414 
415         // we fell through so a match was not found - assertion was false.
416         return false;
417     }
418 }