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