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