001/*
002 * Copyright 2007-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2007-2024 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) 2007-2024 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.Collection;
042import java.util.Collections;
043import java.util.HashSet;
044import java.util.Iterator;
045import java.util.List;
046
047import com.unboundid.asn1.ASN1OctetString;
048import com.unboundid.ldap.sdk.AddRequest;
049import com.unboundid.ldap.sdk.Attribute;
050import com.unboundid.ldap.sdk.ChangeType;
051import com.unboundid.ldap.sdk.Control;
052import com.unboundid.ldap.sdk.Entry;
053import com.unboundid.ldap.sdk.LDAPException;
054import com.unboundid.ldap.sdk.LDAPInterface;
055import com.unboundid.ldap.sdk.LDAPResult;
056import com.unboundid.util.ByteStringBuffer;
057import com.unboundid.util.Debug;
058import com.unboundid.util.NotMutable;
059import com.unboundid.util.NotNull;
060import com.unboundid.util.Nullable;
061import com.unboundid.util.StaticUtils;
062import com.unboundid.util.ThreadSafety;
063import com.unboundid.util.ThreadSafetyLevel;
064import com.unboundid.util.Validator;
065
066
067
068/**
069 * This class defines an LDIF add change record, which can be used to represent
070 * an LDAP add request.  See the documentation for the {@link LDIFChangeRecord}
071 * class for an example demonstrating the process for interacting with LDIF
072 * change records.
073 */
074@NotMutable()
075@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
076public final class LDIFAddChangeRecord
077       extends LDIFChangeRecord
078{
079  /**
080   * The serial version UID for this serializable class.
081   */
082  private static final long serialVersionUID = 4722916031463878423L;
083
084
085
086  // The set of attributes for this add change record.
087  @NotNull private final Attribute[] attributes;
088
089
090
091  /**
092   * Creates a new LDIF add change record with the provided DN and attributes.
093   *
094   * @param  dn          The DN for this LDIF add change record.  It must not be
095   *                     {@code null}.
096   * @param  attributes  The set of attributes for this LDIF add change record.
097   *                     It must not be {@code null} or empty.
098   */
099  public LDIFAddChangeRecord(@NotNull final String dn,
100                             @NotNull final Attribute... attributes)
101  {
102    this(dn, attributes, null);
103  }
104
105
106
107  /**
108   * Creates a new LDIF add change record with the provided DN and attributes.
109   *
110   * @param  dn          The DN for this LDIF add change record.  It must not be
111   *                     {@code null}.
112   * @param  attributes  The set of attributes for this LDIF add change record.
113   *                     It must not be {@code null} or empty.
114   * @param  controls    The set of controls for this LDIF add change record.
115   *                     It may be {@code null} or empty if there are no
116   *                     controls.
117   */
118  public LDIFAddChangeRecord(@NotNull final String dn,
119                             @NotNull final Attribute[] attributes,
120                             @Nullable final List<Control> controls)
121  {
122    super(dn, controls);
123
124    Validator.ensureNotNull(attributes);
125    Validator.ensureTrue(attributes.length > 0,
126         "LDIFAddChangeRecord.attributes must not be empty.");
127
128    this.attributes = attributes;
129  }
130
131
132
133  /**
134   * Creates a new LDIF add change record with the provided DN and attributes.
135   *
136   * @param  dn          The DN for this LDIF add change record.  It must not be
137   *                     {@code null}.
138   * @param  attributes  The set of attributes for this LDIF add change record.
139   *                     It must not be {@code null} or empty.
140   */
141  public LDIFAddChangeRecord(@NotNull final String dn,
142                             @NotNull final List<Attribute> attributes)
143  {
144    this(dn, attributes, null);
145  }
146
147
148
149  /**
150   * Creates a new LDIF add change record with the provided DN and attributes.
151   *
152   * @param  dn          The DN for this LDIF add change record.  It must not be
153   *                     {@code null}.
154   * @param  attributes  The set of attributes for this LDIF add change record.
155   *                     It must not be {@code null} or empty.
156   * @param  controls    The set of controls for this LDIF add change record.
157   *                     It may be {@code null} or empty if there are no
158   *                     controls.
159   */
160  public LDIFAddChangeRecord(@NotNull final String dn,
161                             @NotNull final List<Attribute> attributes,
162                             @Nullable final List<Control> controls)
163  {
164    super(dn, controls);
165
166    Validator.ensureNotNull(attributes);
167    Validator.ensureFalse(attributes.isEmpty(),
168         "LDIFAddChangeRecord.attributes must not be empty.");
169
170    this.attributes = new Attribute[attributes.size()];
171    attributes.toArray(this.attributes);
172  }
173
174
175
176  /**
177   * Creates a new LDIF add change record from the provided entry.
178   *
179   * @param  entry  The entry to use to create this LDIF add change record.  It
180   *                must not be {@code null}.
181   */
182  public LDIFAddChangeRecord(@NotNull final Entry entry)
183  {
184    this(entry, Collections.<Control>emptyList());
185  }
186
187
188
189  /**
190   * Creates a new LDIF add change record from the provided entry.
191   *
192   * @param  entry     The entry to use to create this LDIF add change record.
193   *                   It must not be {@code null}.
194   * @param  controls  The set of controls for this LDIF add change record.  It
195   *                   may be {@code null} or empty if there are no controls.
196   */
197  public LDIFAddChangeRecord(@NotNull final Entry entry,
198                             @Nullable final List<Control> controls)
199  {
200    super(entry.getDN(), controls);
201
202    final Collection<Attribute> attrs = entry.getAttributes();
203    attributes = new Attribute[attrs.size()];
204
205    final Iterator<Attribute> iterator = attrs.iterator();
206    for (int i=0; i < attributes.length; i++)
207    {
208      attributes[i] = iterator.next();
209    }
210  }
211
212
213
214  /**
215   * Creates a new LDIF add change record from the provided add request.
216   *
217   * @param  addRequest  The add request to use to create this LDIF add change
218   *                     record.  It must not be {@code null}.
219   */
220  public LDIFAddChangeRecord(@NotNull final AddRequest addRequest)
221  {
222    super(addRequest.getDN(), addRequest.getControlList());
223
224    final List<Attribute> attrs = addRequest.getAttributes();
225    attributes = new Attribute[attrs.size()];
226
227    final Iterator<Attribute> iterator = attrs.iterator();
228    for (int i=0; i < attributes.length; i++)
229    {
230      attributes[i] = iterator.next();
231    }
232  }
233
234
235
236  /**
237   * Retrieves the set of attributes for this add change record.
238   *
239   * @return  The set of attributes for this add change record.
240   */
241  @NotNull()
242  public Attribute[] getAttributes()
243  {
244    return attributes;
245  }
246
247
248
249  /**
250   * Retrieves the entry that would be created by this add change record.
251   *
252   * @return  The entry that would be created by this add change record.
253   */
254  @NotNull()
255  public Entry getEntryToAdd()
256  {
257    return new Entry(getDN(), attributes);
258  }
259
260
261
262  /**
263   * Creates an add request from this LDIF add change record.    Any controls
264   * included in this change record will be included in the request.
265   *
266   * @return  The add request created from this LDIF add change record.
267   */
268  @NotNull()
269  public AddRequest toAddRequest()
270  {
271    return toAddRequest(true);
272  }
273
274
275
276  /**
277   * Creates an add request from this LDIF add change record, optionally
278   * including any change record controls in the request.
279   *
280   * @param  includeControls  Indicates whether to include any controls in the
281   *                          request.
282   *
283   * @return  The add request created from this LDIF add change record.
284   */
285  @NotNull()
286  public AddRequest toAddRequest(final boolean includeControls)
287  {
288    final AddRequest addRequest = new AddRequest(getDN(), attributes);
289    if (includeControls)
290    {
291      addRequest.setControls(getControls());
292    }
293
294    return addRequest;
295  }
296
297
298
299  /**
300   * {@inheritDoc}
301   */
302  @Override()
303  @NotNull()
304  public ChangeType getChangeType()
305  {
306    return ChangeType.ADD;
307  }
308
309
310
311  /**
312   * {@inheritDoc}
313   */
314  @Override()
315  @NotNull()
316  public LDIFAddChangeRecord duplicate(@Nullable final Control... controls)
317  {
318    return new LDIFAddChangeRecord(getDN(), attributes,
319         StaticUtils.toList(controls));
320  }
321
322
323
324  /**
325   * {@inheritDoc}
326   */
327  @Override()
328  @NotNull()
329  public LDAPResult processChange(@NotNull final LDAPInterface connection,
330                                  final boolean includeControls)
331         throws LDAPException
332  {
333    return connection.add(toAddRequest(includeControls));
334  }
335
336
337
338  /**
339   * {@inheritDoc}
340   */
341  @Override()
342  @NotNull()
343  public String[] toLDIF(final int wrapColumn)
344  {
345    List<String> ldifLines = new ArrayList<>(2*attributes.length);
346    encodeNameAndValue("dn", new ASN1OctetString(getDN()), ldifLines);
347
348    for (final Control c : getControls())
349    {
350      encodeNameAndValue("control", encodeControlString(c), ldifLines);
351    }
352
353    ldifLines.add("changetype: add");
354
355    for (final Attribute a : attributes)
356    {
357      final String name = a.getName();
358      for (final ASN1OctetString value : a.getRawValues())
359      {
360        encodeNameAndValue(name, value, ldifLines);
361      }
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(@NotNull final ByteStringBuffer buffer,
381                     final int wrapColumn)
382  {
383    LDIFWriter.encodeNameAndValue("dn", new ASN1OctetString(getDN()), buffer,
384         wrapColumn);
385    buffer.append(StaticUtils.EOL_BYTES);
386
387    for (final Control c : getControls())
388    {
389      LDIFWriter.encodeNameAndValue("control", encodeControlString(c), buffer,
390           wrapColumn);
391      buffer.append(StaticUtils.EOL_BYTES);
392    }
393
394    LDIFWriter.encodeNameAndValue("changetype", new ASN1OctetString("add"),
395                                  buffer, wrapColumn);
396    buffer.append(StaticUtils.EOL_BYTES);
397
398    for (final Attribute a : attributes)
399    {
400      final String name = a.getName();
401      for (final ASN1OctetString value : a.getRawValues())
402      {
403        LDIFWriter.encodeNameAndValue(name, value, buffer, wrapColumn);
404        buffer.append(StaticUtils.EOL_BYTES);
405      }
406    }
407  }
408
409
410
411  /**
412   * {@inheritDoc}
413   */
414  @Override()
415  public void toLDIFString(@NotNull final StringBuilder buffer,
416                           final int wrapColumn)
417  {
418    LDIFWriter.encodeNameAndValue("dn", new ASN1OctetString(getDN()), buffer,
419         wrapColumn);
420    buffer.append(StaticUtils.EOL);
421
422    for (final Control c : getControls())
423    {
424      LDIFWriter.encodeNameAndValue("control", encodeControlString(c), buffer,
425           wrapColumn);
426      buffer.append(StaticUtils.EOL);
427    }
428
429    LDIFWriter.encodeNameAndValue("changetype", new ASN1OctetString("add"),
430                                  buffer, wrapColumn);
431    buffer.append(StaticUtils.EOL);
432
433    for (final Attribute a : attributes)
434    {
435      final String name = a.getName();
436      for (final ASN1OctetString value : a.getRawValues())
437      {
438        LDIFWriter.encodeNameAndValue(name, value, buffer, wrapColumn);
439        buffer.append(StaticUtils.EOL);
440      }
441    }
442  }
443
444
445
446  /**
447   * {@inheritDoc}
448   */
449  @Override()
450  public int hashCode()
451  {
452    try
453    {
454      int hashCode = getParsedDN().hashCode();
455      for (final Attribute a : attributes)
456      {
457        hashCode += a.hashCode();
458      }
459
460      return hashCode;
461    }
462    catch (final Exception e)
463    {
464      Debug.debugException(e);
465      return new Entry(getDN(), attributes).hashCode();
466    }
467  }
468
469
470
471  /**
472   * {@inheritDoc}
473   */
474  @Override()
475  public boolean equals(@Nullable final Object o)
476  {
477    if (o == null)
478    {
479      return false;
480    }
481
482    if (o == this)
483    {
484      return true;
485    }
486
487    if (! (o instanceof LDIFAddChangeRecord))
488    {
489      return false;
490    }
491
492    final LDIFAddChangeRecord r = (LDIFAddChangeRecord) o;
493
494    final HashSet<Control> c1 = new HashSet<>(getControls());
495    final HashSet<Control> c2 = new HashSet<>(r.getControls());
496    if (! c1.equals(c2))
497    {
498      return false;
499    }
500
501    final Entry e1 = new Entry(getDN(), attributes);
502    final Entry e2 = new Entry(r.getDN(), r.attributes);
503    return e1.equals(e2);
504  }
505
506
507
508  /**
509   * {@inheritDoc}
510   */
511  @Override()
512  public void toString(@NotNull final StringBuilder buffer)
513  {
514    buffer.append("LDIFAddChangeRecord(dn='");
515    buffer.append(getDN());
516    buffer.append("', attrs={");
517
518    for (int i=0; i < attributes.length; i++)
519    {
520      if (i > 0)
521      {
522        buffer.append(", ");
523      }
524      attributes[i].toString(buffer);
525    }
526    buffer.append('}');
527
528    final List<Control> controls = getControls();
529    if (! controls.isEmpty())
530    {
531      buffer.append(", controls={");
532
533      final Iterator<Control> iterator = controls.iterator();
534      while (iterator.hasNext())
535      {
536        iterator.next().toString(buffer);
537        if (iterator.hasNext())
538        {
539          buffer.append(',');
540        }
541      }
542
543      buffer.append('}');
544    }
545
546    buffer.append(')');
547  }
548}