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;
026import java.util.regex.PatternSyntaxException;
027
028import org.apache.directory.api.ldap.model.entry.StringValue;
029import org.apache.directory.api.ldap.model.exception.LdapException;
030import org.apache.directory.api.ldap.model.schema.AttributeType;
031import org.apache.directory.api.ldap.model.schema.Normalizer;
032
033
034/**
035 * Filter expression tree node used to represent a substring assertion.
036 * 
037 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
038 */
039public class SubstringNode extends LeafNode
040{
041    /** The initial fragment before any wildcard */
042    private String initialPattern;
043
044    /** The end fragment after wildcard */
045    private String finalPattern;
046
047    /** List of fragments between wildcard */
048    private List<String> anyPattern;
049
050
051    /**
052     * Creates a new SubstringNode object with only one wildcard and no internal
053     * any fragments between wildcards.
054     * 
055     * @param attributeType the name of the attributeType to substring assert
056     * @param initialPattern the initial fragment
057     * @param finalPattern the final fragment
058     */
059    public SubstringNode( AttributeType attributeType, String initialPattern, String finalPattern )
060    {
061        super( attributeType, AssertionType.SUBSTRING );
062
063        anyPattern = new ArrayList<String>( 2 );
064        this.finalPattern = finalPattern;
065        this.initialPattern = initialPattern;
066    }
067
068
069    /**
070     * Creates a new SubstringNode object with only one wildcard and no internal
071     * any fragments between wildcards.
072     * 
073     * @param attribute the name of the attribute to substring assert
074     * @param initialPattern the initial fragment
075     * @param finalPattern the final fragment
076     */
077    public SubstringNode( String attribute, String initialPattern, String finalPattern )
078    {
079        super( attribute, AssertionType.SUBSTRING );
080
081        anyPattern = new ArrayList<String>( 2 );
082        this.finalPattern = finalPattern;
083        this.initialPattern = initialPattern;
084    }
085
086
087    /**
088     * Creates a new SubstringNode object without any value
089     * 
090     * @param attribute the name of the attribute to substring assert
091     */
092    public SubstringNode( AttributeType attribute )
093    {
094        super( attribute, AssertionType.SUBSTRING );
095
096        anyPattern = new ArrayList<String>( 2 );
097        this.finalPattern = null;
098        this.initialPattern = null;
099    }
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}