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.decorators;
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.DecoderException;
029import org.apache.directory.api.asn1.EncoderException;
030import org.apache.directory.api.asn1.ber.Asn1Container;
031import org.apache.directory.api.asn1.ber.tlv.BerValue;
032import org.apache.directory.api.asn1.ber.tlv.TLV;
033import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
034import org.apache.directory.api.i18n.I18n;
035import org.apache.directory.api.ldap.codec.AttributeValueAssertion;
036import org.apache.directory.api.ldap.codec.api.LdapApiService;
037import org.apache.directory.api.ldap.codec.api.LdapCodecConstants;
038import org.apache.directory.api.ldap.codec.api.LdapMessageContainer;
039import org.apache.directory.api.ldap.codec.api.MessageDecorator;
040import org.apache.directory.api.ldap.codec.search.AndFilter;
041import org.apache.directory.api.ldap.codec.search.AttributeValueAssertionFilter;
042import org.apache.directory.api.ldap.codec.search.ConnectorFilter;
043import org.apache.directory.api.ldap.codec.search.ExtensibleMatchFilter;
044import org.apache.directory.api.ldap.codec.search.Filter;
045import org.apache.directory.api.ldap.codec.search.NotFilter;
046import org.apache.directory.api.ldap.codec.search.OrFilter;
047import org.apache.directory.api.ldap.codec.search.PresentFilter;
048import org.apache.directory.api.ldap.codec.search.SubstringFilter;
049import org.apache.directory.api.ldap.model.entry.Value;
050import org.apache.directory.api.ldap.model.exception.LdapException;
051import org.apache.directory.api.ldap.model.filter.AndNode;
052import org.apache.directory.api.ldap.model.filter.ApproximateNode;
053import org.apache.directory.api.ldap.model.filter.BranchNode;
054import org.apache.directory.api.ldap.model.filter.BranchNormalizedVisitor;
055import org.apache.directory.api.ldap.model.filter.EqualityNode;
056import org.apache.directory.api.ldap.model.filter.ExprNode;
057import org.apache.directory.api.ldap.model.filter.ExtensibleNode;
058import org.apache.directory.api.ldap.model.filter.GreaterEqNode;
059import org.apache.directory.api.ldap.model.filter.LeafNode;
060import org.apache.directory.api.ldap.model.filter.LessEqNode;
061import org.apache.directory.api.ldap.model.filter.NotNode;
062import org.apache.directory.api.ldap.model.filter.OrNode;
063import org.apache.directory.api.ldap.model.filter.PresenceNode;
064import org.apache.directory.api.ldap.model.filter.SimpleNode;
065import org.apache.directory.api.ldap.model.filter.SubstringNode;
066import org.apache.directory.api.ldap.model.message.AbandonListener;
067import org.apache.directory.api.ldap.model.message.AliasDerefMode;
068import org.apache.directory.api.ldap.model.message.Control;
069import org.apache.directory.api.ldap.model.message.Message;
070import org.apache.directory.api.ldap.model.message.MessageTypeEnum;
071import org.apache.directory.api.ldap.model.message.SearchRequest;
072import org.apache.directory.api.ldap.model.message.SearchResultDone;
073import org.apache.directory.api.ldap.model.message.SearchScope;
074import org.apache.directory.api.ldap.model.name.Dn;
075import org.apache.directory.api.util.Strings;
076
077
078/**
079 * A decorator for the SearchRequest message
080 *
081 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
082 */
083public class SearchRequestDecorator extends MessageDecorator<SearchRequest> implements SearchRequest
084{
085    /** The searchRequest length */
086    private int searchRequestLength;
087
088    /** The attributeDescriptionList length */
089    private int attributeDescriptionListLength;
090
091    /** A temporary storage for a terminal Filter */
092    private Filter terminalFilter;
093
094    /** The current filter. This is used while decoding a PDU */
095    private Filter currentFilter;
096
097    /** The global filter. This is used while decoding a PDU */
098    private Filter topFilter;
099
100    /** The SearchRequest TLV id */
101    private int tlvId;
102
103    /** The bytes containing the Dn */
104    private byte[] dnBytes;
105
106
107    /**
108     * Makes a SearchRequest encodable.
109     *
110     * @param codec The LDAP service instance
111     * @param decoratedMessage the decorated SearchRequest
112     */
113    public SearchRequestDecorator( LdapApiService codec, SearchRequest decoratedMessage )
114    {
115        super( codec, decoratedMessage );
116    }
117
118
119    /**
120     * Set the SearchRequest PDU TLV's Id
121     * @param tlvId The TLV id
122     */
123    public void setTlvId( int tlvId )
124    {
125        this.tlvId = tlvId;
126    }
127
128
129    /**
130     * @return The current search filter
131     */
132    public Filter getCurrentFilter()
133    {
134        return currentFilter;
135    }
136
137
138    /**
139     * Gets the search filter associated with this search request.
140     *
141     * @return the expression node for the root of the filter expression tree.
142     */
143    public Filter getCodecFilter()
144    {
145        return topFilter;
146    }
147
148
149    /**
150     * Gets the search filter associated with this search request.
151     *
152     * @return the expression node for the root of the filter expression tree.
153     */
154    public ExprNode getFilterNode()
155    {
156        return transform( topFilter );
157    }
158
159
160    /**
161     * Get the terminal filter
162     *
163     * @return Returns the terminal filter.
164     */
165    public Filter getTerminalFilter()
166    {
167        return terminalFilter;
168    }
169
170
171    /**
172     * Set the terminal filter
173     *
174     * @param terminalFilter the teminalFilter.
175     */
176    public void setTerminalFilter( Filter terminalFilter )
177    {
178        this.terminalFilter = terminalFilter;
179    }
180
181
182    /**
183     * {@inheritDoc}
184     */
185    @Override
186    public SearchRequest setFilter( ExprNode filter )
187    {
188        topFilter = transform( filter );
189
190        return this;
191    }
192
193
194    /**
195     * {@inheritDoc}
196     */
197    @Override
198    public SearchRequest setFilter( String filter ) throws LdapException
199    {
200        getDecorated().setFilter( filter );
201        this.currentFilter = transform( getDecorated().getFilter() );
202
203        return this;
204    }
205
206
207    /**
208     * Set the current filter
209     *
210     * @param filter The filter to set.
211     */
212    public void setCurrentFilter( Filter filter )
213    {
214        currentFilter = filter;
215    }
216
217
218    /**
219     * Add a current filter. We have two cases :
220     * - there is no previous current filter : the filter
221     * is the top level filter
222     * - there is a previous current filter : the filter is added
223     * to the currentFilter set, and the current filter is changed
224     *
225     * In any case, the previous current filter will always be a
226     * ConnectorFilter when this method is called.
227     *
228     * @param localFilter The filter to set.
229     * @throws DecoderException If the filter is invalid
230     */
231    public void addCurrentFilter( Filter localFilter ) throws DecoderException
232    {
233        if ( currentFilter != null )
234        {
235            // Ok, we have a parent. The new Filter will be added to
236            // this parent, and will become the currentFilter if it's a connector.
237            ( ( ConnectorFilter ) currentFilter ).addFilter( localFilter );
238            localFilter.setParent( currentFilter, currentFilter.getTlvId() );
239
240            if ( localFilter instanceof ConnectorFilter )
241            {
242                currentFilter = localFilter;
243            }
244        }
245        else
246        {
247            // No parent. This Filter will become the root.
248            currentFilter = localFilter;
249            currentFilter.setParent( null, tlvId );
250            topFilter = localFilter;
251        }
252    }
253
254
255    /**
256     * This method is used to clear the filter's stack for terminated elements. An element
257     * is considered as terminated either if :
258     *  - it's a final element (ie an element which cannot contains a Filter)
259     *  - its current length equals its expected length.
260     *
261     * @param container The container being decoded
262     */
263    @SuppressWarnings("unchecked")
264    public void unstackFilters( Asn1Container container )
265    {
266        LdapMessageContainer<MessageDecorator<Message>> ldapMessageContainer =
267            ( LdapMessageContainer<MessageDecorator<Message>> ) container;
268
269        TLV tlv = ldapMessageContainer.getCurrentTLV();
270        TLV localParent = tlv.getParent();
271        Filter localFilter = terminalFilter;
272
273        // The parent has been completed, so fold it
274        while ( ( localParent != null ) && ( localParent.getExpectedLength() == 0 ) )
275        {
276            int parentTlvId = localFilter.getParent() != null ? localFilter.getParent().getTlvId() : localFilter
277                .getParentTlvId();
278
279            if ( localParent.getId() != parentTlvId )
280            {
281                localParent = localParent.getParent();
282            }
283            else
284            {
285                Filter filterParent = localFilter.getParent();
286
287                // We have a special case with PresentFilter, which has not been
288                // pushed on the stack, so we need to get its parent's parent
289                if ( localFilter instanceof PresentFilter )
290                {
291                    if ( filterParent == null )
292                    {
293                        // We don't have parent, get out
294                        break;
295                    }
296
297                    filterParent = filterParent.getParent();
298                }
299                else
300                {
301                    filterParent = filterParent.getParent();
302                }
303
304                if ( filterParent != null )
305                {
306                    // The parent is a filter ; it will become the new currentFilter
307                    // and we will loop again.
308                    localFilter = currentFilter;
309                    currentFilter = filterParent;
310                    localParent = localParent.getParent();
311                }
312                else
313                {
314                    // We can stop the recursion, we have reached the searchResult Object
315                    break;
316                }
317            }
318        }
319    }
320
321
322    /**
323     * Transform the Filter part of a SearchRequest to an ExprNode
324     *
325     * @param filter The filter to be transformed
326     * @return An ExprNode
327     */
328    @SuppressWarnings(
329        { "unchecked", "rawtypes" })
330    private ExprNode transform( Filter filter )
331    {
332        if ( filter != null )
333        {
334            // Transform OR, AND or NOT leaves
335            if ( filter instanceof ConnectorFilter )
336            {
337                BranchNode branch;
338
339                if ( filter instanceof AndFilter )
340                {
341                    branch = new AndNode();
342                }
343                else if ( filter instanceof OrFilter )
344                {
345                    branch = new OrNode();
346                }
347                else
348                {
349                    branch = new NotNode();
350                }
351
352                List<Filter> filtersSet = ( ( ConnectorFilter ) filter ).getFilterSet();
353
354                // Loop on all AND/OR children
355                if ( filtersSet != null )
356                {
357                    for ( Filter node : filtersSet )
358                    {
359                        branch.addNode( transform( node ) );
360                    }
361                }
362
363                return branch;
364            }
365            else
366            {
367                // Transform PRESENT or ATTRIBUTE_VALUE_ASSERTION
368                LeafNode branch = null;
369
370                if ( filter instanceof PresentFilter )
371                {
372                    branch = new PresenceNode( ( ( PresentFilter ) filter ).getAttributeDescription() );
373                }
374                else if ( filter instanceof AttributeValueAssertionFilter )
375                {
376                    AttributeValueAssertion ava = ( ( AttributeValueAssertionFilter ) filter ).getAssertion();
377
378                    // Transform =, >=, <=, ~= filters
379                    int filterType = ( ( AttributeValueAssertionFilter ) filter ).getFilterType();
380                    switch ( filterType )
381                    {
382                        case LdapCodecConstants.EQUALITY_MATCH_FILTER:
383                            branch = new EqualityNode( ava.getAttributeDesc(), ava.getAssertionValue() );
384                            break;
385
386                        case LdapCodecConstants.GREATER_OR_EQUAL_FILTER:
387                            branch = new GreaterEqNode( ava.getAttributeDesc(), ava.getAssertionValue() );
388                            break;
389
390                        case LdapCodecConstants.LESS_OR_EQUAL_FILTER:
391                            branch = new LessEqNode( ava.getAttributeDesc(), ava.getAssertionValue() );
392                            break;
393
394                        case LdapCodecConstants.APPROX_MATCH_FILTER:
395                            branch = new ApproximateNode( ava.getAttributeDesc(), ava.getAssertionValue() );
396                            break;
397
398                        default:
399                            throw new IllegalArgumentException( "Unexpected filter type: " + filterType );
400                    }
401
402                }
403                else if ( filter instanceof SubstringFilter )
404                {
405                    // Transform Substring filters
406                    SubstringFilter substrFilter = ( SubstringFilter ) filter;
407                    String initialString = null;
408                    String finalString = null;
409                    List<String> anyString = null;
410
411                    if ( substrFilter.getInitialSubstrings() != null )
412                    {
413                        initialString = substrFilter.getInitialSubstrings();
414                    }
415
416                    if ( substrFilter.getFinalSubstrings() != null )
417                    {
418                        finalString = substrFilter.getFinalSubstrings();
419                    }
420
421                    if ( substrFilter.getAnySubstrings() != null )
422                    {
423                        anyString = new ArrayList<>();
424
425                        for ( String any : substrFilter.getAnySubstrings() )
426                        {
427                            anyString.add( any );
428                        }
429                    }
430
431                    branch = new SubstringNode( anyString, substrFilter.getType(), initialString, finalString );
432                }
433                else if ( filter instanceof ExtensibleMatchFilter )
434                {
435                    // Transform Extensible Match Filter
436                    ExtensibleMatchFilter extFilter = ( ExtensibleMatchFilter ) filter;
437                    String matchingRule = null;
438
439                    Value<?> value = extFilter.getMatchValue();
440
441                    if ( extFilter.getMatchingRule() != null )
442                    {
443                        matchingRule = extFilter.getMatchingRule();
444                    }
445
446                    branch = new ExtensibleNode( extFilter.getType(), value, matchingRule, extFilter.isDnAttributes() );
447                }
448
449                return branch;
450            }
451        }
452        else
453        {
454            // We have found nothing to transform. Return null then.
455            return null;
456        }
457    }
458
459
460    /**
461     * Transform an ExprNode filter to a Filter
462     *
463     * @param exprNode The filter to be transformed
464     * @return A filter
465     */
466    private static Filter transform( ExprNode exprNode )
467    {
468        if ( exprNode != null )
469        {
470            Filter filter = null;
471
472            // Transform OR, AND or NOT leaves
473            if ( exprNode instanceof BranchNode )
474            {
475                if ( exprNode instanceof AndNode )
476                {
477                    filter = new AndFilter();
478                }
479                else if ( exprNode instanceof OrNode )
480                {
481                    filter = new OrFilter();
482                }
483                else
484                {
485                    filter = new NotFilter();
486                }
487
488                List<ExprNode> children = ( ( BranchNode ) exprNode ).getChildren();
489
490                // Loop on all AND/OR children
491                if ( children != null )
492                {
493                    for ( ExprNode child : children )
494                    {
495                        try
496                        {
497                            ( ( ConnectorFilter ) filter ).addFilter( transform( child ) );
498                        }
499                        catch ( DecoderException de )
500                        {
501                            return null;
502                        }
503                    }
504                }
505            }
506            else
507            {
508                if ( exprNode instanceof PresenceNode )
509                {
510                    // Transform Presence Node
511                    filter = new PresentFilter();
512                    ( ( PresentFilter ) filter ).setAttributeDescription( ( ( PresenceNode ) exprNode ).getAttribute() );
513                }
514                else if ( exprNode instanceof SimpleNode<?> )
515                {
516                    if ( exprNode instanceof EqualityNode<?> )
517                    {
518                        filter = new AttributeValueAssertionFilter( LdapCodecConstants.EQUALITY_MATCH_FILTER );
519                        AttributeValueAssertion assertion = new AttributeValueAssertion();
520                        assertion.setAttributeDesc( ( ( EqualityNode<?> ) exprNode ).getAttribute() );
521                        assertion.setAssertionValue( ( ( EqualityNode<?> ) exprNode ).getValue() );
522                        ( ( AttributeValueAssertionFilter ) filter ).setAssertion( assertion );
523                    }
524                    else if ( exprNode instanceof GreaterEqNode<?> )
525                    {
526                        filter = new AttributeValueAssertionFilter( LdapCodecConstants.GREATER_OR_EQUAL_FILTER );
527                        AttributeValueAssertion assertion = new AttributeValueAssertion();
528                        assertion.setAttributeDesc( ( ( GreaterEqNode<?> ) exprNode ).getAttribute() );
529                        assertion.setAssertionValue( ( ( GreaterEqNode<?> ) exprNode ).getValue() );
530                        ( ( AttributeValueAssertionFilter ) filter ).setAssertion( assertion );
531                    }
532                    else if ( exprNode instanceof LessEqNode<?> )
533                    {
534                        filter = new AttributeValueAssertionFilter( LdapCodecConstants.LESS_OR_EQUAL_FILTER );
535                        AttributeValueAssertion assertion = new AttributeValueAssertion();
536                        assertion.setAttributeDesc( ( ( LessEqNode<?> ) exprNode ).getAttribute() );
537                        assertion.setAssertionValue( ( ( LessEqNode<?> ) exprNode ).getValue() );
538                        ( ( AttributeValueAssertionFilter ) filter ).setAssertion( assertion );
539                    }
540                    else if ( exprNode instanceof ApproximateNode<?> )
541                    {
542                        filter = new AttributeValueAssertionFilter( LdapCodecConstants.APPROX_MATCH_FILTER );
543                        AttributeValueAssertion assertion = new AttributeValueAssertion();
544                        assertion.setAttributeDesc( ( ( ApproximateNode<?> ) exprNode ).getAttribute() );
545                        assertion.setAssertionValue( ( ( ApproximateNode<?> ) exprNode ).getValue() );
546                        ( ( AttributeValueAssertionFilter ) filter ).setAssertion( assertion );
547                    }
548                }
549                else if ( exprNode instanceof SubstringNode )
550                {
551                    // Transform Substring Nodes
552                    filter = new SubstringFilter();
553
554                    ( ( SubstringFilter ) filter ).setType( ( ( SubstringNode ) exprNode ).getAttribute() );
555                    String initialString = ( ( SubstringNode ) exprNode ).getInitial();
556                    String finalString = ( ( SubstringNode ) exprNode ).getFinal();
557                    List<String> anyStrings = ( ( SubstringNode ) exprNode ).getAny();
558
559                    if ( initialString != null )
560                    {
561                        ( ( SubstringFilter ) filter ).setInitialSubstrings( initialString );
562                    }
563
564                    if ( finalString != null )
565                    {
566                        ( ( SubstringFilter ) filter ).setFinalSubstrings( finalString );
567                    }
568
569                    if ( anyStrings != null )
570                    {
571                        for ( String any : anyStrings )
572                        {
573                            ( ( SubstringFilter ) filter ).addAnySubstrings( any );
574                        }
575                    }
576                }
577                else if ( exprNode instanceof ExtensibleNode )
578                {
579                    // Transform Extensible Node
580                    filter = new ExtensibleMatchFilter();
581
582                    String attribute = ( ( ExtensibleNode ) exprNode ).getAttribute();
583                    String matchingRule = ( ( ExtensibleNode ) exprNode ).getMatchingRuleId();
584                    boolean dnAttributes = ( ( ExtensibleNode ) exprNode ).hasDnAttributes();
585                    Value<?> value = ( ( ExtensibleNode ) exprNode ).getValue();
586
587                    if ( attribute != null )
588                    {
589                        ( ( ExtensibleMatchFilter ) filter ).setType( attribute );
590                    }
591
592                    if ( matchingRule != null )
593                    {
594                        ( ( ExtensibleMatchFilter ) filter ).setMatchingRule( matchingRule );
595                    }
596
597                    ( ( ExtensibleMatchFilter ) filter ).setMatchValue( value );
598                    ( ( ExtensibleMatchFilter ) filter ).setDnAttributes( dnAttributes );
599                }
600            }
601
602            return filter;
603        }
604        else
605        {
606            // We have found nothing to transform. Return null then.
607            return null;
608        }
609    }
610
611
612    /**
613     * @see Object#hashCode()
614     */
615    @Override
616    public int hashCode()
617    {
618        int hash = 37;
619
620        if ( getDecorated().getBase() != null )
621        {
622            hash = hash * 17 + getDecorated().getBase().hashCode();
623        }
624
625        hash = hash * 17 + getDecorated().getDerefAliases().hashCode();
626        hash = hash * 17 + getDecorated().getScope().hashCode();
627        hash = hash * 17 + Long.valueOf( getDecorated().getSizeLimit() ).hashCode();
628        hash = hash * 17 + getDecorated().getTimeLimit();
629        hash = hash * 17 + ( getDecorated().getTypesOnly() ? 0 : 1 );
630
631        List<String> attributes = getDecorated().getAttributes();
632        if ( attributes != null )
633        {
634            hash = hash * 17 + attributes.size();
635
636            // Order doesn't matter, thus just add hashCode
637            for ( String attr : attributes )
638            {
639                hash = hash + attr.hashCode();
640            }
641        }
642
643        BranchNormalizedVisitor visitor = new BranchNormalizedVisitor();
644        getDecorated().getFilter().accept( visitor );
645        hash = hash * 17 + currentFilter.toString().hashCode();
646        hash = hash * 17 + super.hashCode();
647
648        return hash;
649    }
650
651
652    /**
653     * @see Object#equals(Object)
654     */
655    @Override
656    public boolean equals( Object o )
657    {
658        if ( !super.equals( o ) )
659        {
660            return false;
661        }
662
663        if ( !( o instanceof SearchRequestDecorator ) )
664        {
665            return false;
666        }
667
668        SearchRequestDecorator otherSearchRequestDecorator = ( SearchRequestDecorator ) o;
669
670        if ( ( getDecorated() != null ) && ( !getDecorated().equals( otherSearchRequestDecorator.getDecorated() ) ) )
671        {
672            return false;
673        }
674
675        if ( searchRequestLength != otherSearchRequestDecorator.searchRequestLength )
676        {
677            return false;
678        }
679
680        if ( attributeDescriptionListLength != otherSearchRequestDecorator.attributeDescriptionListLength )
681        {
682            return false;
683        }
684
685        if ( ( terminalFilter != null ) && ( terminalFilter.equals( otherSearchRequestDecorator.terminalFilter ) ) )
686        {
687            return false;
688        }
689
690        if ( ( currentFilter != null ) && ( currentFilter.equals( otherSearchRequestDecorator.currentFilter ) ) )
691        {
692            return false;
693        }
694
695        if ( ( topFilter != null ) && ( topFilter.equals( otherSearchRequestDecorator.topFilter ) ) )
696        {
697            return false;
698        }
699
700        if ( tlvId != otherSearchRequestDecorator.tlvId )
701        {
702            return false;
703        }
704
705        return true;
706    }
707
708
709    //-------------------------------------------------------------------------
710    // The SearchRequest methods
711    //-------------------------------------------------------------------------
712    /**
713     * {@inheritDoc}
714     */
715    @Override
716    public MessageTypeEnum[] getResponseTypes()
717    {
718        return getDecorated().getResponseTypes();
719    }
720
721
722    /**
723     * {@inheritDoc}
724     */
725    @Override
726    public Dn getBase()
727    {
728        return getDecorated().getBase();
729    }
730
731
732    /**
733     * {@inheritDoc}
734     */
735    @Override
736    public SearchRequest setBase( Dn baseDn )
737    {
738        getDecorated().setBase( baseDn );
739
740        return this;
741    }
742
743
744    /**
745     * {@inheritDoc}
746     */
747    @Override
748    public SearchScope getScope()
749    {
750        return getDecorated().getScope();
751    }
752
753
754    /**
755     * {@inheritDoc}
756     */
757    @Override
758    public SearchRequest setScope( SearchScope scope )
759    {
760        getDecorated().setScope( scope );
761
762        return this;
763    }
764
765
766    /**
767     * {@inheritDoc}
768     */
769    @Override
770    public AliasDerefMode getDerefAliases()
771    {
772        return getDecorated().getDerefAliases();
773    }
774
775
776    /**
777     * {@inheritDoc}
778     */
779    @Override
780    public SearchRequest setDerefAliases( AliasDerefMode aliasDerefAliases )
781    {
782        getDecorated().setDerefAliases( aliasDerefAliases );
783
784        return this;
785    }
786
787
788    /**
789     * {@inheritDoc}
790     */
791    @Override
792    public long getSizeLimit()
793    {
794        return getDecorated().getSizeLimit();
795    }
796
797
798    /**
799     * {@inheritDoc}
800     */
801    @Override
802    public SearchRequest setSizeLimit( long entriesMax )
803    {
804        getDecorated().setSizeLimit( entriesMax );
805
806        return this;
807    }
808
809
810    /**
811     * {@inheritDoc}
812     */
813    @Override
814    public int getTimeLimit()
815    {
816        return getDecorated().getTimeLimit();
817    }
818
819
820    /**
821     * {@inheritDoc}
822     */
823    @Override
824    public SearchRequest setTimeLimit( int secondsMax )
825    {
826        getDecorated().setTimeLimit( secondsMax );
827
828        return this;
829    }
830
831
832    /**
833     * {@inheritDoc}
834     */
835    @Override
836    public boolean getTypesOnly()
837    {
838        return getDecorated().getTypesOnly();
839    }
840
841
842    /**
843     * {@inheritDoc}
844     */
845    @Override
846    public SearchRequest setTypesOnly( boolean typesOnly )
847    {
848        getDecorated().setTypesOnly( typesOnly );
849
850        return this;
851    }
852
853
854    /**
855     * {@inheritDoc}
856     */
857    @Override
858    public ExprNode getFilter()
859    {
860        return getDecorated().getFilter();
861    }
862
863
864    /**
865     * {@inheritDoc}
866     */
867    @Override
868    public List<String> getAttributes()
869    {
870        return getDecorated().getAttributes();
871    }
872
873
874    /**
875     * {@inheritDoc}
876     */
877    @Override
878    public SearchRequest addAttributes( String... attributes )
879    {
880        getDecorated().addAttributes( attributes );
881
882        return this;
883    }
884
885
886    /**
887     * {@inheritDoc}
888     */
889    @Override
890    public SearchRequest removeAttribute( String attribute )
891    {
892        getDecorated().removeAttribute( attribute );
893
894        return this;
895    }
896
897
898    //-------------------------------------------------------------------------
899    // The Decorator methods
900    //-------------------------------------------------------------------------
901
902    /**
903     * Compute the SearchRequest length
904     * <br>
905     * SearchRequest :
906     * <pre>
907     * 0x63 L1
908     *  |
909     *  +--&gt; 0x04 L2 baseObject
910     *  +--&gt; 0x0A 0x01 scope
911     *  +--&gt; 0x0A 0x01 derefAliases
912     *  +--&gt; 0x02 0x0(1..4) sizeLimit
913     *  +--&gt; 0x02 0x0(1..4) timeLimit
914     *  +--&gt; 0x01 0x01 typesOnly
915     *  +--&gt; filter.computeLength()
916     *  +--&gt; 0x30 L3 (Attribute description list)
917     *        |
918     *        +--&gt; 0x04 L4-1 Attribute description
919     *        +--&gt; 0x04 L4-2 Attribute description
920     *        +--&gt; ...
921     *        +--&gt; 0x04 L4-i Attribute description
922     *        +--&gt; ...
923     *        +--&gt; 0x04 L4-n Attribute description
924     * </pre>
925     */
926    @Override
927    public int computeLength()
928    {
929        searchRequestLength = 0;
930
931        // The baseObject
932        dnBytes = Strings.getBytesUtf8( getBase().getName() );
933        searchRequestLength += 1 + TLV.getNbBytes( dnBytes.length ) + dnBytes.length;
934
935        // The scope
936        searchRequestLength += 1 + 1 + 1;
937
938        // The derefAliases
939        searchRequestLength += 1 + 1 + 1;
940
941        // The sizeLimit
942        searchRequestLength += 1 + 1 + BerValue.getNbBytes( getSizeLimit() );
943
944        // The timeLimit
945        searchRequestLength += 1 + 1 + BerValue.getNbBytes( getTimeLimit() );
946
947        // The typesOnly
948        searchRequestLength += 1 + 1 + 1;
949
950        // The filter
951        setFilter( getFilter() );
952        searchRequestLength +=
953            getCodecFilter().computeLength();
954
955        // The attributes description list
956        attributeDescriptionListLength = 0;
957
958        if ( ( getAttributes() != null ) && ( !getAttributes().isEmpty() ) )
959        {
960            // Compute the attributes length
961            for ( String attribute : getAttributes() )
962            {
963                // add the attribute length to the attributes length
964                int idLength = Strings.getBytesUtf8( attribute ).length;
965                attributeDescriptionListLength += 1 + TLV.getNbBytes( idLength ) + idLength;
966            }
967        }
968
969        searchRequestLength += 1 + TLV.getNbBytes( attributeDescriptionListLength ) + attributeDescriptionListLength;
970
971        // Return the result.
972        return 1 + TLV.getNbBytes( searchRequestLength ) + searchRequestLength;
973    }
974
975
976    /**
977     * Encode the SearchRequest message to a PDU.
978     * <br>
979     * SearchRequest :
980     * <pre>
981     * 0x63 LL
982     *   0x04 LL baseObject
983     *   0x0A 01 scope
984     *   0x0A 01 derefAliases
985     *   0x02 0N sizeLimit
986     *   0x02 0N timeLimit
987     *   0x01 0x01 typesOnly
988     *   filter.encode()
989     *   0x30 LL attributeDescriptionList
990     *     0x04 LL attributeDescription
991     *     ...
992     *     0x04 LL attributeDescription
993     * </pre>
994     * 
995     * @param buffer The buffer where to put the PDU
996     * @return The PDU.
997     */
998    @Override
999    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
1000    {
1001        try
1002        {
1003            // The SearchRequest Tag
1004            buffer.put( LdapCodecConstants.SEARCH_REQUEST_TAG );
1005            buffer.put( TLV.getBytes( searchRequestLength ) );
1006
1007            // The baseObject
1008            BerValue.encode( buffer, dnBytes );
1009
1010            // The scope
1011            BerValue.encodeEnumerated( buffer, getScope().getScope() );
1012
1013            // The derefAliases
1014            BerValue.encodeEnumerated( buffer, getDerefAliases().getValue() );
1015
1016            // The sizeLimit
1017            BerValue.encode( buffer, getSizeLimit() );
1018
1019            // The timeLimit
1020            BerValue.encode( buffer, getTimeLimit() );
1021
1022            // The typesOnly
1023            BerValue.encode( buffer, getTypesOnly() );
1024
1025            // The filter
1026            getCodecFilter().encode( buffer );
1027
1028            // The attributeDescriptionList
1029            buffer.put( UniversalTag.SEQUENCE.getValue() );
1030            buffer.put( TLV.getBytes( attributeDescriptionListLength ) );
1031
1032            if ( ( getAttributes() != null ) && ( !getAttributes().isEmpty() ) )
1033            {
1034                // encode each attribute
1035                for ( String attribute : getAttributes() )
1036                {
1037                    BerValue.encode( buffer, attribute );
1038                }
1039            }
1040        }
1041        catch ( BufferOverflowException boe )
1042        {
1043            throw new EncoderException( I18n.err( I18n.ERR_04005 ), boe );
1044        }
1045
1046        return buffer;
1047    }
1048
1049
1050    /**
1051     * {@inheritDoc}
1052     */
1053    @Override
1054    public SearchResultDone getResultResponse()
1055    {
1056        return ( SearchResultDone ) getDecorated().getResultResponse();
1057    }
1058
1059
1060    /**
1061     * {@inheritDoc}
1062     */
1063    @Override
1064    public boolean hasResponse()
1065    {
1066        return getDecorated().hasResponse();
1067    }
1068
1069
1070    /**
1071     * {@inheritDoc}
1072     */
1073    @Override
1074    public void abandon()
1075    {
1076        getDecorated().abandon();
1077    }
1078
1079
1080    /**
1081     * {@inheritDoc}
1082     */
1083    @Override
1084    public boolean isAbandoned()
1085    {
1086        return getDecorated().isAbandoned();
1087    }
1088
1089
1090    /**
1091     * {@inheritDoc}
1092     */
1093    @Override
1094    public SearchRequest addAbandonListener( AbandonListener listener )
1095    {
1096        getDecorated().addAbandonListener( listener );
1097
1098        return this;
1099    }
1100
1101
1102    /**
1103     * {@inheritDoc}
1104     */
1105    @Override
1106    public SearchRequest setMessageId( int messageId )
1107    {
1108        return ( SearchRequest ) super.setMessageId( messageId );
1109    }
1110
1111
1112    /**
1113     * {@inheritDoc}
1114     */
1115    @Override
1116    public SearchRequest addControl( Control control )
1117    {
1118        return ( SearchRequest ) super.addControl( control );
1119    }
1120
1121
1122    /**
1123     * {@inheritDoc}
1124     */
1125    @Override
1126    public SearchRequest addAllControls( Control[] controls )
1127    {
1128        return ( SearchRequest ) super.addAllControls( controls );
1129    }
1130
1131
1132    /**
1133     * {@inheritDoc}
1134     */
1135    @Override
1136    public SearchRequest removeControl( Control control )
1137    {
1138        return ( SearchRequest ) super.removeControl( control );
1139    }
1140
1141
1142    /**
1143     * {@inheritDoc}
1144     */
1145    @Override
1146    public boolean isFollowReferrals()
1147    {
1148        return getDecorated().isFollowReferrals();
1149    }
1150
1151
1152    /**
1153     * {@inheritDoc}
1154     */
1155    @Override
1156    public SearchRequest followReferrals()
1157    {
1158        return getDecorated().followReferrals();
1159    }
1160
1161
1162    /**
1163     * {@inheritDoc}
1164     */
1165    @Override
1166    public boolean isIgnoreReferrals()
1167    {
1168        return getDecorated().isIgnoreReferrals();
1169    }
1170
1171
1172    /**
1173     * {@inheritDoc}
1174     */
1175    @Override
1176    public SearchRequest ignoreReferrals()
1177    {
1178        return getDecorated().ignoreReferrals();
1179    }
1180}