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