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     */
021    package com.unboundid.ldap.sdk;
022    
023    
024    
025    import java.util.Collections;
026    import java.util.List;
027    
028    import com.unboundid.asn1.ASN1StreamReader;
029    import com.unboundid.asn1.ASN1StreamReaderSequence;
030    import com.unboundid.util.NotMutable;
031    import com.unboundid.util.ThreadSafety;
032    import 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)
060    public 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    }