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.directory.api.asn1.DecoderException; 027import org.apache.directory.api.dsmlv2.ParserUtils; 028import org.apache.directory.api.i18n.I18n; 029import org.apache.directory.api.ldap.codec.api.LdapApiService; 030import org.apache.directory.api.ldap.codec.api.LdapCodecConstants; 031import org.apache.directory.api.ldap.model.entry.Value; 032import org.apache.directory.api.ldap.model.exception.LdapException; 033import org.apache.directory.api.ldap.model.exception.LdapSchemaException; 034import org.apache.directory.api.ldap.model.filter.AndNode; 035import org.apache.directory.api.ldap.model.filter.ApproximateNode; 036import org.apache.directory.api.ldap.model.filter.BranchNode; 037import org.apache.directory.api.ldap.model.filter.EqualityNode; 038import org.apache.directory.api.ldap.model.filter.ExprNode; 039import org.apache.directory.api.ldap.model.filter.ExtensibleNode; 040import org.apache.directory.api.ldap.model.filter.GreaterEqNode; 041import org.apache.directory.api.ldap.model.filter.LeafNode; 042import org.apache.directory.api.ldap.model.filter.LessEqNode; 043import org.apache.directory.api.ldap.model.filter.NotNode; 044import org.apache.directory.api.ldap.model.filter.OrNode; 045import org.apache.directory.api.ldap.model.filter.PresenceNode; 046import org.apache.directory.api.ldap.model.filter.SimpleNode; 047import org.apache.directory.api.ldap.model.filter.SubstringNode; 048import org.apache.directory.api.ldap.model.message.AliasDerefMode; 049import org.apache.directory.api.ldap.model.message.Control; 050import org.apache.directory.api.ldap.model.message.MessageTypeEnum; 051import org.apache.directory.api.ldap.model.message.SearchRequest; 052import org.apache.directory.api.ldap.model.message.SearchRequestImpl; 053import org.apache.directory.api.ldap.model.message.SearchResultDone; 054import org.apache.directory.api.ldap.model.message.SearchScope; 055import org.apache.directory.api.ldap.model.name.Dn; 056import org.dom4j.Element; 057import org.dom4j.Namespace; 058import org.dom4j.QName; 059 060 061/** 062 * DSML Decorator for SearchRequest 063 * 064 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 065 */ 066public class SearchRequestDsml 067 extends AbstractResultResponseRequestDsml<SearchRequest, SearchResultDone> 068 implements SearchRequest 069{ 070 /** Some string constants */ 071 private static final String DEREF_ALIASES = "derefAliases"; 072 private static final String NAME = "name"; 073 private static final String VALUE = "value"; 074 075 /** A temporary storage for a terminal Filter */ 076 private Filter terminalFilter; 077 078 /** The current filter. This is used while decoding a PDU */ 079 private Filter currentFilter; 080 081 /** The global filter. This is used while decoding a PDU */ 082 private Filter topFilter; 083 084 085 /** 086 * Creates a new getDecoratedMessage() of SearchRequestDsml. 087 * 088 * @param codec The LDAP Service to use 089 */ 090 public SearchRequestDsml( LdapApiService codec ) 091 { 092 super( codec, new SearchRequestImpl() ); 093 } 094 095 096 /** 097 * Creates a new getDecoratedMessage() of SearchRequestDsml. 098 * 099 * @param codec The LDAP Service to use 100 * @param ldapMessage the message to decorate 101 */ 102 public SearchRequestDsml( LdapApiService codec, SearchRequest ldapMessage ) 103 { 104 super( codec, ldapMessage ); 105 } 106 107 108 /** 109 * Gets the search filter associated with this search request. 110 * 111 * @return the expression node for the root of the filter expression tree. 112 */ 113 public Filter getCodecFilter() 114 { 115 return topFilter; 116 } 117 118 119 /** 120 * Gets the search filter associated with this search request. 121 * 122 * @return the expression node for the root of the filter expression tree. 123 * @throws LdapSchemaException If the filter is invalid 124 */ 125 public ExprNode getFilterNode() throws LdapSchemaException 126 { 127 return transform( topFilter ); 128 } 129 130 131 /** 132 * Get the terminal filter 133 * 134 * @return Returns the terminal filter. 135 */ 136 public Filter getTerminalFilter() 137 { 138 return terminalFilter; 139 } 140 141 142 /** 143 * Set the terminal filter 144 * 145 * @param terminalFilter the teminalFilter. 146 */ 147 public void setTerminalFilter( Filter terminalFilter ) 148 { 149 this.terminalFilter = terminalFilter; 150 } 151 152 153 /** 154 * set the currentFilter to its parent 155 */ 156 public void endCurrentConnectorFilter() 157 { 158 currentFilter = currentFilter.getParent(); 159 } 160 161 162 /** 163 * Add a current filter. We have two cases : 164 * <ul> 165 * <li>there is no previous current filter : the filter 166 * is the top level filter</li> 167 * <li>there is a previous current filter : the filter is added 168 * to the currentFilter set, and the current filter is changed</li> 169 * </ul> 170 * In any case, the previous current filter will always be a 171 * ConnectorFilter when this method is called. 172 * 173 * @param localFilter The filter to set. 174 * @throws DecoderException If the added filter is invalid 175 */ 176 public void addCurrentFilter( Filter localFilter ) throws DecoderException 177 { 178 if ( currentFilter != null ) 179 { 180 // Ok, we have a parent. The new Filter will be added to 181 // this parent, and will become the currentFilter if it's a connector. 182 ( ( ConnectorFilter ) currentFilter ).addFilter( localFilter ); 183 localFilter.setParent( currentFilter ); 184 185 if ( localFilter instanceof ConnectorFilter ) 186 { 187 currentFilter = localFilter; 188 } 189 } 190 else 191 { 192 // No parent. This Filter will become the root. 193 currentFilter = localFilter; 194 currentFilter.setParent( null ); 195 topFilter = localFilter; 196 } 197 } 198 199 200 /** 201 * Transform the Filter part of a SearchRequest to an ExprNode 202 * 203 * @param filter The filter to be transformed 204 * @return An ExprNode 205 * @throws LdapSchemaException If the filter contains a wrong schema element 206 */ 207 @SuppressWarnings({ "rawtypes" }) 208 private ExprNode transform( Filter filter ) throws LdapSchemaException 209 { 210 if ( filter != null ) 211 { 212 // Transform OR, AND or NOT leaves 213 if ( filter instanceof ConnectorFilter ) 214 { 215 BranchNode branch; 216 217 if ( filter instanceof AndFilter ) 218 { 219 branch = new AndNode(); 220 } 221 else if ( filter instanceof OrFilter ) 222 { 223 branch = new OrNode(); 224 } 225 else 226 { 227 branch = new NotNode(); 228 } 229 230 List<Filter> filtersSet = ( ( ConnectorFilter ) filter ).getFilterSet(); 231 232 // Loop on all AND/OR children 233 if ( filtersSet != null ) 234 { 235 for ( Filter node : filtersSet ) 236 { 237 branch.addNode( transform( node ) ); 238 } 239 } 240 241 return branch; 242 } 243 else 244 { 245 // Transform PRESENT or ATTRIBUTE_VALUE_ASSERTION 246 LeafNode branch = null; 247 248 if ( filter instanceof PresentFilter ) 249 { 250 branch = new PresenceNode( ( ( PresentFilter ) filter ).getAttributeDescription() ); 251 } 252 else if ( filter instanceof AttributeValueAssertionFilter ) 253 { 254 AttributeValueAssertionFilter avaFilter = ( AttributeValueAssertionFilter ) filter; 255 256 AttributeValueAssertion ava = avaFilter.getAssertion(); 257 258 // Transform =, >=, <=, ~= filters 259 int filterType = avaFilter.getFilterType(); 260 byte[] value = null; 261 262 if ( ava.getAssertionValue() != null ) 263 { 264 value = ava.getAssertionValue().getBytes(); 265 } 266 267 switch ( filterType ) 268 { 269 case LdapCodecConstants.EQUALITY_MATCH_FILTER: 270 branch = new EqualityNode( ava.getAttributeDesc(), value ); 271 break; 272 273 case LdapCodecConstants.GREATER_OR_EQUAL_FILTER: 274 branch = new GreaterEqNode( ava.getAttributeDesc(), value ); 275 break; 276 277 case LdapCodecConstants.LESS_OR_EQUAL_FILTER: 278 branch = new LessEqNode( ava.getAttributeDesc(), value ); 279 break; 280 281 case LdapCodecConstants.APPROX_MATCH_FILTER: 282 branch = new ApproximateNode( ava.getAttributeDesc(), value ); 283 break; 284 285 default: 286 throw new IllegalStateException( I18n.err( I18n.ERR_03042_UNEXPECTED_FILTER_TYPE, filterType ) ); 287 } 288 289 } 290 else if ( filter instanceof SubstringFilter ) 291 { 292 // Transform Substring filters 293 SubstringFilter substrFilter = ( SubstringFilter ) filter; 294 String initialString = null; 295 String finalString = null; 296 List<String> anyString = null; 297 298 if ( substrFilter.getInitialSubstrings() != null ) 299 { 300 initialString = substrFilter.getInitialSubstrings(); 301 } 302 303 if ( substrFilter.getFinalSubstrings() != null ) 304 { 305 finalString = substrFilter.getFinalSubstrings(); 306 } 307 308 if ( substrFilter.getAnySubstrings() != null ) 309 { 310 anyString = new ArrayList<>(); 311 312 for ( String any : substrFilter.getAnySubstrings() ) 313 { 314 anyString.add( any ); 315 } 316 } 317 318 branch = new SubstringNode( anyString, substrFilter.getType(), initialString, finalString ); 319 } 320 else if ( filter instanceof ExtensibleMatchFilter ) 321 { 322 // Transform Extensible Match Filter 323 ExtensibleMatchFilter extFilter = ( ExtensibleMatchFilter ) filter; 324 String matchingRule = null; 325 326 Value value = extFilter.getMatchValue(); 327 328 if ( extFilter.getMatchingRule() != null ) 329 { 330 matchingRule = extFilter.getMatchingRule(); 331 } 332 333 branch = new ExtensibleNode( extFilter.getType(), value, matchingRule, extFilter.isDnAttributes() ); 334 } 335 336 return branch; 337 } 338 } 339 else 340 { 341 // We have found nothing to transform. Return null then. 342 return null; 343 } 344 } 345 346 347 /** 348 * {@inheritDoc} 349 */ 350 @Override 351 public MessageTypeEnum getType() 352 { 353 return getDecorated().getType(); 354 } 355 356 357 /** 358 * {@inheritDoc} 359 */ 360 @Override 361 public Element toDsml( Element root ) 362 { 363 Element element = super.toDsml( root ); 364 365 SearchRequest request = getDecorated(); 366 367 // Dn 368 if ( request.getBase() != null ) 369 { 370 element.addAttribute( "dn", request.getBase().getName() ); 371 } 372 373 // Scope 374 SearchScope scope = request.getScope(); 375 if ( scope != null ) 376 { 377 if ( scope == SearchScope.OBJECT ) 378 { 379 element.addAttribute( "scope", "baseObject" ); 380 } 381 else if ( scope == SearchScope.ONELEVEL ) 382 { 383 element.addAttribute( "scope", "singleLevel" ); 384 } 385 else if ( scope == SearchScope.SUBTREE ) 386 { 387 element.addAttribute( "scope", "wholeSubtree" ); 388 } 389 } 390 391 // DerefAliases 392 AliasDerefMode derefAliases = request.getDerefAliases(); 393 394 switch ( derefAliases ) 395 { 396 case NEVER_DEREF_ALIASES: 397 element.addAttribute( DEREF_ALIASES, "neverDerefAliases" ); 398 break; 399 400 case DEREF_ALWAYS: 401 element.addAttribute( DEREF_ALIASES, "derefAlways" ); 402 break; 403 404 case DEREF_FINDING_BASE_OBJ: 405 element.addAttribute( DEREF_ALIASES, "derefFindingBaseObj" ); 406 break; 407 408 case DEREF_IN_SEARCHING: 409 element.addAttribute( DEREF_ALIASES, "derefInSearching" ); 410 break; 411 412 default: 413 throw new IllegalStateException( I18n.err( I18n.ERR_03043_UNEXPECTED_DEREF_ALIAS, derefAliases ) ); 414 } 415 416 // SizeLimit 417 if ( request.getSizeLimit() != 0L ) 418 { 419 element.addAttribute( "sizeLimit", Long.toString( request.getSizeLimit() ) ); 420 } 421 422 // TimeLimit 423 if ( request.getTimeLimit() != 0 ) 424 { 425 element.addAttribute( "timeLimit", Integer.toString( request.getTimeLimit() ) ); 426 } 427 428 // TypesOnly 429 if ( request.getTypesOnly() ) 430 { 431 element.addAttribute( "typesOnly", "true" ); 432 } 433 434 // Filter 435 Element filterElement = element.addElement( "filter" ); 436 toDsml( filterElement, request.getFilter() ); 437 438 // Attributes 439 List<String> attributes = request.getAttributes(); 440 441 if ( !attributes.isEmpty() ) 442 { 443 Element attributesElement = element.addElement( "attributes" ); 444 445 for ( String entryAttribute : attributes ) 446 { 447 attributesElement.addElement( "attribute" ).addAttribute( NAME, entryAttribute ); 448 } 449 } 450 451 return element; 452 } 453 454 455 /** 456 * Recursively converts the filter of the Search Request into a DSML representation and adds 457 * it to the XML Element corresponding to the Search Request 458 * 459 * @param element 460 * the parent Element 461 * @param filter 462 * the filter to convert 463 */ 464 private void toDsml( Element element, ExprNode filter ) 465 { 466 // AND FILTER 467 if ( filter instanceof AndNode ) 468 { 469 Element newElement = element.addElement( "and" ); 470 471 List<ExprNode> filterList = ( ( AndNode ) filter ).getChildren(); 472 473 for ( int i = 0; i < filterList.size(); i++ ) 474 { 475 toDsml( newElement, filterList.get( i ) ); 476 } 477 } 478 479 // OR FILTER 480 else if ( filter instanceof OrNode ) 481 { 482 Element newElement = element.addElement( "or" ); 483 484 List<ExprNode> filterList = ( ( OrNode ) filter ).getChildren(); 485 486 for ( int i = 0; i < filterList.size(); i++ ) 487 { 488 toDsml( newElement, filterList.get( i ) ); 489 } 490 } 491 492 // NOT FILTER 493 else if ( filter instanceof NotNode ) 494 { 495 Element newElement = element.addElement( "not" ); 496 497 toDsml( newElement, ( ( NotNode ) filter ).getFirstChild() ); 498 } 499 500 // SUBSTRING FILTER 501 else if ( filter instanceof SubstringNode ) 502 { 503 Element newElement = element.addElement( "substrings" ); 504 505 SubstringNode substringFilter = ( SubstringNode ) filter; 506 507 newElement.addAttribute( NAME, substringFilter.getAttribute() ); 508 509 String initial = substringFilter.getInitial(); 510 511 if ( ( initial != null ) && ( !"".equals( initial ) ) ) 512 { 513 newElement.addElement( "initial" ).setText( initial ); 514 } 515 516 List<String> anyList = substringFilter.getAny(); 517 518 for ( int i = 0; i < anyList.size(); i++ ) 519 { 520 newElement.addElement( "any" ).setText( anyList.get( i ) ); 521 } 522 523 String finalString = substringFilter.getFinal(); 524 525 if ( ( finalString != null ) && ( !"".equals( finalString ) ) ) 526 { 527 newElement.addElement( "final" ).setText( finalString ); 528 } 529 } 530 531 // APPROXMATCH, EQUALITYMATCH, GREATEROREQUALS & LESSOREQUAL FILTERS 532 else if ( filter instanceof SimpleNode ) 533 { 534 Element newElement; 535 536 if ( filter instanceof ApproximateNode ) 537 { 538 newElement = element.addElement( "approxMatch" ); 539 } 540 else if ( filter instanceof EqualityNode ) 541 { 542 newElement = element.addElement( "equalityMatch" ); 543 } 544 else if ( filter instanceof GreaterEqNode ) 545 { 546 newElement = element.addElement( "greaterOrEqual" ); 547 } 548 else 549 // it is a LessEqNode ) 550 { 551 newElement = element.addElement( "lessOrEqual" ); 552 } 553 554 String attributeName = ( ( SimpleNode<?> ) filter ).getAttribute(); 555 newElement.addAttribute( NAME, attributeName ); 556 557 Value value = ( ( SimpleNode<?> ) filter ).getValue(); 558 559 if ( value != null ) 560 { 561 if ( ParserUtils.needsBase64Encoding( value ) ) 562 { 563 Namespace xsdNamespace = new Namespace( "xsd", ParserUtils.XML_SCHEMA_URI ); 564 Namespace xsiNamespace = new Namespace( "xsi", ParserUtils.XML_SCHEMA_INSTANCE_URI ); 565 element.getDocument().getRootElement().add( xsdNamespace ); 566 element.getDocument().getRootElement().add( xsiNamespace ); 567 568 Element valueElement = newElement.addElement( VALUE ).addText( 569 ParserUtils.base64Encode( value ) ); 570 valueElement 571 .addAttribute( new QName( "type", xsiNamespace ), "xsd:" + ParserUtils.BASE64BINARY ); 572 } 573 else 574 { 575 newElement.addElement( VALUE ).setText( value.getString() ); 576 } 577 } 578 } 579 580 // PRESENT FILTER 581 else if ( filter instanceof PresenceNode ) 582 { 583 Element newElement = element.addElement( "present" ); 584 585 newElement.addAttribute( NAME, ( ( PresenceNode ) filter ).getAttribute() ); 586 } 587 588 // EXTENSIBLEMATCH 589 else if ( filter instanceof ExtensibleNode ) 590 { 591 Element newElement = element.addElement( "extensibleMatch" ); 592 593 Value value = ( ( ExtensibleNode ) filter ).getValue(); 594 595 if ( value != null ) 596 { 597 if ( ParserUtils.needsBase64Encoding( value ) ) 598 { 599 Namespace xsdNamespace = new Namespace( "xsd", ParserUtils.XML_SCHEMA_URI ); 600 Namespace xsiNamespace = new Namespace( "xsi", ParserUtils.XML_SCHEMA_INSTANCE_URI ); 601 element.getDocument().getRootElement().add( xsdNamespace ); 602 element.getDocument().getRootElement().add( xsiNamespace ); 603 604 Element valueElement = newElement.addElement( VALUE ).addText( 605 ParserUtils.base64Encode( value.getString() ) ); 606 valueElement.addAttribute( new QName( "type", xsiNamespace ), "xsd:" + ParserUtils.BASE64BINARY ); 607 } 608 else 609 { 610 newElement.addElement( VALUE ).setText( value.getString() ); 611 } 612 } 613 614 if ( ( ( ExtensibleNode ) filter ).hasDnAttributes() ) 615 { 616 newElement.addAttribute( "dnAttributes", "true" ); 617 } 618 619 String matchingRule = ( ( ExtensibleNode ) filter ).getMatchingRuleId(); 620 if ( ( matchingRule != null ) && ( "".equals( matchingRule ) ) ) 621 { 622 newElement.addAttribute( "matchingRule", 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 List<String> getAttributes() 809 { 810 return getDecorated().getAttributes(); 811 } 812 813 814 /** 815 * {@inheritDoc} 816 */ 817 @Override 818 public SearchRequest addAttributes( String... attributes ) 819 { 820 getDecorated().addAttributes( attributes ); 821 822 return this; 823 } 824 825 826 /** 827 * {@inheritDoc} 828 */ 829 @Override 830 public SearchRequest removeAttribute( String attribute ) 831 { 832 getDecorated().removeAttribute( attribute ); 833 834 return this; 835 } 836 837 838 /** 839 * {@inheritDoc} 840 */ 841 @Override 842 public SearchRequest setMessageId( int messageId ) 843 { 844 return ( SearchRequest ) super.setMessageId( messageId ); 845 } 846 847 848 /** 849 * {@inheritDoc} 850 */ 851 @Override 852 public SearchRequest addControl( Control control ) 853 { 854 return ( SearchRequest ) super.addControl( control ); 855 } 856 857 858 /** 859 * {@inheritDoc} 860 */ 861 @Override 862 public SearchRequest addAllControls( Control[] controls ) 863 { 864 return ( SearchRequest ) super.addAllControls( controls ); 865 } 866 867 868 /** 869 * {@inheritDoc} 870 */ 871 @Override 872 public SearchRequest removeControl( Control control ) 873 { 874 return ( SearchRequest ) super.removeControl( control ); 875 } 876 877 878 /** 879 * {@inheritDoc} 880 */ 881 @Override 882 public boolean isFollowReferrals() 883 { 884 return getDecorated().isFollowReferrals(); 885 } 886 887 888 /** 889 * {@inheritDoc} 890 */ 891 @Override 892 public SearchRequest followReferrals() 893 { 894 return getDecorated().followReferrals(); 895 } 896 897 898 /** 899 * {@inheritDoc} 900 */ 901 @Override 902 public boolean isIgnoreReferrals() 903 { 904 return getDecorated().isIgnoreReferrals(); 905 } 906 907 908 /** 909 * {@inheritDoc} 910 */ 911 @Override 912 public SearchRequest ignoreReferrals() 913 { 914 return getDecorated().ignoreReferrals(); 915 } 916}