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}