001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.directory.api.ldap.model.message; 021 022 023import java.text.ParseException; 024import java.util.ArrayList; 025import java.util.Arrays; 026import java.util.Collections; 027import java.util.List; 028 029import org.apache.directory.api.i18n.I18n; 030import org.apache.directory.api.ldap.model.exception.LdapException; 031import org.apache.directory.api.ldap.model.exception.LdapProtocolErrorException; 032import org.apache.directory.api.ldap.model.filter.BranchNormalizedVisitor; 033import org.apache.directory.api.ldap.model.filter.ExprNode; 034import org.apache.directory.api.ldap.model.filter.FilterParser; 035import org.apache.directory.api.ldap.model.name.Dn; 036 037 038/** 039 * SearchRequest implementation. 040 * 041 * @author <a href="mailto:dev@directory.apache.org"> Apache Directory Project</a> 042 */ 043public class SearchRequestImpl extends AbstractAbandonableRequest implements SearchRequest 044{ 045 static final long serialVersionUID = -5655881944020886218L; 046 047 /** Search base distinguished name */ 048 private Dn baseDn; 049 050 /** Search filter expression tree's root node */ 051 private ExprNode filterNode; 052 053 /** Search scope enumeration value */ 054 private SearchScope scope; 055 056 /** Types only return flag */ 057 private boolean typesOnly; 058 059 /** Max size in entries to return */ 060 private long sizeLimit; 061 062 /** Max seconds to wait for search to complete */ 063 private int timeLimit; 064 065 /** Alias dereferencing mode enumeration value (default to DEREF_ALWAYS) */ 066 private AliasDerefMode aliasDerefMode = AliasDerefMode.DEREF_ALWAYS; 067 068 /** Attributes to return */ 069 private List<String> attributes = new ArrayList<>(); 070 071 /** The final result containing SearchResponseDone response */ 072 private SearchResultDone response; 073 074 /** A flag set to tell the search what to do wth referrals */ 075 private ReferralsPolicyEnum referralHandling = ReferralsPolicyEnum.THROW; 076 077 078 // ----------------------------------------------------------------------- 079 // Constructors 080 // ----------------------------------------------------------------------- 081 /** 082 * Creates a SearcRequest implementing object used to search the 083 * DIT. 084 */ 085 public SearchRequestImpl() 086 { 087 super( -1, MessageTypeEnum.SEARCH_REQUEST ); 088 } 089 090 091 // ------------------------------------------------------------------------ 092 // SearchRequest Interface Method Implementations 093 // ------------------------------------------------------------------------ 094 095 /** 096 * {@inheritDoc} 097 */ 098 @Override 099 public List<String> getAttributes() 100 { 101 return Collections.unmodifiableList( attributes ); 102 } 103 104 105 /** 106 * Gets the search base as a distinguished name. 107 * 108 * @return the search base 109 */ 110 @Override 111 public Dn getBase() 112 { 113 return baseDn; 114 } 115 116 117 /** 118 * {@inheritDoc} 119 */ 120 @Override 121 public SearchRequest setBase( Dn base ) 122 { 123 baseDn = base; 124 125 return this; 126 } 127 128 129 /** 130 * {@inheritDoc} 131 */ 132 @Override 133 public AliasDerefMode getDerefAliases() 134 { 135 return aliasDerefMode; 136 } 137 138 139 /** 140 * {@inheritDoc} 141 */ 142 @Override 143 public SearchRequest setDerefAliases( AliasDerefMode aliasDerefAliases ) 144 { 145 this.aliasDerefMode = aliasDerefAliases; 146 147 return this; 148 } 149 150 151 /** 152 * {@inheritDoc} 153 */ 154 @Override 155 public ExprNode getFilter() 156 { 157 return filterNode; 158 } 159 160 161 /** 162 * {@inheritDoc} 163 */ 164 @Override 165 public SearchRequest setFilter( ExprNode filter ) 166 { 167 this.filterNode = filter; 168 return this; 169 } 170 171 172 /** 173 * {@inheritDoc} 174 */ 175 @Override 176 public SearchRequest setFilter( String filter ) throws LdapException 177 { 178 try 179 { 180 filterNode = FilterParser.parse( filter ); 181 } 182 catch ( ParseException pe ) 183 { 184 throw new LdapProtocolErrorException( I18n.err( I18n.ERR_13508_INVALID_FILTER, filter ), pe ); 185 } 186 187 return this; 188 } 189 190 191 /** 192 * {@inheritDoc} 193 */ 194 @Override 195 public MessageTypeEnum[] getResponseTypes() 196 { 197 return RESPONSE_TYPES; 198 } 199 200 201 /** 202 * {@inheritDoc} 203 */ 204 @Override 205 public SearchScope getScope() 206 { 207 return scope; 208 } 209 210 211 /** 212 * {@inheritDoc} 213 */ 214 @Override 215 public SearchRequest setScope( SearchScope scope ) 216 { 217 this.scope = scope; 218 219 return this; 220 } 221 222 223 /** 224 * {@inheritDoc} 225 */ 226 @Override 227 public long getSizeLimit() 228 { 229 return sizeLimit; 230 } 231 232 233 /** 234 * {@inheritDoc} 235 */ 236 @Override 237 public SearchRequest setSizeLimit( long entriesMax ) 238 { 239 sizeLimit = entriesMax; 240 241 return this; 242 } 243 244 245 /** 246 * {@inheritDoc} 247 */ 248 @Override 249 public int getTimeLimit() 250 { 251 return timeLimit; 252 } 253 254 255 /** 256 * {@inheritDoc} 257 */ 258 @Override 259 public SearchRequest setTimeLimit( int secondsMax ) 260 { 261 timeLimit = secondsMax; 262 263 return this; 264 } 265 266 267 /** 268 * {@inheritDoc} 269 */ 270 @Override 271 public boolean getTypesOnly() 272 { 273 return typesOnly; 274 } 275 276 277 /** 278 * {@inheritDoc} 279 */ 280 @Override 281 public SearchRequest setTypesOnly( boolean typesOnly ) 282 { 283 this.typesOnly = typesOnly; 284 285 return this; 286 } 287 288 289 /** 290 * {@inheritDoc} 291 */ 292 @Override 293 public SearchRequest addAttributes( String... attributesToAdd ) 294 { 295 this.attributes.addAll( Arrays.asList( attributesToAdd ) ); 296 297 return this; 298 } 299 300 301 /** 302 * {@inheritDoc} 303 */ 304 @Override 305 public SearchRequest removeAttribute( String attribute ) 306 { 307 attributes.remove( attribute ); 308 309 return this; 310 } 311 312 313 /** 314 * {@inheritDoc} 315 */ 316 @Override 317 public SearchResultDone getResultResponse() 318 { 319 if ( response == null ) 320 { 321 response = new SearchResultDoneImpl( getMessageId() ); 322 } 323 324 return response; 325 } 326 327 328 /** 329 * {@inheritDoc} 330 */ 331 @Override 332 public SearchRequest setMessageId( int messageId ) 333 { 334 super.setMessageId( messageId ); 335 336 return this; 337 } 338 339 340 /** 341 * {@inheritDoc} 342 */ 343 @Override 344 public SearchRequest addControl( Control control ) 345 { 346 return ( SearchRequest ) super.addControl( control ); 347 } 348 349 350 /** 351 * {@inheritDoc} 352 */ 353 @Override 354 public SearchRequest addAllControls( Control[] controls ) 355 { 356 return ( SearchRequest ) super.addAllControls( controls ); 357 } 358 359 360 /** 361 * {@inheritDoc} 362 */ 363 @Override 364 public SearchRequest removeControl( Control control ) 365 { 366 return ( SearchRequest ) super.removeControl( control ); 367 } 368 369 370 /** 371 * {@inheritDoc} 372 */ 373 @Override 374 public int hashCode() 375 { 376 int hash = 37; 377 378 if ( baseDn != null ) 379 { 380 hash = hash * 17 + baseDn.hashCode(); 381 } 382 383 hash = hash * 17 + aliasDerefMode.hashCode(); 384 hash = hash * 17 + scope.hashCode(); 385 hash = hash * 17 + Long.valueOf( sizeLimit ).hashCode(); 386 hash = hash * 17 + timeLimit; 387 hash = hash * 17 + ( typesOnly ? 0 : 1 ); 388 389 if ( attributes != null ) 390 { 391 hash = hash * 17 + attributes.size(); 392 393 // Order doesn't matter, thus just add hashCode 394 for ( String attr : attributes ) 395 { 396 if ( attr != null ) 397 { 398 hash = hash + attr.hashCode(); 399 } 400 } 401 } 402 403 BranchNormalizedVisitor visitor = new BranchNormalizedVisitor(); 404 filterNode.accept( visitor ); 405 hash = hash * 17 + filterNode.toString().hashCode(); 406 hash = hash * 17 + super.hashCode(); 407 408 return hash; 409 } 410 411 412 /** 413 * Checks to see if two search requests are equal. The Lockable properties 414 * and the get/set context specific parameters are not consulted to 415 * determine equality. The filter expression tree comparison will normalize 416 * the child order of filter branch nodes then generate a string 417 * representation which is comparable. For the time being this is a very 418 * costly operation. 419 * 420 * @param obj the object to check for equality to this SearchRequest 421 * @return true if the obj is a SearchRequest and equals this SearchRequest, 422 * false otherwise 423 */ 424 @Override 425 public boolean equals( Object obj ) 426 { 427 if ( obj == this ) 428 { 429 return true; 430 } 431 432 if ( !super.equals( obj ) ) 433 { 434 return false; 435 } 436 437 SearchRequest req = ( SearchRequest ) obj; 438 439 if ( !req.getBase().equals( baseDn ) ) 440 { 441 return false; 442 } 443 444 if ( req.getDerefAliases() != aliasDerefMode ) 445 { 446 return false; 447 } 448 449 if ( req.getScope() != scope ) 450 { 451 return false; 452 } 453 454 if ( req.getSizeLimit() != sizeLimit ) 455 { 456 return false; 457 } 458 459 if ( req.getTimeLimit() != timeLimit ) 460 { 461 return false; 462 } 463 464 if ( req.getTypesOnly() != typesOnly ) 465 { 466 return false; 467 } 468 469 if ( req.getAttributes() == null && attributes != null && !attributes.isEmpty() ) 470 { 471 return false; 472 } 473 474 if ( req.getAttributes() != null && attributes == null && !req.getAttributes().isEmpty() ) 475 { 476 return false; 477 } 478 479 if ( req.getAttributes() != null && attributes != null ) 480 { 481 if ( req.getAttributes().size() != attributes.size() ) 482 { 483 return false; 484 } 485 486 for ( String attribute : attributes ) 487 { 488 if ( !req.getAttributes().contains( attribute ) ) 489 { 490 return false; 491 } 492 } 493 } 494 495 BranchNormalizedVisitor visitor = new BranchNormalizedVisitor(); 496 req.getFilter().accept( visitor ); 497 filterNode.accept( visitor ); 498 499 String myFilterString = filterNode.toString(); 500 String reqFilterString = req.getFilter().toString(); 501 502 return myFilterString.equals( reqFilterString ); 503 } 504 505 506 /** 507 * Return a string the represent a SearchRequest 508 * {@inheritDoc} 509 */ 510 @Override 511 public String toString() 512 { 513 StringBuilder sb = new StringBuilder(); 514 515 sb.append( " SearchRequest\n" ); 516 sb.append( " baseDn : '" ).append( baseDn ).append( "'\n" ); 517 518 if ( filterNode != null ) 519 { 520 sb.append( " filter : '" ); 521 sb.append( filterNode.toString() ); 522 sb.append( "'\n" ); 523 } 524 525 sb.append( " scope : " ); 526 527 switch ( scope ) 528 { 529 case OBJECT: 530 sb.append( "base object" ); 531 break; 532 533 case ONELEVEL: 534 sb.append( "single level" ); 535 break; 536 537 case SUBTREE: 538 sb.append( "whole subtree" ); 539 break; 540 541 default: 542 throw new IllegalArgumentException( I18n.err( I18n.ERR_13509_UNEXPECTED_SCOPE, scope ) ); 543 } 544 545 sb.append( '\n' ); 546 547 sb.append( " typesOnly : " ).append( typesOnly ).append( '\n' ); 548 549 sb.append( " Size Limit : " ); 550 551 if ( sizeLimit == 0L ) 552 { 553 sb.append( "no limit" ); 554 } 555 else 556 { 557 sb.append( sizeLimit ); 558 } 559 560 sb.append( '\n' ); 561 562 sb.append( " Time Limit : " ); 563 564 if ( timeLimit == 0 ) 565 { 566 sb.append( "no limit" ); 567 } 568 else 569 { 570 sb.append( timeLimit ); 571 } 572 573 sb.append( '\n' ); 574 575 sb.append( " Deref Aliases : " ); 576 577 switch ( aliasDerefMode ) 578 { 579 case NEVER_DEREF_ALIASES: 580 sb.append( "never Deref Aliases" ); 581 break; 582 583 case DEREF_IN_SEARCHING: 584 sb.append( "deref In Searching" ); 585 break; 586 587 case DEREF_FINDING_BASE_OBJ: 588 sb.append( "deref Finding Base Obj" ); 589 break; 590 591 case DEREF_ALWAYS: 592 sb.append( "deref Always" ); 593 break; 594 595 default: 596 throw new IllegalArgumentException( I18n.err( I18n.ERR_13517_UNEXPECTED_ALIAS_DEREF_MODE, aliasDerefMode ) ); 597 } 598 599 sb.append( '\n' ); 600 sb.append( " attributes : " ); 601 602 boolean isFirst = true; 603 604 if ( attributes != null ) 605 { 606 for ( String attribute : attributes ) 607 { 608 if ( isFirst ) 609 { 610 isFirst = false; 611 } 612 else 613 { 614 sb.append( ", " ); 615 } 616 617 sb.append( '\'' ).append( attribute ).append( '\'' ); 618 } 619 } 620 621 sb.append( '\n' ); 622 623 // The controls 624 sb.append( super.toString() ); 625 626 return super.toString( sb.toString() ); 627 } 628 629 630 /** 631 * {@inheritDoc} 632 */ 633 @Override 634 public boolean isFollowReferrals() 635 { 636 return referralHandling == ReferralsPolicyEnum.FOLLOW; 637 } 638 639 640 /** 641 * {@inheritDoc} 642 */ 643 @Override 644 public SearchRequest followReferrals() 645 { 646 referralHandling = ReferralsPolicyEnum.FOLLOW; 647 648 return this; 649 } 650 651 652 /** 653 * {@inheritDoc} 654 */ 655 @Override 656 public boolean isIgnoreReferrals() 657 { 658 return referralHandling == ReferralsPolicyEnum.IGNORE; 659 } 660 661 662 /** 663 * {@inheritDoc} 664 */ 665 @Override 666 public SearchRequest ignoreReferrals() 667 { 668 referralHandling = ReferralsPolicyEnum.IGNORE; 669 670 return this; 671 } 672}