001/*
002 * Copyright 2011-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2015-2018 Ping Identity Corporation
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.ldap.sdk.unboundidds;
022
023
024
025import java.util.ArrayList;
026import java.util.Arrays;
027import java.util.Collections;
028import java.util.Date;
029import java.util.List;
030
031import com.unboundid.ldap.sdk.Attribute;
032import com.unboundid.ldap.sdk.ChangeLogEntry;
033import com.unboundid.ldap.sdk.ChangeType;
034import com.unboundid.ldap.sdk.Entry;
035import com.unboundid.ldap.sdk.LDAPException;
036import com.unboundid.ldap.sdk.Modification;
037import com.unboundid.ldap.sdk.ModificationType;
038import com.unboundid.ldap.sdk.ReadOnlyEntry;
039import com.unboundid.util.NotMutable;
040import com.unboundid.util.ThreadSafety;
041import com.unboundid.util.ThreadSafetyLevel;
042
043import static com.unboundid.ldap.sdk.unboundidds.UnboundIDDSMessages.*;
044
045
046
047/**
048 * This class provides an implementation of a changelog entry which provides
049 * support for all standard changelog entry attributes as well as those unique
050 * to the Ping Identity, UnboundID, and Alcatel-Lucent 8661 Directory Server.
051 * <BR>
052 * <BLOCKQUOTE>
053 *   <B>NOTE:</B>  This class, and other classes within the
054 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
055 *   supported for use against Ping Identity, UnboundID, and Alcatel-Lucent 8661
056 *   server products.  These classes provide support for proprietary
057 *   functionality or for external specifications that are not considered stable
058 *   or mature enough to be guaranteed to work in an interoperable way with
059 *   other types of LDAP servers.
060 * </BLOCKQUOTE>
061 */
062@NotMutable()
063@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
064public final class UnboundIDChangeLogEntry
065       extends ChangeLogEntry
066{
067  /**
068   * The name of the attribute used to hold the previous values for all
069   * attributes affected by the change.
070   */
071  public static final String ATTR_BEFORE_VALUES = "ds-changelog-before-values";
072
073
074
075  /**
076   * The name of the attribute used to hold the resulting values for all
077   * attributes affected by the change.
078   */
079  public static final String ATTR_AFTER_VALUES = "ds-changelog-after-values";
080
081
082
083  /**
084   * The name of the attribute used to indicate whether the operation represents
085   * a change to a soft-deleted entry.
086   */
087  public static final String ATTR_CHANGE_TO_SOFT_DELETED_ENTRY =
088       "ds-change-to-soft-deleted-entry";
089
090
091
092  /**
093   * The name of the attribute used to hold the values of key attributes from
094   * the entry after the change was applied.
095   */
096  public static final String ATTR_KEY_VALUES =
097       "ds-changelog-entry-key-attr-values";
098
099
100
101  /**
102   * The name of the attribute used to hold information about updated attributes
103   * which had more values (whether before the change, after the change, or
104   * both) than allowed to be shown in the before/after values attributes.
105   */
106  public static final String ATTR_EXCEEDED_MAX_VALUES =
107       "ds-changelog-attr-exceeded-max-values-count";
108
109
110
111  /**
112   * The name of the attribute used to hold information about the number of
113   * user attributes that may have been excluded by access control and/or
114   * sensitive attribute processing.
115   */
116  public static final String ATTR_EXCLUDED_USER_ATTR_COUNT =
117       "ds-changelog-num-excluded-user-attributes";
118
119
120
121  /**
122   * The name of the attribute used to hold information about the number of
123   * operational attributes that may have been excluded by access control and/or
124   * sensitive attribute processing.
125   */
126  public static final String ATTR_EXCLUDED_OPERATIONAL_ATTR_COUNT =
127       "ds-changelog-num-excluded-operational-attributes";
128
129
130
131  /**
132   * The name of the attribute used to hold information about the names of the
133   * user attributes that may have been excluded by access control and/or
134   * sensitive attribute processing.
135   */
136  public static final String ATTR_EXCLUDED_USER_ATTR_NAME =
137       "ds-changelog-excluded-user-attribute";
138
139
140
141  /**
142   * The name of the attribute used to hold information about the names of the
143   * operational attributes that may have been excluded by access control and/or
144   * sensitive attribute processing.
145   */
146  public static final String ATTR_EXCLUDED_OPERATIONAL_ATTR_NAME =
147       "ds-changelog-excluded-operational-attribute";
148
149
150
151  /**
152   * The name of the attribute used to hold the entryUUID value for the entry
153   * that was targeted by the change.
154   */
155  public static final String ATTR_TARGET_UNIQUE_ID = "targetUniqueID";
156
157
158
159  /**
160   * The name of the attribute used to hold a timestamp of the time the change
161   * was processed.
162   */
163  public static final String ATTR_CHANGE_TIME = "changeTime";
164
165
166
167  /**
168   * The name of the attribute used to hold the local change sequence number
169   * assigned to the change.
170   */
171  public static final String ATTR_LOCAL_CSN = "localCSN";
172
173
174
175  /**
176   * The name of the attribute used to hold the DN of the soft-deleted entry
177   * resulting from a soft delete operation.
178   */
179  public static final String ATTR_SOFT_DELETE_TO_DN = "ds-soft-delete-entry-dn";
180
181
182
183  /**
184   * The name of the attribute used to hold the names of the attributes targeted
185   * by the change.
186   */
187  public static final String ATTR_TARGET_ATTRIBUTE =
188       "ds-changelog-target-attribute";
189
190
191
192  /**
193   * The name of the attribute used to hold the DN of the soft-deleted entry
194   * from which the content of an undelete was obtained.
195   */
196  public static final String ATTR_UNDELETE_FROM_DN = "ds-undelete-from-dn";
197
198
199
200  /**
201   * The name of the attribute used to hold information about virtual values
202   * for an add or delete operation.
203   */
204  public static final String ATTR_VIRTUAL_ATTRS =
205       "ds-changelog-virtual-attributes";
206
207
208
209  /**
210   * The name of the attribute used to hold information about virtual values
211   * for modified attributes before the change.
212   */
213  public static final String ATTR_BEFORE_VIRTUAL_VALUES =
214       "ds-changelog-before-virtual-values";
215
216
217
218  /**
219   * The name of the attribute used to hold information about virtual values
220   * for modified attributes after the change.
221   */
222  public static final String ATTR_AFTER_VIRTUAL_VALUES =
223       "ds-changelog-after-virtual-values";
224
225
226
227  /**
228   * The name of the attribute used to hold information about virtual values
229   * for key attributes after the change.
230   */
231  public static final String ATTR_KEY_VIRTUAL_VALUES =
232       "ds-changelog-entry-key-virtual-values";
233
234
235
236  /**
237   * The name of the attribute used to hold information about updated attributes
238   * which had more virtual values (whether before the change, after the change,
239   * or both) than allowed to be shown in the before/after values attributes.
240   */
241  public static final String ATTR_VIRTUAL_EXCEEDED_MAX_VALUES =
242       "ds-changelog-virtual-attr-exceeded-max-values-count";
243
244
245
246  /**
247   * The name of the attribute used to hold the entryUUID values for the
248   * notification destinations matched by the change.
249   */
250  public static final String ATTR_NOTIFICATION_DESTINATION_ENTRY_UUID =
251       "ds-notification-destination-entry-uuid";
252
253
254
255  /**
256   * The name of the attribute used to hold a number of properties related to
257   * the notification matched by the change.
258   */
259  public static final String ATTR_NOTIFICATION_PROPERTIES =
260       "ds-changelog-notification-properties";
261
262
263
264  /**
265   * The serial version UID for this serializable class.
266   */
267  private static final long serialVersionUID = -6127912254495185946L;
268
269
270
271  // Indicates whether the changelog record represents a change to a
272  // soft-deleted entry.
273  private final Boolean changeToSoftDeletedEntry;
274
275  // The time that the change was processed.
276  private final Date changeTime;
277
278  // The number of user attributes excluded by access control and/or sensitive
279  // attribute processing.
280  private final Integer numExcludedUserAttributes;
281
282  // The number of operational attributes excluded by access control and/or
283  // sensitive attribute processing.
284  private final Integer numExcludedOperationalAttributes;
285
286  // The names of virtual attributes as they appeared in the entry after an add
287  // or before a delete operation.
288  private final List<Attribute> entryVirtualAttributes;
289
290  // The values of key attributes as they appeared in the entry after the change
291  // was applied (or before the delete if the entry was removed).
292  private final List<Attribute> keyEntryAttributes;
293
294  // The virtual values of key attributes as they appeared in the entry after
295  // the change was applied (or before the delete if the entry was removed).
296  private final List<Attribute> keyEntryVirtualAttributes;
297
298  // The updated attributes as they appeared in the entry after the change was
299  // applied.
300  private final List<Attribute> updatedAttributesAfterChange;
301
302  // The updated attributes as they appeared in the entry before the change was
303  // applied.
304  private final List<Attribute> updatedAttributesBeforeChange;
305
306  // The virtual values of updated attributes as they appeared in the entry
307  // after the change was applied.
308  private final List<Attribute> updatedVirtualAttributesAfterChange;
309
310  // The virtual values of updated attributes as they appeared in the entry
311  // before the change was applied.
312  private final List<Attribute> updatedVirtualAttributesBeforeChange;
313
314  // Information about updated attributes that had more values than are allowed
315  // to be included in the ds-changelog-before-values or
316  // ds-changelog-after-values attributes.
317  private final List<ChangeLogEntryAttributeExceededMaxValuesCount>
318       attributesThatExceededMaxValuesCount;
319
320  // Information about updated attributes that had more virtual values than are
321  // allowed to be included in the ds-changelog-before-virtual-values or
322  // ds-changelog-after-virtual-values attributes.
323  private final List<ChangeLogEntryAttributeExceededMaxValuesCount>
324       virtualAttributesThatExceededMaxValuesCount;
325
326  // The names of user attributes excluded by access control and/or sensitive
327  // attribute processing.
328  private final List<String> excludedUserAttributeNames;
329
330  // The names of operational attributes excluded by access control and/or
331  // sensitive attribute processing.
332  private final List<String> excludedOperationalAttributeNames;
333
334  // The entryUUID values for the notification destinations matched by the
335  // change.
336  private final List<String> notificationDestinationEntryUUIDs;
337
338  // The values of any notification properties for the change.
339  private final List<String> notificationProperties;
340
341  // The names of the attributes targeted by the change.
342  private final List<String> targetAttributeNames;
343
344  // The local change sequence number for the change.
345  private final String localCSN;
346
347  // The DN of the soft-deleted entry resulting from a soft delete operation.
348  private final String softDeleteToDN;
349
350  // The entryUUID value for the target entry.
351  private final String targetUniqueID;
352
353  // The DN of the soft-deleted entry from which the content of an undelete
354  // operation was created.
355  private final String undeleteFromDN;
356
357
358
359  /**
360   * Creates a new UnboundID changelog entry object from the provided entry.
361   *
362   * @param  entry  The entry from which to create this changelog entry.
363   *
364   * @throws  LDAPException  If the provided entry cannot be parsed as a
365   *                         changelog entry.
366   */
367  public UnboundIDChangeLogEntry(final Entry entry)
368         throws LDAPException
369  {
370    super(entry);
371
372    final String targetDN = entry.getAttributeValue(ATTR_TARGET_DN);
373
374    targetUniqueID = entry.getAttributeValue(ATTR_TARGET_UNIQUE_ID);
375    localCSN       = entry.getAttributeValue(ATTR_LOCAL_CSN);
376    changeTime     = entry.getAttributeValueAsDate(ATTR_CHANGE_TIME);
377    softDeleteToDN = entry.getAttributeValue(ATTR_SOFT_DELETE_TO_DN);
378    undeleteFromDN = entry.getAttributeValue(ATTR_UNDELETE_FROM_DN);
379
380    changeToSoftDeletedEntry =
381         entry.getAttributeValueAsBoolean(ATTR_CHANGE_TO_SOFT_DELETED_ENTRY);
382
383    if (entry.hasAttribute(ATTR_VIRTUAL_ATTRS))
384    {
385      entryVirtualAttributes = parseAddAttributeList(entry, ATTR_VIRTUAL_ATTRS,
386           targetDN);
387    }
388    else
389    {
390      entryVirtualAttributes = Collections.emptyList();
391    }
392
393    if (entry.hasAttribute(ATTR_BEFORE_VALUES))
394    {
395      updatedAttributesBeforeChange = parseAddAttributeList(entry,
396           ATTR_BEFORE_VALUES, targetDN);
397    }
398    else
399    {
400      updatedAttributesBeforeChange = Collections.emptyList();
401    }
402
403    if (entry.hasAttribute(ATTR_BEFORE_VIRTUAL_VALUES))
404    {
405      updatedVirtualAttributesBeforeChange = parseAddAttributeList(entry,
406           ATTR_BEFORE_VIRTUAL_VALUES, targetDN);
407    }
408    else
409    {
410      updatedVirtualAttributesBeforeChange = Collections.emptyList();
411    }
412
413    if (entry.hasAttribute(ATTR_AFTER_VALUES))
414    {
415      updatedAttributesAfterChange = parseAddAttributeList(entry,
416           ATTR_AFTER_VALUES, targetDN);
417    }
418    else
419    {
420      updatedAttributesAfterChange = Collections.emptyList();
421    }
422
423    if (entry.hasAttribute(ATTR_AFTER_VIRTUAL_VALUES))
424    {
425      updatedVirtualAttributesAfterChange = parseAddAttributeList(entry,
426           ATTR_AFTER_VIRTUAL_VALUES, targetDN);
427    }
428    else
429    {
430      updatedVirtualAttributesAfterChange = Collections.emptyList();
431    }
432
433    if (entry.hasAttribute(ATTR_KEY_VALUES))
434    {
435      keyEntryAttributes =
436           parseAddAttributeList(entry, ATTR_KEY_VALUES, targetDN);
437    }
438    else
439    {
440      keyEntryAttributes = Collections.emptyList();
441    }
442
443    if (entry.hasAttribute(ATTR_KEY_VIRTUAL_VALUES))
444    {
445      keyEntryVirtualAttributes =
446           parseAddAttributeList(entry, ATTR_KEY_VIRTUAL_VALUES, targetDN);
447    }
448    else
449    {
450      keyEntryVirtualAttributes = Collections.emptyList();
451    }
452
453    final Attribute exceededMaxValues =
454         entry.getAttribute(ATTR_EXCEEDED_MAX_VALUES);
455    if (exceededMaxValues == null)
456    {
457      attributesThatExceededMaxValuesCount = Collections.emptyList();
458    }
459    else
460    {
461      final String[] values = exceededMaxValues.getValues();
462      final ArrayList<ChangeLogEntryAttributeExceededMaxValuesCount> l =
463           new ArrayList<ChangeLogEntryAttributeExceededMaxValuesCount>(
464                values.length);
465      for (final String value : values)
466      {
467        l.add(new ChangeLogEntryAttributeExceededMaxValuesCount(value));
468      }
469      attributesThatExceededMaxValuesCount = Collections.unmodifiableList(l);
470    }
471
472    final Attribute virtualExceededMaxValues =
473         entry.getAttribute(ATTR_VIRTUAL_EXCEEDED_MAX_VALUES);
474    if (virtualExceededMaxValues == null)
475    {
476      virtualAttributesThatExceededMaxValuesCount = Collections.emptyList();
477    }
478    else
479    {
480      final String[] values = virtualExceededMaxValues.getValues();
481      final ArrayList<ChangeLogEntryAttributeExceededMaxValuesCount> l =
482           new ArrayList<ChangeLogEntryAttributeExceededMaxValuesCount>(
483                values.length);
484      for (final String value : values)
485      {
486        l.add(new ChangeLogEntryAttributeExceededMaxValuesCount(value));
487      }
488      virtualAttributesThatExceededMaxValuesCount =
489           Collections.unmodifiableList(l);
490    }
491
492    numExcludedUserAttributes =
493         entry.getAttributeValueAsInteger(ATTR_EXCLUDED_USER_ATTR_COUNT);
494    numExcludedOperationalAttributes =
495         entry.getAttributeValueAsInteger(ATTR_EXCLUDED_OPERATIONAL_ATTR_COUNT);
496
497    final String[] excludedUserAttrNames =
498         entry.getAttributeValues(ATTR_EXCLUDED_USER_ATTR_NAME);
499    if (excludedUserAttrNames == null)
500    {
501      excludedUserAttributeNames = Collections.emptyList();
502    }
503    else
504    {
505      excludedUserAttributeNames = Collections.unmodifiableList(
506           new ArrayList<String>(Arrays.asList(excludedUserAttrNames)));
507    }
508
509    final String[] excludedOpAttrNames =
510         entry.getAttributeValues(ATTR_EXCLUDED_OPERATIONAL_ATTR_NAME);
511    if (excludedOpAttrNames == null)
512    {
513      excludedOperationalAttributeNames = Collections.emptyList();
514    }
515    else
516    {
517      excludedOperationalAttributeNames = Collections.unmodifiableList(
518           new ArrayList<String>(Arrays.asList(excludedOpAttrNames)));
519    }
520
521    final String[] targetAttrNames =
522         entry.getAttributeValues(ATTR_TARGET_ATTRIBUTE);
523    if (targetAttrNames == null)
524    {
525      targetAttributeNames = Collections.emptyList();
526    }
527    else
528    {
529      targetAttributeNames = Collections.unmodifiableList(
530           new ArrayList<String>(Arrays.asList(targetAttrNames)));
531    }
532
533    final String[] notificationUUIDValues =
534         entry.getAttributeValues(ATTR_NOTIFICATION_DESTINATION_ENTRY_UUID);
535    if (notificationUUIDValues == null)
536    {
537      notificationDestinationEntryUUIDs = Collections.emptyList();
538    }
539    else
540    {
541      notificationDestinationEntryUUIDs = Collections.unmodifiableList(
542           new ArrayList<String>(Arrays.asList(notificationUUIDValues)));
543    }
544
545    final String[] notificationPropertyValues =
546         entry.getAttributeValues(ATTR_NOTIFICATION_PROPERTIES);
547    if (notificationPropertyValues == null)
548    {
549      notificationProperties = Collections.emptyList();
550    }
551    else
552    {
553      notificationProperties = Collections.unmodifiableList(
554           new ArrayList<String>(Arrays.asList(notificationPropertyValues)));
555    }
556  }
557
558
559
560  /**
561   * Retrieves the entryUUID value of the entry targeted by the change, if
562   * available.
563   *
564   * @return  The entryUUID value of the entry targeted by the change, or
565   *          {@code null} if it was not included in the changelog entry.
566   */
567  public String getTargetUniqueID()
568  {
569    return targetUniqueID;
570  }
571
572
573
574  /**
575   * Retrieves the local change sequence number (CSN) for the change, if
576   * available.
577   *
578   * @return  The local CSN for the change, or {@code null} if it was not
579   *          included in the changelog entry.
580   */
581  public String getLocalCSN()
582  {
583    return localCSN;
584  }
585
586
587
588  /**
589   * Retrieves the time that the change was processed, if available.
590   *
591   * @return  The time that the change was processed, or {@code null} if it was
592   *           not included in the changelog entry.
593   */
594  public Date getChangeTime()
595  {
596    return changeTime;
597  }
598
599
600
601  /**
602   * Retrieves the attribute list for an add changelog entry, optionally
603   * including information about virtual attributes.
604   *
605   * @param  includeVirtual  Indicates whether to include both real and virtual
606   *                         values (if {@code true}, or only real values (if
607   *                         {@code false}), for the attributes to be returned.
608   *
609   * @return  The attribute list for an add changelog entry, optionally
610   *          including virtual attributes, or {@code null} if this changelog
611   *          entry does not represent an add operation.
612   */
613  public List<Attribute> getAddAttributes(final boolean includeVirtual)
614  {
615    if (includeVirtual && (getChangeType() == ChangeType.ADD) &&
616         (! entryVirtualAttributes.isEmpty()))
617    {
618      final Entry e = new Entry(getTargetDN(), getAddAttributes());
619      for (final Attribute a : entryVirtualAttributes)
620      {
621        e.addAttribute(a);
622      }
623
624      return Collections.unmodifiableList(
625           new ArrayList<Attribute>(e.getAttributes()));
626    }
627    else
628    {
629      return getAddAttributes();
630    }
631  }
632
633
634
635  /**
636   * Retrieves the virtual attribute list for an add changelog entry, if
637   * available.
638   *
639   * @return  The virtual attribute list for an add changelog entry, or
640   *           {@code null} if the changelog entry does not represent an add
641   *           operation, or an empty list if it does represent an add operation
642   *           but no virtual attribute information is available in the
643   *           changelog entry.
644   */
645  public List<Attribute> getAddVirtualAttributes()
646  {
647    if (getChangeType() == ChangeType.ADD)
648    {
649      return entryVirtualAttributes;
650    }
651    else
652    {
653      return null;
654    }
655  }
656
657
658
659  /**
660   * Retrieves the list of attributes contained in the target entry at the time
661   * that it was deleted, optionally including information about virtual
662   * attributes.
663   *
664   * @param  includeVirtual  Indicates whether to include both real and virtual
665   *                         values (if {@code true}, or only real values (if
666   *                         {@code false}), for the attributes to be returned.
667   *
668   * @return  The list of attributes contained in the target entry at the time
669   *           that it was deleted, optionally including virtual attributes, or
670   *           {@code null} if this changelog entry does not represent a delete
671   *           operation or no deleted attribute information is available.
672   */
673  public List<Attribute> getDeletedEntryAttributes(
674       final boolean includeVirtual)
675  {
676    if (includeVirtual && (getChangeType() == ChangeType.DELETE) &&
677         (! entryVirtualAttributes.isEmpty()))
678    {
679      final Entry e;
680      final List<Attribute> realAttrs = getDeletedEntryAttributes();
681      if (realAttrs != null)
682      {
683        e = new Entry(getTargetDN(), realAttrs);
684        for (final Attribute a : entryVirtualAttributes)
685        {
686          e.addAttribute(a);
687        }
688      }
689      else
690      {
691        e = new Entry(getTargetDN(), entryVirtualAttributes);
692      }
693
694      return Collections.unmodifiableList(
695           new ArrayList<Attribute>(e.getAttributes()));
696    }
697    else
698    {
699      return getDeletedEntryAttributes();
700    }
701  }
702
703
704
705  /**
706   * Retrieves the virtual attribute list for a delete changelog entry, if
707   * available.
708   *
709   * @return  The virtual attribute list for a delete changelog entry, or
710   *          {@code null} if the changelog entry does not represent a delete
711   *          operation, or an empty list if it does represent a delete
712   *          operation but no virtual attribute information is available in the
713   *          changelog entry.
714   */
715  public List<Attribute> getDeletedEntryVirtualAttributes()
716  {
717    if (getChangeType() == ChangeType.DELETE)
718    {
719      return entryVirtualAttributes;
720    }
721    else
722    {
723      return null;
724    }
725  }
726
727
728
729  /**
730   * Retrieves a list containing the set of attributes that were updated in the
731   * associated modify or modify DN operation as they appeared before the change
732   * was processed.  Virtual attribute information will not be included.
733   *
734   * @return  A list containing the set of updated attributes as they appeared
735   *          in the entry before the associated modify or modify DN was
736   *          processed, or an empty list if the change was not a modify or
737   *          modify DN operation, none of the updated attributes previously
738   *          existed in the target entry, the previous versions of the updated
739   *          attributes had too many values to include, or the server is not
740   *          configured to provide (or does not support providing) previous
741   *          versions of updated attributes.
742   */
743  public List<Attribute> getUpdatedAttributesBeforeChange()
744  {
745    return updatedAttributesBeforeChange;
746  }
747
748
749
750  /**
751   * Retrieves a list containing the set of attributes (optionally including
752   * both real and virtual values) that were updated in the associated modify or
753   * modify DN operation as they appeared before the change was processed.
754   *
755   * @param  includeVirtual  Indicates whether to include both real and virtual
756   *                         values (if {@code true}, or only real values (if
757   *                         {@code false}), for the attributes to be returned.
758   *
759   * @return  A list containing the set of updated attributes as they appeared
760   *          in the entry before the associated modify or modify DN was
761   *          processed, or an empty list if the change was not a modify or
762   *          modify DN operation, none of the updated attributes previously
763   *          existed in the target entry, the previous versions of the updated
764   *          attributes had too many values to include, or the server is not
765   *          configured to provide (or does not support providing) previous
766   *          versions of updated attributes.
767   */
768  public List<Attribute> getUpdatedAttributesBeforeChange(
769                              final boolean includeVirtual)
770  {
771    if (includeVirtual && (! updatedVirtualAttributesBeforeChange.isEmpty()))
772    {
773      final Entry e = new Entry(getTargetDN(), updatedAttributesBeforeChange);
774      for (final Attribute a : updatedVirtualAttributesBeforeChange)
775      {
776        e.addAttribute(a);
777      }
778
779      return Collections.unmodifiableList(
780           new ArrayList<Attribute>(e.getAttributes()));
781    }
782    else
783    {
784      return updatedAttributesBeforeChange;
785    }
786  }
787
788
789
790  /**
791   * Retrieves a list containing information about virtual values for attributes
792   * that were updated in the associated modify or modify DN operation, as they
793   * appeared in the entry before the change was processed.
794   *
795   * @return  A list containing information about virtual values for attributes
796   *          that were updated in the associated modify or modify DN operation,
797   *          as they appeared in the entry before the change was processed.  It
798   *          may be empty if the change was not a modify or modify DN
799   *          operation, or if the changelog entry did not include any
800   *          information about virtual attributes as they appeared before the
801   *          change.
802   */
803  public List<Attribute> getUpdatedVirtualAttributesBeforeChange()
804  {
805    return updatedVirtualAttributesBeforeChange;
806  }
807
808
809
810  /**
811   * Retrieves a list containing the set of attributes that were updated in the
812   * associated modify or modify DN operation as they appeared after the change
813   * was processed.  Virtual attribute information will not be included.
814   *
815   * @return  A list containing the set of updated attributes as they appeared
816   *          in the entry after the associated modify or modify DN was
817   *          processed, or an empty list if the change was not a modify or
818   *          modify DN operation, none of the updated attributes existed in the
819   *          entry after the change was processed, the resulting versions of
820   *          the updated attributes had too many values to include, or the
821   *          server is not configured to provide (or does not support
822   *          providing) resulting versions of updated attributes.
823   */
824  public List<Attribute> getUpdatedAttributesAfterChange()
825  {
826    return updatedAttributesAfterChange;
827  }
828
829
830
831  /**
832   * Retrieves a list containing the set of attributes (optionally including
833   * both real and virtual values) that were updated in the associated modify or
834   * modify DN operation as they appeared after the change was processed.
835   *
836   * @param  includeVirtual  Indicates whether to include both real and virtual
837   *                         values (if {@code true}, or only real values (if
838   *                         {@code false}), for the attributes to be returned.
839   *
840   * @return  A list containing the set of updated attributes as they appeared
841   *          in the entry after the associated modify or modify DN was
842   *          processed, or an empty list if the change was not a modify or
843   *          modify DN operation, none of the updated attributes previously
844   *          existed in the target entry, the previous versions of the updated
845   *          attributes had too many values to include, or the server is not
846   *          configured to provide (or does not support providing) previous
847   *          versions of updated attributes.
848   */
849  public List<Attribute> getUpdatedAttributesAfterChange(
850                              final boolean includeVirtual)
851  {
852    if (includeVirtual && (! updatedVirtualAttributesAfterChange.isEmpty()))
853    {
854      final Entry e = new Entry(getTargetDN(), updatedAttributesAfterChange);
855      for (final Attribute a : updatedVirtualAttributesAfterChange)
856      {
857        e.addAttribute(a);
858      }
859
860      return Collections.unmodifiableList(
861           new ArrayList<Attribute>(e.getAttributes()));
862    }
863    else
864    {
865      return updatedAttributesAfterChange;
866    }
867  }
868
869
870
871  /**
872   * Retrieves a list containing information about virtual values for attributes
873   * that were updated in the associated modify or modify DN operation, as they
874   * appeared in the entry after the change was processed.
875   *
876   * @return  A list containing information about virtual values for attributes
877   *          that were updated in the associated modify or modify DN operation,
878   *          as they appeared in the entry after the change was processed.  It
879   *          may be empty if the change was not a modify or modify DN
880   *          operation, or if the changelog entry did not include any
881   *          information about virtual attributes as they appeared after the
882   *          change.
883   */
884  public List<Attribute> getUpdatedVirtualAttributesAfterChange()
885  {
886    return updatedVirtualAttributesAfterChange;
887  }
888
889
890
891  /**
892   * Retrieves information about any attributes updated in the associated modify
893   * or modify DN operation that had too many values to include in the changelog
894   * entry's set of before and/or after values.
895   *
896   * @return  Information about attributes updated in the associated modify or
897   *          modify DN operation that had too many values to include in the
898   *          changelog entry's set of before and/or after values, or an empty
899   *          list if none of the updated attributes had too many values, the
900   *          server is not configured to provide (or does not support
901   *          providing) previous and resulting versions of updated attributes,
902   *          or the change was not the result of a modify or modify DN
903   *          operation.
904   */
905  public List<ChangeLogEntryAttributeExceededMaxValuesCount>
906              getAttributesThatExceededMaxValuesCount()
907  {
908    return attributesThatExceededMaxValuesCount;
909  }
910
911
912
913  /**
914   * Retrieves information about any attributes updated in the associated modify
915   * or modify DN operation that had too many virtual values to include in the
916   * changelog entry's set of before and/or after virtual values.
917   *
918   * @return  Information about attributes updated in the associated modify or
919   *          modify DN operation that had too many virtual values to include in
920   *          the changelog entry's set of before and/or after virtual values,
921   *          or an empty list if none of the updated attributes had too many
922   *          virtual values, the server is not configured to provide (or does
923   *          not support providing) previous and resulting versions of updated
924   *          attributes, or the change was not the result of a modify or modify
925   *          DN operation.
926   */
927  public List<ChangeLogEntryAttributeExceededMaxValuesCount>
928              getVirtualAttributesThatExceededMaxValuesCount()
929  {
930    return virtualAttributesThatExceededMaxValuesCount;
931  }
932
933
934
935  /**
936   * Retrieves a list containing key attributes from the target entry, as
937   * defined in the server configuration.  For add, modify, and modify DN
938   * operations, this will include the key attributes as they appeared in the
939   * entry after the change had been processed.  For delete operations, this
940   * will include the key attributes as they appeared in the entry just before
941   * it was removed.
942   *
943   * @return  A list containing key attributes from the target entry, or an
944   *          empty list if the associated entry did not have any key attributes
945   *          or there are no key attribute types defined in the server
946   *          configuration.
947   */
948  public List<Attribute> getKeyEntryAttributes()
949  {
950    return keyEntryAttributes;
951  }
952
953
954
955  /**
956   * Retrieves a list containing key attributes from the target entry, as
957   * defined in the server configuration.  For add, modify, and modify DN
958   * operations, this will include the key attributes as they appeared in the
959   * entry after the change had been processed.  For delete operations, this
960   * will include the key attributes as they appeared in the entry just before
961   * it was removed.
962   *
963   * @param  includeVirtual  Indicates whether to include both real and virtual
964   *                         values (if {@code true}, or only real values (if
965   *                         {@code false}), for the attributes to be returned.
966   *
967   * @return  A list containing key attributes from the target entry, or an
968   *          empty list if the associated entry did not have any key attributes
969   *          or there are no key attribute types defined in the server
970   *          configuration.
971   */
972  public List<Attribute> getKeyEntryAttributes(final boolean includeVirtual)
973  {
974    if (includeVirtual && (! keyEntryVirtualAttributes.isEmpty()))
975    {
976      final Entry e = new Entry(getTargetDN(), keyEntryAttributes);
977      for (final Attribute a : keyEntryVirtualAttributes)
978      {
979        e.addAttribute(a);
980      }
981
982      return Collections.unmodifiableList(
983           new ArrayList<Attribute>(e.getAttributes()));
984    }
985    else
986    {
987      return keyEntryAttributes;
988    }
989  }
990
991
992
993  /**
994   * Retrieves a list containing virtual values for key attributes from the
995   * target entry, as defined in the server configuration.  For add, modify, and
996   * modify DN operations, this will include the virtual values for key
997   * attributes as they appeared in the entry after the change had been
998   * processed.  For delete operations, this will include the virtual values for
999   * key attributes as they appeared in the entry just before it was removed.
1000   *
1001   * @return  A list containing virtual values for key attributes from the
1002   *          target entry, or an empty list if the associated entry did not
1003   *          have any virtual values for key attributes or there are no key
1004   *          attribute types defined in the server configuration.
1005   */
1006  public List<Attribute> getKeyEntryVirtualAttributes()
1007  {
1008    return keyEntryVirtualAttributes;
1009  }
1010
1011
1012
1013  /**
1014   * Retrieves the number of user attributes for which information was excluded
1015   * from the changelog entry by access control and/or sensitive attribute
1016   * processing, if available.
1017   *
1018   * @return  The number of user attributes for which information was excluded
1019   *          from the changelog entry by access control and/or sensitive
1020   *          attribute processing, or -1 if that information was not included
1021   *          in the changelog entry.
1022   */
1023  public int getNumExcludedUserAttributes()
1024  {
1025    if (numExcludedUserAttributes == null)
1026    {
1027      return -1;
1028    }
1029    else
1030    {
1031      return numExcludedUserAttributes;
1032    }
1033  }
1034
1035
1036
1037  /**
1038   * Retrieves the number of operational attributes for which information was
1039   * excluded from the changelog entry by access control and/or sensitive
1040   * attribute processing, if available.
1041   *
1042   * @return  The number of operational attributes for which information was
1043   *          excluded from the changelog entry by access control and/or
1044   *          sensitive attribute processing, or -1 if that information was not
1045   *          included in the changelog entry.
1046   */
1047  public int getNumExcludedOperationalAttributes()
1048  {
1049    if (numExcludedOperationalAttributes == null)
1050    {
1051      return -1;
1052    }
1053    else
1054    {
1055      return numExcludedOperationalAttributes;
1056    }
1057  }
1058
1059
1060
1061  /**
1062   * Retrieves the names of any user attributes for which information was
1063   * excluded from the changelog entry by access control and/or sensitive
1064   * attribute processing, if available.
1065   *
1066   * @return  The names of any user attributes for which information was
1067   *          excluded from the changelog entry by access control and/or
1068   *          sensitive attribute processing, or an empty list if that
1069   *          information was not included in the changelog entry.
1070   */
1071  public List<String> getExcludedUserAttributeNames()
1072  {
1073    return excludedUserAttributeNames;
1074  }
1075
1076
1077
1078  /**
1079   * Retrieves the names of any operational attributes for which information was
1080   * excluded from the changelog entry by access control and/or sensitive
1081   * attribute processing, if available.
1082   *
1083   * @return  The names of any operational attributes for which information was
1084   *          excluded from the changelog entry by access control and/or
1085   *          sensitive processing, or an empty list if that information was not
1086   *          included in the changelog entry.
1087   */
1088  public List<String> getExcludedOperationalAttributeNames()
1089  {
1090    return excludedOperationalAttributeNames;
1091  }
1092
1093
1094
1095  /**
1096   * Indicates whether the associated modify or delete operation targeted a
1097   * soft-deleted entry.
1098   *
1099   * @return  {@code true} if the modify or delete operation targeted a
1100   *          soft-deleted entry, {@code false} if not, or {@code null} if that
1101   *          information was not included in the changelog entry (which likely
1102   *          indicates that the operation did not target a soft-deleted
1103   *          entry).
1104   */
1105  public Boolean getChangeToSoftDeletedEntry()
1106  {
1107    return changeToSoftDeletedEntry;
1108  }
1109
1110
1111
1112  /**
1113   * Retrieves the DN of the soft-deleted entry that resulted from the
1114   * associated soft delete operation.
1115   *
1116   * @return  The DN of the soft-deleted entry that resulted from the associated
1117   *          soft delete operation, or {@code null} if that information was not
1118   *          included in the changelog entry (e.g., because it does not
1119   *          represent a soft delete operation).
1120   */
1121  public String getSoftDeleteToDN()
1122  {
1123    return softDeleteToDN;
1124  }
1125
1126
1127
1128  /**
1129   * Retrieves the DN of the soft-deleted entry from which the content of an add
1130   * operation was obtained, if that operation represents an undelete rather
1131   * than a normal add.
1132   *
1133   * @return  The DN of the soft-deleted entry from which the content of an add
1134   *          operation was obtained, or {@code null} if that information was
1135   *          not included in the changelog entry (e.g., because it does not
1136   *          represent an undelete operation).
1137   */
1138  public String getUndeleteFromDN()
1139  {
1140    return undeleteFromDN;
1141  }
1142
1143
1144
1145  /**
1146   * Retrieves the names of any attributes targeted by the change, if available.
1147   * For an add operation, this may include the attributes in the entry that
1148   * was added.  For a delete operation, this may include the attributes in the
1149   * entry that was deleted.  For a modify operation, this may include the
1150   * attributes targeted by modifications.  For a modify DN operation, this may
1151   * include attributes used in the new RDN and potentially any other attributes
1152   * altered during the change.
1153   * <BR><BR>
1154   * Note that this information may not be available in all changelog entries or
1155   * Directory Server versions, and complete information about some changes may
1156   * only be available in some changelog configurations (e.g., information about
1157   * attributes included in delete operations may only be available if
1158   * changelog-deleted-entry-include-attribute is configured, and information
1159   * about changes to non-RDN attributes for modify DN operations may only be
1160   * available if changelog-max-before-after-values is configured).
1161   *
1162   * @return  The names of any attributes targeted by the change, or an empty
1163   *          list if that information was not included in the changelog entry.
1164   */
1165  public List<String> getTargetAttributeNames()
1166  {
1167    return targetAttributeNames;
1168  }
1169
1170
1171
1172  /**
1173   * Retrieves a list of the entryUUID values for any notification destinations
1174   * for which the change matches one or more subscriptions.
1175   *
1176   * @return  A list of the entryUUID values for any notification destinations
1177   *          for which the change matches one or more subscriptions, or an
1178   *          empty list if that information was not included in the changelog
1179   *          entry.
1180   */
1181  public List<String> getNotificationDestinationEntryUUIDs()
1182  {
1183    return notificationDestinationEntryUUIDs;
1184  }
1185
1186
1187
1188  /**
1189   * Retrieves a list of any notification properties included in the changelog
1190   * entry.
1191   *
1192   * @return  A list of any notification properties included in the changelog
1193   *          entry, or an empty list if that information was not included in
1194   *          the changelog entry.
1195   */
1196  public List<String> getNotificationProperties()
1197  {
1198    return notificationProperties;
1199  }
1200
1201
1202
1203  /**
1204   * Retrieves the specified attribute as it appeared in the target entry before
1205   * the change was processed, if available.  It will not include any virtual
1206   * values.
1207   *
1208   * @param  name  The name of the attribute to retrieve as it appeared before
1209   *               the change.
1210   *
1211   * @return  The requested attribute as it appeared in the target entry before
1212   *          the change was processed, or {@code null} if it was not available
1213   *          in the changelog entry.
1214   *
1215   * @throws  ChangeLogEntryAttributeExceededMaxValuesException  If the
1216   *               specified attribute had more values before the change than
1217   *               may be included in a changelog entry.
1218   */
1219  public Attribute getAttributeBeforeChange(final String name)
1220         throws ChangeLogEntryAttributeExceededMaxValuesException
1221  {
1222    return getAttributeBeforeChange(name, false);
1223  }
1224
1225
1226
1227  /**
1228   * Retrieves the specified attribute as it appeared in the target entry before
1229   * the change was processed, if available.  It may optionally include virtual
1230   * values.
1231   *
1232   * @param  name            The name of the attribute to retrieve as it
1233   *                         appeared before the change.
1234   * @param  includeVirtual  Indicates whether to include both real and virtual
1235   *                         values (if {@code true}, or only real values (if
1236   *                         {@code false}), for the attribute to be returned.
1237   *
1238   * @return  The requested attribute as it appeared in the target entry before
1239   *          the change was processed, or {@code null} if it was not available
1240   *          in the changelog entry.
1241   *
1242   * @throws  ChangeLogEntryAttributeExceededMaxValuesException  If the
1243   *               specified attribute had more values before the change than
1244   *               may be included in a changelog entry.
1245   */
1246  public Attribute getAttributeBeforeChange(final String name,
1247                                            final boolean includeVirtual)
1248         throws ChangeLogEntryAttributeExceededMaxValuesException
1249  {
1250    if (getChangeType() == ChangeType.ADD)
1251    {
1252      return null;
1253    }
1254
1255    for (final Attribute a : getUpdatedAttributesBeforeChange(includeVirtual))
1256    {
1257      if (a.getName().equalsIgnoreCase(name))
1258      {
1259        return a;
1260      }
1261    }
1262
1263    for (final ChangeLogEntryAttributeExceededMaxValuesCount a :
1264         attributesThatExceededMaxValuesCount)
1265    {
1266      if (a.getAttributeName().equalsIgnoreCase(name))
1267      {
1268        // TODO:  In the event that the before count was exceeded but the after
1269        // count was not, then we may be able to reconstruct the before values
1270        // if the changes included deleting specific values for the attribute.
1271        throw new ChangeLogEntryAttributeExceededMaxValuesException(
1272             ERR_CHANGELOG_EXCEEDED_BEFORE_VALUE_COUNT.get(name, getTargetDN(),
1273                  a.getBeforeCount()),
1274             a);
1275      }
1276    }
1277
1278    if (includeVirtual)
1279    {
1280      for (final ChangeLogEntryAttributeExceededMaxValuesCount a :
1281           virtualAttributesThatExceededMaxValuesCount)
1282      {
1283        if (a.getAttributeName().equalsIgnoreCase(name))
1284        {
1285          // TODO:  In the event that the before count was exceeded but the
1286          // after count was not, then we may be able to reconstruct the before
1287          // values if the changes included deleting specific values for the
1288          // attribute.
1289          throw new ChangeLogEntryAttributeExceededMaxValuesException(
1290               ERR_CHANGELOG_EXCEEDED_VIRTUAL_BEFORE_VALUE_COUNT.get(name,
1291                    getTargetDN(), a.getBeforeCount()),
1292               a);
1293        }
1294      }
1295    }
1296
1297    for (final Attribute a : getKeyEntryAttributes(includeVirtual))
1298    {
1299      if (a.getName().equalsIgnoreCase(name))
1300      {
1301        return a;
1302      }
1303    }
1304
1305    final List<Attribute> deletedAttrs =
1306         getDeletedEntryAttributes(includeVirtual);
1307    if (deletedAttrs != null)
1308    {
1309      for (final Attribute a : deletedAttrs)
1310      {
1311        if (a.getName().equalsIgnoreCase(name))
1312        {
1313          return a;
1314        }
1315      }
1316    }
1317
1318    return null;
1319  }
1320
1321
1322
1323  /**
1324   * Retrieves the specified attribute as it appeared in the target entry after
1325   * the change was processed, if available.    It will not include any virtual
1326   * values.
1327   *
1328   * @param  name  The name of the attribute to retrieve as it appeared after
1329   *               the change.
1330   *
1331   * @return  The requested attribute as it appeared in the target entry after
1332   *          the change was processed, or {@code null} if it was not available
1333   *          in the changelog entry.
1334   *
1335   * @throws  ChangeLogEntryAttributeExceededMaxValuesException  If the
1336   *               specified attribute had more values before the change than
1337   *               may be included in a changelog entry.
1338   */
1339  public Attribute getAttributeAfterChange(final String name)
1340         throws ChangeLogEntryAttributeExceededMaxValuesException
1341  {
1342    return getAttributeAfterChange(name, false);
1343  }
1344
1345
1346
1347  /**
1348   * Retrieves the specified attribute as it appeared in the target entry after
1349   * the change was processed, if available.  It may optionally include virtual
1350   * values.
1351   *
1352   * @param  name            The name of the attribute to retrieve as it
1353   *                         appeared after the change.
1354   * @param  includeVirtual  Indicates whether to include both real and virtual
1355   *                         values (if {@code true}, or only real values (if
1356   *                         {@code false}), for the attributes to be returned.
1357   *
1358   * @return  The requested attribute as it appeared in the target entry after
1359   *          the change was processed, or {@code null} if it was not available
1360   *          in the changelog entry.
1361   *
1362   * @throws  ChangeLogEntryAttributeExceededMaxValuesException  If the
1363   *               specified attribute had more values before the change than
1364   *               may be included in a changelog entry.
1365   */
1366  public Attribute getAttributeAfterChange(final String name,
1367                                           final boolean includeVirtual)
1368         throws ChangeLogEntryAttributeExceededMaxValuesException
1369  {
1370    if (getChangeType() == ChangeType.DELETE)
1371    {
1372      return null;
1373    }
1374
1375    for (final Attribute a : getUpdatedAttributesAfterChange(includeVirtual))
1376    {
1377      if (a.getName().equalsIgnoreCase(name))
1378      {
1379        return a;
1380      }
1381    }
1382
1383    for (final Attribute a : getKeyEntryAttributes(includeVirtual))
1384    {
1385      if (a.getName().equalsIgnoreCase(name))
1386      {
1387        return a;
1388      }
1389    }
1390
1391    for (final ChangeLogEntryAttributeExceededMaxValuesCount a :
1392         attributesThatExceededMaxValuesCount)
1393    {
1394      if (a.getAttributeName().equalsIgnoreCase(name))
1395      {
1396        // TODO:  In the event that the after count was exceeded but the before
1397        // count was not, then we may be able to reconstruct the after values
1398        // if the changes included adding specific values for the attribute.
1399        throw new ChangeLogEntryAttributeExceededMaxValuesException(
1400             ERR_CHANGELOG_EXCEEDED_AFTER_VALUE_COUNT.get(name, getTargetDN(),
1401                  a.getAfterCount()),
1402             a);
1403      }
1404    }
1405
1406    if (includeVirtual)
1407    {
1408      for (final ChangeLogEntryAttributeExceededMaxValuesCount a :
1409           virtualAttributesThatExceededMaxValuesCount)
1410      {
1411        if (a.getAttributeName().equalsIgnoreCase(name))
1412        {
1413          // TODO:  In the event that the after count was exceeded but the
1414          // before count was not, then we may be able to reconstruct the after
1415          // values if the changes included adding specific values for the
1416          // attribute.
1417          throw new ChangeLogEntryAttributeExceededMaxValuesException(
1418               ERR_CHANGELOG_EXCEEDED_VIRTUAL_AFTER_VALUE_COUNT.get(name,
1419                    getTargetDN(), a.getAfterCount()),
1420               a);
1421        }
1422      }
1423    }
1424
1425    final List<Attribute> addAttrs = getAddAttributes(includeVirtual);
1426    if (addAttrs != null)
1427    {
1428      for (final Attribute a : addAttrs)
1429      {
1430        if (a.getName().equalsIgnoreCase(name))
1431        {
1432          return a;
1433        }
1434      }
1435    }
1436
1437    final List<Modification> mods = getModifications();
1438    if (mods != null)
1439    {
1440      for (final Modification m : mods)
1441      {
1442        if (m.getAttributeName().equalsIgnoreCase(name))
1443        {
1444          final byte[][] values = m.getValueByteArrays();
1445          if ((m.getModificationType() == ModificationType.REPLACE) &&
1446              (values.length > 0))
1447          {
1448            return new Attribute(name, values);
1449          }
1450        }
1451      }
1452    }
1453
1454    return null;
1455  }
1456
1457
1458
1459  /**
1460   * Attempts to construct a partial representation of the target entry as it
1461   * appeared before the change was processed.  The information contained in the
1462   * constructed entry will be based solely on information contained in the
1463   * changelog entry, including information provided in the deletedEntryAttrs,
1464   * ds-changelog-before-values, ds-changelog-after-values,
1465   * ds-changelog-entry-key-attr-values, and
1466   * ds-changelog-attr-exceeded-max-values-count attributes.  It will not
1467   * include any virtual attribute information.
1468   *
1469   * @return  A partial representation of the target entry as it appeared before
1470   *          the change was processed, or {@code null} if the change was an
1471   *          add operation and therefore the entry did not exist before the
1472   *          change.
1473   */
1474  public ReadOnlyEntry constructPartialEntryBeforeChange()
1475  {
1476    return constructPartialEntryBeforeChange(false);
1477  }
1478
1479
1480
1481  /**
1482   * Attempts to construct a partial representation of the target entry as it
1483   * appeared before the change was processed.  The information contained in the
1484   * constructed entry will be based solely on information contained in the
1485   * changelog entry, including information provided in the deletedEntryAttrs,
1486   * ds-changelog-before-values, ds-changelog-after-values,
1487   * ds-changelog-entry-key-attr-values, and
1488   * ds-changelog-attr-exceeded-max-values-count attributes, and optionally
1489   * virtual versions of all of those elements.
1490   *
1491   * @param  includeVirtual  Indicates whether to include both real and virtual
1492   *                         values (if {@code true}, or only real values (if
1493   *                         {@code false}), for the attributes to be returned.
1494   *
1495   * @return  A partial representation of the target entry as it appeared before
1496   *          the change was processed, or {@code null} if the change was an
1497   *          add operation and therefore the entry did not exist before the
1498   *          change.
1499   */
1500  public ReadOnlyEntry constructPartialEntryBeforeChange(
1501                            final boolean includeVirtual)
1502  {
1503    if (getChangeType() == ChangeType.ADD)
1504    {
1505      return null;
1506    }
1507
1508    final Entry e = new Entry(getTargetDN());
1509
1510    // If there is a set of deleted entry attributes available, then use them.
1511    final List<Attribute> deletedEntryAttrs =
1512         getDeletedEntryAttributes(includeVirtual);
1513    if (deletedEntryAttrs != null)
1514    {
1515      for (final Attribute a : deletedEntryAttrs)
1516      {
1517        e.addAttribute(a);
1518      }
1519    }
1520
1521    // If there is a set of before attributes, then use them.
1522    for (final Attribute a : getUpdatedAttributesBeforeChange(includeVirtual))
1523    {
1524      e.addAttribute(a);
1525    }
1526
1527    // If there is a set of key attributes, then only use them if the
1528    // associated attributes aren't already in the entry and aren't in either
1529    // the after values and exceeded max values count.
1530    for (final Attribute a : getKeyEntryAttributes(includeVirtual))
1531    {
1532      boolean shouldExclude = e.hasAttribute(a.getName());
1533
1534      for (final Attribute ba : getUpdatedAttributesAfterChange(includeVirtual))
1535      {
1536        if (ba.getName().equalsIgnoreCase(a.getName()))
1537        {
1538          shouldExclude = true;
1539        }
1540      }
1541
1542      for (final ChangeLogEntryAttributeExceededMaxValuesCount ea :
1543           attributesThatExceededMaxValuesCount)
1544      {
1545        if (ea.getAttributeName().equalsIgnoreCase(a.getName()))
1546        {
1547          // TODO:  In the event that the before count was exceeded but the
1548          // after count was not, then we may be able to reconstruct the before
1549          // values if the changes included deleting specific values for the
1550          // attribute.
1551          shouldExclude = true;
1552        }
1553      }
1554
1555      if (includeVirtual)
1556      {
1557        for (final ChangeLogEntryAttributeExceededMaxValuesCount ea :
1558             virtualAttributesThatExceededMaxValuesCount)
1559        {
1560          if (ea.getAttributeName().equalsIgnoreCase(a.getName()))
1561          {
1562            // TODO:  In the event that the before count was exceeded but the
1563            // after count was not, then we may be able to reconstruct the
1564            // before values if the changes included deleting specific values
1565            // for the attribute.
1566            shouldExclude = true;
1567          }
1568        }
1569      }
1570
1571      if (! shouldExclude)
1572      {
1573        e.addAttribute(a);
1574      }
1575    }
1576
1577    // NOTE:  Although we could possibly get additional attribute values from
1578    // the entry's RDN, that can't be considered authoritative because those
1579    // same attributes may have additional values that aren't in the RDN, and we
1580    // don't want to include an attribute without the entire set of values.
1581
1582    return new ReadOnlyEntry(e);
1583  }
1584
1585
1586
1587  /**
1588   * Attempts to construct a partial representation of the target entry as it
1589   * appeared after the change was processed.  The information contained in the
1590   * constructed entry will be based solely on information contained in the
1591   * changelog entry, including information provided in the changes,
1592   * ds-changelog-after-values, and ds-changelog-entry-key-attr-values
1593   * attributes.  It will not include any virtual attribute information.
1594   *
1595   * @return  A partial representation of the target entry as it appeared after
1596   *          the change was processed, or {@code null} if the change was a
1597   *          delete operation and therefore did not exist after the change.
1598   */
1599  public ReadOnlyEntry constructPartialEntryAfterChange()
1600  {
1601    return constructPartialEntryAfterChange(false);
1602  }
1603
1604
1605
1606  /**
1607   * Attempts to construct a partial representation of the target entry as it
1608   * appeared after the change was processed.  The information contained in the
1609   * constructed entry will be based solely on information contained in the
1610   * changelog entry, including information provided in the changes,
1611   * ds-changelog-after-values, and ds-changelog-entry-key-attr-values
1612   * attributes, and optionally virtual versions of all of those elements.
1613   *
1614   * @param  includeVirtual  Indicates whether to include both real and virtual
1615   *                         values (if {@code true}, or only real values (if
1616   *                         {@code false}), for the attributes to be returned.
1617   *
1618   * @return  A partial representation of the target entry as it appeared after
1619   *          the change was processed, or {@code null} if the change was a
1620   *          delete operation and therefore did not exist after the change.
1621   */
1622  public ReadOnlyEntry constructPartialEntryAfterChange(
1623                            final boolean includeVirtual)
1624  {
1625    final Entry e;
1626    switch (getChangeType())
1627    {
1628      case ADD:
1629      case MODIFY:
1630        e = new Entry(getTargetDN());
1631        break;
1632
1633      case MODIFY_DN:
1634        e = new Entry(getNewDN());
1635        break;
1636
1637      case DELETE:
1638      default:
1639        return null;
1640    }
1641
1642
1643    // If there is a set of add attributes, then use them.
1644    final List<Attribute> addAttrs = getAddAttributes(includeVirtual);
1645    if (addAttrs != null)
1646    {
1647      for (final Attribute a : addAttrs)
1648      {
1649        e.addAttribute(a);
1650      }
1651    }
1652
1653    // If there is a set of modifications and any of them are replace
1654    // modifications with a set of values, then we can use them to determine
1655    // the new values of those attributes.
1656    final List<Modification> mods = getModifications();
1657    if (mods != null)
1658    {
1659      for (final Modification m : mods)
1660      {
1661        final byte[][] values = m.getValueByteArrays();
1662        if ((m.getModificationType() == ModificationType.REPLACE) &&
1663            (values.length > 0))
1664        {
1665          e.addAttribute(m.getAttributeName(), values);
1666        }
1667      }
1668    }
1669
1670    // If there is a set of after attributes, then use them.
1671    for (final Attribute a : getUpdatedAttributesAfterChange(includeVirtual))
1672    {
1673      e.addAttribute(a);
1674    }
1675
1676    // If there is a set of key attributes, then use them.
1677    for (final Attribute a : getKeyEntryAttributes(includeVirtual))
1678    {
1679      e.addAttribute(a);
1680    }
1681
1682    // TODO:  In the event that the after count was exceeded but the before
1683    // count was not, then we may be able to reconstruct the after values if the
1684    // changes included adding specific values for the attribute.
1685
1686    // NOTE:  Although we could possibly get additional attribute values from
1687    // the entry's RDN, that can't be considered authoritative because those
1688    // same attributes may have additional values that aren't in the RDN, and we
1689    // don't want to include an attribute without the entire set of values.
1690
1691    return new ReadOnlyEntry(e);
1692  }
1693}