001/* 002 * Copyright 2007-2017 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2017 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 */ 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.NotMutable; 034import com.unboundid.util.ThreadSafety; 035import com.unboundid.util.ThreadSafetyLevel; 036 037import static com.unboundid.ldap.sdk.LDAPMessages.*; 038import static com.unboundid.util.Debug.*; 039import static com.unboundid.util.StaticUtils.*; 040import 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) 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 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}