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