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