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