PrometheusDataList.java

/*
 * Prometheus: Application Framework
 * Copyright 2012-2026. Tony Washer
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.  You may obtain a copy
 * of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package io.github.tonywasher.joceanus.prometheus.data;

import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
import io.github.tonywasher.joceanus.metis.data.MetisDataEditState;
import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataList;
import io.github.tonywasher.joceanus.metis.data.MetisDataResource;
import io.github.tonywasher.joceanus.metis.data.MetisDataState;
import io.github.tonywasher.joceanus.metis.field.MetisFieldItem;
import io.github.tonywasher.joceanus.metis.field.MetisFieldSet;
import io.github.tonywasher.joceanus.metis.list.MetisListIndexed;
import io.github.tonywasher.joceanus.metis.list.MetisListKey;
import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataInfoItem.PrometheusDataInfoList;
import io.github.tonywasher.joceanus.prometheus.data.PrometheusTableItem.PrometheusTableList;
import io.github.tonywasher.joceanus.prometheus.exc.PrometheusDataException;

import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Generic implementation of a DataList for DataItems.
 *
 * @param <T> the item type
 * @author Tony Washer
 */
public abstract class PrometheusDataList<T extends PrometheusDataItem>
        implements MetisFieldItem, MetisDataList<T>, PrometheusTableList<T> {
    /**
     * DataList interface.
     */
    public interface PrometheusDataListSet {
        /**
         * Obtain the list for a class.
         *
         * @param <L>       the list type
         * @param pDataType the data type
         * @param pClass    the list class
         * @return the list
         */
        <L extends PrometheusDataList<?>> L getDataList(MetisListKey pDataType,
                                                        Class<L> pClass);

        /**
         * Does this list have the dataType?
         *
         * @param pDataType the dataType
         * @return true/false
         */
        boolean hasDataType(MetisListKey pDataType);
    }

    /**
     * Report fields.
     */
    @SuppressWarnings("rawtypes")
    private static final MetisFieldSet<PrometheusDataList> FIELD_DEFS = MetisFieldSet.newFieldSet(PrometheusDataList.class);

    /*
     * Declare Fields.
     */
    static {
        FIELD_DEFS.declareLocalField(MetisDataResource.LIST_SIZE, PrometheusDataList::size);
        FIELD_DEFS.declareLocalField(PrometheusDataResource.DATALIST_STYLE, PrometheusDataList::getStyle);
        FIELD_DEFS.declareLocalField(PrometheusDataResource.DATASET_NAME, PrometheusDataList::getDataSet);
        FIELD_DEFS.declareLocalField(PrometheusDataResource.DATALIST_MAPS, PrometheusDataList::getDataMap);
        FIELD_DEFS.declareLocalField(PrometheusDataResource.DATASET_VERSION, PrometheusDataList::getVersion);
        FIELD_DEFS.declareLocalField(PrometheusDataResource.DATAITEM_EDITSTATE, PrometheusDataList::getEditState);
        FIELD_DEFS.declareLocalField(PrometheusDataResource.DATAITEM_TYPE, PrometheusDataList::getItemType);
        FIELD_DEFS.declareLocalField(PrometheusDataResource.DATAITEM_BASE, PrometheusDataList::getBaseList);
    }

    /**
     * The list.
     */
    private final MetisListIndexed<T> theList;

    /**
     * The class.
     */
    private final Class<T> theBaseClazz;

    /**
     * The style of the list.
     */
    private PrometheusListStyle theStyle = PrometheusListStyle.CORE;

    /**
     * The edit state of the list.
     */
    private MetisDataEditState theEdit = MetisDataEditState.CLEAN;

    /**
     * The DataSet.
     */
    private PrometheusDataSet theDataSet;

    /**
     * The item type.
     */
    private final MetisListKey theItemType;

    /**
     * The base list (for extracts).
     */
    private PrometheusDataList<? extends PrometheusDataItem> theBase;

    /**
     * DataMap.
     */
    private PrometheusDataMapItem theDataMap;

    /**
     * The version.
     */
    private int theVersion;

    /**
     * The validator.
     */
    private PrometheusDataValidator theValidator;

    /**
     * Construct a new object.
     *
     * @param pBaseClass the class of the underlying object
     * @param pDataSet   the owning dataSet
     * @param pItemType  the item type
     * @param pStyle     the new {@link PrometheusListStyle}
     */
    protected PrometheusDataList(final Class<T> pBaseClass,
                                 final PrometheusDataSet pDataSet,
                                 final MetisListKey pItemType,
                                 final PrometheusListStyle pStyle) {
        theBaseClazz = pBaseClass;
        theStyle = pStyle;
        theItemType = pItemType;
        theDataSet = pDataSet;

        /* Create the list */
        theList = new MetisListIndexed<>();
        theList.setComparator(PrometheusDataItem::compareTo);
    }

    /**
     * Construct a clone object.
     *
     * @param pSource the list to clone
     */
    protected PrometheusDataList(final PrometheusDataList<T> pSource) {
        this(pSource.getBaseClass(), pSource.getDataSet(), pSource.getItemType(), PrometheusListStyle.COPY);
        theBase = pSource;
    }

    @Override
    public List<T> getUnderlyingList() {
        return theList.getUnderlyingList();
    }

    /**
     * Obtain item fields.
     *
     * @return the item fields
     */
    public abstract MetisFieldSetDef getItemFields();

    @Override
    public String formatObject(final OceanusDataFormatter pFormatter) {
        return getDataFieldSet().getName();
    }

    @Override
    public boolean add(final T pItem) {
        return theList.add(pItem);
    }

    @Override
    public void add(final int pIndex,
                    final T pItem) {
        theList.add(pIndex, pItem);
    }

    @Override
    public boolean remove(final Object pItem) {
        return theList.remove(pItem);
    }

    @Override
    public void clear() {
        theList.clear();
    }

    /**
     * reSort the list.
     */
    public void reSort() {
        theList.sortList();
    }

    @Override
    public Class<T> getBaseClass() {
        return theBaseClazz;
    }

    /**
     * Obtain item by id.
     *
     * @param pId the id to lookup
     * @return the item (or null if not present)
     */
    public T findItemById(final Integer pId) {
        return theList.getItemById(pId);
    }

    /**
     * Should this list be included in DataXml?
     *
     * @return true/false
     */
    public boolean includeDataXML() {
        return true;
    }

    /**
     * Obtain List Name.
     *
     * @return the ListName
     */
    public abstract String listName();

    /**
     * Get the style of the list.
     *
     * @return the list style
     */
    public PrometheusListStyle getStyle() {
        return theStyle;
    }

    /**
     * Get the type of the list.
     *
     * @return the item type
     */
    public MetisListKey getItemType() {
        return theItemType;
    }

    /**
     * Get the dataSet.
     *
     * @return the dataSet
     */
    public PrometheusDataSet getDataSet() {
        return theDataSet;
    }

    /**
     * Set the version.
     *
     * @param pVersion the version
     */
    public void setVersion(final int pVersion) {
        theVersion = pVersion;
    }

    /**
     * Set the style of the list.
     *
     * @param pStyle the list style
     */
    protected void setStyle(final PrometheusListStyle pStyle) {
        theStyle = pStyle;
    }

    /**
     * Get the EditState of the list.
     *
     * @return the Edit State
     */
    public MetisDataEditState getEditState() {
        return theEdit;
    }

    /**
     * Get the Version of the list.
     *
     * @return the Version
     */
    public int getVersion() {
        return theVersion;
    }

    /**
     * Get the Base of the list.
     *
     * @return the Base list
     */
    public PrometheusDataList<?> getBaseList() {
        return theBase;
    }

    /**
     * Obtain the DataMap.
     *
     * @return the enumClass
     */
    protected PrometheusDataMapItem getDataMap() {
        return theDataMap;
    }

    /**
     * Determine whether the list got any errors.
     *
     * @return <code>true/false</code>
     */
    public boolean hasErrors() {
        return theEdit == MetisDataEditState.ERROR;
    }

    /**
     * Determine whether the list got any updates.
     *
     * @return <code>true/false</code>
     */
    public boolean hasUpdates() {
        /* We have changes if version is non-zero */
        return theVersion != 0;
    }

    /**
     * Determine whether the list is valid (or are there errors/non-validated changes).
     *
     * @return <code>true/false</code>
     */
    public boolean isValid() {
        return theEdit == MetisDataEditState.CLEAN
                || theEdit == MetisDataEditState.VALID;
    }

    @Override
    public boolean isLocked() {
        return false;
    }

    /**
     * Set the base DataList.
     *
     * @param pBase the list that this list is based upon
     */
    protected void setBase(final PrometheusDataList<? extends PrometheusDataItem> pBase) {
        theBase = pBase;
    }

    /**
     * Obtain a copy of the Id Map.
     *
     * @return the Id map.
     */
    public Map<Integer, T> copyIdMap() {
        return theList.copyIdMap();
    }

    /**
     * Obtain the validator.
     *
     * @return the validator
     */
    public PrometheusDataValidator getValidator() {
        if (theValidator == null) {
            theValidator = getDataSet().getValidator(theItemType);
        }
        return theValidator;
    }

    /**
     * Obtain an empty list based on this list.
     *
     * @param pStyle the style of the empty list
     * @return the list
     */
    protected abstract PrometheusDataList<T> getEmptyList(PrometheusListStyle pStyle);

    /**
     * Derive an cloned extract of the source list.
     *
     * @param pData   the dataSet
     * @param pSource the source list
     * @throws OceanusException on error
     */
    protected void cloneList(final PrometheusDataSet pData,
                             final PrometheusDataList<?> pSource) throws OceanusException {
        /* Correct the dataSet reference */
        theDataSet = pData;

        /* Populate the list */
        pSource.populateList(this);

        /* Remove base reference and reset to CORE list */
        theBase = null;
        theStyle = PrometheusListStyle.CORE;
    }

    /**
     * Derive an extract of this list.
     *
     * @param pStyle the Style of the extract
     * @return the derived list
     * @throws OceanusException on error
     */
    public PrometheusDataList<T> deriveList(final PrometheusListStyle pStyle) throws OceanusException {
        /* Obtain an empty list of the correct style */
        final PrometheusDataList<T> myList = getEmptyList(pStyle);

        /* Populate the list */
        populateList(myList);

        /* Return the derived list */
        return myList;
    }

    /**
     * Populate a list extract.
     *
     * @param pList the list to populate
     * @throws OceanusException on error
     */
    protected void populateList(final PrometheusDataList<?> pList) throws OceanusException {
        /* Determine special styles */
        final PrometheusListStyle myStyle = pList.getStyle();
        final boolean isUpdate = myStyle == PrometheusListStyle.UPDATE;
        final boolean isClone = myStyle == PrometheusListStyle.CLONE;

        /* Create an iterator for all items in the list */
        final Iterator<? extends PrometheusDataItem> myIterator = iterator();

        /* Loop through the list */
        while (myIterator.hasNext()) {
            /* Access the item and its state */
            final PrometheusDataItem myCurr = myIterator.next();
            final MetisDataState myState = myCurr.getState();

            /* If this is an UPDATE list, ignore clean elements */
            if (isUpdate && myState == MetisDataState.CLEAN) {
                continue;
            }

            /* Copy the item */
            pList.addCopyItem(myCurr);
        }

        /* If this is a Clone list */
        if (isClone) {
            /* Adjust the links */
            pList.resolveDataSetLinks();
        }
    }

    /**
     * Adjust links.
     *
     * @throws OceanusException on error
     */
    public void resolveDataSetLinks() throws OceanusException {
        /* Loop through the list */
        final Iterator<? extends PrometheusDataItem> myIterator = iterator();
        while (myIterator.hasNext()) {
            /* Access the item */
            final PrometheusDataItem myCurr = myIterator.next();

            /* Adjust the links */
            myCurr.resolveDataSetLinks();
        }
    }

    /**
     * Construct a difference extract between two DataLists. The difference extract will only have
     * items that differ between the two lists. Items that are in the new list, but not in the old
     * list will be viewed as inserted. Items that are in the old list but not in the new list will
     * be viewed as deleted. Items that are in both list but differ will be viewed as changed
     *
     * @param pDataSet the difference DataSet
     * @param pOld     The old list to compare to
     * @return the difference list
     */
    public PrometheusDataList<T> deriveDifferences(final PrometheusDataSet pDataSet,
                                                   final PrometheusDataList<?> pOld) {
        /* Obtain an empty list of the correct style */
        final PrometheusDataList<T> myList = getEmptyList(PrometheusListStyle.DIFFER);
        myList.theDataSet = pDataSet;

        /* Access an Id Map of the old list */
        final Map<Integer, ?> myOld = pOld.copyIdMap();

        /* Loop through the new list */
        final Iterator<T> myIterator = iterator();
        while (myIterator.hasNext()) {
            /* Locate the item in the old list */
            final PrometheusDataItem myCurr = myIterator.next();
            PrometheusDataItem myItem = (PrometheusDataItem) myOld.get(myCurr.getIndexedId());

            /* If the item does not exist in the old list */
            if (myItem == null) {
                /* Insert a new item */
                myItem = myList.addCopyItem(myCurr);
                myItem.setNewVersion();

                /* else the item exists in the old list */
            } else {
                /* If the item has changed */
                if (!myCurr.equals(myItem)) {
                    /* Copy the item */
                    final PrometheusDataItem myNew = myList.addCopyItem(myCurr);
                    myNew.setBase(myItem);

                    /* Ensure that we record the correct history */
                    myNew.setHistory(myItem);
                }

                /* Remove the item from the map */
                myOld.remove(myItem.getIndexedId());
            }
        }

        /* Loop through the remaining items in the old list */
        final Iterator<?> myOldIterator = myOld.values().iterator();
        while (myOldIterator.hasNext()) {
            /* Insert a new item */
            final PrometheusDataItem myCurr = (PrometheusDataItem) myOldIterator.next();
            final PrometheusDataItem myItem = myList.addCopyItem(myCurr);
            myItem.setBase(null);
            myItem.setDeleted(true);
        }

        /* Return the difference list */
        return myList;
    }

    /**
     * Re-base the list against a database image. This method is used to re-synchronise between two
     * sources. Items that are in this list, but not in the base list will be viewed as inserted.
     * Items that are in the base list but not in this list list will be viewed as deleted. Items
     * that are in both list but differ will be viewed as changed
     *
     * @param pBase The base list to re-base on
     * @return are there any changes
     */
    public boolean reBase(final PrometheusDataList<?> pBase) {
        /* Access an Id Map of the old list */
        final Map<Integer, ?> myBase = pBase.copyIdMap();
        boolean bChanges = false;

        /* Loop through this list */
        final Iterator<T> myIterator = iterator();
        while (myIterator.hasNext()) {
            /* Locate the item in the base list */
            final T myCurr = myIterator.next();
            final PrometheusDataItem myItem = (PrometheusDataItem) myBase.get(myCurr.getIndexedId());

            /* If the underlying item does not exist */
            if (myItem == null) {
                /* Mark this as a new item */
                myCurr.getValues().setVersion(getVersion() + 1);
                myCurr.setBase(null);
                bChanges = true;

                /* else the item exists in the old list */
            } else {
                /* if it has changed */
                if (!myCurr.equals(myItem)) {
                    /* Set correct history */
                    myCurr.setHistory(myItem);
                    myCurr.setBase(null);
                    bChanges = true;

                    /* else it is identical */
                } else {
                    /* Mark this as a clean item */
                    myCurr.clearHistory();
                    myCurr.setBase(null);
                }

                /* Remove the old item */
                myBase.remove(myItem.getIndexedId());
            }
        }

        /* Loop through the remaining items in the base list */
        final Iterator<?> myBaseIterator = myBase.values().iterator();
        while (myBaseIterator.hasNext()) {
            /* Insert a new item */
            final PrometheusDataItem myCurr = (PrometheusDataItem) myBaseIterator.next();
            final T myItem = addCopyItem(myCurr);
            myItem.setBase(null);
            myItem.setHistory(myCurr);
            myItem.getValues().setDeletion(true);
            bChanges = true;
        }

        /* Return flag */
        return bChanges;
    }

    /**
     * Is the Id unique in this list.
     *
     * @param uId the Id to check
     * @return Whether the id is unique <code>true/false</code>
     */
    public boolean isIdUnique(final Integer uId) {
        /* Its unique if its unassigned or greater than the max id */
        if (uId == null
                || uId == 0
                || uId > theList.getNextId()) {
            return true;
        }

        /* Check in list */
        return !theList.containsId(uId);
    }

    /**
     * Generate/Record new id for the item.
     *
     * @param pItem the new item
     */
    protected void setNewId(final PrometheusDataItem pItem) {
        /* Access the Id */
        final Integer myId = pItem.getIndexedId();

        /* If we need to generate a new id */
        if (myId == null
                || myId == 0) {
            /* Obtain the next Id */
            pItem.setIndexedId(theList.allocateNextId());
        }
    }

    /**
     * Touch underlying items that are referenced by items in this list.
     */
    public void touchUnderlyingItems() {
        /* Loop through items in the list */
        final Iterator<T> myIterator = iterator();
        while (myIterator.hasNext()) {
            final T myItem = myIterator.next();

            /* If the item is not deleted */
            if (!myItem.isDeleted()) {
                /* Touch underlying items */
                myItem.touchUnderlyingItems();
            }
        }
    }

    /**
     * Set the EditState for the list (forcible on error/change).
     *
     * @param pState the new {@link MetisDataEditState} (only ERROR/DIRTY)
     */
    public void setEditState(final MetisDataEditState pState) {
        switch (pState) {
            case CLEAN:
            case VALID:
            case ERROR:
                theEdit = pState;
                break;
            case DIRTY:
                if (theEdit != MetisDataEditState.ERROR) {
                    theEdit = pState;
                }
                break;
            default:
                break;
        }
    }

    /**
     * Validate the data items.
     *
     * @return the error list (or null if no errors)
     */
    public PrometheusDataErrorList validate() {
        /* Allocate error list */
        PrometheusDataErrorList myErrors = null;
        MetisDataEditState myState = MetisDataEditState.CLEAN;

        /* Loop through the items */
        final Iterator<T> myIterator = iterator();
        while (myIterator.hasNext()) {
            final T myCurr = myIterator.next();

            /* Clear errors for the item */
            myCurr.clearErrors();

            /* Skip deleted items */
            if (myCurr.isDeleted()) {
                myCurr.setValidEdit();
                myState = myState.combineState(MetisDataEditState.VALID);
                continue;
            }

            /* Validate the item and build up the state */
            myCurr.validate();
            myState = myState.combineState(myCurr.getEditState());

            /* If the item is in error */
            if (myCurr.hasErrors()) {
                /* If this is the first error */
                if (myErrors == null) {
                    /* Allocate error list */
                    myErrors = new PrometheusDataErrorList();
                }

                /* Add to the error list */
                myErrors.add(myCurr);
            }
        }

        /* Store the edit state */
        theEdit = myState;

        /* Return the errors */
        return myErrors;
    }

    /**
     * Perform a validation on data load.
     *
     * @throws OceanusException on error
     */
    public void validateOnLoad() throws OceanusException {
        /* Validate the list */
        final PrometheusDataErrorList myErrors = validate();
        if (myErrors != null) {
            throw new PrometheusDataException(myErrors, PrometheusDataItem.ERROR_VALIDATION);
        }
    }

    /**
     * Allocate the dataMap.
     *
     * @return the dataMap
     */
    protected abstract PrometheusDataMapItem allocateDataMap();

    /**
     * Set map.
     *
     * @param pMap the map
     */
    protected void setDataMap(final PrometheusDataMapItem pMap) {
        theDataMap = pMap;
    }

    /**
     * Ensure map.
     */
    public void ensureMap() {
        /* Allocate/Reset the map */
        if (theDataMap == null) {
            theDataMap = allocateDataMap();
        } else {
            theDataMap.resetMap();
        }
    }

    /**
     * Build map of the data.
     */
    public void mapData() {
        /* Ensure the map */
        ensureMap();

        /* Loop through the items */
        final Iterator<T> myIterator = iterator();
        while (myIterator.hasNext()) {
            final T myItem = myIterator.next();

            /* If the item is not deleted */
            if (!myItem.isDeleted()) {
                /* Map the item */
                myItem.touchUnderlyingItems();
                theDataMap.adjustForItem(myItem);
            }
        }
    }

    /**
     * PostProcess a loaded list.
     *
     * @throws OceanusException on error
     */
    public void postProcessOnLoad() throws OceanusException {
        /* Default action is to resolve links and then sort */
        resolveDataSetLinks();
        theList.sortList();

        /* Map the data */
        mapData();

        /* Now validate the list */
        validateOnLoad();
    }

    /**
     * Update Maps.
     */
    public void updateMaps() {
        /* Ensure the map */
        ensureMap();

        /* Loop through items clearing active flag */
        final Iterator<T> myIterator = iterator();
        while (myIterator.hasNext()) {
            final T myItem = myIterator.next();

            /* If the item is not deleted */
            if (!myItem.isDeleted()) {
                /* Update Maps */
                myItem.updateMaps();
            }
        }
    }

    /**
     * postProcessOnUpdate.
     */
    public void postProcessOnUpdate() {
        /* Note whether this is a DataInfoList */
        final boolean isDataInfo = this instanceof PrometheusDataInfoList;

        /* Reset the map */
        if (theDataMap != null) {
            theDataMap.resetMap();
        }

        /* Loop through items clearing active flag */
        final Iterator<T> myIterator = iterator();
        MetisDataEditState myState = MetisDataEditState.CLEAN;
        while (myIterator.hasNext()) {
            final T myCurr = myIterator.next();

            /* Clear errors for the item */
            myCurr.clearErrors();

            /* Skip deleted items */
            if (myCurr.isDeleted()) {
                myCurr.setValidEdit();
                myState = myState.combineState(MetisDataEditState.VALID);
                continue;
            }

            /* If this is not a DataInfo */
            if (!isDataInfo) {
                /* Adjust touches and update map */
                myCurr.touchOnUpdate();
                myCurr.adjustMapForItem();
            }

            /* Validate the item and build up the state */
            myCurr.validate();
            myState = myState.combineState(myCurr.getEditState());
        }

        /* Store the edit state */
        theEdit = myState;
    }

    /**
     * Create a new element in the list copied from another element (to be over-written).
     *
     * @param pElement - element to base new item on
     * @return the newly allocated item
     */
    public abstract T addCopyItem(PrometheusDataItem pElement);

    /**
     * Create a new empty element in the edit list (to be over-written).
     *
     * @return the newly allocated item
     */
    public abstract T addNewItem();

    /**
     * Create a new element according to the DataValues.
     *
     * @param pValues the data values
     * @return the newly allocated item
     * @throws OceanusException on error
     */
    public abstract T addValuesItem(PrometheusDataValues pValues) throws OceanusException;

    /**
     * Locate an item by name (if possible).
     *
     * @param pName the name of the item
     * @return the matching item
     */
    public T findItemByName(final String pName) {
        return null;
    }

    /**
     * Rewind items to the required version.
     *
     * @param pVersion the version to rewind to
     */
    public void rewindToVersion(final int pVersion) {
        /* Loop through the elements */
        final Iterator<T> myIterator = theList.iterator();
        while (myIterator.hasNext()) {
            final T myCurr = myIterator.next();

            /* If the version is before required version */
            if (myCurr.getValues().getVersion() <= pVersion) {
                /* Ignore */
                continue;
            }

            /* If the item was created after the required version */
            if (myCurr.getOriginalValues().getVersion() > pVersion) {
                /* Remove from list */
                myIterator.remove();
                myCurr.deRegister();

                /* Re-Loop */
                continue;
            }

            /* Adjust values */
            myCurr.rewindToVersion(pVersion);
        }

        /* Adjust list value */
        setVersion(pVersion);

        /* ReSort the list unless we are an edit list */
        if (theStyle != PrometheusListStyle.EDIT) {
            theList.sortList();
        }
    }

    /**
     * Condense history.
     *
     * @param pNewVersion the new maximum version
     */
    public void condenseHistory(final int pNewVersion) {
        /* Loop through the elements */
        final Iterator<T> myIterator = theList.iterator();
        while (myIterator.hasNext()) {
            final T myCurr = myIterator.next();

            /* If the version is before required version */
            if (myCurr.getValues().getVersion() < pNewVersion) {
                /* Ignore */
                continue;
            }

            /* If the item is in DELNEW state */
            if (myCurr.isDeleted()
                    && (myCurr.getOriginalValues().getVersion() >= pNewVersion)) {
                /* Remove from list */
                myIterator.remove();
                myCurr.deRegister();

                /* Re-Loop */
                continue;
            }

            /* Condense the history */
            myCurr.condenseHistory(pNewVersion);
        }

        /* Adjust list value */
        setVersion(pNewVersion);
    }

    /**
     * ListStyles.
     */
    public enum PrometheusListStyle {
        /**
         * Core list holding the true version of the data.
         */
        CORE,

        /**
         * Deep Copy clone for security updates.
         */
        CLONE,

        /**
         * Shallow Copy list for comparison purposes. Only references to other items can be added to
         * the list
         */
        COPY,

        /**
         * Partial extract of the data for the purposes of editing.
         */
        EDIT,

        /**
         * List of changes to be applied to database.
         */
        UPDATE,

        /**
         * List of differences.
         */
        DIFFER;
    }
}