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.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 LDIFModifyDNChangeRecord duplicate(final Control... controls) 331 { 332 return new LDIFModifyDNChangeRecord(getDN(), newRDN, deleteOldRDN, 333 newSuperiorDN, StaticUtils.toList(controls)); 334 } 335 336 337 338 /** 339 * {@inheritDoc} 340 */ 341 @Override() 342 public LDAPResult processChange(final LDAPInterface connection, 343 final boolean includeControls) 344 throws LDAPException 345 { 346 return connection.modifyDN(toModifyDNRequest(includeControls)); 347 } 348 349 350 351 /** 352 * {@inheritDoc} 353 */ 354 @Override() 355 public String[] toLDIF(final int wrapColumn) 356 { 357 List<String> ldifLines = new ArrayList<>(10); 358 encodeNameAndValue("dn", new ASN1OctetString(getDN()), ldifLines); 359 360 for (final Control c : getControls()) 361 { 362 encodeNameAndValue("control", encodeControlString(c), ldifLines); 363 } 364 365 ldifLines.add("changetype: moddn"); 366 encodeNameAndValue("newrdn", new ASN1OctetString(newRDN), ldifLines); 367 ldifLines.add("deleteoldrdn: " + (deleteOldRDN ? "1" : "0")); 368 369 if (newSuperiorDN != null) 370 { 371 encodeNameAndValue("newsuperior", new ASN1OctetString(newSuperiorDN), 372 ldifLines); 373 } 374 375 if (wrapColumn > 2) 376 { 377 ldifLines = LDIFWriter.wrapLines(wrapColumn, ldifLines); 378 } 379 380 final String[] ldifArray = new String[ldifLines.size()]; 381 ldifLines.toArray(ldifArray); 382 return ldifArray; 383 } 384 385 386 387 /** 388 * {@inheritDoc} 389 */ 390 @Override() 391 public void toLDIF(final ByteStringBuffer buffer, final int wrapColumn) 392 { 393 LDIFWriter.encodeNameAndValue("dn", new ASN1OctetString(getDN()), buffer, 394 wrapColumn); 395 buffer.append(StaticUtils.EOL_BYTES); 396 397 for (final Control c : getControls()) 398 { 399 LDIFWriter.encodeNameAndValue("control", encodeControlString(c), buffer, 400 wrapColumn); 401 buffer.append(StaticUtils.EOL_BYTES); 402 } 403 404 LDIFWriter.encodeNameAndValue("changetype", new ASN1OctetString("moddn"), 405 buffer, wrapColumn); 406 buffer.append(StaticUtils.EOL_BYTES); 407 408 LDIFWriter.encodeNameAndValue("newrdn", new ASN1OctetString(newRDN), buffer, 409 wrapColumn); 410 buffer.append(StaticUtils.EOL_BYTES); 411 412 if (deleteOldRDN) 413 { 414 LDIFWriter.encodeNameAndValue("deleteoldrdn", new ASN1OctetString("1"), 415 buffer, wrapColumn); 416 } 417 else 418 { 419 LDIFWriter.encodeNameAndValue("deleteoldrdn", new ASN1OctetString("0"), 420 buffer, wrapColumn); 421 } 422 buffer.append(StaticUtils.EOL_BYTES); 423 424 if (newSuperiorDN != null) 425 { 426 LDIFWriter.encodeNameAndValue("newsuperior", 427 new ASN1OctetString(newSuperiorDN), buffer, 428 wrapColumn); 429 buffer.append(StaticUtils.EOL_BYTES); 430 } 431 } 432 433 434 435 /** 436 * {@inheritDoc} 437 */ 438 @Override() 439 public void toLDIFString(final StringBuilder buffer, final int wrapColumn) 440 { 441 LDIFWriter.encodeNameAndValue("dn", new ASN1OctetString(getDN()), buffer, 442 wrapColumn); 443 buffer.append(StaticUtils.EOL); 444 445 for (final Control c : getControls()) 446 { 447 LDIFWriter.encodeNameAndValue("control", encodeControlString(c), buffer, 448 wrapColumn); 449 buffer.append(StaticUtils.EOL); 450 } 451 452 LDIFWriter.encodeNameAndValue("changetype", new ASN1OctetString("moddn"), 453 buffer, wrapColumn); 454 buffer.append(StaticUtils.EOL); 455 456 LDIFWriter.encodeNameAndValue("newrdn", new ASN1OctetString(newRDN), buffer, 457 wrapColumn); 458 buffer.append(StaticUtils.EOL); 459 460 if (deleteOldRDN) 461 { 462 LDIFWriter.encodeNameAndValue("deleteoldrdn", new ASN1OctetString("1"), 463 buffer, wrapColumn); 464 } 465 else 466 { 467 LDIFWriter.encodeNameAndValue("deleteoldrdn", new ASN1OctetString("0"), 468 buffer, wrapColumn); 469 } 470 buffer.append(StaticUtils.EOL); 471 472 if (newSuperiorDN != null) 473 { 474 LDIFWriter.encodeNameAndValue("newsuperior", 475 new ASN1OctetString(newSuperiorDN), buffer, 476 wrapColumn); 477 buffer.append(StaticUtils.EOL); 478 } 479 } 480 481 482 483 /** 484 * {@inheritDoc} 485 */ 486 @Override() 487 public int hashCode() 488 { 489 int hashCode; 490 try 491 { 492 hashCode = getParsedDN().hashCode() + getParsedNewRDN().hashCode(); 493 if (newSuperiorDN != null) 494 { 495 hashCode += getParsedNewSuperiorDN().hashCode(); 496 } 497 } 498 catch (final Exception e) 499 { 500 Debug.debugException(e); 501 hashCode = StaticUtils.toLowerCase(getDN()).hashCode() + 502 StaticUtils.toLowerCase(newRDN).hashCode(); 503 if (newSuperiorDN != null) 504 { 505 hashCode += StaticUtils.toLowerCase(newSuperiorDN).hashCode(); 506 } 507 } 508 509 if (deleteOldRDN) 510 { 511 hashCode++; 512 } 513 514 return hashCode; 515 } 516 517 518 519 /** 520 * {@inheritDoc} 521 */ 522 @Override() 523 public boolean equals(final Object o) 524 { 525 if (o == null) 526 { 527 return false; 528 } 529 530 if (o == this) 531 { 532 return true; 533 } 534 535 if (! (o instanceof LDIFModifyDNChangeRecord)) 536 { 537 return false; 538 } 539 540 final LDIFModifyDNChangeRecord r = (LDIFModifyDNChangeRecord) o; 541 542 final HashSet<Control> c1 = new HashSet<>(getControls()); 543 final HashSet<Control> c2 = new HashSet<>(r.getControls()); 544 if (! c1.equals(c2)) 545 { 546 return false; 547 } 548 549 try 550 { 551 if (! getParsedDN().equals(r.getParsedDN())) 552 { 553 return false; 554 } 555 } 556 catch (final Exception e) 557 { 558 Debug.debugException(e); 559 if (! StaticUtils.toLowerCase(getDN()).equals( 560 StaticUtils.toLowerCase(r.getDN()))) 561 { 562 return false; 563 } 564 } 565 566 try 567 { 568 if (! getParsedNewRDN().equals(r.getParsedNewRDN())) 569 { 570 return false; 571 } 572 } 573 catch (final Exception e) 574 { 575 Debug.debugException(e); 576 if (! StaticUtils.toLowerCase(newRDN).equals( 577 StaticUtils.toLowerCase(r.newRDN))) 578 { 579 return false; 580 } 581 } 582 583 if (newSuperiorDN == null) 584 { 585 if (r.newSuperiorDN != null) 586 { 587 return false; 588 } 589 } 590 else 591 { 592 if (r.newSuperiorDN == null) 593 { 594 return false; 595 } 596 597 try 598 { 599 if (! getParsedNewSuperiorDN().equals(r.getParsedNewSuperiorDN())) 600 { 601 return false; 602 } 603 } 604 catch (final Exception e) 605 { 606 Debug.debugException(e); 607 if (! StaticUtils.toLowerCase(newSuperiorDN).equals( 608 StaticUtils.toLowerCase(r.newSuperiorDN))) 609 { 610 return false; 611 } 612 } 613 } 614 615 return (deleteOldRDN == r.deleteOldRDN); 616 } 617 618 619 620 /** 621 * {@inheritDoc} 622 */ 623 @Override() 624 public void toString(final StringBuilder buffer) 625 { 626 buffer.append("LDIFModifyDNChangeRecord(dn='"); 627 buffer.append(getDN()); 628 buffer.append("', newRDN='"); 629 buffer.append(newRDN); 630 buffer.append("', deleteOldRDN="); 631 buffer.append(deleteOldRDN); 632 633 if (newSuperiorDN != null) 634 { 635 buffer.append(", newSuperiorDN='"); 636 buffer.append(newSuperiorDN); 637 buffer.append('\''); 638 } 639 640 final List<Control> controls = getControls(); 641 if (! controls.isEmpty()) 642 { 643 buffer.append(", controls={"); 644 645 final Iterator<Control> iterator = controls.iterator(); 646 while (iterator.hasNext()) 647 { 648 iterator.next().toString(buffer); 649 if (iterator.hasNext()) 650 { 651 buffer.append(','); 652 } 653 } 654 655 buffer.append('}'); 656 } 657 658 buffer.append(')'); 659 } 660}