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