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.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.entry.StringValue;
28  import org.apache.directory.api.ldap.model.exception.LdapException;
29  import org.apache.directory.api.ldap.model.schema.AttributeType;
30  import org.apache.directory.api.ldap.model.schema.Normalizer;
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 ) );
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 );
323             }
324 
325             String finalStr = null;
326 
327             if ( finalPattern != null )
328             {
329                 finalStr = normalizer.normalize( finalPattern );
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 );
340         }
341 
342         String finalStr = null;
343 
344         if ( finalPattern != null )
345         {
346             finalStr = normalizer.normalize( finalPattern );
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 == null ) || !( obj instanceof SubstringNode ) )
365         {
366             return false;
367         }
368         SubstringNode that = ( SubstringNode ) obj;
369 
370         if ( initialPattern == null )
371         {
372             if ( that.initialPattern != null )
373             {
374                 return false;
375             }
376         }
377         else
378         {
379             if ( !initialPattern.equals( that.initialPattern ) )
380             {
381                 return false;
382             }
383         }
384 
385         if ( finalPattern == null )
386         {
387             if ( that.finalPattern != null )
388             {
389                 return false;
390             }
391         }
392         else
393         {
394             if ( !finalPattern.equals( that.finalPattern ) )
395             {
396                 return false;
397             }
398         }
399 
400         return super.equals( obj );
401     }
402 
403 
404     /**
405      * @see Object#hashCode()
406      * @return the instance's hash code 
407      */
408     @Override
409     public int hashCode()
410     {
411         int h = 37;
412 
413         h = h * 17 + super.hashCode();
414         h = h * 17 + ( initialPattern != null ? initialPattern.hashCode() : 0 );
415 
416         if ( anyPattern != null )
417         {
418             for ( String pattern : anyPattern )
419             {
420                 h = h * 17 + pattern.hashCode();
421             }
422         }
423 
424         h = h * 17 + ( finalPattern != null ? finalPattern.hashCode() : 0 );
425 
426         return h;
427     }
428 
429 
430     /**
431      * @see java.lang.Object#toString()
432      * @return A string representing the AndNode
433      */
434     @Override
435     public String toString()
436     {
437         StringBuilder buf = new StringBuilder();
438 
439         buf.append( '(' );
440 
441         if ( attributeType != null )
442         {
443             buf.append( attributeType.getName() );
444         }
445         else
446         {
447             buf.append( attribute );
448         }
449 
450         buf.append( '=' );
451 
452         if ( null != initialPattern )
453         {
454             buf.append( escapeFilterValue( new StringValue( initialPattern ) ) ).append( '*' );
455         }
456         else
457         {
458             buf.append( '*' );
459         }
460 
461         if ( null != anyPattern )
462         {
463             for ( String any : anyPattern )
464             {
465                 buf.append( escapeFilterValue( new StringValue( any ) ) );
466                 buf.append( '*' );
467             }
468         }
469 
470         if ( null != finalPattern )
471         {
472             buf.append( escapeFilterValue( new StringValue( finalPattern ) ) );
473         }
474 
475         buf.append( super.toString() );
476 
477         buf.append( ')' );
478 
479         return buf.toString();
480     }
481 }