001    /*
002     * Copyright 2007-2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2016 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.ArrayList;
026    import java.util.Collection;
027    import java.util.Iterator;
028    
029    import com.unboundid.asn1.ASN1StreamReader;
030    import com.unboundid.asn1.ASN1StreamReaderSequence;
031    import com.unboundid.ldap.protocol.LDAPResponse;
032    import com.unboundid.ldap.sdk.schema.Schema;
033    import com.unboundid.util.NotMutable;
034    import com.unboundid.util.ThreadSafety;
035    import com.unboundid.util.ThreadSafetyLevel;
036    
037    import static com.unboundid.ldap.sdk.LDAPMessages.*;
038    import static com.unboundid.util.Debug.*;
039    import static com.unboundid.util.StaticUtils.*;
040    import static com.unboundid.util.Validator.*;
041    
042    
043    
044    /**
045     * This class provides a data structure for representing an LDAP search result
046     * entry.  This is a {@link ReadOnlyEntry} object that may also include zero
047     * or more controls included with the entry returned from the server.
048     */
049    @NotMutable()
050    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
051    public final class SearchResultEntry
052           extends ReadOnlyEntry
053           implements LDAPResponse
054    {
055      /**
056       * The serial version UID for this serializable class.
057       */
058      private static final long serialVersionUID = -290721544252526163L;
059    
060    
061    
062      // The set of controls returned with this search result entry.
063      private final Control[] controls;
064    
065      // The message ID for the LDAP message containing this response.
066      private final int messageID;
067    
068    
069    
070      /**
071       * Creates a new search result entry with the provided information.
072       *
073       * @param  dn          The DN for this search result entry.  It must not be
074       *                     {@code null}.
075       * @param  attributes  The set of attributes to include in this search result
076       *                     entry.  It must not be {@code null}.
077       * @param  controls    The set of controls for this search result entry.  It
078       *                     must not be {@code null}.
079       */
080      public SearchResultEntry(final String dn, final Attribute[] attributes,
081                               final Control... controls)
082      {
083        this(-1, dn, null, attributes, controls);
084      }
085    
086    
087    
088      /**
089       * Creates a new search result entry with the provided information.
090       *
091       * @param  messageID   The message ID for the LDAP message containing this
092       *                     response.
093       * @param  dn          The DN for this search result entry.  It must not be
094       *                     {@code null}.
095       * @param  attributes  The set of attributes to include in this search result
096       *                     entry.  It must not be {@code null}.
097       * @param  controls    The set of controls for this search result entry.  It
098       *                     must not be {@code null}.
099       */
100      public SearchResultEntry(final int messageID, final String dn,
101                               final Attribute[] attributes,
102                               final Control... controls)
103      {
104        this(messageID, dn, null, attributes, controls);
105      }
106    
107    
108    
109      /**
110       * Creates a new search result entry with the provided information.
111       *
112       * @param  messageID   The message ID for the LDAP message containing this
113       *                     response.
114       * @param  dn          The DN for this search result entry.  It must not be
115       *                     {@code null}.
116       * @param  schema      The schema to use for operations involving this entry.
117       *                     It may be {@code null} if no schema is available.
118       * @param  attributes  The set of attributes to include in this search result
119       *                     entry.  It must not be {@code null}.
120       * @param  controls    The set of controls for this search result entry.  It
121       *                     must not be {@code null}.
122       */
123      public SearchResultEntry(final int messageID, final String dn,
124                               final Schema schema, final Attribute[] attributes,
125                               final Control... controls)
126      {
127        super(dn, schema, attributes);
128    
129        ensureNotNull(controls);
130    
131        this.messageID = messageID;
132        this.controls  = controls;
133      }
134    
135    
136    
137      /**
138       * Creates a new search result entry with the provided information.
139       *
140       * @param  dn          The DN for this search result entry.  It must not be
141       *                     {@code null}.
142       * @param  attributes  The set of attributes to include in this search result
143       *                     entry.  It must not be {@code null}.
144       * @param  controls    The set of controls for this search result entry.  It
145       *                     must not be {@code null}.
146       */
147      public SearchResultEntry(final String dn,
148                               final Collection<Attribute> attributes,
149                               final Control... controls)
150      {
151        this(-1, dn, null, attributes, controls);
152      }
153    
154    
155    
156      /**
157       * Creates a new search result entry with the provided information.
158       *
159       * @param  messageID   The message ID for the LDAP message containing this
160       *                     response.
161       * @param  dn          The DN for this search result entry.  It must not be
162       *                     {@code null}.
163       * @param  attributes  The set of attributes to include in this search result
164       *                     entry.  It must not be {@code null}.
165       * @param  controls    The set of controls for this search result entry.  It
166       *                     must not be {@code null}.
167       */
168      public SearchResultEntry(final int messageID, final String dn,
169                               final Collection<Attribute> attributes,
170                               final Control... controls)
171      {
172        this(messageID, dn, null, attributes, controls);
173      }
174    
175    
176    
177      /**
178       * Creates a new search result entry with the provided information.
179       *
180       * @param  messageID   The message ID for the LDAP message containing this
181       *                     response.
182       * @param  dn          The DN for this search result entry.  It must not be
183       *                     {@code null}.
184       * @param  schema      The schema to use for operations involving this entry.
185       *                     It may be {@code null} if no schema is available.
186       * @param  attributes  The set of attributes to include in this search result
187       *                     entry.  It must not be {@code null}.
188       * @param  controls    The set of controls for this search result entry.  It
189       *                     must not be {@code null}.
190       */
191      public SearchResultEntry(final int messageID, final String dn,
192                               final Schema schema,
193                               final Collection<Attribute> attributes,
194                               final Control... controls)
195      {
196        super(dn, schema, attributes);
197    
198        ensureNotNull(controls);
199    
200        this.messageID = messageID;
201        this.controls  = controls;
202      }
203    
204    
205    
206      /**
207       * Creates a new search result entry from the provided entry.
208       *
209       * @param  entry     The entry to use to create this search result entry.  It
210       *                   must not be {@code null}.
211       * @param  controls  The set of controls for this search result entry.  It
212       *                   must not be {@code null}.
213       */
214      public SearchResultEntry(final Entry entry, final Control... controls)
215      {
216        this(-1, entry, controls);
217      }
218    
219    
220    
221      /**
222       * Creates a new search result entry from the provided entry.
223       *
224       * @param  messageID  The message ID for the LDAP message containing this
225       *                    response.
226       * @param  entry      The entry to use to create this search result entry.  It
227       *                    must not be {@code null}.
228       * @param  controls   The set of controls for this search result entry.  It
229       *                    must not be {@code null}.
230       */
231      public SearchResultEntry(final int messageID, final Entry entry,
232                               final Control... controls)
233      {
234        super(entry);
235    
236        ensureNotNull(controls);
237    
238        this.messageID = messageID;
239        this.controls  = controls;
240      }
241    
242    
243    
244      /**
245       * Creates a new search result entry object with the protocol op and controls
246       * read from the given ASN.1 stream reader.
247       *
248       * @param  messageID        The message ID for the LDAP message containing
249       *                          this response.
250       * @param  messageSequence  The ASN.1 stream reader sequence used in the
251       *                          course of reading the LDAP message elements.
252       * @param  reader           The ASN.1 stream reader from which to read the
253       *                          protocol op and controls.
254       * @param  schema           The schema to use to select the appropriate
255       *                          matching rule to use for each attribute.  It may
256       *                          be {@code null} if the default matching rule
257       *                          should always be used.
258       *
259       * @return  The decoded search result entry object.
260       *
261       * @throws  LDAPException  If a problem occurs while reading or decoding data
262       *                         from the ASN.1 stream reader.
263       */
264      static SearchResultEntry readSearchEntryFrom(final int messageID,
265                  final ASN1StreamReaderSequence messageSequence,
266                  final ASN1StreamReader reader, final Schema schema)
267             throws LDAPException
268      {
269        try
270        {
271          reader.beginSequence();
272          final String dn = reader.readString();
273    
274          final ArrayList<Attribute> attrList = new ArrayList<Attribute>(10);
275          final ASN1StreamReaderSequence attrSequence = reader.beginSequence();
276          while (attrSequence.hasMoreElements())
277          {
278            attrList.add(Attribute.readFrom(reader, schema));
279          }
280    
281          Control[] controls = NO_CONTROLS;
282          if (messageSequence.hasMoreElements())
283          {
284            final ArrayList<Control> controlList = new ArrayList<Control>(5);
285            final ASN1StreamReaderSequence controlSequence = reader.beginSequence();
286            while (controlSequence.hasMoreElements())
287            {
288              controlList.add(Control.readFrom(reader));
289            }
290    
291            controls = new Control[controlList.size()];
292            controlList.toArray(controls);
293          }
294    
295          return new SearchResultEntry(messageID, dn, schema, attrList, controls);
296        }
297        catch (LDAPException le)
298        {
299          debugException(le);
300          throw le;
301        }
302        catch (Exception e)
303        {
304          debugException(e);
305          throw new LDAPException(ResultCode.DECODING_ERROR,
306               ERR_SEARCH_ENTRY_CANNOT_DECODE.get(getExceptionMessage(e)), e);
307        }
308      }
309    
310    
311    
312      /**
313       * {@inheritDoc}
314       */
315      public int getMessageID()
316      {
317        return messageID;
318      }
319    
320    
321    
322      /**
323       * Retrieves the set of controls returned with this search result entry.
324       * Individual response controls of a specific type may be retrieved and
325       * decoded using the {@code get} method in the response control class.
326       *
327       * @return  The set of controls returned with this search result entry.
328       */
329      public Control[] getControls()
330      {
331        return controls;
332      }
333    
334    
335    
336      /**
337       * Retrieves the control with the specified OID.  If there is more than one
338       * control with the given OID, then the first will be returned.
339       *
340       * @param  oid  The OID of the control to retrieve.
341       *
342       * @return  The control with the requested OID, or {@code null} if there is no
343       *          such control for this search result entry.
344       */
345      public Control getControl(final String oid)
346      {
347        for (final Control c : controls)
348        {
349          if (c.getOID().equals(oid))
350          {
351            return c;
352          }
353        }
354    
355        return null;
356      }
357    
358    
359    
360      /**
361       * Generates a hash code for this entry.
362       *
363       * @return  The generated hash code for this entry.
364       */
365      @Override()
366      public int hashCode()
367      {
368        int hashCode = super.hashCode();
369    
370        for (final Control c : controls)
371        {
372          hashCode += c.hashCode();
373        }
374    
375        return hashCode;
376      }
377    
378    
379    
380      /**
381       * Indicates whether the provided object is equal to this entry.  The provided
382       * object will only be considered equal to this entry if it is an entry with
383       * the same DN and set of attributes.
384       *
385       * @param  o  The object for which to make the determination.
386       *
387       * @return  {@code true} if the provided object is considered equal to this
388       *          entry, or {@code false} if not.
389       */
390      @Override()
391      public boolean equals(final Object o)
392      {
393        if (! super.equals(o))
394        {
395          return false;
396        }
397    
398        if (! (o instanceof SearchResultEntry))
399        {
400          return false;
401        }
402    
403        final SearchResultEntry e = (SearchResultEntry) o;
404    
405        if (controls.length != e.controls.length)
406        {
407          return false;
408        }
409    
410        for (int i=0; i < controls.length; i++)
411        {
412          if (! controls[i].equals(e.controls[i]))
413          {
414            return false;
415          }
416        }
417    
418        return true;
419      }
420    
421    
422    
423      /**
424       * Appends a string representation of this entry to the provided buffer.
425       *
426       * @param  buffer  The buffer to which to append the string representation of
427       *                 this entry.
428       */
429      @Override()
430      public void toString(final StringBuilder buffer)
431      {
432        buffer.append("SearchResultEntry(dn='");
433        buffer.append(getDN());
434        buffer.append('\'');
435    
436        if (messageID >= 0)
437        {
438          buffer.append(", messageID=");
439          buffer.append(messageID);
440        }
441    
442        buffer.append(", attributes={");
443    
444        final Iterator<Attribute> iterator = getAttributes().iterator();
445    
446        while (iterator.hasNext())
447        {
448          iterator.next().toString(buffer);
449          if (iterator.hasNext())
450          {
451            buffer.append(", ");
452          }
453        }
454    
455        buffer.append("}, controls={");
456    
457        for (int i=0; i < controls.length; i++)
458        {
459          if (i > 0)
460          {
461            buffer.append(", ");
462          }
463    
464          controls[i].toString(buffer);
465        }
466    
467        buffer.append("})");
468      }
469    }