001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *  
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *  
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License. 
018 *  
019 */
020package org.apache.directory.api.ldap.model.filter;
021
022
023import java.util.ArrayList;
024import java.util.List;
025import java.util.regex.Pattern;
026
027import org.apache.directory.api.ldap.model.exception.LdapException;
028import org.apache.directory.api.ldap.model.schema.AttributeType;
029import org.apache.directory.api.ldap.model.schema.Normalizer;
030import org.apache.directory.api.ldap.model.schema.PrepareString;
031
032
033/**
034 * Filter expression tree node used to represent a substring assertion.
035 * 
036 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
037 */
038public class SubstringNode extends LeafNode
039{
040    /** The initial fragment before any wildcard */
041    private String initialPattern;
042
043    /** The end fragment after wildcard */
044    private String finalPattern;
045
046    /** List of fragments between wildcard */
047    private List<String> anyPattern;
048
049
050    /**
051     * Creates a new SubstringNode object with only one wildcard and no internal
052     * any fragments between wildcards.
053     * 
054     * @param attributeType the name of the attributeType to substring assert
055     * @param initialPattern the initial fragment
056     * @param finalPattern the final fragment
057     */
058    public SubstringNode( AttributeType attributeType, String initialPattern, String finalPattern )
059    {
060        super( attributeType, AssertionType.SUBSTRING );
061
062        anyPattern = new ArrayList<>( 2 );
063        this.finalPattern = finalPattern;
064        this.initialPattern = initialPattern;
065    }
066
067
068    /**
069     * Creates a new SubstringNode object with only one wildcard and no internal
070     * any fragments between wildcards.
071     * 
072     * @param attribute the name of the attribute to substring assert
073     * @param initialPattern the initial fragment
074     * @param finalPattern the final fragment
075     */
076    public SubstringNode( String attribute, String initialPattern, String finalPattern )
077    {
078        super( attribute, AssertionType.SUBSTRING );
079
080        anyPattern = new ArrayList<>( 2 );
081        this.finalPattern = finalPattern;
082        this.initialPattern = initialPattern;
083    }
084
085
086    /**
087     * Creates a new SubstringNode object without any value
088     * 
089     * @param attribute the name of the attribute to substring assert
090     */
091    public SubstringNode( AttributeType attribute )
092    {
093        super( attribute, AssertionType.SUBSTRING );
094
095        anyPattern = new ArrayList<>( 2 );
096        this.finalPattern = null;
097        this.initialPattern = null;
098    }
099
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}