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.shared.ldap.codec.search;
021
022
023import java.nio.BufferOverflowException;
024import java.nio.ByteBuffer;
025import java.util.ArrayList;
026import java.util.List;
027
028import org.apache.directory.shared.asn1.EncoderException;
029import org.apache.directory.shared.asn1.ber.tlv.TLV;
030import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
031import org.apache.directory.shared.asn1.ber.tlv.Value;
032import org.apache.directory.shared.i18n.I18n;
033import org.apache.directory.shared.ldap.codec.api.LdapConstants;
034import org.apache.directory.shared.util.Strings;
035
036
037/**
038 * A Object that stores the substring filter. 
039 * 
040 * A substring filter follow this
041 * grammar : 
042 * 
043 * substring = attr "=" ( ([initial] any [final] | 
044 *                        (initial [any] [final) | 
045 *                        ([initial] [any] final) ) 
046 *                       
047 * initial = value 
048 * any = "*" *(value "*")
049 * final = value
050 * 
051 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
052 */
053public class SubstringFilter extends Filter
054{
055    // ~ Instance fields
056    // ----------------------------------------------------------------------------
057
058    /** The substring filter type (an attributeDescription) */
059    private String type;
060    
061    /** The type length */
062    private int typeLength;
063
064    /**
065     * This member is used to control the length of the three parts of the
066     * substring filter
067     */
068    private int substringsLength;
069
070    /** The initial filter */
071    private String initialSubstrings;
072
073    /** The any filter. It's a list of LdapString */
074    private List<String> anySubstrings = new ArrayList<String>( 1 );
075
076    /** The final filter */
077    private String finalSubstrings;
078
079    /** Temporary storage for substringsFilter length */
080    private int substringsFilterLength;
081
082    /** Temporary storage for substringsFilter sequence length */
083    private int substringsFilterSequenceLength;
084
085
086    // ~ Methods
087    // ------------------------------------------------------------------------------------
088
089    /**
090     * The constructor. We will create the 'any' subsring arraylist with only
091     * one element.
092     */
093    public SubstringFilter( int tlvId )
094    {
095        super( tlvId );
096    }
097    
098    
099    /**
100     * The constructor. We will create the 'any' subsring arraylist with only
101     * one element.
102     */
103    public SubstringFilter()
104    {
105        super();
106    }
107
108
109    /**
110     * Get the internal substrings
111     * 
112     * @return Returns the anySubstrings.
113     */
114    public List<String> getAnySubstrings()
115    {
116        return anySubstrings;
117    }
118
119
120    /**
121     * Add a internal substring
122     * 
123     * @param any The anySubstrings to set.
124     */
125    public void addAnySubstrings( String any )
126    {
127        this.anySubstrings.add( any );
128    }
129
130
131    /**
132     * Get the final substring
133     * 
134     * @return Returns the finalSubstrings.
135     */
136    public String getFinalSubstrings()
137    {
138        return finalSubstrings;
139    }
140
141
142    /**
143     * Set the final substring
144     * 
145     * @param finalSubstrings The finalSubstrings to set.
146     */
147    public void setFinalSubstrings( String finalSubstrings )
148    {
149        this.finalSubstrings = finalSubstrings;
150    }
151
152
153    /**
154     * Get the initial substring
155     * 
156     * @return Returns the initialSubstrings.
157     */
158    public String getInitialSubstrings()
159    {
160        return initialSubstrings;
161    }
162
163
164    /**
165     * Set the initial substring
166     * 
167     * @param initialSubstrings The initialSubstrings to set.
168     */
169    public void setInitialSubstrings( String initialSubstrings )
170    {
171        this.initialSubstrings = initialSubstrings;
172    }
173
174
175    /**
176     * Get the attribute
177     * 
178     * @return Returns the type.
179     */
180    public String getType()
181    {
182        return type;
183    }
184
185
186    /**
187     * Set the attribute to match
188     * 
189     * @param type The type to set.
190     */
191    public void setType( String type )
192    {
193        this.type = type;
194    }
195
196
197    /**
198     * @return Returns the substringsLength.
199     */
200    public int getSubstringsLength()
201    {
202        return substringsLength;
203    }
204
205
206    /**
207     * @param substringsLength The substringsLength to set.
208     */
209    public void setSubstringsLength( int substringsLength )
210    {
211        this.substringsLength = substringsLength;
212    }
213
214
215    /**
216     * Compute the SubstringFilter length 
217     * 
218     * SubstringFilter : 
219     * 0xA4 L1 
220     *   | 
221     *   +--> 0x04 L2 type 
222     *   +--> 0x30 L3 
223     *          | 
224     *         [+--> 0x80 L4 initial] 
225     *         [+--> 0x81 L5-1 any] 
226     *         [+--> 0x81 L5-2 any] 
227     *         [+--> ... 
228     *         [+--> 0x81 L5-i any] 
229     *         [+--> ... 
230     *         [+--> 0x81 L5-n any] 
231     *         [+--> 0x82 L6 final]
232     */
233    public int computeLength()
234    {
235        // The type
236        typeLength = Strings.getBytesUtf8(type).length;
237        
238        substringsFilterLength = 1 + TLV.getNbBytes( typeLength ) + typeLength;
239        substringsFilterSequenceLength = 0;
240
241        if ( initialSubstrings != null )
242        {
243            int initialLength = Strings.getBytesUtf8(initialSubstrings).length;
244            substringsFilterSequenceLength += 1 + TLV.getNbBytes( initialLength )
245                + initialLength;
246        }
247
248        if ( anySubstrings != null )
249        {
250            for ( String any:anySubstrings )
251            {
252                int anyLength = Strings.getBytesUtf8(any).length;
253                substringsFilterSequenceLength += 1 + TLV.getNbBytes( anyLength ) + anyLength;
254            }
255        }
256
257        if ( finalSubstrings != null )
258        {
259            int finalLength = Strings.getBytesUtf8(finalSubstrings).length;
260            substringsFilterSequenceLength += 1 + TLV.getNbBytes( finalLength )
261                + finalLength;
262        }
263
264        substringsFilterLength += 1 + TLV.getNbBytes( substringsFilterSequenceLength )
265            + substringsFilterSequenceLength;
266
267        return 1 + TLV.getNbBytes( substringsFilterLength ) + substringsFilterLength;
268    }
269
270
271    /**
272     * Encode the Substrings Filter to a PDU. 
273     * 
274     * Substrings Filter :
275     * 
276     * 0xA4 LL 
277     * 0x30 LL substringsFilter
278     *   0x04 LL type
279     *   0x30 LL substrings sequence
280     *    |  0x80 LL initial
281     *    | /  [0x81 LL any]* 
282     *    |/   [0x82 LL final]
283     *    +--[0x81 LL any]+
284     *     \   [0x82 LL final]
285     *      \
286     *       0x82 LL final
287     * 
288     * @param buffer The buffer where to put the PDU
289     * @return The PDU.
290     */
291    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
292    {
293        if ( buffer == null )
294        {
295            throw new EncoderException( I18n.err( I18n.ERR_04023 ) );
296        }
297
298        try
299        {
300            // The SubstringFilter Tag
301            buffer.put( ( byte ) LdapConstants.SUBSTRINGS_FILTER_TAG );
302            buffer.put( TLV.getBytes( substringsFilterLength ) );
303
304            // The type
305            Value.encode( buffer, type.getBytes() );
306
307            // The SubstringSequenceFilter Tag
308            buffer.put( UniversalTag.SEQUENCE.getValue() );
309            buffer.put( TLV.getBytes( substringsFilterSequenceLength ) );
310
311            if ( ( initialSubstrings == null ) && ( ( anySubstrings == null ) || ( anySubstrings.size() == 0 ) )
312                && ( finalSubstrings == null ) )
313            {
314                throw new EncoderException( I18n.err( I18n.ERR_04058 ) );
315            }
316
317            // The initial substring
318            if ( initialSubstrings != null )
319            {
320                byte[] initialBytes = Strings.getBytesUtf8(initialSubstrings);
321                buffer.put( ( byte ) LdapConstants.SUBSTRINGS_FILTER_INITIAL_TAG );
322                buffer.put( TLV.getBytes( initialBytes.length ) );
323                buffer.put( initialBytes );
324            }
325
326            // The any substrings
327            if ( anySubstrings != null )
328            {
329                for ( String any:anySubstrings )
330                {
331                    byte[] anyBytes = Strings.getBytesUtf8(any);
332                    buffer.put( ( byte ) LdapConstants.SUBSTRINGS_FILTER_ANY_TAG );
333                    buffer.put( TLV.getBytes( anyBytes.length ) );
334                    buffer.put( anyBytes );
335                }
336            }
337
338            // The final substring
339            if ( finalSubstrings != null )
340            {
341                byte[] finalBytes = Strings.getBytesUtf8(finalSubstrings);
342                buffer.put( ( byte ) LdapConstants.SUBSTRINGS_FILTER_FINAL_TAG );
343                buffer.put( TLV.getBytes( finalBytes.length ) );
344                buffer.put( finalBytes );
345            }
346        }
347        catch ( BufferOverflowException boe )
348        {
349            throw new EncoderException( I18n.err( I18n.ERR_04005 ) );
350        }
351
352        return buffer;
353    }
354
355
356    /**
357     * Return a string compliant with RFC 2254 representing a Substring filter
358     * 
359     * @return The substring filter string
360     */
361    public String toString()
362    {
363
364        StringBuffer sb = new StringBuffer();
365
366        if ( initialSubstrings != null )
367        {
368            sb.append( initialSubstrings );
369        }
370
371        sb.append( '*' );
372
373        if ( anySubstrings != null )
374        {
375            for ( String any:anySubstrings )
376            {
377                sb.append( any ).append( '*' );
378            }
379        }
380
381        if ( finalSubstrings != null )
382        {
383            sb.append( finalSubstrings );
384        }
385
386        return sb.toString();
387    }
388}