001/* 002 * Copyright 2007-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2019 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}