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