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.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 (final LDAPException le) 298 { 299 debugException(le); 300 throw le; 301 } 302 catch (final 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 @Override() 316 public int getMessageID() 317 { 318 return messageID; 319 } 320 321 322 323 /** 324 * Retrieves the set of controls returned with this search result entry. 325 * Individual response controls of a specific type may be retrieved and 326 * decoded using the {@code get} method in the response control class. 327 * 328 * @return The set of controls returned with this search result entry. 329 */ 330 public Control[] getControls() 331 { 332 return controls; 333 } 334 335 336 337 /** 338 * Retrieves the control with the specified OID. If there is more than one 339 * control with the given OID, then the first will be returned. 340 * 341 * @param oid The OID of the control to retrieve. 342 * 343 * @return The control with the requested OID, or {@code null} if there is no 344 * such control for this search result entry. 345 */ 346 public Control getControl(final String oid) 347 { 348 for (final Control c : controls) 349 { 350 if (c.getOID().equals(oid)) 351 { 352 return c; 353 } 354 } 355 356 return null; 357 } 358 359 360 361 /** 362 * Generates a hash code for this entry. 363 * 364 * @return The generated hash code for this entry. 365 */ 366 @Override() 367 public int hashCode() 368 { 369 int hashCode = super.hashCode(); 370 371 for (final Control c : controls) 372 { 373 hashCode += c.hashCode(); 374 } 375 376 return hashCode; 377 } 378 379 380 381 /** 382 * Indicates whether the provided object is equal to this entry. The provided 383 * object will only be considered equal to this entry if it is an entry with 384 * the same DN and set of attributes. 385 * 386 * @param o The object for which to make the determination. 387 * 388 * @return {@code true} if the provided object is considered equal to this 389 * entry, or {@code false} if not. 390 */ 391 @Override() 392 public boolean equals(final Object o) 393 { 394 if (! super.equals(o)) 395 { 396 return false; 397 } 398 399 if (! (o instanceof SearchResultEntry)) 400 { 401 return false; 402 } 403 404 final SearchResultEntry e = (SearchResultEntry) o; 405 406 if (controls.length != e.controls.length) 407 { 408 return false; 409 } 410 411 for (int i=0; i < controls.length; i++) 412 { 413 if (! controls[i].equals(e.controls[i])) 414 { 415 return false; 416 } 417 } 418 419 return true; 420 } 421 422 423 424 /** 425 * Appends a string representation of this entry to the provided buffer. 426 * 427 * @param buffer The buffer to which to append the string representation of 428 * this entry. 429 */ 430 @Override() 431 public void toString(final StringBuilder buffer) 432 { 433 buffer.append("SearchResultEntry(dn='"); 434 buffer.append(getDN()); 435 buffer.append('\''); 436 437 if (messageID >= 0) 438 { 439 buffer.append(", messageID="); 440 buffer.append(messageID); 441 } 442 443 buffer.append(", attributes={"); 444 445 final Iterator<Attribute> iterator = getAttributes().iterator(); 446 447 while (iterator.hasNext()) 448 { 449 iterator.next().toString(buffer); 450 if (iterator.hasNext()) 451 { 452 buffer.append(", "); 453 } 454 } 455 456 buffer.append("}, controls={"); 457 458 for (int i=0; i < controls.length; i++) 459 { 460 if (i > 0) 461 { 462 buffer.append(", "); 463 } 464 465 controls[i].toString(buffer); 466 } 467 468 buffer.append("})"); 469 } 470}