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