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.ldif; 022 023 024 025import java.util.ArrayList; 026import java.util.HashSet; 027import java.util.Iterator; 028import java.util.List; 029 030import com.unboundid.asn1.ASN1OctetString; 031import com.unboundid.ldap.sdk.ChangeType; 032import com.unboundid.ldap.sdk.Control; 033import com.unboundid.ldap.sdk.DN; 034import com.unboundid.ldap.sdk.LDAPException; 035import com.unboundid.ldap.sdk.LDAPInterface; 036import com.unboundid.ldap.sdk.LDAPResult; 037import com.unboundid.ldap.sdk.ModifyDNRequest; 038import com.unboundid.ldap.sdk.RDN; 039import com.unboundid.util.ByteStringBuffer; 040import com.unboundid.util.Debug; 041import com.unboundid.util.NotMutable; 042import com.unboundid.util.StaticUtils; 043import com.unboundid.util.ThreadSafety; 044import com.unboundid.util.ThreadSafetyLevel; 045import com.unboundid.util.Validator; 046 047 048 049/** 050 * This class defines an LDIF modify DN change record, which can be used to 051 * represent an LDAP modify DN request. See the documentation for the 052 * {@link LDIFChangeRecord} class for an example demonstrating the process for 053 * interacting with LDIF change records. 054 */ 055@NotMutable() 056@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 057public final class LDIFModifyDNChangeRecord 058 extends LDIFChangeRecord 059{ 060 /** 061 * The serial version UID for this serializable class. 062 */ 063 private static final long serialVersionUID = 5804442145450388071L; 064 065 066 067 // Indicates whether to delete the current RDN value. 068 private final boolean deleteOldRDN; 069 070 // The parsed new superior DN for the entry. 071 private volatile DN parsedNewSuperiorDN; 072 073 // The parsed new RDN for the entry. 074 private volatile RDN parsedNewRDN; 075 076 // The new RDN value for the entry. 077 private final String newRDN; 078 079 // The new superior DN for the entry, if available. 080 private final String newSuperiorDN; 081 082 083 084 /** 085 * Creates a new LDIF modify DN change record with the provided information. 086 * 087 * @param dn The current DN for the entry. It must not be 088 * {@code null}. 089 * @param newRDN The new RDN value for the entry. It must not be 090 * {@code null}. 091 * @param deleteOldRDN Indicates whether to delete the currentRDN value 092 * from the entry. 093 * @param newSuperiorDN The new superior DN for this LDIF modify DN change 094 * record. It may be {@code null} if the entry is not 095 * to be moved below a new parent. 096 */ 097 public LDIFModifyDNChangeRecord(final String dn, final String newRDN, 098 final boolean deleteOldRDN, 099 final String newSuperiorDN) 100 { 101 this(dn, newRDN, deleteOldRDN, newSuperiorDN, null); 102 } 103 104 105 106 /** 107 * Creates a new LDIF modify DN change record with the provided information. 108 * 109 * @param dn The current DN for the entry. It must not be 110 * {@code null}. 111 * @param newRDN The new RDN value for the entry. It must not be 112 * {@code null}. 113 * @param deleteOldRDN Indicates whether to delete the currentRDN value 114 * from the entry. 115 * @param newSuperiorDN The new superior DN for this LDIF modify DN change 116 * record. It may be {@code null} if the entry is not 117 * to be moved below a new parent. 118 * @param controls The set of controls for this LDIF modify DN change 119 * record. It may be {@code null} or empty if there 120 * are no controls. 121 */ 122 public LDIFModifyDNChangeRecord(final String dn, final String newRDN, 123 final boolean deleteOldRDN, 124 final String newSuperiorDN, 125 final List<Control> controls) 126 { 127 super(dn, controls); 128 129 Validator.ensureNotNull(newRDN); 130 131 this.newRDN = newRDN; 132 this.deleteOldRDN = deleteOldRDN; 133 this.newSuperiorDN = newSuperiorDN; 134 135 parsedNewRDN = null; 136 parsedNewSuperiorDN = null; 137 } 138 139 140 141 /** 142 * Creates a new LDIF modify DN change record from the provided modify DN 143 * request. 144 * 145 * @param modifyDNRequest The modify DN request to use to create this LDIF 146 * modify DN change record. It must not be 147 * {@code null}. 148 */ 149 public LDIFModifyDNChangeRecord(final ModifyDNRequest modifyDNRequest) 150 { 151 super(modifyDNRequest.getDN(), modifyDNRequest.getControlList()); 152 153 newRDN = modifyDNRequest.getNewRDN(); 154 deleteOldRDN = modifyDNRequest.deleteOldRDN(); 155 newSuperiorDN = modifyDNRequest.getNewSuperiorDN(); 156 157 parsedNewRDN = null; 158 parsedNewSuperiorDN = null; 159 } 160 161 162 163 /** 164 * Retrieves the new RDN value for the entry. 165 * 166 * @return The new RDN value for the entry. 167 */ 168 public String getNewRDN() 169 { 170 return newRDN; 171 } 172 173 174 175 /** 176 * Retrieves the parsed new RDN value for the entry. 177 * 178 * @return The parsed new RDN value for the entry. 179 * 180 * @throws LDAPException If a problem occurs while trying to parse the new 181 * RDN. 182 */ 183 public RDN getParsedNewRDN() 184 throws LDAPException 185 { 186 if (parsedNewRDN == null) 187 { 188 parsedNewRDN = new RDN(newRDN); 189 } 190 191 return parsedNewRDN; 192 } 193 194 195 196 /** 197 * Indicates whether to delete the current RDN value from the entry. 198 * 199 * @return {@code true} if the current RDN value should be removed from the 200 * entry, or {@code false} if not. 201 */ 202 public boolean deleteOldRDN() 203 { 204 return deleteOldRDN; 205 } 206 207 208 209 /** 210 * Retrieves the new superior DN for the entry, if applicable. 211 * 212 * @return The new superior DN for the entry, or {@code null} if the entry is 213 * not to be moved below a new parent. 214 */ 215 public String getNewSuperiorDN() 216 { 217 return newSuperiorDN; 218 } 219 220 221 222 /** 223 * Retrieves the parsed new superior DN for the entry, if applicable. 224 * 225 * @return The parsed new superior DN for the entry, or {@code null} if the 226 * entry is not to be moved below a new parent. 227 * 228 * @throws LDAPException If a problem occurs while trying to parse the new 229 * superior DN. 230 */ 231 public DN getParsedNewSuperiorDN() 232 throws LDAPException 233 { 234 if ((parsedNewSuperiorDN == null) && (newSuperiorDN != null)) 235 { 236 parsedNewSuperiorDN = new DN(newSuperiorDN); 237 } 238 239 return parsedNewSuperiorDN; 240 } 241 242 243 244 /** 245 * Retrieves the DN that the entry should have after the successful completion 246 * of the operation. 247 * 248 * @return The DN that the entry should have after the successful completion 249 * of the operation. 250 * 251 * @throws LDAPException If a problem occurs while trying to parse the 252 * target DN, new RDN, or new superior DN. 253 */ 254 public DN getNewDN() 255 throws LDAPException 256 { 257 if (newSuperiorDN == null) 258 { 259 final DN parentDN = getParsedDN().getParent(); 260 if (parentDN == null) 261 { 262 return new DN(getParsedNewRDN()); 263 } 264 else 265 { 266 return new DN(getParsedNewRDN(), parentDN); 267 } 268 } 269 else 270 { 271 return new DN(getParsedNewRDN(), getParsedNewSuperiorDN()); 272 } 273 } 274 275 276 277 /** 278 * Creates a modify DN request from this LDIF modify DN change record. Any 279 * change record controls will be included in the request 280 * 281 * @return The modify DN request created from this LDIF modify DN change 282 * record. 283 */ 284 public ModifyDNRequest toModifyDNRequest() 285 { 286 return toModifyDNRequest(true); 287 } 288 289 290 291 /** 292 * Creates a modify DN request from this LDIF modify DN change record, 293 * optionally including any change record controls in the request. 294 * 295 * @param includeControls Indicates whether to include any controls in the 296 * request. 297 * 298 * @return The modify DN request created from this LDIF modify DN change 299 * record. 300 */ 301 public ModifyDNRequest toModifyDNRequest(final boolean includeControls) 302 { 303 final ModifyDNRequest modifyDNRequest = 304 new ModifyDNRequest(getDN(), newRDN, deleteOldRDN, newSuperiorDN); 305 if (includeControls) 306 { 307 modifyDNRequest.setControls(getControls()); 308 } 309 310 return modifyDNRequest; 311 } 312 313 314 315 /** 316 * {@inheritDoc} 317 */ 318 @Override() 319 public ChangeType getChangeType() 320 { 321 return ChangeType.MODIFY_DN; 322 } 323 324 325 326 /** 327 * {@inheritDoc} 328 */ 329 @Override() 330 public LDAPResult processChange(final LDAPInterface connection, 331 final boolean includeControls) 332 throws LDAPException 333 { 334 return connection.modifyDN(toModifyDNRequest(includeControls)); 335 } 336 337 338 339 /** 340 * {@inheritDoc} 341 */ 342 @Override() 343 public String[] toLDIF(final int wrapColumn) 344 { 345 List<String> ldifLines = new ArrayList<>(10); 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: moddn"); 354 encodeNameAndValue("newrdn", new ASN1OctetString(newRDN), ldifLines); 355 ldifLines.add("deleteoldrdn: " + (deleteOldRDN ? "1" : "0")); 356 357 if (newSuperiorDN != null) 358 { 359 encodeNameAndValue("newsuperior", new ASN1OctetString(newSuperiorDN), 360 ldifLines); 361 } 362 363 if (wrapColumn > 2) 364 { 365 ldifLines = LDIFWriter.wrapLines(wrapColumn, ldifLines); 366 } 367 368 final String[] ldifArray = new String[ldifLines.size()]; 369 ldifLines.toArray(ldifArray); 370 return ldifArray; 371 } 372 373 374 375 /** 376 * {@inheritDoc} 377 */ 378 @Override() 379 public void toLDIF(final ByteStringBuffer buffer, final int wrapColumn) 380 { 381 LDIFWriter.encodeNameAndValue("dn", new ASN1OctetString(getDN()), buffer, 382 wrapColumn); 383 buffer.append(StaticUtils.EOL_BYTES); 384 385 for (final Control c : getControls()) 386 { 387 LDIFWriter.encodeNameAndValue("control", encodeControlString(c), buffer, 388 wrapColumn); 389 buffer.append(StaticUtils.EOL_BYTES); 390 } 391 392 LDIFWriter.encodeNameAndValue("changetype", new ASN1OctetString("moddn"), 393 buffer, wrapColumn); 394 buffer.append(StaticUtils.EOL_BYTES); 395 396 LDIFWriter.encodeNameAndValue("newrdn", new ASN1OctetString(newRDN), buffer, 397 wrapColumn); 398 buffer.append(StaticUtils.EOL_BYTES); 399 400 if (deleteOldRDN) 401 { 402 LDIFWriter.encodeNameAndValue("deleteoldrdn", new ASN1OctetString("1"), 403 buffer, wrapColumn); 404 } 405 else 406 { 407 LDIFWriter.encodeNameAndValue("deleteoldrdn", new ASN1OctetString("0"), 408 buffer, wrapColumn); 409 } 410 buffer.append(StaticUtils.EOL_BYTES); 411 412 if (newSuperiorDN != null) 413 { 414 LDIFWriter.encodeNameAndValue("newsuperior", 415 new ASN1OctetString(newSuperiorDN), buffer, 416 wrapColumn); 417 buffer.append(StaticUtils.EOL_BYTES); 418 } 419 } 420 421 422 423 /** 424 * {@inheritDoc} 425 */ 426 @Override() 427 public void toLDIFString(final StringBuilder buffer, final int wrapColumn) 428 { 429 LDIFWriter.encodeNameAndValue("dn", new ASN1OctetString(getDN()), buffer, 430 wrapColumn); 431 buffer.append(StaticUtils.EOL); 432 433 for (final Control c : getControls()) 434 { 435 LDIFWriter.encodeNameAndValue("control", encodeControlString(c), buffer, 436 wrapColumn); 437 buffer.append(StaticUtils.EOL); 438 } 439 440 LDIFWriter.encodeNameAndValue("changetype", new ASN1OctetString("moddn"), 441 buffer, wrapColumn); 442 buffer.append(StaticUtils.EOL); 443 444 LDIFWriter.encodeNameAndValue("newrdn", new ASN1OctetString(newRDN), buffer, 445 wrapColumn); 446 buffer.append(StaticUtils.EOL); 447 448 if (deleteOldRDN) 449 { 450 LDIFWriter.encodeNameAndValue("deleteoldrdn", new ASN1OctetString("1"), 451 buffer, wrapColumn); 452 } 453 else 454 { 455 LDIFWriter.encodeNameAndValue("deleteoldrdn", new ASN1OctetString("0"), 456 buffer, wrapColumn); 457 } 458 buffer.append(StaticUtils.EOL); 459 460 if (newSuperiorDN != null) 461 { 462 LDIFWriter.encodeNameAndValue("newsuperior", 463 new ASN1OctetString(newSuperiorDN), buffer, 464 wrapColumn); 465 buffer.append(StaticUtils.EOL); 466 } 467 } 468 469 470 471 /** 472 * {@inheritDoc} 473 */ 474 @Override() 475 public int hashCode() 476 { 477 int hashCode; 478 try 479 { 480 hashCode = getParsedDN().hashCode() + getParsedNewRDN().hashCode(); 481 if (newSuperiorDN != null) 482 { 483 hashCode += getParsedNewSuperiorDN().hashCode(); 484 } 485 } 486 catch (final Exception e) 487 { 488 Debug.debugException(e); 489 hashCode = StaticUtils.toLowerCase(getDN()).hashCode() + 490 StaticUtils.toLowerCase(newRDN).hashCode(); 491 if (newSuperiorDN != null) 492 { 493 hashCode += StaticUtils.toLowerCase(newSuperiorDN).hashCode(); 494 } 495 } 496 497 if (deleteOldRDN) 498 { 499 hashCode++; 500 } 501 502 return hashCode; 503 } 504 505 506 507 /** 508 * {@inheritDoc} 509 */ 510 @Override() 511 public boolean equals(final Object o) 512 { 513 if (o == null) 514 { 515 return false; 516 } 517 518 if (o == this) 519 { 520 return true; 521 } 522 523 if (! (o instanceof LDIFModifyDNChangeRecord)) 524 { 525 return false; 526 } 527 528 final LDIFModifyDNChangeRecord r = (LDIFModifyDNChangeRecord) o; 529 530 final HashSet<Control> c1 = new HashSet<>(getControls()); 531 final HashSet<Control> c2 = new HashSet<>(r.getControls()); 532 if (! c1.equals(c2)) 533 { 534 return false; 535 } 536 537 try 538 { 539 if (! getParsedDN().equals(r.getParsedDN())) 540 { 541 return false; 542 } 543 } 544 catch (final Exception e) 545 { 546 Debug.debugException(e); 547 if (! StaticUtils.toLowerCase(getDN()).equals( 548 StaticUtils.toLowerCase(r.getDN()))) 549 { 550 return false; 551 } 552 } 553 554 try 555 { 556 if (! getParsedNewRDN().equals(r.getParsedNewRDN())) 557 { 558 return false; 559 } 560 } 561 catch (final Exception e) 562 { 563 Debug.debugException(e); 564 if (! StaticUtils.toLowerCase(newRDN).equals( 565 StaticUtils.toLowerCase(r.newRDN))) 566 { 567 return false; 568 } 569 } 570 571 if (newSuperiorDN == null) 572 { 573 if (r.newSuperiorDN != null) 574 { 575 return false; 576 } 577 } 578 else 579 { 580 if (r.newSuperiorDN == null) 581 { 582 return false; 583 } 584 585 try 586 { 587 if (! getParsedNewSuperiorDN().equals(r.getParsedNewSuperiorDN())) 588 { 589 return false; 590 } 591 } 592 catch (final Exception e) 593 { 594 Debug.debugException(e); 595 if (! StaticUtils.toLowerCase(newSuperiorDN).equals( 596 StaticUtils.toLowerCase(r.newSuperiorDN))) 597 { 598 return false; 599 } 600 } 601 } 602 603 return (deleteOldRDN == r.deleteOldRDN); 604 } 605 606 607 608 /** 609 * {@inheritDoc} 610 */ 611 @Override() 612 public void toString(final StringBuilder buffer) 613 { 614 buffer.append("LDIFModifyDNChangeRecord(dn='"); 615 buffer.append(getDN()); 616 buffer.append("', newRDN='"); 617 buffer.append(newRDN); 618 buffer.append("', deleteOldRDN="); 619 buffer.append(deleteOldRDN); 620 621 if (newSuperiorDN != null) 622 { 623 buffer.append(", newSuperiorDN='"); 624 buffer.append(newSuperiorDN); 625 buffer.append('\''); 626 } 627 628 final List<Control> controls = getControls(); 629 if (! controls.isEmpty()) 630 { 631 buffer.append(", controls={"); 632 633 final Iterator<Control> iterator = controls.iterator(); 634 while (iterator.hasNext()) 635 { 636 iterator.next().toString(buffer); 637 if (iterator.hasNext()) 638 { 639 buffer.append(','); 640 } 641 } 642 643 buffer.append('}'); 644 } 645 646 buffer.append(')'); 647 } 648}