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    import com.unboundid.util.NotMutable;
041    import com.unboundid.util.ThreadSafety;
042    import com.unboundid.util.ThreadSafetyLevel;
043    
044    import static com.unboundid.util.Debug.*;
045    import static com.unboundid.util.StaticUtils.*;
046    import 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)
058    public 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    }