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