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