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.factory;
021
022import java.util.Iterator;
023import java.util.List;
024
025import org.apache.directory.api.asn1.ber.tlv.BerValue;
026import org.apache.directory.api.asn1.util.Asn1Buffer;
027import org.apache.directory.api.ldap.codec.api.LdapApiService;
028import org.apache.directory.api.ldap.codec.api.LdapCodecConstants;
029import org.apache.directory.api.ldap.model.filter.AndNode;
030import org.apache.directory.api.ldap.model.filter.ApproximateNode;
031import org.apache.directory.api.ldap.model.filter.BranchNode;
032import org.apache.directory.api.ldap.model.filter.EqualityNode;
033import org.apache.directory.api.ldap.model.filter.ExprNode;
034import org.apache.directory.api.ldap.model.filter.ExtensibleNode;
035import org.apache.directory.api.ldap.model.filter.GreaterEqNode;
036import org.apache.directory.api.ldap.model.filter.LessEqNode;
037import org.apache.directory.api.ldap.model.filter.NotNode;
038import org.apache.directory.api.ldap.model.filter.OrNode;
039import org.apache.directory.api.ldap.model.filter.PresenceNode;
040import org.apache.directory.api.ldap.model.filter.SimpleNode;
041import org.apache.directory.api.ldap.model.filter.SubstringNode;
042import org.apache.directory.api.ldap.model.message.Message;
043import org.apache.directory.api.ldap.model.message.SearchRequest;
044import org.apache.directory.api.util.Strings;
045
046/**
047 * The SearchRequest factory.
048 *
049 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
050 */
051public final class SearchRequestFactory implements Messagefactory
052{
053    /** The static instance */
054    public static final SearchRequestFactory INSTANCE = new SearchRequestFactory();
055
056    private SearchRequestFactory()
057    {
058        // Nothing to do
059    }
060
061    /**
062     * Recursively encode the children of a connector node (AND, OR, NOT)
063     *
064     * @param buffer The buffer where to put the PDU
065     * @param children The children to encode
066     */
067    private void encodeFilters( Asn1Buffer buffer, Iterator<ExprNode> children )
068    {
069        if ( children.hasNext() )
070        {
071            ExprNode child = children.next();
072
073            // Recurse
074            encodeFilters( buffer, children );
075
076            // And finally the child, at the right position
077            encodeFilter( buffer, child );
078        }
079    }
080
081
082    /**
083     * Encode a BranchNode.
084     * <br>
085     * BranchFilter :
086     * <pre>
087     * 0xA1/0xA2/0xA3 LL
088     *  filter.encode() ... filter.encode()
089     * </pre>
090     *
091     * @param buffer The buffer where to put the PDU
092     * @param node The Branch filter to encode
093     * @param tag the ASN.1 type
094     */
095    private void encodeFilter( Asn1Buffer buffer, BranchNode node, byte tag )
096    {
097        int start = buffer.getPos();
098
099        // encode each filter
100        List<ExprNode> children = node.getChildren();
101
102        if ( ( children != null ) && ( !children.isEmpty() ) )
103        {
104            encodeFilters( buffer, children.iterator() );
105        }
106
107        // The BranchNode sequence
108        BerValue.encodeSequence( buffer, tag, start );
109    }
110
111
112    /**
113     * Encode a SimpleNode.
114     * <br>
115     * SimpleFilter :
116     * <pre>
117     * 0xA3/0xA5/0xA6/A8 LL
118     *   0x04 LL attributeDesc
119     *   0x04 LL assertionValue
120     * </pre>
121     *
122     * @param buffer The buffer where to put the PDU
123     * @param node The Simple filter to encode
124     * @param tag the ASN.1 type
125     */
126    private void encodeFilter( Asn1Buffer buffer, SimpleNode<?> node, byte tag )
127    {
128        int start = buffer.getPos();
129
130        // The attribute desc
131        BerValue.encodeOctetString( buffer, node.getValue().getBytes() );
132
133        // The assertion desc
134        BerValue.encodeOctetString( buffer, node.getAttribute() );
135
136        // The EqualityNode sequence
137        BerValue.encodeSequence( buffer, tag, start );
138    }
139
140
141    /**
142     * Encode a PresenceNode.
143     * <br>
144     * PresentFilter :
145     * <pre>
146     * 0x87 L1 present
147     * </pre>
148     *
149     * @param buffer The buffer where to put the PDU
150     * @param node The Presence filter to encode
151     */
152    private void encodeFilter( Asn1Buffer buffer, PresenceNode node )
153    {
154        // The PresentFilter Tag
155        BerValue.encodeOctetString( buffer, ( byte ) LdapCodecConstants.PRESENT_FILTER_TAG,
156            Strings.getBytesUtf8( node.getAttribute() ) );
157    }
158
159
160    /**
161     * Encode a SubstringNode.
162     * <br>
163     * Substrings Filter :
164     * <pre>
165     * 0xA4 LL
166     *   0x04 LL type
167     *   0x30 LL substrings sequence
168     *    |  0x80 LL initial
169     *    | /  [0x81 LL any]*
170     *    |/   [0x82 LL final]
171     *    +--[0x81 LL any]+
172     *     \   [0x82 LL final]
173     *      \
174     *       0x82 LL final
175     * </pre>
176     *
177     * @param buffer The buffer where to put the PDU
178     * @param node The Substring filter to encode
179     */
180    private void encodeFilter( Asn1Buffer buffer, SubstringNode node )
181    {
182        int start = buffer.getPos();
183
184        // The final
185        if ( node.getFinal() != null )
186        {
187            BerValue.encodeOctetString( buffer, ( byte ) LdapCodecConstants.SUBSTRINGS_FILTER_FINAL_TAG,
188                Strings.getBytesUtf8( node.getFinal() ) );
189        }
190
191        // The any
192        List<String> any = node.getAny();
193
194        if ( any != null )
195        {
196            for ( int i = any.size(); i > 0; i-- )
197            {
198                BerValue.encodeOctetString( buffer, ( byte ) LdapCodecConstants.SUBSTRINGS_FILTER_ANY_TAG,
199                    Strings.getBytesUtf8( any.get( i - 1 ) ) );
200            }
201        }
202
203        // The initial
204        if ( node.getInitial() != null )
205        {
206            BerValue.encodeOctetString( buffer, ( byte ) LdapCodecConstants.SUBSTRINGS_FILTER_INITIAL_TAG,
207                Strings.getBytesUtf8( node.getInitial() ) );
208        }
209
210        // The Substring sequence
211        BerValue.encodeSequence( buffer, start );
212
213        // The type
214        BerValue.encodeOctetString( buffer, node.getAttribute() );
215
216        // The EqualityNode sequence
217        BerValue.encodeSequence( buffer, ( byte ) LdapCodecConstants.SUBSTRINGS_FILTER_TAG, start );
218    }
219
220
221    /**
222     * Encode an ExtensibleNode.
223     * <br>
224     * ExtensibleMatch filter :
225     * <pre>
226     * 0xA9 L1
227     *   |
228     *  [+--&gt; 0x81 L3 matchingRule]
229     *  [+--&gt; 0x82 L4 type]
230     *  [+--&gt; 0x83 L5 matchValue]
231     *  [+--&gt; 0x01 0x01 dnAttributes]
232     * </pre>
233     *
234     * @param buffer The buffer where to put the PDU
235     * @param node The ExtensibleMatch filter to encode
236     */
237    private void encodeFilter( Asn1Buffer buffer, ExtensibleNode node )
238    {
239        int start = buffer.getPos();
240
241        // The dnAttributes flag, if true only
242        if ( node.hasDnAttributes() )
243        {
244            BerValue.encodeBoolean( buffer, ( byte ) LdapCodecConstants.DN_ATTRIBUTES_FILTER_TAG, true );
245        }
246
247        // The matching value
248        if ( node.getValue() != null )
249        {
250            BerValue.encodeOctetString( buffer, ( byte ) LdapCodecConstants.MATCH_VALUE_TAG,
251                node.getValue().getBytes() );
252        }
253
254        // The type
255        if ( node.getAttribute() != null )
256        {
257            BerValue.encodeOctetString( buffer, ( byte ) LdapCodecConstants.MATCHING_RULE_TYPE_TAG,
258                Strings.getBytesUtf8( node.getAttribute() ) );
259        }
260
261        // The matching rule
262        if ( node.getMatchingRuleId() != null )
263        {
264            BerValue.encodeOctetString( buffer, ( byte ) LdapCodecConstants.MATCHING_RULE_ID_TAG,
265                Strings.getBytesUtf8( node.getMatchingRuleId() ) );
266        }
267
268        // The EqualityNode sequence
269        BerValue.encodeSequence( buffer, ( byte ) LdapCodecConstants.EXTENSIBLE_MATCH_FILTER_TAG, start );
270    }
271
272
273    /**
274     * Encode a Search Filter
275     *
276     * @param buffer The buffer where to put the PDU
277     * @param node The top filter
278     */
279    private void encodeFilter( Asn1Buffer buffer, ExprNode node )
280    {
281        switch ( node.getClass().getSimpleName() )
282        {
283            case "AndNode" :
284                encodeFilter( buffer, ( AndNode ) node, ( byte ) LdapCodecConstants.AND_FILTER_TAG );
285                break;
286
287            case "ApproximateNode" :
288                encodeFilter( buffer, ( ApproximateNode<?> ) node, ( byte ) LdapCodecConstants.APPROX_MATCH_FILTER_TAG );
289                break;
290
291            case "EqualityNode" :
292                encodeFilter( buffer, ( EqualityNode<?> ) node, ( byte ) LdapCodecConstants.EQUALITY_MATCH_FILTER_TAG );
293                break;
294
295            case "ExtensibleNode" :
296                encodeFilter( buffer, ( ExtensibleNode ) node );
297                break;
298
299            case "GreaterEqNode" :
300                encodeFilter( buffer, ( GreaterEqNode<?> ) node, ( byte ) LdapCodecConstants.GREATER_OR_EQUAL_FILTER_TAG );
301                break;
302
303            case "LessEqNode" :
304                encodeFilter( buffer, ( LessEqNode<?> ) node, ( byte ) LdapCodecConstants.LESS_OR_EQUAL_FILTER_TAG );
305                break;
306
307            case "NotNode" :
308                encodeFilter( buffer, ( NotNode ) node, ( byte ) LdapCodecConstants.NOT_FILTER_TAG );
309                break;
310
311            case "OrNode" :
312                encodeFilter( buffer, ( OrNode ) node, ( byte ) LdapCodecConstants.OR_FILTER_TAG );
313                break;
314
315            case "PresenceNode" :
316                encodeFilter( buffer, ( PresenceNode ) node );
317                break;
318
319            case "SubstringNode" :
320                encodeFilter( buffer, ( SubstringNode ) node );
321                break;
322
323            default:
324                break;
325        }
326    }
327
328    /**
329     * Encode the SearchRequest message to a PDU.
330     * <br>
331     * SearchRequest :
332     * <pre>
333     * 0x63 LL
334     *   0x04 LL baseObject
335     *   0x0A 01 scope
336     *   0x0A 01 derefAliases
337     *   0x02 0N sizeLimit
338     *   0x02 0N timeLimit
339     *   0x01 0x01 typesOnly
340     *   filter.encode()
341     *   0x30 LL attributeDescriptionList
342     *     0x04 LL attributeDescription
343     *     ...
344     *     0x04 LL attributeDescription
345     * </pre>
346     *
347     * @param codec The LdapApiService instance
348     * @param buffer The buffer where to put the PDU
349     * @param message the ModifyRequest to encode
350     */
351    @Override
352    public void encodeReverse( LdapApiService codec, Asn1Buffer buffer, Message message )
353    {
354        int start = buffer.getPos();
355        SearchRequest searchRequest = ( SearchRequest ) message;
356
357        // The attributes, if any
358        List<String> attributes = searchRequest.getAttributes();
359
360        if ( ( attributes != null ) && ( !attributes.isEmpty() ) )
361        {
362            for ( int i = attributes.size(); i > 0; i-- )
363            {
364                BerValue.encodeOctetString( buffer, attributes.get( i - 1 ) );
365            }
366        }
367
368        // The attributes sequence
369        BerValue.encodeSequence( buffer, start );
370
371        // The filter
372        encodeFilter( buffer, searchRequest.getFilter() );
373
374        // The typesOnly
375        BerValue.encodeBoolean( buffer, searchRequest.getTypesOnly() );
376
377        // The timeLimit
378        BerValue.encodeInteger( buffer, searchRequest.getTimeLimit() );
379
380        // The sizeLimit
381        BerValue.encodeInteger( buffer, searchRequest.getSizeLimit() );
382
383        // The derefAliases
384        BerValue.encodeEnumerated( buffer, searchRequest.getDerefAliases().getValue() );
385
386        // The scope
387        BerValue.encodeEnumerated( buffer, searchRequest.getScope().getScope() );
388
389        // The base object
390        BerValue.encodeOctetString( buffer, Strings.getBytesUtf8( searchRequest.getBase().getName() ) );
391
392        // The SearchRequest tag
393        BerValue.encodeSequence( buffer, LdapCodecConstants.SEARCH_REQUEST_TAG, start );
394    }
395}