001/* 002 * Copyright 2007-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2007-2024 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2007-2024 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldif; 037 038 039 040import java.util.ArrayList; 041import java.util.Collection; 042import java.util.Collections; 043import java.util.HashSet; 044import java.util.Iterator; 045import java.util.List; 046 047import com.unboundid.asn1.ASN1OctetString; 048import com.unboundid.ldap.sdk.AddRequest; 049import com.unboundid.ldap.sdk.Attribute; 050import com.unboundid.ldap.sdk.ChangeType; 051import com.unboundid.ldap.sdk.Control; 052import com.unboundid.ldap.sdk.Entry; 053import com.unboundid.ldap.sdk.LDAPException; 054import com.unboundid.ldap.sdk.LDAPInterface; 055import com.unboundid.ldap.sdk.LDAPResult; 056import com.unboundid.util.ByteStringBuffer; 057import com.unboundid.util.Debug; 058import com.unboundid.util.NotMutable; 059import com.unboundid.util.NotNull; 060import com.unboundid.util.Nullable; 061import com.unboundid.util.StaticUtils; 062import com.unboundid.util.ThreadSafety; 063import com.unboundid.util.ThreadSafetyLevel; 064import com.unboundid.util.Validator; 065 066 067 068/** 069 * This class defines an LDIF add change record, which can be used to represent 070 * an LDAP add request. See the documentation for the {@link LDIFChangeRecord} 071 * class for an example demonstrating the process for interacting with LDIF 072 * change records. 073 */ 074@NotMutable() 075@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 076public final class LDIFAddChangeRecord 077 extends LDIFChangeRecord 078{ 079 /** 080 * The serial version UID for this serializable class. 081 */ 082 private static final long serialVersionUID = 4722916031463878423L; 083 084 085 086 // The set of attributes for this add change record. 087 @NotNull private final Attribute[] attributes; 088 089 090 091 /** 092 * Creates a new LDIF add change record with the provided DN and attributes. 093 * 094 * @param dn The DN for this LDIF add change record. It must not be 095 * {@code null}. 096 * @param attributes The set of attributes for this LDIF add change record. 097 * It must not be {@code null} or empty. 098 */ 099 public LDIFAddChangeRecord(@NotNull final String dn, 100 @NotNull final Attribute... attributes) 101 { 102 this(dn, attributes, null); 103 } 104 105 106 107 /** 108 * Creates a new LDIF add change record with the provided DN and attributes. 109 * 110 * @param dn The DN for this LDIF add change record. It must not be 111 * {@code null}. 112 * @param attributes The set of attributes for this LDIF add change record. 113 * It must not be {@code null} or empty. 114 * @param controls The set of controls for this LDIF add change record. 115 * It may be {@code null} or empty if there are no 116 * controls. 117 */ 118 public LDIFAddChangeRecord(@NotNull final String dn, 119 @NotNull final Attribute[] attributes, 120 @Nullable final List<Control> controls) 121 { 122 super(dn, controls); 123 124 Validator.ensureNotNull(attributes); 125 Validator.ensureTrue(attributes.length > 0, 126 "LDIFAddChangeRecord.attributes must not be empty."); 127 128 this.attributes = attributes; 129 } 130 131 132 133 /** 134 * Creates a new LDIF add change record with the provided DN and attributes. 135 * 136 * @param dn The DN for this LDIF add change record. It must not be 137 * {@code null}. 138 * @param attributes The set of attributes for this LDIF add change record. 139 * It must not be {@code null} or empty. 140 */ 141 public LDIFAddChangeRecord(@NotNull final String dn, 142 @NotNull final List<Attribute> attributes) 143 { 144 this(dn, attributes, null); 145 } 146 147 148 149 /** 150 * Creates a new LDIF add change record with the provided DN and attributes. 151 * 152 * @param dn The DN for this LDIF add change record. It must not be 153 * {@code null}. 154 * @param attributes The set of attributes for this LDIF add change record. 155 * It must not be {@code null} or empty. 156 * @param controls The set of controls for this LDIF add change record. 157 * It may be {@code null} or empty if there are no 158 * controls. 159 */ 160 public LDIFAddChangeRecord(@NotNull final String dn, 161 @NotNull final List<Attribute> attributes, 162 @Nullable final List<Control> controls) 163 { 164 super(dn, controls); 165 166 Validator.ensureNotNull(attributes); 167 Validator.ensureFalse(attributes.isEmpty(), 168 "LDIFAddChangeRecord.attributes must not be empty."); 169 170 this.attributes = new Attribute[attributes.size()]; 171 attributes.toArray(this.attributes); 172 } 173 174 175 176 /** 177 * Creates a new LDIF add change record from the provided entry. 178 * 179 * @param entry The entry to use to create this LDIF add change record. It 180 * must not be {@code null}. 181 */ 182 public LDIFAddChangeRecord(@NotNull final Entry entry) 183 { 184 this(entry, Collections.<Control>emptyList()); 185 } 186 187 188 189 /** 190 * Creates a new LDIF add change record from the provided entry. 191 * 192 * @param entry The entry to use to create this LDIF add change record. 193 * It must not be {@code null}. 194 * @param controls The set of controls for this LDIF add change record. It 195 * may be {@code null} or empty if there are no controls. 196 */ 197 public LDIFAddChangeRecord(@NotNull final Entry entry, 198 @Nullable final List<Control> controls) 199 { 200 super(entry.getDN(), controls); 201 202 final Collection<Attribute> attrs = entry.getAttributes(); 203 attributes = new Attribute[attrs.size()]; 204 205 final Iterator<Attribute> iterator = attrs.iterator(); 206 for (int i=0; i < attributes.length; i++) 207 { 208 attributes[i] = iterator.next(); 209 } 210 } 211 212 213 214 /** 215 * Creates a new LDIF add change record from the provided add request. 216 * 217 * @param addRequest The add request to use to create this LDIF add change 218 * record. It must not be {@code null}. 219 */ 220 public LDIFAddChangeRecord(@NotNull final AddRequest addRequest) 221 { 222 super(addRequest.getDN(), addRequest.getControlList()); 223 224 final List<Attribute> attrs = addRequest.getAttributes(); 225 attributes = new Attribute[attrs.size()]; 226 227 final Iterator<Attribute> iterator = attrs.iterator(); 228 for (int i=0; i < attributes.length; i++) 229 { 230 attributes[i] = iterator.next(); 231 } 232 } 233 234 235 236 /** 237 * Retrieves the set of attributes for this add change record. 238 * 239 * @return The set of attributes for this add change record. 240 */ 241 @NotNull() 242 public Attribute[] getAttributes() 243 { 244 return attributes; 245 } 246 247 248 249 /** 250 * Retrieves the entry that would be created by this add change record. 251 * 252 * @return The entry that would be created by this add change record. 253 */ 254 @NotNull() 255 public Entry getEntryToAdd() 256 { 257 return new Entry(getDN(), attributes); 258 } 259 260 261 262 /** 263 * Creates an add request from this LDIF add change record. Any controls 264 * included in this change record will be included in the request. 265 * 266 * @return The add request created from this LDIF add change record. 267 */ 268 @NotNull() 269 public AddRequest toAddRequest() 270 { 271 return toAddRequest(true); 272 } 273 274 275 276 /** 277 * Creates an add request from this LDIF add change record, optionally 278 * including any change record controls in the request. 279 * 280 * @param includeControls Indicates whether to include any controls in the 281 * request. 282 * 283 * @return The add request created from this LDIF add change record. 284 */ 285 @NotNull() 286 public AddRequest toAddRequest(final boolean includeControls) 287 { 288 final AddRequest addRequest = new AddRequest(getDN(), attributes); 289 if (includeControls) 290 { 291 addRequest.setControls(getControls()); 292 } 293 294 return addRequest; 295 } 296 297 298 299 /** 300 * {@inheritDoc} 301 */ 302 @Override() 303 @NotNull() 304 public ChangeType getChangeType() 305 { 306 return ChangeType.ADD; 307 } 308 309 310 311 /** 312 * {@inheritDoc} 313 */ 314 @Override() 315 @NotNull() 316 public LDIFAddChangeRecord duplicate(@Nullable final Control... controls) 317 { 318 return new LDIFAddChangeRecord(getDN(), attributes, 319 StaticUtils.toList(controls)); 320 } 321 322 323 324 /** 325 * {@inheritDoc} 326 */ 327 @Override() 328 @NotNull() 329 public LDAPResult processChange(@NotNull final LDAPInterface connection, 330 final boolean includeControls) 331 throws LDAPException 332 { 333 return connection.add(toAddRequest(includeControls)); 334 } 335 336 337 338 /** 339 * {@inheritDoc} 340 */ 341 @Override() 342 @NotNull() 343 public String[] toLDIF(final int wrapColumn) 344 { 345 List<String> ldifLines = new ArrayList<>(2*attributes.length); 346 encodeNameAndValue("dn", new ASN1OctetString(getDN()), ldifLines); 347 348 for (final Control c : getControls()) 349 { 350 encodeNameAndValue("control", encodeControlString(c), ldifLines); 351 } 352 353 ldifLines.add("changetype: add"); 354 355 for (final Attribute a : attributes) 356 { 357 final String name = a.getName(); 358 for (final ASN1OctetString value : a.getRawValues()) 359 { 360 encodeNameAndValue(name, value, ldifLines); 361 } 362 } 363 364 if (wrapColumn > 2) 365 { 366 ldifLines = LDIFWriter.wrapLines(wrapColumn, ldifLines); 367 } 368 369 final String[] ldifArray = new String[ldifLines.size()]; 370 ldifLines.toArray(ldifArray); 371 return ldifArray; 372 } 373 374 375 376 /** 377 * {@inheritDoc} 378 */ 379 @Override() 380 public void toLDIF(@NotNull final ByteStringBuffer buffer, 381 final int wrapColumn) 382 { 383 LDIFWriter.encodeNameAndValue("dn", new ASN1OctetString(getDN()), buffer, 384 wrapColumn); 385 buffer.append(StaticUtils.EOL_BYTES); 386 387 for (final Control c : getControls()) 388 { 389 LDIFWriter.encodeNameAndValue("control", encodeControlString(c), buffer, 390 wrapColumn); 391 buffer.append(StaticUtils.EOL_BYTES); 392 } 393 394 LDIFWriter.encodeNameAndValue("changetype", new ASN1OctetString("add"), 395 buffer, wrapColumn); 396 buffer.append(StaticUtils.EOL_BYTES); 397 398 for (final Attribute a : attributes) 399 { 400 final String name = a.getName(); 401 for (final ASN1OctetString value : a.getRawValues()) 402 { 403 LDIFWriter.encodeNameAndValue(name, value, buffer, wrapColumn); 404 buffer.append(StaticUtils.EOL_BYTES); 405 } 406 } 407 } 408 409 410 411 /** 412 * {@inheritDoc} 413 */ 414 @Override() 415 public void toLDIFString(@NotNull final StringBuilder buffer, 416 final int wrapColumn) 417 { 418 LDIFWriter.encodeNameAndValue("dn", new ASN1OctetString(getDN()), buffer, 419 wrapColumn); 420 buffer.append(StaticUtils.EOL); 421 422 for (final Control c : getControls()) 423 { 424 LDIFWriter.encodeNameAndValue("control", encodeControlString(c), buffer, 425 wrapColumn); 426 buffer.append(StaticUtils.EOL); 427 } 428 429 LDIFWriter.encodeNameAndValue("changetype", new ASN1OctetString("add"), 430 buffer, wrapColumn); 431 buffer.append(StaticUtils.EOL); 432 433 for (final Attribute a : attributes) 434 { 435 final String name = a.getName(); 436 for (final ASN1OctetString value : a.getRawValues()) 437 { 438 LDIFWriter.encodeNameAndValue(name, value, buffer, wrapColumn); 439 buffer.append(StaticUtils.EOL); 440 } 441 } 442 } 443 444 445 446 /** 447 * {@inheritDoc} 448 */ 449 @Override() 450 public int hashCode() 451 { 452 try 453 { 454 int hashCode = getParsedDN().hashCode(); 455 for (final Attribute a : attributes) 456 { 457 hashCode += a.hashCode(); 458 } 459 460 return hashCode; 461 } 462 catch (final Exception e) 463 { 464 Debug.debugException(e); 465 return new Entry(getDN(), attributes).hashCode(); 466 } 467 } 468 469 470 471 /** 472 * {@inheritDoc} 473 */ 474 @Override() 475 public boolean equals(@Nullable final Object o) 476 { 477 if (o == null) 478 { 479 return false; 480 } 481 482 if (o == this) 483 { 484 return true; 485 } 486 487 if (! (o instanceof LDIFAddChangeRecord)) 488 { 489 return false; 490 } 491 492 final LDIFAddChangeRecord r = (LDIFAddChangeRecord) o; 493 494 final HashSet<Control> c1 = new HashSet<>(getControls()); 495 final HashSet<Control> c2 = new HashSet<>(r.getControls()); 496 if (! c1.equals(c2)) 497 { 498 return false; 499 } 500 501 final Entry e1 = new Entry(getDN(), attributes); 502 final Entry e2 = new Entry(r.getDN(), r.attributes); 503 return e1.equals(e2); 504 } 505 506 507 508 /** 509 * {@inheritDoc} 510 */ 511 @Override() 512 public void toString(@NotNull final StringBuilder buffer) 513 { 514 buffer.append("LDIFAddChangeRecord(dn='"); 515 buffer.append(getDN()); 516 buffer.append("', attrs={"); 517 518 for (int i=0; i < attributes.length; i++) 519 { 520 if (i > 0) 521 { 522 buffer.append(", "); 523 } 524 attributes[i].toString(buffer); 525 } 526 buffer.append('}'); 527 528 final List<Control> controls = getControls(); 529 if (! controls.isEmpty()) 530 { 531 buffer.append(", controls={"); 532 533 final Iterator<Control> iterator = controls.iterator(); 534 while (iterator.hasNext()) 535 { 536 iterator.next().toString(buffer); 537 if (iterator.hasNext()) 538 { 539 buffer.append(','); 540 } 541 } 542 543 buffer.append('}'); 544 } 545 546 buffer.append(')'); 547 } 548}