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.evaluator;
21  
22  
23  import java.util.Iterator;
24  import java.util.regex.Pattern;
25  
26  import org.apache.directory.api.ldap.model.entry.Attribute;
27  import org.apache.directory.api.ldap.model.entry.Entry;
28  import org.apache.directory.api.ldap.model.entry.Value;
29  import org.apache.directory.api.ldap.model.exception.LdapException;
30  import org.apache.directory.api.ldap.model.filter.SubstringNode;
31  import org.apache.directory.api.ldap.model.schema.AttributeType;
32  import org.apache.directory.api.ldap.model.schema.MatchingRule;
33  import org.apache.directory.api.ldap.model.schema.Normalizer;
34  import org.apache.directory.api.ldap.model.schema.SchemaManager;
35  import org.apache.directory.api.ldap.model.schema.normalizers.NoOpNormalizer;
36  import org.apache.directory.server.core.api.partition.PartitionTxn;
37  import org.apache.directory.server.xdbm.IndexEntry;
38  import org.apache.directory.server.xdbm.Store;
39  import org.apache.directory.server.xdbm.search.Evaluator;
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   */
47  public class SubstringEvaluator implements Evaluator<SubstringNode>
48  {
49      /** Database used while evaluating candidates */
50      private final Store db;
51  
52      /** Reference to the SchemaManager */
53      private final SchemaManager schemaManager;
54  
55      /** The Substring expression */
56      private final SubstringNode node;
57  
58      /** The regular expression generated for the SubstringNode pattern */
59      private final Pattern regex;
60  
61      /** The AttributeType we will use for the evaluation */
62      private final AttributeType attributeType;
63  
64      /** The associated normalizer */
65      private final Normalizer normalizer;
66  
67  
68      /**
69       * Creates a new SubstringEvaluator for substring expressions.
70       *
71       * @param node the substring expression node
72       * @param db the database this evaluator uses
73       * @param schemaManager the schema manager
74       * @throws LdapException if there are failures accessing resources and the db
75       */
76      public SubstringEvaluator( SubstringNode node, Store db, SchemaManager schemaManager ) throws LdapException
77      {
78          this.db = db;
79          this.node = node;
80          this.schemaManager = schemaManager;
81          this.attributeType = node.getAttributeType();
82  
83          MatchingRule rule = attributeType.getSubstring();
84  
85          if ( rule == null )
86          {
87              rule = attributeType.getEquality();
88          }
89  
90          if ( rule != null )
91          {
92              normalizer = rule.getNormalizer();
93          }
94          else
95          {
96              normalizer = new NoOpNormalizer( attributeType.getSyntaxOid() );
97          }
98  
99          // compile the regular expression to search for a matching attribute
100         // if the attributeType is humanReadable
101         if ( attributeType.getSyntax().isHumanReadable() )
102         {
103             regex = node.getRegex( normalizer );
104         }
105         else
106         {
107             regex = null;
108         }
109     }
110 
111 
112     /**
113      * {@inheritDoc}
114      */
115     @SuppressWarnings("unchecked")
116     @Override
117     public boolean evaluate( PartitionTxn partitionTxn, IndexEntry<?, String> indexEntryQM ) throws LdapException
118     {
119         IndexEntry<String, String> indexEntry = ( IndexEntry<String, String> ) indexEntryQM;
120 
121         Entry entry = indexEntry.getEntry();
122 
123         // resuscitate the entry if it has not been and set entry in IndexEntry
124         if ( null == entry )
125         {
126             entry = db.fetch( partitionTxn, indexEntry.getId() );
127 
128             if ( null == entry )
129             {
130                 // The entry is not anymore present : get out
131                 return false;
132             }
133 
134             indexEntry.setEntry( entry );
135         }
136 
137         /*
138          * Don't make a call here to evaluateWithoutIndex( Entry ) for
139          * code reuse since we do want to set the value on the indexEntry on
140          * matches.
141          */
142 
143         // get the attribute
144         Attribute attr = entry.get( attributeType );
145 
146         // if the attribute exists and the pattern matches return true
147         if ( attr != null )
148         {
149             /*
150              * Cycle through the attribute values testing normalized version
151              * obtained from using the substring matching rule's normalizer.
152              * The test uses the comparator obtained from the appropriate
153              * substring matching rule.
154              */
155             if ( attr.isHumanReadable() )
156             {
157                 for ( Value value : attr )
158                 {
159                     String strValue = value.getString();
160                     String normalizedValue = attr.getAttributeType().getEquality().getNormalizer().normalize( strValue );
161 
162                     // Once match is found cleanup and return true
163                     if ( regex.matcher( normalizedValue ).matches() )
164                     {
165                         // before returning we set the normalized value
166                         indexEntry.setKey( strValue );
167                         return true;
168                     }
169                 }
170             }
171             else
172             {
173                 // Slightly more complex. We won't be able to use a regex to check
174                 // the value.
175                 for ( Value value : attr )
176                 {
177                     byte[] byteValue = value.getBytes();
178 
179                     // Once match is found cleanup and return true
180                     // @TODO : implement this check.
181                     /*
182                     if ( check( byteValue ) )
183                     {
184                         // before returning we set the normalized value
185                         indexEntry.setKey( byteValue );
186                         return true;
187                     }
188                     */
189                 }
190             }
191 
192             // Fall through as we didn't find any matching value for this attribute.
193             // We will have to check in the potential descendant, if any.
194         }
195 
196         // If we do not have the attribute, loop through the descendant
197         // May be the node Attribute has descendant ?
198         if ( schemaManager.getAttributeTypeRegistry().hasDescendants( attributeType ) )
199         {
200             // TODO check to see if descendant handling is necessary for the
201             // index so we can match properly even when for example a name
202             // attribute is used instead of more specific commonName
203             Iterator<AttributeType> descendants = schemaManager.getAttributeTypeRegistry().descendants( attributeType );
204 
205             while ( descendants.hasNext() )
206             {
207                 AttributeType descendant = descendants.next();
208 
209                 attr = entry.get( descendant );
210 
211                 if ( null != attr )
212                 {
213 
214                     /*
215                      * Cycle through the attribute values testing normalized version
216                      * obtained from using the substring matching rule's normalizer.
217                      * The test uses the comparator obtained from the appropriate
218                      * substring matching rule.
219                      */
220                     for ( Value value : attr )
221                     {
222                         String strValue = value.getString();
223                         String normalizedValue = attr.getAttributeType().getEquality().getNormalizer().normalize( strValue );
224 
225                         // Once match is found cleanup and return true
226                         if ( regex.matcher( normalizedValue ).matches() )
227                         {
228                             // before returning we set the normalized value
229                             indexEntry.setKey( strValue );
230                             return true;
231                         }
232                     }
233                 }
234             }
235         }
236 
237         // we fell through so a match was not found - assertion was false.
238         return false;
239     }
240 
241 
242     /**
243      * {@inheritDoc}
244      */
245     @Override
246     public boolean evaluate( Entry entry ) throws LdapException
247     {
248         // get the attribute
249         Attribute attr = entry.get( attributeType );
250 
251         // if the attribute exists and the pattern matches return true
252         if ( attr != null )
253         {
254             /*
255              * Cycle through the attribute values testing normalized version
256              * obtained from using the substring matching rule's normalizer.
257              * The test uses the comparator obtained from the appropriate
258              * substring matching rule.
259              */
260             for ( Value value : attr )
261             {
262                 String strValue = value.getString();
263 
264                 // Once match is found cleanup and return true
265                 if ( regex.matcher( strValue ).matches() )
266                 {
267                     return true;
268                 }
269             }
270 
271             // Fall through as we didn't find any matching value for this attribute.
272             // We will have to check in the potential descendant, if any.
273         }
274 
275         // If we do not have the attribute, loop through the descendant
276         // May be the node Attribute has descendant ?
277         if ( schemaManager.getAttributeTypeRegistry().hasDescendants( attributeType ) )
278         {
279             // TODO check to see if descendant handling is necessary for the
280             // index so we can match properly even when for example a name
281             // attribute is used instead of more specific commonName
282             Iterator<AttributeType> descendants = schemaManager.getAttributeTypeRegistry().descendants( attributeType );
283 
284             while ( descendants.hasNext() )
285             {
286                 AttributeType descendant = descendants.next();
287 
288                 attr = entry.get( descendant );
289 
290                 if ( null != attr )
291                 {
292 
293                     /*
294                      * Cycle through the attribute values testing normalized version
295                      * obtained from using the substring matching rule's normalizer.
296                      * The test uses the comparator obtained from the appropriate
297                      * substring matching rule.
298                      */
299                     for ( Value value : attr )
300                     {
301                         String strValue = value.getString();
302 
303                         // Once match is found cleanup and return true
304                         if ( regex.matcher( strValue ).matches() )
305                         {
306                             return true;
307                         }
308                     }
309                 }
310             }
311         }
312 
313         // we fell through so a match was not found - assertion was false.
314         return false;
315     }
316 
317 
318     public Pattern getPattern()
319     {
320         return regex;
321     }
322 
323 
324     /**
325      * {@inheritDoc}
326      */
327     @Override
328     public SubstringNode getExpression()
329     {
330         return node;
331     }
332 
333 
334     /**
335      * @see Object#toString()
336      */
337     @Override
338     public String toString( String tabs )
339     {
340         StringBuilder sb = new StringBuilder();
341 
342         sb.append( tabs ).append( "SubstringEvaluator : " ).append( node ).append( "\n" );
343 
344         return sb.toString();
345     }
346 
347 
348     /**
349      * @see Object#toString()
350      */
351     @Override
352     public String toString()
353     {
354         return toString( "" );
355     }
356 }