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