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