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 *    https://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.dsmlv2.request;
021
022
023import java.util.ArrayList;
024import java.util.List;
025
026import org.apache.commons.text.StringEscapeUtils;
027import org.apache.directory.api.asn1.DecoderException;
028import org.apache.directory.api.dsmlv2.DsmlLiterals;
029import org.apache.directory.api.dsmlv2.ParserUtils;
030import org.apache.directory.api.i18n.I18n;
031import org.apache.directory.api.ldap.codec.api.LdapApiService;
032import org.apache.directory.api.ldap.codec.api.LdapCodecConstants;
033import org.apache.directory.api.ldap.model.entry.Value;
034import org.apache.directory.api.ldap.model.exception.LdapException;
035import org.apache.directory.api.ldap.model.exception.LdapSchemaException;
036import org.apache.directory.api.ldap.model.filter.AndNode;
037import org.apache.directory.api.ldap.model.filter.ApproximateNode;
038import org.apache.directory.api.ldap.model.filter.BranchNode;
039import org.apache.directory.api.ldap.model.filter.EqualityNode;
040import org.apache.directory.api.ldap.model.filter.ExprNode;
041import org.apache.directory.api.ldap.model.filter.ExtensibleNode;
042import org.apache.directory.api.ldap.model.filter.GreaterEqNode;
043import org.apache.directory.api.ldap.model.filter.LeafNode;
044import org.apache.directory.api.ldap.model.filter.LessEqNode;
045import org.apache.directory.api.ldap.model.filter.NotNode;
046import org.apache.directory.api.ldap.model.filter.OrNode;
047import org.apache.directory.api.ldap.model.filter.PresenceNode;
048import org.apache.directory.api.ldap.model.filter.SimpleNode;
049import org.apache.directory.api.ldap.model.filter.SubstringNode;
050import org.apache.directory.api.ldap.model.message.AliasDerefMode;
051import org.apache.directory.api.ldap.model.message.Control;
052import org.apache.directory.api.ldap.model.message.MessageTypeEnum;
053import org.apache.directory.api.ldap.model.message.SearchRequest;
054import org.apache.directory.api.ldap.model.message.SearchRequestImpl;
055import org.apache.directory.api.ldap.model.message.SearchResultDone;
056import org.apache.directory.api.ldap.model.message.SearchScope;
057import org.apache.directory.api.ldap.model.name.Dn;
058import org.apache.directory.api.ldap.model.schema.SchemaManager;
059import org.apache.directory.api.util.Strings;
060import org.dom4j.Element;
061import org.dom4j.Namespace;
062import org.dom4j.QName;
063
064
065/**
066 * DSML Decorator for SearchRequest
067 *
068 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
069 */
070public class SearchRequestDsml
071    extends AbstractResultResponseRequestDsml<SearchRequest, SearchResultDone>
072    implements SearchRequest
073{
074    /** A temporary storage for a terminal Filter */
075    private Filter terminalFilter;
076
077    /** The current filter. This is used while decoding a PDU */
078    private Filter currentFilter;
079
080    /** The global filter. This is used while decoding a PDU */
081    private Filter topFilter;
082
083
084    /**
085     * Creates a new getDecoratedMessage() of SearchRequestDsml.
086     * 
087     * @param codec The LDAP Service to use
088     */
089    public SearchRequestDsml( LdapApiService codec )
090    {
091        super( codec, new SearchRequestImpl() );
092    }
093
094
095    /**
096     * Creates a new getDecoratedMessage() of SearchRequestDsml.
097     *
098     * @param codec The LDAP Service to use
099     * @param ldapMessage the message to decorate
100     */
101    public SearchRequestDsml( LdapApiService codec, SearchRequest ldapMessage )
102    {
103        super( codec, ldapMessage );
104    }
105
106
107    /**
108     * Gets the search filter associated with this search request.
109     *
110     * @return the expression node for the root of the filter expression tree.
111     */
112    public Filter getCodecFilter()
113    {
114        return topFilter;
115    }
116
117
118    /**
119     * Gets the search filter associated with this search request.
120     *
121     * @return the expression node for the root of the filter expression tree.
122     * @throws LdapSchemaException If the filter is invalid
123     */
124    public ExprNode getFilterNode() throws LdapSchemaException
125    {
126        return transform( topFilter );
127    }
128
129
130    /**
131     * Get the terminal filter
132     *
133     * @return Returns the terminal filter.
134     */
135    public Filter getTerminalFilter()
136    {
137        return terminalFilter;
138    }
139
140
141    /**
142     * Set the terminal filter
143     *
144     * @param terminalFilter the teminalFilter.
145     */
146    public void setTerminalFilter( Filter terminalFilter )
147    {
148        this.terminalFilter = terminalFilter;
149    }
150
151
152    /**
153     * set the currentFilter to its parent
154     */
155    public void endCurrentConnectorFilter()
156    {
157        currentFilter = currentFilter.getParent();
158    }
159
160
161    /**
162     * Add a current filter. We have two cases :
163     * <ul>
164     *   <li>there is no previous current filter : the filter
165     *     is the top level filter</li>
166     *   <li>there is a previous current filter : the filter is added
167     *     to the currentFilter set, and the current filter is changed</li>
168     * </ul>
169     * In any case, the previous current filter will always be a
170     * ConnectorFilter when this method is called.
171     *
172     * @param localFilter The filter to set.
173     * @throws DecoderException If the added filter is invalid
174     */
175    public void addCurrentFilter( Filter localFilter ) throws DecoderException
176    {
177        if ( currentFilter != null )
178        {
179            // Ok, we have a parent. The new Filter will be added to
180            // this parent, and will become the currentFilter if it's a connector.
181            ( ( ConnectorFilter ) currentFilter ).addFilter( localFilter );
182            localFilter.setParent( currentFilter );
183
184            if ( localFilter instanceof ConnectorFilter )
185            {
186                currentFilter = localFilter;
187            }
188        }
189        else
190        {
191            // No parent. This Filter will become the root.
192            currentFilter = localFilter;
193            currentFilter.setParent( null );
194            topFilter = localFilter;
195        }
196    }
197
198
199    /**
200     * Transform the Filter part of a SearchRequest to an ExprNode
201     *
202     * @param filter The filter to be transformed
203     * @return An ExprNode
204     * @throws LdapSchemaException If the filter contains a wrong schema element
205     */
206    @SuppressWarnings({ "rawtypes" })
207    private ExprNode transform( Filter filter ) throws LdapSchemaException
208    {
209        if ( filter != null )
210        {
211            // Transform OR, AND or NOT leaves
212            if ( filter instanceof ConnectorFilter )
213            {
214                BranchNode branch;
215
216                if ( filter instanceof AndFilter )
217                {
218                    branch = new AndNode();
219                }
220                else if ( filter instanceof OrFilter )
221                {
222                    branch = new OrNode();
223                }
224                else
225                {
226                    branch = new NotNode();
227                }
228
229                List<Filter> filtersSet = ( ( ConnectorFilter ) filter ).getFilterSet();
230
231                // Loop on all AND/OR children
232                if ( filtersSet != null )
233                {
234                    for ( Filter node : filtersSet )
235                    {
236                        branch.addNode( transform( node ) );
237                    }
238                }
239
240                return branch;
241            }
242            else
243            {
244                // Transform PRESENT or ATTRIBUTE_VALUE_ASSERTION
245                LeafNode branch = null;
246
247                if ( filter instanceof PresentFilter )
248                {
249                    branch = new PresenceNode( ( ( PresentFilter ) filter ).getAttributeDescription() );
250                }
251                else if ( filter instanceof AttributeValueAssertionFilter )
252                {
253                    AttributeValueAssertionFilter avaFilter = ( AttributeValueAssertionFilter ) filter;
254
255                    AttributeValueAssertion ava = avaFilter.getAssertion();
256
257                    // Transform =, >=, <=, ~= filters
258                    int filterType = avaFilter.getFilterType();
259                    byte[] value = null;
260                    
261                    if ( ava.getAssertionValue() != null )
262                    {
263                        value = ava.getAssertionValue().getBytes();
264                    }
265                    
266                    switch ( filterType )
267                    {
268                        case LdapCodecConstants.EQUALITY_MATCH_FILTER:
269                            branch = new EqualityNode( ava.getAttributeDesc(), value );
270                            break;
271
272                        case LdapCodecConstants.GREATER_OR_EQUAL_FILTER:
273                            branch = new GreaterEqNode( ava.getAttributeDesc(), value );
274                            break;
275
276                        case LdapCodecConstants.LESS_OR_EQUAL_FILTER:
277                            branch = new LessEqNode( ava.getAttributeDesc(), value );
278                            break;
279
280                        case LdapCodecConstants.APPROX_MATCH_FILTER:
281                            branch = new ApproximateNode( ava.getAttributeDesc(), value );
282                            break;
283
284                        default:
285                            throw new IllegalStateException( I18n.err( I18n.ERR_03042_UNEXPECTED_FILTER_TYPE, filterType ) );
286                    }
287
288                }
289                else if ( filter instanceof SubstringFilter )
290                {
291                    // Transform Substring filters
292                    SubstringFilter substrFilter = ( SubstringFilter ) filter;
293                    String initialString = null;
294                    String finalString = null;
295                    List<String> anyString = null;
296
297                    if ( substrFilter.getInitialSubstrings() != null )
298                    {
299                        initialString = substrFilter.getInitialSubstrings();
300                    }
301
302                    if ( substrFilter.getFinalSubstrings() != null )
303                    {
304                        finalString = substrFilter.getFinalSubstrings();
305                    }
306
307                    if ( substrFilter.getAnySubstrings() != null )
308                    {
309                        anyString = new ArrayList<>();
310
311                        for ( String any : substrFilter.getAnySubstrings() )
312                        {
313                            anyString.add( any );
314                        }
315                    }
316
317                    branch = new SubstringNode( anyString, substrFilter.getType(), initialString, finalString );
318                }
319                else if ( filter instanceof ExtensibleMatchFilter )
320                {
321                    // Transform Extensible Match Filter
322                    ExtensibleMatchFilter extFilter = ( ExtensibleMatchFilter ) filter;
323                    String matchingRule = null;
324
325                    Value value = extFilter.getMatchValue();
326
327                    if ( extFilter.getMatchingRule() != null )
328                    {
329                        matchingRule = extFilter.getMatchingRule();
330                    }
331
332                    branch = new ExtensibleNode( extFilter.getType(), value, matchingRule, extFilter.isDnAttributes() );
333                }
334
335                return branch;
336            }
337        }
338        else
339        {
340            // We have found nothing to transform. Return null then.
341            return null;
342        }
343    }
344
345
346    /**
347     * {@inheritDoc}
348     */
349    @Override
350    public MessageTypeEnum getType()
351    {
352        return getDecorated().getType();
353    }
354
355
356    /**
357     * {@inheritDoc}
358     */
359    @Override
360    public Element toDsml( Element root )
361    {
362        Element element = super.toDsml( root );
363
364        SearchRequest request = getDecorated();
365
366        // Dn
367        if ( request.getBase() != null )
368        {
369            element.addAttribute( DsmlLiterals.DN, request.getBase().getName() );
370        }
371
372        // Scope
373        SearchScope scope = request.getScope();
374        if ( scope != null )
375        {
376            if ( scope == SearchScope.OBJECT )
377            {
378                element.addAttribute( DsmlLiterals.SCOPE, DsmlLiterals.BASE_OBJECT );
379            }
380            else if ( scope == SearchScope.ONELEVEL )
381            {
382                element.addAttribute( DsmlLiterals.SCOPE, DsmlLiterals.SINGLE_LEVEL );
383            }
384            else if ( scope == SearchScope.SUBTREE )
385            {
386                element.addAttribute( DsmlLiterals.SCOPE, DsmlLiterals.WHOLE_SUBTREE );
387            }
388        }
389
390        // DerefAliases
391        AliasDerefMode derefAliases = request.getDerefAliases();
392
393        switch ( derefAliases )
394        {
395            case NEVER_DEREF_ALIASES:
396                element.addAttribute( DsmlLiterals.DEREF_ALIASES, DsmlLiterals.NEVER_DEREF_ALIASES );
397                break;
398
399            case DEREF_ALWAYS:
400                element.addAttribute( DsmlLiterals.DEREF_ALIASES, DsmlLiterals.DEREF_ALWAYS );
401                break;
402
403            case DEREF_FINDING_BASE_OBJ:
404                element.addAttribute( DsmlLiterals.DEREF_ALIASES, DsmlLiterals.DEREF_FINDING_BASE_OBJ );
405                break;
406
407            case DEREF_IN_SEARCHING:
408                element.addAttribute( DsmlLiterals.DEREF_ALIASES, DsmlLiterals.DEREF_IN_SEARCHING );
409                break;
410
411            default:
412                throw new IllegalStateException( I18n.err( I18n.ERR_03043_UNEXPECTED_DEREF_ALIAS, derefAliases ) );
413        }
414
415        // SizeLimit
416        if ( request.getSizeLimit() != 0L )
417        {
418            element.addAttribute( DsmlLiterals.SIZE_LIMIT, Long.toString( request.getSizeLimit() ) );
419        }
420
421        // TimeLimit
422        if ( request.getTimeLimit() != 0 )
423        {
424            element.addAttribute( DsmlLiterals.TIME_LIMIT, Integer.toString( request.getTimeLimit() ) );
425        }
426
427        // TypesOnly
428        if ( request.getTypesOnly() )
429        {
430            element.addAttribute( DsmlLiterals.TYPES_ONLY,  DsmlLiterals.TRUE );
431        }
432
433        // Filter
434        Element filterElement = element.addElement( DsmlLiterals.FILTER );
435        toDsml( filterElement, request.getFilter() );
436
437        // Attributes
438        List<String> attributes = request.getAttributes();
439
440        if ( !attributes.isEmpty() )
441        {
442            Element attributesElement = element.addElement( DsmlLiterals.ATTRIBUTES );
443
444            for ( String entryAttribute : attributes )
445            {
446                attributesElement.addElement( DsmlLiterals.ATTRIBUTE ).addAttribute( DsmlLiterals.NAME, entryAttribute );
447            }
448        }
449
450        return element;
451    }
452
453
454    /**
455     * Recursively converts the filter of the Search Request into a DSML representation and adds 
456     * it to the XML Element corresponding to the Search Request
457     *
458     * @param element
459     *      the parent Element
460     * @param filter
461     *      the filter to convert
462     */
463    private void toDsml( Element element, ExprNode filter )
464    {
465        // AND FILTER
466        if ( filter instanceof AndNode )
467        {
468            Element newElement = element.addElement( DsmlLiterals.AND );
469
470            List<ExprNode> filterList = ( ( AndNode ) filter ).getChildren();
471
472            for ( int i = 0; i < filterList.size(); i++ )
473            {
474                toDsml( newElement, filterList.get( i ) );
475            }
476        }
477
478        // OR FILTER
479        else if ( filter instanceof OrNode )
480        {
481            Element newElement = element.addElement( DsmlLiterals.OR );
482
483            List<ExprNode> filterList = ( ( OrNode ) filter ).getChildren();
484
485            for ( int i = 0; i < filterList.size(); i++ )
486            {
487                toDsml( newElement, filterList.get( i ) );
488            }
489        }
490
491        // NOT FILTER
492        else if ( filter instanceof NotNode )
493        {
494            Element newElement = element.addElement( DsmlLiterals.NOT );
495
496            toDsml( newElement, ( ( NotNode ) filter ).getFirstChild() );
497        }
498
499        // SUBSTRING FILTER
500        else if ( filter instanceof SubstringNode )
501        {
502            Element newElement = element.addElement( DsmlLiterals.SUBSTRINGS );
503
504            SubstringNode substringFilter = ( SubstringNode ) filter;
505
506            newElement.addAttribute( DsmlLiterals.NAME, substringFilter.getAttribute() );
507
508            String initial = substringFilter.getInitial();
509
510            if ( Strings.isNotEmpty( initial ) )
511            {
512                newElement.addElement( DsmlLiterals.INITIAL ).setText( initial );
513            }
514
515            List<String> anyList = substringFilter.getAny();
516
517            for ( int i = 0; i < anyList.size(); i++ )
518            {
519                newElement.addElement( DsmlLiterals.ANY ).setText( anyList.get( i ) );
520            }
521
522            String finalString = substringFilter.getFinal();
523
524            if ( Strings.isNotEmpty( finalString  ) )
525            {
526                newElement.addElement( DsmlLiterals.FINAL ).setText( finalString );
527            }
528        }
529
530        // APPROXMATCH, EQUALITYMATCH, GREATEROREQUALS & LESSOREQUAL FILTERS
531        else if ( filter instanceof SimpleNode )
532        {
533            Element newElement;
534
535            if ( filter instanceof ApproximateNode )
536            {
537                newElement = element.addElement( DsmlLiterals.APPROX_MATCH );
538            }
539            else if ( filter instanceof EqualityNode )
540            {
541                newElement = element.addElement( DsmlLiterals.EQUALITY_MATCH );
542            }
543            else if ( filter instanceof GreaterEqNode )
544            {
545                newElement = element.addElement( DsmlLiterals.GREATER_OR_EQUAL );
546            }
547            else
548            // it is a LessEqNode )
549            {
550                newElement = element.addElement( DsmlLiterals.LESS_OR_EQUAL );
551            }
552
553            String attributeName = ( ( SimpleNode<?> ) filter ).getAttribute();
554            newElement.addAttribute( DsmlLiterals.NAME, attributeName );
555
556            Value value = ( ( SimpleNode<?> ) filter ).getValue();
557            
558            if ( value != null )
559            {
560                if ( value.isHumanReadable() )
561                {
562                    newElement.addElement( DsmlLiterals.VALUE ).setText( StringEscapeUtils.escapeXml11( value.getString() ) );
563                }
564                else
565                {
566                    Namespace xsdNamespace = new Namespace( ParserUtils.XSD, ParserUtils.XML_SCHEMA_URI );
567                    Namespace xsiNamespace = new Namespace( ParserUtils.XSI, ParserUtils.XML_SCHEMA_INSTANCE_URI );
568                    element.getDocument().getRootElement().add( xsdNamespace );
569                    element.getDocument().getRootElement().add( xsiNamespace );
570
571                    Element valueElement = newElement.addElement( DsmlLiterals.VALUE ).addText(
572                        ParserUtils.base64Encode( value.getBytes() ) );
573                    valueElement
574                        .addAttribute( new QName( DsmlLiterals.TYPE, xsiNamespace ), ParserUtils.XSD_COLON + ParserUtils.BASE64BINARY );
575                }
576            }
577        }
578
579        // PRESENT FILTER
580        else if ( filter instanceof PresenceNode )
581        {
582            Element newElement = element.addElement( DsmlLiterals.PRESENT );
583
584            newElement.addAttribute( DsmlLiterals.NAME, ( ( PresenceNode ) filter ).getAttribute() );
585        }
586
587        // EXTENSIBLEMATCH
588        else if ( filter instanceof ExtensibleNode )
589        {
590            Element newElement = element.addElement( DsmlLiterals.EXTENSIBLE_MATCH );
591
592            Value value = ( ( ExtensibleNode ) filter ).getValue();
593            
594            if ( value != null )
595            {
596                if ( !value.isHumanReadable() )
597                {
598                    Namespace xsdNamespace = new Namespace( ParserUtils.XSD, ParserUtils.XML_SCHEMA_URI );
599                    Namespace xsiNamespace = new Namespace( ParserUtils.XSI, ParserUtils.XML_SCHEMA_INSTANCE_URI );
600                    element.getDocument().getRootElement().add( xsdNamespace );
601                    element.getDocument().getRootElement().add( xsiNamespace );
602
603                    Element valueElement = newElement.addElement( DsmlLiterals.VALUE ).addText(
604                        ParserUtils.base64Encode( value.getBytes() ) );
605                    valueElement.addAttribute( new QName( DsmlLiterals.TYPE, xsiNamespace ), ParserUtils.XSD_COLON + ParserUtils.BASE64BINARY );
606                }
607                else
608                {
609                    newElement.addElement( DsmlLiterals.VALUE ).setText( value.getString() );
610                }
611            }
612
613            if ( ( ( ExtensibleNode ) filter ).hasDnAttributes() )
614            {
615                newElement.addAttribute( DsmlLiterals.DN_ATTRIBUTES,  DsmlLiterals.TRUE );
616            }
617
618            String matchingRule = ( ( ExtensibleNode ) filter ).getMatchingRuleId();
619            
620            if ( Strings.isNotEmpty( matchingRule ) )
621            {
622                newElement.addAttribute( DsmlLiterals.MATCHING_RULE, matchingRule );
623            }
624        }
625    }
626
627
628    /**
629     * {@inheritDoc}
630     */
631    @Override
632    public MessageTypeEnum[] getResponseTypes()
633    {
634        return getDecorated().getResponseTypes();
635    }
636
637
638    /**
639     * {@inheritDoc}
640     */
641    @Override
642    public Dn getBase()
643    {
644        return getDecorated().getBase();
645    }
646
647
648    /**
649     * {@inheritDoc}
650     */
651    @Override
652    public SearchRequest setBase( Dn baseDn )
653    {
654        getDecorated().setBase( baseDn );
655
656        return this;
657    }
658
659
660    /**
661     * {@inheritDoc}
662     */
663    @Override
664    public SearchScope getScope()
665    {
666        return getDecorated().getScope();
667    }
668
669
670    /**
671     * {@inheritDoc}
672     */
673    @Override
674    public SearchRequest setScope( SearchScope scope )
675    {
676        getDecorated().setScope( scope );
677
678        return this;
679    }
680
681
682    /**
683     * {@inheritDoc}
684     */
685    @Override
686    public AliasDerefMode getDerefAliases()
687    {
688        return getDecorated().getDerefAliases();
689    }
690
691
692    /**
693     * {@inheritDoc}
694     */
695    @Override
696    public SearchRequest setDerefAliases( AliasDerefMode aliasDerefAliases )
697    {
698        getDecorated().setDerefAliases( aliasDerefAliases );
699
700        return this;
701    }
702
703
704    /**
705     * {@inheritDoc}
706     */
707    @Override
708    public long getSizeLimit()
709    {
710        return getDecorated().getSizeLimit();
711    }
712
713
714    /**
715     * {@inheritDoc}
716     */
717    @Override
718    public SearchRequest setSizeLimit( long entriesMax )
719    {
720        getDecorated().setSizeLimit( entriesMax );
721
722        return this;
723    }
724
725
726    /**
727     * {@inheritDoc}
728     */
729    @Override
730    public int getTimeLimit()
731    {
732        return getDecorated().getTimeLimit();
733    }
734
735
736    /**
737     * {@inheritDoc}
738     */
739    @Override
740    public SearchRequest setTimeLimit( int secondsMax )
741    {
742        getDecorated().setTimeLimit( secondsMax );
743
744        return this;
745    }
746
747
748    /**
749     * {@inheritDoc}
750     */
751    @Override
752    public boolean getTypesOnly()
753    {
754        return getDecorated().getTypesOnly();
755    }
756
757
758    /**
759     * {@inheritDoc}
760     */
761    @Override
762    public SearchRequest setTypesOnly( boolean typesOnly )
763    {
764        getDecorated().setTypesOnly( typesOnly );
765
766        return this;
767    }
768
769
770    /**
771     * {@inheritDoc}
772     */
773    @Override
774    public ExprNode getFilter()
775    {
776        return getDecorated().getFilter();
777    }
778
779
780    /**
781     * {@inheritDoc}
782     */
783    @Override
784    public SearchRequest setFilter( ExprNode filter )
785    {
786        getDecorated().setFilter( filter );
787
788        return this;
789    }
790
791
792    /**
793     * {@inheritDoc}
794     */
795    @Override
796    public SearchRequest setFilter( String filter ) throws LdapException
797    {
798        getDecorated().setFilter( filter );
799
800        return this;
801    }
802
803
804    /**
805     * {@inheritDoc}
806     */
807    @Override
808    public SearchRequest setFilter( SchemaManager schemaManager, String filter ) throws LdapException
809    {
810        getDecorated().setFilter( schemaManager, filter );
811
812        return this;
813    }
814
815
816    /**
817     * {@inheritDoc}
818     */
819    @Override
820    public List<String> getAttributes()
821    {
822        return getDecorated().getAttributes();
823    }
824
825
826    /**
827     * {@inheritDoc}
828     */
829    @Override
830    public SearchRequest addAttributes( String... attributes )
831    {
832        getDecorated().addAttributes( attributes );
833
834        return this;
835    }
836
837
838    /**
839     * {@inheritDoc}
840     */
841    @Override
842    public SearchRequest removeAttribute( String attribute )
843    {
844        getDecorated().removeAttribute( attribute );
845
846        return this;
847    }
848
849
850    /**
851     * {@inheritDoc}
852     */
853    @Override
854    public SearchRequest setMessageId( int messageId )
855    {
856        return ( SearchRequest ) super.setMessageId( messageId );
857    }
858
859
860    /**
861     * {@inheritDoc}
862     */
863    @Override
864    public SearchRequest addControl( Control control )
865    {
866        return ( SearchRequest ) super.addControl( control );
867    }
868
869
870    /**
871     * {@inheritDoc}
872     */
873    @Override
874    public SearchRequest addAllControls( Control[] controls )
875    {
876        return ( SearchRequest ) super.addAllControls( controls );
877    }
878
879
880    /**
881     * {@inheritDoc}
882     */
883    @Override
884    public SearchRequest removeControl( Control control )
885    {
886        return ( SearchRequest ) super.removeControl( control );
887    }
888
889
890    /**
891     * {@inheritDoc}
892     */
893    @Override
894    public boolean isFollowReferrals()
895    {
896        return getDecorated().isFollowReferrals();
897    }
898
899
900    /**
901     * {@inheritDoc}
902     */
903    @Override
904    public SearchRequest followReferrals()
905    {
906        return getDecorated().followReferrals();
907    }
908
909
910    /**
911     * {@inheritDoc}
912     */
913    @Override
914    public boolean isIgnoreReferrals()
915    {
916        return getDecorated().isIgnoreReferrals();
917    }
918
919
920    /**
921     * {@inheritDoc}
922     */
923    @Override
924    public SearchRequest ignoreReferrals()
925    {
926        return getDecorated().ignoreReferrals();
927    }
928}