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