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.filter;
21  
22  
23  import java.util.ArrayList;
24  import java.util.List;
25  import java.util.regex.Pattern;
26  
27  import org.apache.directory.api.ldap.model.exception.LdapException;
28  import org.apache.directory.api.ldap.model.schema.AttributeType;
29  import org.apache.directory.api.ldap.model.schema.Normalizer;
30  import org.apache.directory.api.ldap.model.schema.PrepareString;
31  
32  
33  /**
34   * Filter expression tree node used to represent a substring assertion.
35   * 
36   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
37   */
38  public class SubstringNode extends LeafNode
39  {
40      /** The initial fragment before any wildcard */
41      private String initialPattern;
42  
43      /** The end fragment after wildcard */
44      private String finalPattern;
45  
46      /** List of fragments between wildcard */
47      private List<String> anyPattern;
48  
49  
50      /**
51       * Creates a new SubstringNode object with only one wildcard and no internal
52       * any fragments between wildcards.
53       * 
54       * @param attributeType the name of the attributeType to substring assert
55       * @param initialPattern the initial fragment
56       * @param finalPattern the final fragment
57       */
58      public SubstringNode( AttributeType attributeType, String initialPattern, String finalPattern )
59      {
60          super( attributeType, AssertionType.SUBSTRING );
61  
62          anyPattern = new ArrayList<>( 2 );
63          this.finalPattern = finalPattern;
64          this.initialPattern = initialPattern;
65      }
66  
67  
68      /**
69       * Creates a new SubstringNode object with only one wildcard and no internal
70       * any fragments between wildcards.
71       * 
72       * @param attribute the name of the attribute to substring assert
73       * @param initialPattern the initial fragment
74       * @param finalPattern the final fragment
75       */
76      public SubstringNode( String attribute, String initialPattern, String finalPattern )
77      {
78          super( attribute, AssertionType.SUBSTRING );
79  
80          anyPattern = new ArrayList<>( 2 );
81          this.finalPattern = finalPattern;
82          this.initialPattern = initialPattern;
83      }
84  
85  
86      /**
87       * Creates a new SubstringNode object without any value
88       * 
89       * @param attribute the name of the attribute to substring assert
90       */
91      public SubstringNode( AttributeType attribute )
92      {
93          super( attribute, AssertionType.SUBSTRING );
94  
95          anyPattern = new ArrayList<>( 2 );
96          this.finalPattern = null;
97          this.initialPattern = null;
98      }
99  
100 
101     /**
102      * Creates a new SubstringNode object without any value
103      * 
104      * @param attributeType the attributeType to substring assert
105      */
106     public SubstringNode( String attributeType )
107     {
108         super( attributeType, AssertionType.SUBSTRING );
109 
110         anyPattern = new ArrayList<>( 2 );
111         this.finalPattern = null;
112         this.initialPattern = null;
113     }
114 
115 
116     /**
117      * Creates a new SubstringNode object more than one wildcard and an any
118      * list.
119      * 
120      * @param anyPattern list of internal fragments between wildcards
121      * @param attributeType the attributeType to substring assert
122      * @param initialPattern the initial fragment
123      * @param finalPattern the final fragment
124      */
125     public SubstringNode( List<String> anyPattern, AttributeType attributeType, String initialPattern,
126         String finalPattern )
127     {
128         super( attributeType, AssertionType.SUBSTRING );
129 
130         this.anyPattern = anyPattern;
131         this.finalPattern = finalPattern;
132         this.initialPattern = initialPattern;
133     }
134 
135 
136     /**
137      * Creates a new SubstringNode object more than one wildcard and an any
138      * list.
139      * 
140      * @param anyPattern list of internal fragments between wildcards
141      * @param attribute the name of the attribute to substring assert
142      * @param initialPattern the initial fragment
143      * @param finalPattern the final fragment
144      */
145     public SubstringNode( List<String> anyPattern, String attribute, String initialPattern, String finalPattern )
146     {
147         super( attribute, AssertionType.SUBSTRING );
148 
149         this.anyPattern = anyPattern;
150         this.finalPattern = finalPattern;
151         this.initialPattern = initialPattern;
152     }
153 
154 
155     /**
156      * Creates a regular expression from an LDAP substring assertion filter
157      * specification.
158      *
159      * @param initialPattern
160      *            the initial fragment before wildcards
161      * @param anyPattern
162      *            fragments surrounded by wildcards if any
163      * @param finalPattern
164      *            the final fragment after last wildcard if any
165      * @return the regular expression for the substring match filter
166      * @throws java.util.regex.PatternSyntaxException
167      *             if a syntactically correct regular expression cannot be
168      *             compiled
169      */
170     public static Pattern getRegex( String initialPattern, String[] anyPattern, String finalPattern )
171     {
172         StringBuilder buf = new StringBuilder();
173 
174         if ( initialPattern != null )
175         {
176             buf.append( '^' ).append( Pattern.quote( initialPattern ) );
177         }
178 
179         if ( anyPattern != null )
180         {
181             for ( int i = 0; i < anyPattern.length; i++ )
182             {
183                 buf.append( ".*" ).append( Pattern.quote( anyPattern[i] ) );
184             }
185         }
186 
187         if ( finalPattern != null )
188         {
189             buf.append( ".*" ).append( Pattern.quote( finalPattern ) );
190         }
191         else
192         {
193             buf.append( ".*" );
194         }
195 
196         return Pattern.compile( buf.toString() );
197     }
198 
199 
200     /**
201      * Clone the Node
202      */
203     @Override
204     public ExprNode clone()
205     {
206         ExprNode clone = super.clone();
207 
208         if ( anyPattern != null )
209         {
210             ( ( SubstringNode ) clone ).anyPattern = new ArrayList<>();
211 
212             for ( String any : anyPattern )
213             {
214                 ( ( SubstringNode ) clone ).anyPattern.add( any );
215             }
216         }
217 
218         return clone;
219     }
220 
221 
222     /**
223      * Gets the initial fragment.
224      * 
225      * @return the initial prefix
226      */
227     public final String getInitial()
228     {
229         return initialPattern;
230     }
231 
232 
233     /**
234      * Set the initial pattern
235      * @param initialPattern The initial pattern
236      */
237     public void setInitial( String initialPattern )
238     {
239         this.initialPattern = initialPattern;
240     }
241 
242 
243     /**
244      * Gets the final fragment or suffix.
245      * 
246      * @return the suffix
247      */
248     public final String getFinal()
249     {
250         return finalPattern;
251     }
252 
253 
254     /**
255      * Set the final pattern
256      * @param finalPattern The final pattern
257      */
258     public void setFinal( String finalPattern )
259     {
260         this.finalPattern = finalPattern;
261     }
262 
263 
264     /**
265      * Gets the list of wildcard surrounded any fragments.
266      * 
267      * @return the any fragments
268      */
269     public final List<String> getAny()
270     {
271         return anyPattern;
272     }
273 
274 
275     /**
276      * Set the any patterns
277      * @param anyPattern The any patterns
278      */
279     public void setAny( List<String> anyPattern )
280     {
281         this.anyPattern = anyPattern;
282     }
283 
284 
285     /**
286      * Add an any pattern
287      * @param anyPattern The any pattern
288      */
289     public void addAny( String anyPattern )
290     {
291         this.anyPattern.add( anyPattern );
292     }
293 
294 
295     /**
296      * Gets the compiled regular expression for the substring expression.
297      * 
298      * @param normalizer the normalizer to use for pattern component normalization
299      * @return the equivalent compiled regular expression
300      * @throws LdapException if there are problems while normalizing
301      */
302     public final Pattern getRegex( Normalizer normalizer ) throws LdapException
303     {
304         if ( ( anyPattern != null ) && ( !anyPattern.isEmpty() ) )
305         {
306             String[] any = new String[anyPattern.size()];
307 
308             for ( int i = 0; i < any.length; i++ )
309             {
310                 any[i] = normalizer.normalize( anyPattern.get( i ), PrepareString.AssertionType.SUBSTRING_ANY );
311 
312                 if ( any[i].length() == 0 )
313                 {
314                     any[i] = " ";
315                 }
316             }
317 
318             String initialStr = null;
319 
320             if ( initialPattern != null )
321             {
322                 initialStr = normalizer.normalize( initialPattern, PrepareString.AssertionType.SUBSTRING_INITIAL );
323             }
324 
325             String finalStr = null;
326 
327             if ( finalPattern != null )
328             {
329                 finalStr = normalizer.normalize( finalPattern, PrepareString.AssertionType.SUBSTRING_FINAL );
330             }
331 
332             return getRegex( initialStr, any, finalStr );
333         }
334 
335         String initialStr = null;
336 
337         if ( initialPattern != null )
338         {
339             initialStr = normalizer.normalize( initialPattern, PrepareString.AssertionType.SUBSTRING_INITIAL );
340         }
341 
342         String finalStr = null;
343 
344         if ( finalPattern != null )
345         {
346             finalStr = normalizer.normalize( finalPattern, PrepareString.AssertionType.SUBSTRING_FINAL );
347         }
348 
349         return getRegex( initialStr, null, finalStr );
350     }
351 
352 
353     /**
354      * {@inheritDoc}
355      */
356     @Override
357     public boolean equals( Object obj )
358     {
359         if ( obj == this )
360         {
361             return true;
362         }
363 
364         if ( !( obj instanceof SubstringNode ) )
365         {
366             return false;
367         }
368         
369         SubstringNode that = ( SubstringNode ) obj;
370 
371         if ( initialPattern == null )
372         {
373             if ( that.initialPattern != null )
374             {
375                 return false;
376             }
377         }
378         else
379         {
380             if ( !initialPattern.equals( that.initialPattern ) )
381             {
382                 return false;
383             }
384         }
385 
386         if ( finalPattern == null )
387         {
388             if ( that.finalPattern != null )
389             {
390                 return false;
391             }
392         }
393         else
394         {
395             if ( !finalPattern.equals( that.finalPattern ) )
396             {
397                 return false;
398             }
399         }
400 
401         return super.equals( obj );
402     }
403 
404 
405     /**
406      * @see Object#hashCode()
407      * @return the instance's hash code 
408      */
409     @Override
410     public int hashCode()
411     {
412         int h = 37;
413 
414         h = h * 17 + super.hashCode();
415         h = h * 17 + ( initialPattern != null ? initialPattern.hashCode() : 0 );
416 
417         if ( anyPattern != null )
418         {
419             for ( String pattern : anyPattern )
420             {
421                 h = h * 17 + pattern.hashCode();
422             }
423         }
424 
425         h = h * 17 + ( finalPattern != null ? finalPattern.hashCode() : 0 );
426 
427         return h;
428     }
429 
430 
431     /**
432      * @see java.lang.Object#toString()
433      * @return A string representing the AndNode
434      */
435     @Override
436     public String toString()
437     {
438         StringBuilder buf = new StringBuilder();
439 
440         buf.append( '(' );
441 
442         if ( attributeType != null )
443         {
444             buf.append( attributeType.getName() );
445         }
446         else
447         {
448             buf.append( attribute );
449         }
450 
451         buf.append( '=' );
452 
453         if ( null != initialPattern )
454         {
455             buf.append( escapeFilterValue( initialPattern ) ).append( '*' );
456         }
457         else
458         {
459             buf.append( '*' );
460         }
461 
462         if ( null != anyPattern )
463         {
464             for ( String any : anyPattern )
465             {
466                 buf.append( escapeFilterValue( any ) );
467                 buf.append( '*' );
468             }
469         }
470 
471         if ( null != finalPattern )
472         {
473             buf.append( escapeFilterValue( finalPattern ) );
474         }
475 
476         buf.append( super.toString() );
477 
478         buf.append( ')' );
479 
480         return buf.toString();
481     }
482 }