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