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     */
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    
041    import static com.unboundid.util.Debug.*;
042    import static com.unboundid.util.StaticUtils.*;
043    import static com.unboundid.util.Validator.*;
044    
045    
046    
047    /**
048     * This class defines an LDIF modify DN change record, which can be used to
049     * represent an LDAP modify DN request.  See the documentation for the
050     * {@code LDIFChangeRecord} class for an example demonstrating the process for
051     * interacting with LDIF change records.
052     */
053    public final class LDIFModifyDNChangeRecord
054           extends LDIFChangeRecord
055    {
056      /**
057       * The serial version UID for this serializable class.
058       */
059      private static final long serialVersionUID = 5804442145450388071L;
060    
061    
062    
063      // Indicates whether to delete the current RDN value.
064      private final boolean deleteOldRDN;
065    
066      // The parsed new superior DN for the entry.
067      private volatile DN parsedNewSuperiorDN;
068    
069      // The parsed new RDN for the entry.
070      private volatile RDN parsedNewRDN;
071    
072      // The new RDN value for the entry.
073      private final String newRDN;
074    
075      // The new superior DN for the entry, if available.
076      private final String newSuperiorDN;
077    
078    
079    
080      /**
081       * Creates a new LDIF modify DN change record with the provided information.
082       *
083       * @param  dn             The current DN for the entry.  It must not be
084       *                        {@code null}.
085       * @param  newRDN         The new RDN value for the entry.  It must not be
086       *                        {@code null}.
087       * @param  deleteOldRDN   Indicates whether to delete the currentRDN value
088       *                        from the entry.
089       * @param  newSuperiorDN  The new superior DN for this LDIF modify DN change
090       *                        record.  It may be {@code null} if the entry is not
091       *                        to be moved below a new parent.
092       */
093      public LDIFModifyDNChangeRecord(final String dn, final String newRDN,
094                                      final boolean deleteOldRDN,
095                                      final String newSuperiorDN)
096      {
097        this(dn, newRDN, deleteOldRDN, newSuperiorDN, null);
098      }
099    
100    
101    
102      /**
103       * Creates a new LDIF modify DN change record with the provided information.
104       *
105       * @param  dn             The current DN for the entry.  It must not be
106       *                        {@code null}.
107       * @param  newRDN         The new RDN value for the entry.  It must not be
108       *                        {@code null}.
109       * @param  deleteOldRDN   Indicates whether to delete the currentRDN value
110       *                        from the entry.
111       * @param  newSuperiorDN  The new superior DN for this LDIF modify DN change
112       *                        record.  It may be {@code null} if the entry is not
113       *                        to be moved below a new parent.
114       * @param  controls       The set of controls for this LDIF modify DN change
115       *                        record.  It may be {@code null} or empty if there
116       *                        are no controls.
117       */
118      public LDIFModifyDNChangeRecord(final String dn, final String newRDN,
119                                      final boolean deleteOldRDN,
120                                      final String newSuperiorDN,
121                                      final List<Control> controls)
122      {
123        super(dn, controls);
124    
125        ensureNotNull(newRDN);
126    
127        this.newRDN        = newRDN;
128        this.deleteOldRDN  = deleteOldRDN;
129        this.newSuperiorDN = newSuperiorDN;
130    
131        parsedNewRDN        = null;
132        parsedNewSuperiorDN = null;
133      }
134    
135    
136    
137      /**
138       * Creates a new LDIF modify DN change record from the provided modify DN
139       * request.
140       *
141       * @param  modifyDNRequest  The modify DN request to use to create this LDIF
142       *                          modify DN change record.  It must not be
143       *                          {@code null}.
144       */
145      public LDIFModifyDNChangeRecord(final ModifyDNRequest modifyDNRequest)
146      {
147        super(modifyDNRequest.getDN(), modifyDNRequest.getControlList());
148    
149        newRDN        = modifyDNRequest.getNewRDN();
150        deleteOldRDN  = modifyDNRequest.deleteOldRDN();
151        newSuperiorDN = modifyDNRequest.getNewSuperiorDN();
152    
153        parsedNewRDN        = null;
154        parsedNewSuperiorDN = null;
155      }
156    
157    
158    
159      /**
160       * Retrieves the new RDN value for the entry.
161       *
162       * @return  The new RDN value for the entry.
163       */
164      public String getNewRDN()
165      {
166        return newRDN;
167      }
168    
169    
170    
171      /**
172       * Retrieves the parsed new RDN value for the entry.
173       *
174       * @return  The parsed new RDN value for the entry.
175       *
176       * @throws  LDAPException  If a problem occurs while trying to parse the new
177       *                         RDN.
178       */
179      public RDN getParsedNewRDN()
180             throws LDAPException
181      {
182        if (parsedNewRDN == null)
183        {
184          parsedNewRDN = new RDN(newRDN);
185        }
186    
187        return parsedNewRDN;
188      }
189    
190    
191    
192      /**
193       * Indicates whether to delete the current RDN value from the entry.
194       *
195       * @return  {@code true} if the current RDN value should be removed from the
196       *          entry, or {@code false} if not.
197       */
198      public boolean deleteOldRDN()
199      {
200        return deleteOldRDN;
201      }
202    
203    
204    
205      /**
206       * Retrieves the new superior DN for the entry, if applicable.
207       *
208       * @return  The new superior DN for the entry, or {@code null} if the entry is
209       *          not to be moved below a new parent.
210       */
211      public String getNewSuperiorDN()
212      {
213        return newSuperiorDN;
214      }
215    
216    
217    
218      /**
219       * Retrieves the parsed new superior DN for the entry, if applicable.
220       *
221       * @return  The parsed new superior DN for the entry, or {@code null} if the
222       *          entry is not to be moved below a new parent.
223       *
224       * @throws  LDAPException  If a problem occurs while trying to parse the new
225       *                         superior DN.
226       */
227      public DN getParsedNewSuperiorDN()
228             throws LDAPException
229      {
230        if ((parsedNewSuperiorDN == null) && (newSuperiorDN != null))
231        {
232          parsedNewSuperiorDN = new DN(newSuperiorDN);
233        }
234    
235        return parsedNewSuperiorDN;
236      }
237    
238    
239    
240      /**
241       * Retrieves the DN that the entry should have after the successful completion
242       * of the operation.
243       *
244       * @return  The DN that the entry should have after the successful completion
245       *          of the operation.
246       *
247       * @throws  LDAPException  If a problem occurs while trying to parse the
248       *                         target DN, new RDN, or new superior DN.
249       */
250      public DN getNewDN()
251             throws LDAPException
252      {
253        if (newSuperiorDN == null)
254        {
255          final DN parentDN = getParsedDN().getParent();
256          if (parentDN == null)
257          {
258            return new DN(getParsedNewRDN());
259          }
260          else
261          {
262            return new DN(getParsedNewRDN(), parentDN);
263          }
264        }
265        else
266        {
267          return new DN(getParsedNewRDN(), getParsedNewSuperiorDN());
268        }
269      }
270    
271    
272    
273      /**
274       * Creates a modify DN request from this LDIF modify DN change record.  Any
275       * change record controls will be included in the request
276       *
277       * @return  The modify DN request created from this LDIF modify DN change
278       *          record.
279       */
280      public ModifyDNRequest toModifyDNRequest()
281      {
282        return toModifyDNRequest(true);
283      }
284    
285    
286    
287      /**
288       * Creates a modify DN request from this LDIF modify DN change record,
289       * optionally including any change record controls in the request.
290       *
291       * @param  includeControls  Indicates whether to include any controls in the
292       *                          request.
293       *
294       * @return  The modify DN request created from this LDIF modify DN change
295       *          record.
296       */
297      public ModifyDNRequest toModifyDNRequest(final boolean includeControls)
298      {
299        final ModifyDNRequest modifyDNRequest =
300             new ModifyDNRequest(getDN(), newRDN, deleteOldRDN, newSuperiorDN);
301        if (includeControls)
302        {
303          modifyDNRequest.setControls(getControls());
304        }
305    
306        return modifyDNRequest;
307      }
308    
309    
310    
311      /**
312       * {@inheritDoc}
313       */
314      @Override()
315      public ChangeType getChangeType()
316      {
317        return ChangeType.MODIFY_DN;
318      }
319    
320    
321    
322      /**
323       * {@inheritDoc}
324       */
325      @Override()
326      public LDAPResult processChange(final LDAPInterface connection,
327                                      final boolean includeControls)
328             throws LDAPException
329      {
330        return connection.modifyDN(toModifyDNRequest(includeControls));
331      }
332    
333    
334    
335      /**
336       * {@inheritDoc}
337       */
338      @Override()
339      public String[] toLDIF(final int wrapColumn)
340      {
341        List<String> ldifLines = new ArrayList<String>(10);
342        ldifLines.add(LDIFWriter.encodeNameAndValue("dn",
343             new ASN1OctetString(getDN())));
344    
345        for (final Control c : getControls())
346        {
347          ldifLines.add(LDIFWriter.encodeNameAndValue("control",
348               encodeControlString(c)));
349        }
350    
351        ldifLines.add("changetype: moddn");
352        ldifLines.add(LDIFWriter.encodeNameAndValue("newrdn",
353             new ASN1OctetString(newRDN)));
354        ldifLines.add("deleteoldrdn: " + (deleteOldRDN ? "1" : "0"));
355    
356        if (newSuperiorDN != null)
357        {
358          ldifLines.add(LDIFWriter.encodeNameAndValue("newsuperior",
359               new ASN1OctetString(newSuperiorDN)));
360        }
361    
362        if (wrapColumn > 2)
363        {
364          ldifLines = LDIFWriter.wrapLines(wrapColumn, ldifLines);
365        }
366    
367        final String[] ldifArray = new String[ldifLines.size()];
368        ldifLines.toArray(ldifArray);
369        return ldifArray;
370      }
371    
372    
373    
374      /**
375       * {@inheritDoc}
376       */
377      @Override()
378      public void toLDIF(final ByteStringBuffer buffer, final int wrapColumn)
379      {
380        LDIFWriter.encodeNameAndValue("dn", new ASN1OctetString(getDN()), buffer,
381             wrapColumn);
382        buffer.append(EOL_BYTES);
383    
384        for (final Control c : getControls())
385        {
386          LDIFWriter.encodeNameAndValue("control", encodeControlString(c), buffer,
387               wrapColumn);
388          buffer.append(EOL_BYTES);
389        }
390    
391        LDIFWriter.encodeNameAndValue("changetype", new ASN1OctetString("moddn"),
392                                      buffer, wrapColumn);
393        buffer.append(EOL_BYTES);
394    
395        LDIFWriter.encodeNameAndValue("newrdn", new ASN1OctetString(newRDN), buffer,
396                                      wrapColumn);
397        buffer.append(EOL_BYTES);
398    
399        if (deleteOldRDN)
400        {
401          LDIFWriter.encodeNameAndValue("deleteoldrdn", new ASN1OctetString("1"),
402                                        buffer, wrapColumn);
403        }
404        else
405        {
406          LDIFWriter.encodeNameAndValue("deleteoldrdn", new ASN1OctetString("0"),
407                                        buffer, wrapColumn);
408        }
409        buffer.append(EOL_BYTES);
410    
411        if (newSuperiorDN != null)
412        {
413          LDIFWriter.encodeNameAndValue("newsuperior",
414                                        new ASN1OctetString(newSuperiorDN), buffer,
415                                        wrapColumn);
416          buffer.append(EOL_BYTES);
417        }
418      }
419    
420    
421    
422      /**
423       * {@inheritDoc}
424       */
425      @Override()
426      public void toLDIFString(final StringBuilder buffer, final int wrapColumn)
427      {
428        LDIFWriter.encodeNameAndValue("dn", new ASN1OctetString(getDN()), buffer,
429                                      wrapColumn);
430        buffer.append(EOL);
431    
432        for (final Control c : getControls())
433        {
434          LDIFWriter.encodeNameAndValue("control", encodeControlString(c), buffer,
435               wrapColumn);
436          buffer.append(EOL);
437        }
438    
439        LDIFWriter.encodeNameAndValue("changetype", new ASN1OctetString("moddn"),
440                                      buffer, wrapColumn);
441        buffer.append(EOL);
442    
443        LDIFWriter.encodeNameAndValue("newrdn", new ASN1OctetString(newRDN), buffer,
444                                      wrapColumn);
445        buffer.append(EOL);
446    
447        if (deleteOldRDN)
448        {
449          LDIFWriter.encodeNameAndValue("deleteoldrdn", new ASN1OctetString("1"),
450                                        buffer, wrapColumn);
451        }
452        else
453        {
454          LDIFWriter.encodeNameAndValue("deleteoldrdn", new ASN1OctetString("0"),
455                                        buffer, wrapColumn);
456        }
457        buffer.append(EOL);
458    
459        if (newSuperiorDN != null)
460        {
461          LDIFWriter.encodeNameAndValue("newsuperior",
462                                        new ASN1OctetString(newSuperiorDN), buffer,
463                                        wrapColumn);
464          buffer.append(EOL);
465        }
466      }
467    
468    
469    
470      /**
471       * {@inheritDoc}
472       */
473      @Override()
474      public int hashCode()
475      {
476        int hashCode;
477        try
478        {
479          hashCode = getParsedDN().hashCode() + getParsedNewRDN().hashCode();
480          if (newSuperiorDN != null)
481          {
482            hashCode += getParsedNewSuperiorDN().hashCode();
483          }
484        }
485        catch (final Exception e)
486        {
487          debugException(e);
488          hashCode = toLowerCase(getDN()).hashCode() +
489                     toLowerCase(newRDN).hashCode();
490          if (newSuperiorDN != null)
491          {
492            hashCode += toLowerCase(newSuperiorDN).hashCode();
493          }
494        }
495    
496        if (deleteOldRDN)
497        {
498          hashCode++;
499        }
500    
501        return hashCode;
502      }
503    
504    
505    
506      /**
507       * {@inheritDoc}
508       */
509      @Override()
510      public boolean equals(final Object o)
511      {
512        if (o == null)
513        {
514          return false;
515        }
516    
517        if (o == this)
518        {
519          return true;
520        }
521    
522        if (! (o instanceof LDIFModifyDNChangeRecord))
523        {
524          return false;
525        }
526    
527        final LDIFModifyDNChangeRecord r = (LDIFModifyDNChangeRecord) o;
528    
529        final HashSet<Control> c1 = new HashSet<Control>(getControls());
530        final HashSet<Control> c2 = new HashSet<Control>(r.getControls());
531        if (! c1.equals(c2))
532        {
533          return false;
534        }
535    
536        try
537        {
538          if (! getParsedDN().equals(r.getParsedDN()))
539          {
540            return false;
541          }
542        }
543        catch (final Exception e)
544        {
545          debugException(e);
546          if (! toLowerCase(getDN()).equals(toLowerCase(r.getDN())))
547          {
548            return false;
549          }
550        }
551    
552        try
553        {
554          if (! getParsedNewRDN().equals(r.getParsedNewRDN()))
555          {
556            return false;
557          }
558        }
559        catch (final Exception e)
560        {
561          debugException(e);
562          if (! toLowerCase(newRDN).equals(toLowerCase(r.newRDN)))
563          {
564            return false;
565          }
566        }
567    
568        if (newSuperiorDN == null)
569        {
570          if (r.newSuperiorDN != null)
571          {
572            return false;
573          }
574        }
575        else
576        {
577          if (r.newSuperiorDN == null)
578          {
579            return false;
580          }
581    
582          try
583          {
584            if (! getParsedNewSuperiorDN().equals(r.getParsedNewSuperiorDN()))
585            {
586              return false;
587            }
588          }
589          catch (final Exception e)
590          {
591            debugException(e);
592            if (! toLowerCase(newSuperiorDN).equals(toLowerCase(r.newSuperiorDN)))
593            {
594              return false;
595            }
596          }
597        }
598    
599        return (deleteOldRDN == r.deleteOldRDN);
600      }
601    
602    
603    
604      /**
605       * {@inheritDoc}
606       */
607      @Override()
608      public void toString(final StringBuilder buffer)
609      {
610        buffer.append("LDIFModifyDNChangeRecord(dn='");
611        buffer.append(getDN());
612        buffer.append("', newRDN='");
613        buffer.append(newRDN);
614        buffer.append("', deleteOldRDN=");
615        buffer.append(deleteOldRDN);
616    
617        if (newSuperiorDN != null)
618        {
619          buffer.append(", newSuperiorDN='");
620          buffer.append(newSuperiorDN);
621          buffer.append('\'');
622        }
623    
624        final List<Control> controls = getControls();
625        if (! controls.isEmpty())
626        {
627          buffer.append(", controls={");
628    
629          final Iterator<Control> iterator = controls.iterator();
630          while (iterator.hasNext())
631          {
632            iterator.next().toString(buffer);
633            if (iterator.hasNext())
634            {
635              buffer.append(',');
636            }
637          }
638    
639          buffer.append('}');
640        }
641    
642        buffer.append(')');
643      }
644    }