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<>( 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    /**
081     * The constructor. We will create the 'any' subsring arraylist with only
082     * one element.
083     * 
084     * @param tlvId The TLV identifier
085     */
086    public SubstringFilter( int tlvId )
087    {
088        super( tlvId );
089    }
090
091
092    /**
093     * The constructor. We will create the 'any' subsring arraylist with only
094     * one element.
095     */
096    public SubstringFilter()
097    {
098        super();
099    }
100
101
102    /**
103     * Get the internal substrings
104     * 
105     * @return Returns the anySubstrings.
106     */
107    public List<String> getAnySubstrings()
108    {
109        return anySubstrings;
110    }
111
112
113    /**
114     * Add a internal substring
115     * 
116     * @param any The anySubstrings to set.
117     */
118    public void addAnySubstrings( String any )
119    {
120        this.anySubstrings.add( any );
121    }
122
123
124    /**
125     * Get the final substring
126     * 
127     * @return Returns the finalSubstrings.
128     */
129    public String getFinalSubstrings()
130    {
131        return finalSubstrings;
132    }
133
134
135    /**
136     * Set the final substring
137     * 
138     * @param finalSubstrings The finalSubstrings to set.
139     */
140    public void setFinalSubstrings( String finalSubstrings )
141    {
142        this.finalSubstrings = finalSubstrings;
143    }
144
145
146    /**
147     * Get the initial substring
148     * 
149     * @return Returns the initialSubstrings.
150     */
151    public String getInitialSubstrings()
152    {
153        return initialSubstrings;
154    }
155
156
157    /**
158     * Set the initial substring
159     * 
160     * @param initialSubstrings The initialSubstrings to set.
161     */
162    public void setInitialSubstrings( String initialSubstrings )
163    {
164        this.initialSubstrings = initialSubstrings;
165    }
166
167
168    /**
169     * Get the attribute
170     * 
171     * @return Returns the type.
172     */
173    public String getType()
174    {
175        return type;
176    }
177
178
179    /**
180     * Set the attribute to match
181     * 
182     * @param type The type to set.
183     */
184    public void setType( String type )
185    {
186        this.type = type;
187    }
188
189
190    /**
191     * @return Returns the substringsLength.
192     */
193    public int getSubstringsLength()
194    {
195        return substringsLength;
196    }
197
198
199    /**
200     * @param substringsLength The substringsLength to set.
201     */
202    public void setSubstringsLength( int substringsLength )
203    {
204        this.substringsLength = substringsLength;
205    }
206
207
208    /**
209     * Compute the SubstringFilter length 
210     * <br>
211     * SubstringFilter :
212     * <pre> 
213     * 0xA4 L1 
214     *   | 
215     *   +--&gt; 0x04 L2 type 
216     *   +--&gt; 0x30 L3 
217     *          | 
218     *         [+--&gt; 0x80 L4 initial] 
219     *         [+--&gt; 0x81 L5-1 any] 
220     *         [+--&gt; 0x81 L5-2 any] 
221     *         [+--&gt; ... 
222     *         [+--&gt; 0x81 L5-i any] 
223     *         [+--&gt; ... 
224     *         [+--&gt; 0x81 L5-n any] 
225     *         [+--&gt; 0x82 L6 final]
226     * </pre>
227     * 
228     * @return The encoded length
229     */
230    @Override
231    public int computeLength()
232    {
233        // The type
234        int typeLength = Strings.getBytesUtf8( type ).length;
235
236        substringsFilterLength = 1 + TLV.getNbBytes( typeLength ) + typeLength;
237        substringsFilterSequenceLength = 0;
238
239        if ( initialSubstrings != null )
240        {
241            int initialLength = Strings.getBytesUtf8( initialSubstrings ).length;
242            substringsFilterSequenceLength += 1 + TLV.getNbBytes( initialLength )
243                + initialLength;
244        }
245
246        if ( anySubstrings != null )
247        {
248            for ( String any : anySubstrings )
249            {
250                int anyLength = Strings.getBytesUtf8( any ).length;
251                substringsFilterSequenceLength += 1 + TLV.getNbBytes( anyLength ) + anyLength;
252            }
253        }
254
255        if ( finalSubstrings != null )
256        {
257            int finalLength = Strings.getBytesUtf8( finalSubstrings ).length;
258            substringsFilterSequenceLength += 1 + TLV.getNbBytes( finalLength )
259                + finalLength;
260        }
261
262        substringsFilterLength += 1 + TLV.getNbBytes( substringsFilterSequenceLength )
263            + substringsFilterSequenceLength;
264
265        return 1 + TLV.getNbBytes( substringsFilterLength ) + substringsFilterLength;
266    }
267
268
269    /**
270     * Encode the Substrings Filter to a PDU. 
271     * <br>
272     * Substrings Filter :
273     * <pre>
274     * 0xA4 LL 
275     * 0x30 LL substringsFilter
276     *   0x04 LL type
277     *   0x30 LL substrings sequence
278     *    |  0x80 LL initial
279     *    | /  [0x81 LL any]* 
280     *    |/   [0x82 LL final]
281     *    +--[0x81 LL any]+
282     *     \   [0x82 LL final]
283     *      \
284     *       0x82 LL final
285     * </pre>
286     * 
287     * @param buffer The buffer where to put the PDU
288     * @return The PDU.
289     */
290    @Override
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 ) LdapCodecConstants.SUBSTRINGS_FILTER_TAG );
302            buffer.put( TLV.getBytes( substringsFilterLength ) );
303
304            // The type
305            BerValue.encode( buffer, Strings.getBytesUtf8( type ) );
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.isEmpty() )
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 ) LdapCodecConstants.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 ) LdapCodecConstants.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 ) LdapCodecConstants.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 ), boe );
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    @Override
362    public String toString()
363    {
364
365        StringBuilder sb = new StringBuilder();
366
367        if ( initialSubstrings != null )
368        {
369            sb.append( initialSubstrings );
370        }
371
372        sb.append( '*' );
373
374        if ( anySubstrings != null )
375        {
376            for ( String any : anySubstrings )
377            {
378                sb.append( any ).append( '*' );
379            }
380        }
381
382        if ( finalSubstrings != null )
383        {
384            sb.append( finalSubstrings );
385        }
386
387        return sb.toString();
388    }
389}