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