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