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