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     */
020    package org.apache.directory.shared.ldap.codec.search;
021    
022    
023    import java.nio.BufferOverflowException;
024    import java.nio.ByteBuffer;
025    import java.util.ArrayList;
026    import java.util.List;
027    
028    import org.apache.directory.shared.asn1.ber.tlv.TLV;
029    import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
030    import org.apache.directory.shared.asn1.ber.tlv.Value;
031    import org.apache.directory.shared.asn1.codec.EncoderException;
032    import org.apache.directory.shared.ldap.codec.LdapConstants;
033    import org.apache.directory.shared.ldap.util.StringTools;
034    
035    
036    /**
037     * A Object that stores the substring filter. 
038     * 
039     * A substring filter follow this
040     * grammar : 
041     * 
042     * substring = attr "=" ( ([initial] any [final] | 
043     *                        (initial [any] [final) | 
044     *                        ([initial] [any] final) ) 
045     *                       
046     * initial = value 
047     * any = "*" *(value "*")
048     * final = value
049     * 
050     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
051     * @version $Rev: 664290 $, $Date: 2008-06-07 08:28:06 +0200 (Sam, 07 jui 2008) $, 
052     */
053    public 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 = StringTools.getBytesUtf8( type ).length;
237            
238            substringsFilterLength = 1 + TLV.getNbBytes( typeLength ) + typeLength;
239            substringsFilterSequenceLength = 0;
240    
241            if ( initialSubstrings != null )
242            {
243                int initialLength = StringTools.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 = StringTools.getBytesUtf8( any ).length; 
253                    substringsFilterSequenceLength += 1 + TLV.getNbBytes( anyLength ) + anyLength;
254                }
255            }
256    
257            if ( finalSubstrings != null )
258            {
259                int finalLength = StringTools.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( "Cannot put a PDU in a null buffer !" );
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_TAG );
309                buffer.put( TLV.getBytes( substringsFilterSequenceLength ) );
310    
311                if ( ( initialSubstrings == null ) && ( ( anySubstrings == null ) || ( anySubstrings.size() == 0 ) )
312                    && ( finalSubstrings == null ) )
313                {
314                    throw new EncoderException( "Cannot have a null initial, any and final substring" );
315                }
316    
317                // The initial substring
318                if ( initialSubstrings != null )
319                {
320                    byte[] initialBytes = StringTools.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 = StringTools.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 = StringTools.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( "The PDU buffer size is too small !" );
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    }