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