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 */
021package com.unboundid.ldif;
022
023
024
025import java.util.Collections;
026import java.util.List;
027
028import com.unboundid.asn1.ASN1OctetString;
029import com.unboundid.ldap.sdk.ChangeType;
030import com.unboundid.ldap.sdk.Control;
031import com.unboundid.ldap.sdk.DN;
032import com.unboundid.ldap.sdk.Entry;
033import com.unboundid.ldap.sdk.LDAPException;
034import com.unboundid.ldap.sdk.LDAPInterface;
035import com.unboundid.ldap.sdk.LDAPResult;
036import com.unboundid.util.ByteStringBuffer;
037import com.unboundid.util.NotExtensible;
038import com.unboundid.util.ThreadSafety;
039import com.unboundid.util.ThreadSafetyLevel;
040
041import static com.unboundid.util.Validator.*;
042
043
044
045/**
046 * This class provides a base class for LDIF change records, which can be used
047 * to represent add, delete, modify, and modify DN operations in LDIF form.
048 * <BR><BR>
049 * <H2>Example</H2>
050 * The following example iterates through all of the change records contained in
051 * an LDIF file and attempts to apply those changes to a directory server:
052 * <PRE>
053 * LDIFReader ldifReader = new LDIFReader(pathToLDIFFile);
054 *
055 * int changesRead = 0;
056 * int changesProcessed = 0;
057 * int errorsEncountered = 0;
058 * while (true)
059 * {
060 *   LDIFChangeRecord changeRecord;
061 *   try
062 *   {
063 *     changeRecord = ldifReader.readChangeRecord();
064 *     if (changeRecord == null)
065 *     {
066 *       // All changes have been processed.
067 *       break;
068 *     }
069 *
070 *     changesRead++;
071 *   }
072 *   catch (LDIFException le)
073 *   {
074 *     errorsEncountered++;
075 *     if (le.mayContinueReading())
076 *     {
077 *       // A recoverable error occurred while attempting to read a change
078 *       // record, at or near line number le.getLineNumber()
079 *       // The change record will be skipped, but we'll try to keep reading
080 *       // from the LDIF file.
081 *       continue;
082 *     }
083 *     else
084 *     {
085 *       // An unrecoverable error occurred while attempting to read a change
086 *       // record, at or near line number le.getLineNumber()
087 *       // No further LDIF processing will be performed.
088 *       break;
089 *     }
090 *   }
091 *   catch (IOException ioe)
092 *   {
093 *     // An I/O error occurred while attempting to read from the LDIF file.
094 *     // No further LDIF processing will be performed.
095 *     errorsEncountered++;
096 *     break;
097 *   }
098 *
099 *   // Try to process the change in a directory server.
100 *   LDAPResult operationResult;
101 *   try
102 *   {
103 *     operationResult = changeRecord.processChange(connection);
104 *     // If we got here, then the change should have been processed
105 *     // successfully.
106 *     changesProcessed++;
107 *   }
108 *   catch (LDAPException le)
109 *   {
110 *     // If we got here, then the change attempt failed.
111 *     operationResult = le.toLDAPResult();
112 *     errorsEncountered++;
113 *   }
114 * }
115 *
116 * ldifReader.close();
117 * </PRE>
118 */
119@NotExtensible()
120@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE)
121public abstract class LDIFChangeRecord
122       implements LDIFRecord
123{
124  /**
125   * The serial version UID for this serializable class.
126   */
127  private static final long serialVersionUID = 6917212392170911115L;
128
129
130
131  // The set of controls for the LDIF change record.
132  private final List<Control> controls;
133
134  // The parsed DN for this LDIF change record.
135  private volatile DN parsedDN;
136
137  // The DN for this LDIF change record.
138  private final String dn;
139
140
141
142  /**
143   * Creates a new LDIF change record with the provided DN.
144   *
145   * @param  dn        The DN of the LDIF change record to create.  It must not
146   *                   be {@code null}.
147   * @param  controls  The set of controls for the change record to create.  It
148   *                   may be {@code null} or empty if no controls are needed.
149   */
150  protected LDIFChangeRecord(final String dn, final List<Control> controls)
151  {
152    ensureNotNull(dn);
153
154    this.dn = dn;
155    parsedDN = null;
156
157    if (controls == null)
158    {
159      this.controls = Collections.emptyList();
160    }
161    else
162    {
163      this.controls = Collections.unmodifiableList(controls);
164    }
165  }
166
167
168
169  /**
170   * Retrieves the DN for this LDIF change record.
171   *
172   * @return  The DN for this LDIF change record.
173   */
174  public final String getDN()
175  {
176    return dn;
177  }
178
179
180
181  /**
182   * Retrieves the parsed DN for this LDIF change record.
183   *
184   * @return  The DN for this LDIF change record.
185   *
186   * @throws  LDAPException  If a problem occurs while trying to parse the DN.
187   */
188  public final DN getParsedDN()
189         throws LDAPException
190  {
191    if (parsedDN == null)
192    {
193      parsedDN = new DN(dn);
194    }
195
196    return parsedDN;
197  }
198
199
200
201  /**
202   * Retrieves the type of operation represented by this LDIF change record.
203   *
204   * @return  The type of operation represented by this LDIF change record.
205   */
206  public abstract ChangeType getChangeType();
207
208
209
210  /**
211   * Retrieves the set of controls for this LDIF change record.
212   *
213   * @return  The set of controls for this LDIF change record, or an empty array
214   *          if there are no controls.
215   */
216  public List<Control> getControls()
217  {
218    return controls;
219  }
220
221
222
223  /**
224   * Apply the change represented by this LDIF change record to a directory
225   * server using the provided connection.  Any controls included in the
226   * change record will be included in the request.
227   *
228   * @param  connection  The connection to use to apply the change.
229   *
230   * @return  An object providing information about the result of the operation.
231   *
232   * @throws  LDAPException  If an error occurs while processing this change
233   *                         in the associated directory server.
234   */
235  public final LDAPResult processChange(final LDAPInterface connection)
236         throws LDAPException
237  {
238    return processChange(connection, true);
239  }
240
241
242
243  /**
244   * Apply the change represented by this LDIF change record to a directory
245   * server using the provided connection, optionally including any change
246   * record controls in the request.
247   *
248   * @param  connection       The connection to use to apply the change.
249   * @param  includeControls  Indicates whether to include any controls in the
250   *                          request.
251   *
252   * @return  An object providing information about the result of the operation.
253   *
254   * @throws  LDAPException  If an error occurs while processing this change
255   *                         in the associated directory server.
256   */
257  public abstract LDAPResult processChange(final LDAPInterface connection,
258                                           final boolean includeControls)
259         throws LDAPException;
260
261
262
263  /**
264   * Retrieves an {@code Entry} representation of this change record.  This is
265   * intended only for internal use by the LDIF reader when operating
266   * asynchronously in the case that it is not possible to know ahead of time
267   * whether a user will attempt to read an LDIF record by {@code readEntry} or
268   * {@code readChangeRecord}.  In the event that the LDIF file has an entry
269   * whose first attribute is "changetype" and the client wants to read it as
270   * an entry rather than a change record, then this may be used to generate an
271   * entry representing the change record.
272   *
273   * @return  The entry representation of this change record.
274   *
275   * @throws  LDIFException  If this change record cannot be represented as a
276   *                         valid entry.
277   */
278  final Entry toEntry()
279        throws LDIFException
280  {
281    return new Entry(toLDIF());
282  }
283
284
285
286  /**
287   * Retrieves a string array whose lines contain an LDIF representation of this
288   * change record.
289   *
290   * @return  A string array whose lines contain an LDIF representation of this
291   *          change record.
292   */
293  public final String[] toLDIF()
294  {
295    return toLDIF(0);
296  }
297
298
299
300  /**
301   * Retrieves a string array whose lines contain an LDIF representation of this
302   * change record.
303   *
304   * @param  wrapColumn  The column at which to wrap long lines.  A value that
305   *                     is less than or equal to two indicates that no
306   *                     wrapping should be performed.
307   *
308   * @return  A string array whose lines contain an LDIF representation of this
309   *          change record.
310   */
311  public abstract String[] toLDIF(final int wrapColumn);
312
313
314
315  /**
316   * Appends an LDIF string representation of this change record to the provided
317   * buffer.
318   *
319   * @param  buffer  The buffer to which to append an LDIF representation of
320   *                 this change record.
321   */
322  public final void toLDIF(final ByteStringBuffer buffer)
323  {
324    toLDIF(buffer, 0);
325  }
326
327
328
329  /**
330   * Appends an LDIF string representation of this change record to the provided
331   * buffer.
332   *
333   * @param  buffer      The buffer to which to append an LDIF representation of
334   *                     this change record.
335   * @param  wrapColumn  The column at which to wrap long lines.  A value that
336   *                     is less than or equal to two indicates that no
337   *                     wrapping should be performed.
338   */
339  public abstract void toLDIF(final ByteStringBuffer buffer,
340                              final int wrapColumn);
341
342
343
344  /**
345   * Retrieves an LDIF string representation of this change record.
346   *
347   * @return  An LDIF string representation of this change record.
348   */
349  public final String toLDIFString()
350  {
351    final StringBuilder buffer = new StringBuilder();
352    toLDIFString(buffer, 0);
353    return buffer.toString();
354  }
355
356
357
358  /**
359   * Retrieves an LDIF string representation of this change record.
360   *
361   * @param  wrapColumn  The column at which to wrap long lines.  A value that
362   *                     is less than or equal to two indicates that no
363   *                     wrapping should be performed.
364   *
365   * @return  An LDIF string representation of this change record.
366   */
367  public final String toLDIFString(final int wrapColumn)
368  {
369    final StringBuilder buffer = new StringBuilder();
370    toLDIFString(buffer, wrapColumn);
371    return buffer.toString();
372  }
373
374
375
376  /**
377   * Appends an LDIF string representation of this change record to the provided
378   * buffer.
379   *
380   * @param  buffer  The buffer to which to append an LDIF representation of
381   *                 this change record.
382   */
383  public final void toLDIFString(final StringBuilder buffer)
384  {
385    toLDIFString(buffer, 0);
386  }
387
388
389
390  /**
391   * Appends an LDIF string representation of this change record to the provided
392   * buffer.
393   *
394   * @param  buffer      The buffer to which to append an LDIF representation of
395   *                     this change record.
396   * @param  wrapColumn  The column at which to wrap long lines.  A value that
397   *                     is less than or equal to two indicates that no
398   *                     wrapping should be performed.
399   */
400  public abstract void toLDIFString(final StringBuilder buffer,
401                                    final int wrapColumn);
402
403
404
405  /**
406   * Retrieves a hash code for this change record.
407   *
408   * @return  A hash code for this change record.
409   */
410  @Override()
411  public abstract int hashCode();
412
413
414
415  /**
416   * Indicates whether the provided object is equal to this LDIF change record.
417   *
418   * @param  o  The object for which to make the determination.
419   *
420   * @return  {@code true} if the provided object is equal to this LDIF change
421   *          record, or {@code false} if not.
422   */
423  @Override()
424  public abstract boolean equals(final Object o);
425
426
427
428  /**
429   * Encodes a string representation of the provided control for use in the
430   * LDIF representation of the change record.
431   *
432   * @param  c  The control to be encoded.
433   *
434   * @return  The string representation of the control.
435   */
436  static ASN1OctetString encodeControlString(final Control c)
437  {
438    final ByteStringBuffer buffer = new ByteStringBuffer();
439    buffer.append(c.getOID());
440
441    if (c.isCritical())
442    {
443      buffer.append(" true");
444    }
445    else
446    {
447      buffer.append(" false");
448    }
449
450    final ASN1OctetString value = c.getValue();
451    if (value != null)
452    {
453      LDIFWriter.encodeValue(value, buffer);
454    }
455
456    return buffer.toByteString().toASN1OctetString();
457  }
458
459
460
461  /**
462   * Retrieves a single-line string representation of this change record.
463   *
464   * @return  A single-line string representation of this change record.
465   */
466  @Override()
467  public final String toString()
468  {
469    final StringBuilder buffer = new StringBuilder();
470    toString(buffer);
471    return buffer.toString();
472  }
473
474
475
476  /**
477   * Appends a single-line string representation of this change record to the
478   * provided buffer.
479   *
480   * @param  buffer  The buffer to which the information should be written.
481   */
482  public abstract void toString(final StringBuilder buffer);
483}