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