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