001    /*
002     * Copyright 2007-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2015 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        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    }