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}