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