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