001/* 002 * Copyright 2007-2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2007-2020 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2008-2020 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.sdk; 037 038 039 040import java.util.Collections; 041import java.util.List; 042 043import com.unboundid.asn1.ASN1StreamReader; 044import com.unboundid.asn1.ASN1StreamReaderSequence; 045import com.unboundid.util.NotMutable; 046import com.unboundid.util.ThreadSafety; 047import com.unboundid.util.ThreadSafetyLevel; 048 049 050 051/** 052 * This class provides a data structure for holding information about the result 053 * of processing a search request. This includes the elements of the 054 * {@link LDAPResult} object, but also contains additional information specific 055 * to the search operation. This includes: 056 * <UL> 057 * <LI>The number of {@link SearchResultEntry} objects returned from the 058 * server. This will be available regardless of whether the entries are 059 * included in this search result object or were returned through a 060 * {@link SearchResultListener}.</LI> 061 * <LI>The number of {@link SearchResultReference} objects returned from the 062 * server. This will be available regardless of whether the entries are 063 * included in this search result object or were returned through a 064 * {@link SearchResultListener}.</LI> 065 * <LI>A list of the {@link SearchResultEntry} objects returned from the 066 * server. This will be {@code null} if a {@link SearchResultListener} 067 * was used to return the entries.</LI> 068 * <LI>A list of the {@link SearchResultReference} objects returned from the 069 * server. This will be {@code null} if a {@link SearchResultListener} 070 * was used to return the entries.</LI> 071 * </UL> 072 */ 073@NotMutable() 074@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 075public final class SearchResult 076 extends LDAPResult 077{ 078 /** 079 * The serial version UID for this serializable class. 080 */ 081 private static final long serialVersionUID = 1938208530894131198L; 082 083 084 085 // The number of matching entries returned for this search. 086 private int numEntries; 087 088 // The number of search result references returned for this search. 089 private int numReferences; 090 091 // A list that may be used to hold the search result entries returned for 092 // this search. 093 private List<SearchResultEntry> searchEntries; 094 095 // A list that may be used to hold the search result references returned for 096 // this search. 097 private List<SearchResultReference> searchReferences; 098 099 100 101 /** 102 * Creates a new search result object with the provided information. This 103 * version of the constructor should be used if the search result entries and 104 * references were returned to the client via the {@code SearchResultListener} 105 * interface. 106 * 107 * @param messageID The message ID for the LDAP message that is 108 * associated with this LDAP result. 109 * @param resultCode The result code from the search result done 110 * response. 111 * @param diagnosticMessage The diagnostic message from the search result 112 * done response, if available. 113 * @param matchedDN The matched DN from the search result done 114 * response, if available. 115 * @param referralURLs The set of referral URLs from the search result 116 * done response, if available. 117 * @param numEntries The number of search result entries returned 118 * for this search. 119 * @param numReferences The number of search result references returned 120 * for this search. 121 * @param responseControls The set of controls from the search result done 122 * response, if available. 123 */ 124 public SearchResult(final int messageID, final ResultCode resultCode, 125 final String diagnosticMessage, final String matchedDN, 126 final String[] referralURLs, final int numEntries, 127 final int numReferences, final Control[] responseControls) 128 { 129 super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs, 130 responseControls); 131 132 this.numEntries = numEntries; 133 this.numReferences = numReferences; 134 135 searchEntries = null; 136 searchReferences = null; 137 } 138 139 140 141 /** 142 * Creates a new search result object with the provided information. This 143 * version of the constructor should be used if the search result entries and 144 * references were collected in lists rather than returned to the requester 145 * through the {@code SearchResultListener} interface. 146 * 147 * @param messageID The message ID for the LDAP message that is 148 * associated with this LDAP result. 149 * @param resultCode The result code from the search result done 150 * response. 151 * @param diagnosticMessage The diagnostic message from the search result 152 * done response, if available. 153 * @param matchedDN The matched DN from the search result done 154 * response, if available. 155 * @param referralURLs The set of referral URLs from the search result 156 * done response, if available. 157 * @param searchEntries A list containing the set of search result 158 * entries returned by the server. It may only be 159 * {@code null} if the search result entries were 160 * returned through the 161 * {@code SearchResultListener} interface. 162 * @param searchReferences A list containing the set of search result 163 * references returned by the server. It may only 164 * be {@code null} if the search result entries 165 * were returned through the 166 * {@code SearchResultListener} interface. 167 * @param numEntries The number of search result entries returned 168 * for this search. 169 * @param numReferences The number of search result references returned 170 * for this search. 171 * @param responseControls The set of controls from the search result done 172 * response, if available. 173 */ 174 public SearchResult(final int messageID, final ResultCode resultCode, 175 final String diagnosticMessage, final String matchedDN, 176 final String[] referralURLs, 177 final List<SearchResultEntry> searchEntries, 178 final List<SearchResultReference> searchReferences, 179 final int numEntries, final int numReferences, 180 final Control[] responseControls) 181 { 182 super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs, 183 responseControls); 184 185 this.numEntries = numEntries; 186 this.numReferences = numReferences; 187 this.searchEntries = searchEntries; 188 this.searchReferences = searchReferences; 189 } 190 191 192 193 /** 194 * Creates a new search result object with the information from the provided 195 * LDAP result. 196 * 197 * @param ldapResult The LDAP result to use to create the contents of this 198 * search result. 199 */ 200 public SearchResult(final LDAPResult ldapResult) 201 { 202 super(ldapResult); 203 204 if (ldapResult instanceof SearchResult) 205 { 206 final SearchResult searchResult = (SearchResult) ldapResult; 207 numEntries = searchResult.numEntries; 208 numReferences = searchResult.numReferences; 209 searchEntries = searchResult.searchEntries; 210 searchReferences = searchResult.searchReferences; 211 } 212 else 213 { 214 numEntries = -1; 215 numReferences = -1; 216 searchEntries = null; 217 searchReferences = null; 218 } 219 } 220 221 222 223 /** 224 * Creates a new search result object with the information from the provided 225 * LDAP exception. 226 * 227 * @param ldapException The LDAP exception to use to create the contents of 228 * this search result. 229 */ 230 public SearchResult(final LDAPException ldapException) 231 { 232 this(ldapException.toLDAPResult()); 233 } 234 235 236 237 /** 238 * Creates a new search result object with the provided message ID and with 239 * the protocol op and controls read from the given ASN.1 stream reader. 240 * 241 * @param messageID The LDAP message ID for the LDAP message that is 242 * associated with this LDAP result. 243 * @param messageSequence The ASN.1 stream reader sequence used in the 244 * course of reading the LDAP message elements. 245 * @param reader The ASN.1 stream reader from which to read the 246 * protocol op and controls. 247 * 248 * @return The decoded search result object. 249 * 250 * @throws LDAPException If a problem occurs while reading or decoding data 251 * from the ASN.1 stream reader. 252 */ 253 static SearchResult readSearchResultFrom(final int messageID, 254 final ASN1StreamReaderSequence messageSequence, 255 final ASN1StreamReader reader) 256 throws LDAPException 257 { 258 final LDAPResult r = 259 LDAPResult.readLDAPResultFrom(messageID, messageSequence, reader); 260 261 return new SearchResult(messageID, r.getResultCode(), 262 r.getDiagnosticMessage(), r.getMatchedDN(), r.getReferralURLs(), 263 -1, -1, r.getResponseControls()); 264 } 265 266 267 268 /** 269 * Retrieves the number of matching entries returned for the search operation. 270 * 271 * @return The number of matching entries returned for the search operation. 272 */ 273 public int getEntryCount() 274 { 275 return numEntries; 276 } 277 278 279 280 /** 281 * Retrieves the number of search references returned for the search 282 * operation. This may be zero even if search references were received if the 283 * connection used when processing the search was configured to automatically 284 * follow referrals. 285 * 286 * @return The number of search references returned for the search operation. 287 */ 288 public int getReferenceCount() 289 { 290 return numReferences; 291 } 292 293 294 295 /** 296 * Retrieves a list containing the matching entries returned from the search 297 * operation. This will only be available if a {@code SearchResultListener} 298 * was not used during the search. 299 * 300 * @return A list containing the matching entries returned from the search 301 * operation, or {@code null} if a {@code SearchResultListener} was 302 * used during the search. 303 */ 304 public List<SearchResultEntry> getSearchEntries() 305 { 306 if (searchEntries == null) 307 { 308 return null; 309 } 310 311 return Collections.unmodifiableList(searchEntries); 312 } 313 314 315 316 /** 317 * Retrieves the search result entry with the specified DN from the set of 318 * entries returned. This will only be available if a 319 * {@code SearchResultListener} was not used during the search. 320 * 321 * @param dn The DN of the search result entry to retrieve. It must not 322 * be {@code null}. 323 * 324 * @return The search result entry with the provided DN, or {@code null} if 325 * the specified entry was not returned, or if a 326 * {@code SearchResultListener} was used for the search. 327 * 328 * @throws LDAPException If a problem is encountered while attempting to 329 * parse the provided DN or a search entry DN. 330 */ 331 public SearchResultEntry getSearchEntry(final String dn) 332 throws LDAPException 333 { 334 if (searchEntries == null) 335 { 336 return null; 337 } 338 339 final DN parsedDN = new DN(dn); 340 for (final SearchResultEntry e : searchEntries) 341 { 342 if (parsedDN.equals(e.getParsedDN())) 343 { 344 return e; 345 } 346 } 347 348 return null; 349 } 350 351 352 353 /** 354 * Retrieves a list containing the search references returned from the search 355 * operation. This will only be available if a {@code SearchResultListener} 356 * was not used during the search, and may be empty even if search references 357 * were received if the connection used when processing the search was 358 * configured to automatically follow referrals. 359 * 360 * @return A list containing the search references returned from the search 361 * operation, or {@code null} if a {@code SearchResultListener} was 362 * used during the search. 363 */ 364 public List<SearchResultReference> getSearchReferences() 365 { 366 if (searchReferences == null) 367 { 368 return null; 369 } 370 371 return Collections.unmodifiableList(searchReferences); 372 } 373 374 375 376 /** 377 * Provides information about the entries and references returned for the 378 * search operation. This must only be called when a search result is created 379 * and the search result must not be altered at any point after that. 380 * 381 * @param numEntries The number of entries returned for the search 382 * operation. 383 * @param searchEntries A list containing the entries returned from the 384 * search operation, or {@code null} if a 385 * {@code SearchResultListener} was used during the 386 * search. 387 * @param numReferences The number of references returned for the search 388 * operation. 389 * @param searchReferences A list containing the search references returned 390 * from the search operation, or {@code null} if a 391 * {@code SearchResultListener} was used during the 392 * search. 393 */ 394 void setCounts(final int numEntries, 395 final List<SearchResultEntry> searchEntries, 396 final int numReferences, 397 final List<SearchResultReference> searchReferences) 398 { 399 this.numEntries = numEntries; 400 this.numReferences = numReferences; 401 402 if (searchEntries == null) 403 { 404 this.searchEntries = null; 405 } 406 else 407 { 408 this.searchEntries = Collections.unmodifiableList(searchEntries); 409 } 410 411 if (searchReferences == null) 412 { 413 this.searchReferences = null; 414 } 415 else 416 { 417 this.searchReferences = Collections.unmodifiableList(searchReferences); 418 } 419 } 420 421 422 423 /** 424 * Appends a string representation of this LDAP result to the provided buffer. 425 * 426 * @param buffer The buffer to which to append a string representation of 427 * this LDAP result. 428 */ 429 @Override() 430 public void toString(final StringBuilder buffer) 431 { 432 buffer.append("SearchResult(resultCode="); 433 buffer.append(getResultCode()); 434 435 final int messageID = getMessageID(); 436 if (messageID >= 0) 437 { 438 buffer.append(", messageID="); 439 buffer.append(messageID); 440 } 441 442 final String diagnosticMessage = getDiagnosticMessage(); 443 if (diagnosticMessage != null) 444 { 445 buffer.append(", diagnosticMessage='"); 446 buffer.append(diagnosticMessage); 447 buffer.append('\''); 448 } 449 450 final String matchedDN = getMatchedDN(); 451 if (matchedDN != null) 452 { 453 buffer.append(", matchedDN='"); 454 buffer.append(matchedDN); 455 buffer.append('\''); 456 } 457 458 final String[] referralURLs = getReferralURLs(); 459 if (referralURLs.length > 0) 460 { 461 buffer.append(", referralURLs={"); 462 for (int i=0; i < referralURLs.length; i++) 463 { 464 if (i > 0) 465 { 466 buffer.append(", "); 467 } 468 469 buffer.append('\''); 470 buffer.append(referralURLs[i]); 471 buffer.append('\''); 472 } 473 buffer.append('}'); 474 } 475 476 if (numEntries >= 0) 477 { 478 buffer.append(", entriesReturned="); 479 buffer.append(numEntries); 480 } 481 482 if (numReferences >= 0) 483 { 484 buffer.append(", referencesReturned="); 485 buffer.append(numReferences); 486 } 487 488 final Control[] responseControls = getResponseControls(); 489 if (responseControls.length > 0) 490 { 491 buffer.append(", responseControls={"); 492 for (int i=0; i < responseControls.length; i++) 493 { 494 if (i > 0) 495 { 496 buffer.append(", "); 497 } 498 499 buffer.append(responseControls[i]); 500 } 501 buffer.append('}'); 502 } 503 504 buffer.append(')'); 505 } 506}