001/*
002 * Copyright 2007-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2018 Ping Identity Corporation
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.ArrayList;
026import java.util.Collection;
027import java.util.Iterator;
028
029import com.unboundid.asn1.ASN1StreamReader;
030import com.unboundid.asn1.ASN1StreamReaderSequence;
031import com.unboundid.ldap.protocol.LDAPResponse;
032import com.unboundid.ldap.sdk.schema.Schema;
033import com.unboundid.util.Debug;
034import com.unboundid.util.NotMutable;
035import com.unboundid.util.StaticUtils;
036import com.unboundid.util.ThreadSafety;
037import com.unboundid.util.ThreadSafetyLevel;
038import com.unboundid.util.Validator;
039
040import static com.unboundid.ldap.sdk.LDAPMessages.*;
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)
051public 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    Validator.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    Validator.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    Validator.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<>(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<>(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 (final LDAPException le)
298    {
299      Debug.debugException(le);
300      throw le;
301    }
302    catch (final Exception e)
303    {
304      Debug.debugException(e);
305      throw new LDAPException(ResultCode.DECODING_ERROR,
306           ERR_SEARCH_ENTRY_CANNOT_DECODE.get(
307                StaticUtils.getExceptionMessage(e)),
308           e);
309    }
310  }
311
312
313
314  /**
315   * {@inheritDoc}
316   */
317  @Override()
318  public int getMessageID()
319  {
320    return messageID;
321  }
322
323
324
325  /**
326   * Retrieves the set of controls returned with this search result entry.
327   * Individual response controls of a specific type may be retrieved and
328   * decoded using the {@code get} method in the response control class.
329   *
330   * @return  The set of controls returned with this search result entry.
331   */
332  public Control[] getControls()
333  {
334    return controls;
335  }
336
337
338
339  /**
340   * Retrieves the control with the specified OID.  If there is more than one
341   * control with the given OID, then the first will be returned.
342   *
343   * @param  oid  The OID of the control to retrieve.
344   *
345   * @return  The control with the requested OID, or {@code null} if there is no
346   *          such control for this search result entry.
347   */
348  public Control getControl(final String oid)
349  {
350    for (final Control c : controls)
351    {
352      if (c.getOID().equals(oid))
353      {
354        return c;
355      }
356    }
357
358    return null;
359  }
360
361
362
363  /**
364   * Generates a hash code for this entry.
365   *
366   * @return  The generated hash code for this entry.
367   */
368  @Override()
369  public int hashCode()
370  {
371    int hashCode = super.hashCode();
372
373    for (final Control c : controls)
374    {
375      hashCode += c.hashCode();
376    }
377
378    return hashCode;
379  }
380
381
382
383  /**
384   * Indicates whether the provided object is equal to this entry.  The provided
385   * object will only be considered equal to this entry if it is an entry with
386   * the same DN and set of attributes.
387   *
388   * @param  o  The object for which to make the determination.
389   *
390   * @return  {@code true} if the provided object is considered equal to this
391   *          entry, or {@code false} if not.
392   */
393  @Override()
394  public boolean equals(final Object o)
395  {
396    if (! super.equals(o))
397    {
398      return false;
399    }
400
401    if (! (o instanceof SearchResultEntry))
402    {
403      return false;
404    }
405
406    final SearchResultEntry e = (SearchResultEntry) o;
407
408    if (controls.length != e.controls.length)
409    {
410      return false;
411    }
412
413    for (int i=0; i < controls.length; i++)
414    {
415      if (! controls[i].equals(e.controls[i]))
416      {
417        return false;
418      }
419    }
420
421    return true;
422  }
423
424
425
426  /**
427   * Appends a string representation of this entry to the provided buffer.
428   *
429   * @param  buffer  The buffer to which to append the string representation of
430   *                 this entry.
431   */
432  @Override()
433  public void toString(final StringBuilder buffer)
434  {
435    buffer.append("SearchResultEntry(dn='");
436    buffer.append(getDN());
437    buffer.append('\'');
438
439    if (messageID >= 0)
440    {
441      buffer.append(", messageID=");
442      buffer.append(messageID);
443    }
444
445    buffer.append(", attributes={");
446
447    final Iterator<Attribute> iterator = getAttributes().iterator();
448
449    while (iterator.hasNext())
450    {
451      iterator.next().toString(buffer);
452      if (iterator.hasNext())
453      {
454        buffer.append(", ");
455      }
456    }
457
458    buffer.append("}, controls={");
459
460    for (int i=0; i < controls.length; i++)
461    {
462      if (i > 0)
463      {
464        buffer.append(", ");
465      }
466
467      controls[i].toString(buffer);
468    }
469
470    buffer.append("})");
471  }
472}