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.entry.StringValue;
028import org.apache.directory.api.ldap.model.exception.LdapException;
029import org.apache.directory.api.ldap.model.schema.AttributeType;
030import org.apache.directory.api.ldap.model.schema.Normalizer;
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 ) );
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}