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 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 }