PrometheusDataInfoItem.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.date.OceanusDate;
import io.github.tonywasher.joceanus.oceanus.date.OceanusDateFormatter;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusDecimalParser;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusMoney;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusPrice;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusRate;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusRatio;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusUnits;
import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
import io.github.tonywasher.joceanus.metis.field.MetisFieldSet;
import io.github.tonywasher.joceanus.metis.list.MetisListKey;
import io.github.tonywasher.joceanus.prometheus.exc.PrometheusDataException;
import io.github.tonywasher.joceanus.prometheus.exc.PrometheusLogicException;

import java.util.Date;

/**
 * Representation of an information extension of a DataItem.
 *
 * @author Tony Washer
 */
public abstract class PrometheusDataInfoItem
        extends PrometheusEncryptedDataItem {
    /**
     * Maximum DataLength.
     */
    public static final int DATALEN = 512;

    /**
     * Report fields.
     */
    private static final PrometheusEncryptedFieldSet<PrometheusDataInfoItem> FIELD_DEFS = PrometheusEncryptedFieldSet.newEncryptedFieldSet(PrometheusDataInfoItem.class);

    /*
     * FieldIds.
     */
    static {
        FIELD_DEFS.declareLinkField(PrometheusDataResource.DATAINFO_TYPE);
        FIELD_DEFS.declareLinkField(PrometheusDataResource.DATAINFO_OWNER);
        FIELD_DEFS.declareEncryptedContextField(PrometheusDataResource.DATAINFO_VALUE);
        FIELD_DEFS.declareDerivedVersionedField(PrometheusDataResource.DATAINFO_LINK);
    }

    /**
     * Invalid Data Type Error.
     */
    protected static final String ERROR_BADDATATYPE = PrometheusDataResource.DATAINFO_ERROR_TYPE.getValue();

    /**
     * Invalid Data Error.
     */
    protected static final String ERROR_BADDATA = PrometheusDataResource.DATAINFO_ERROR_DATA.getValue();

    /**
     * Invalid Info Class Error.
     */
    protected static final String ERROR_BADINFOCLASS = PrometheusDataResource.DATAINFO_ERROR_CLASS.getValue();

    /**
     * Copy Constructor.
     *
     * @param pList the list
     * @param pInfo The Info to copy
     */
    protected PrometheusDataInfoItem(final PrometheusDataInfoList<?> pList,
                                     final PrometheusDataInfoItem pInfo) {
        /* Set standard values */
        super(pList, pInfo);
    }

    /**
     * Edit Constructor.
     *
     * @param pList the list
     */
    protected PrometheusDataInfoItem(final PrometheusDataInfoList<?> pList) {
        /* Set standard values */
        super(pList, 0);
    }

    /**
     * Secure constructor.
     *
     * @param pList       the list
     * @param uId         the id
     * @param uKeySetId   the keySet id
     * @param uInfoTypeId the info id
     * @param uOwnerId    the owner id
     * @throws OceanusException on error
     */
    protected PrometheusDataInfoItem(final PrometheusDataInfoList<?> pList,
                                     final Integer uId,
                                     final Integer uKeySetId,
                                     final Integer uInfoTypeId,
                                     final Integer uOwnerId) throws OceanusException {
        /* Initialise the item */
        super(pList, uId);

        /* Record the Ids */
        setValueInfoType(uInfoTypeId);
        setValueOwner(uOwnerId);

        /* Store the keySetId */
        setDataKeySet(uKeySetId);
    }

    /**
     * Basic constructor.
     *
     * @param pList     the list
     * @param uId       the id
     * @param pInfoType the info type
     * @param pOwner    the owner
     */
    protected PrometheusDataInfoItem(final PrometheusDataInfoList<?> pList,
                                     final Integer uId,
                                     final PrometheusStaticDataItem pInfoType,
                                     final PrometheusDataItem pOwner) {
        /* Initialise the item */
        super(pList, uId);

        /* Record the parameters */
        setValueInfoType(pInfoType);
        setValueOwner(pOwner);
    }

    /**
     * Basic constructor.
     *
     * @param pList   the list
     * @param pValues the values
     * @throws OceanusException on error
     */
    protected PrometheusDataInfoItem(final PrometheusDataInfoList<?> pList,
                                     final PrometheusDataValues pValues) throws OceanusException {
        /* Initialise the item */
        super(pList, pValues);

        /* Store the InfoType */
        Object myValue = pValues.getValue(PrometheusDataResource.DATAINFO_TYPE);
        if (myValue instanceof Integer i) {
            setValueInfoType(i);
        } else if (myValue instanceof String s) {
            setValueInfoType(s);
        } else if (myValue instanceof PrometheusStaticDataItem myStatic) {
            setValueInfoType(myStatic);
        }

        /* Store the Owner */
        myValue = pValues.getValue(PrometheusDataResource.DATAINFO_OWNER);
        if (myValue instanceof Integer i) {
            setValueOwner(i);
        } else if (myValue instanceof String s) {
            setValueOwner(s);
        } else if (myValue instanceof PrometheusDataItem myItem) {
            setValueOwner(myItem);
        }
    }

    @Override
    public String toString() {
        /* Access Info Type Value */
        final PrometheusEncryptedValues myValues = getValues();
        final Object myType = myValues.getValue(PrometheusDataResource.DATAINFO_TYPE, Object.class);
        if (!(myType instanceof PrometheusStaticDataItem)) {
            return super.toString();
        }

        /* Access InfoType */
        final PrometheusStaticDataItem myInfoType = (PrometheusStaticDataItem) myType;

        /* Access class */
        return myInfoType.getName() + "=" + super.toString();
    }

    @Override
    public String formatObject(final OceanusDataFormatter pFormatter) {
        /* Access Info Type Value */
        final PrometheusEncryptedValues myValues = getValues();
        final Object myType = myValues.getValue(PrometheusDataResource.DATAINFO_TYPE, Object.class);
        if (!(myType instanceof PrometheusStaticDataItem)) {
            return super.formatObject(pFormatter);
        }

        /* Access InfoType */
        final PrometheusStaticDataItem myInfoType = getInfoType();

        /* Access formatter */
        final OceanusDataFormatter myFormatter = getDataSet().getDataFormatter();
        final PrometheusDataInfoClass myInfoClass = (PrometheusDataInfoClass) myInfoType.getStaticClass();

        /* Switch on type of Data */
        switch (myInfoClass.getDataType()) {
            case LINK:
            case LINKPAIR:
            case LINKSET:
                return myFormatter.formatObject(getLink());
            default:
                return myFormatter.formatObject(getValue(Object.class));
        }
    }

    /**
     * Obtain InfoType.
     *
     * @return the InfoTypeId
     */
    public abstract PrometheusStaticDataItem getInfoType();

    /**
     * Obtain InfoClass.
     *
     * @return the InfoClass
     */
    public abstract PrometheusDataInfoClass getInfoClass();

    /**
     * Obtain InfoTypeId.
     *
     * @return the InfoTypeId
     */
    public Integer getInfoTypeId() {
        return getInfoType().getIndexedId();
    }

    /**
     * Obtain Owner.
     *
     * @return the Owner
     */
    public abstract PrometheusDataItem getOwner();

    /**
     * Obtain OwnerId.
     *
     * @return the OwnerId
     */
    public Integer getOwnerId() {
        return getOwner().getIndexedId();
    }

    /**
     * Obtain Link.
     *
     * @param <X>    the link type
     * @param pClass the class of the link
     * @return the Link
     */
    public <X extends PrometheusDataItem> X getLink(final Class<X> pClass) {
        return getValues().getValue(PrometheusDataResource.DATAINFO_LINK, pClass);
    }

    /**
     * Get Link name.
     *
     * @return the link name
     */
    public String getLinkName() {
        return null;
    }

    /**
     * Obtain Link.
     *
     * @return the Link
     */
    protected PrometheusDataItem getLink() {
        return getValues().getValue(PrometheusDataResource.DATAINFO_LINK, PrometheusDataItem.class);
    }

    /**
     * Obtain Value as object.
     *
     * @param <X>    the object type
     * @param pClass the object class
     * @return the Value
     */
    public <X> X getValue(final Class<X> pClass) {
        return getValues().getValue(PrometheusDataResource.DATAINFO_VALUE, pClass);
    }

    /**
     * Obtain Value as underlying object.
     *
     * @return the Value
     */
    public PrometheusEncryptedPair getField() {
        return getField(getValues());
    }

    /**
     * Obtain Encrypted Bytes.
     *
     * @return the Bytes
     */
    public byte[] getValueBytes() {
        return getValues().getEncryptedBytes(PrometheusDataResource.DATAINFO_VALUE);
    }

    /**
     * Obtain Value as object.
     *
     * @param <X>       the object type
     * @param pValueSet the valueSet
     * @param pClass    the object class
     * @return the Value
     */
    public static <X> X getValue(final PrometheusEncryptedValues pValueSet,
                                 final Class<X> pClass) {
        return pValueSet.isDeletion()
                ? null
                : pValueSet.getValue(PrometheusDataResource.DATAINFO_VALUE, pClass);
    }

    /**
     * Obtain Value as encrypted field.
     *
     * @param pValueSet the valueSet
     * @return the Value
     */
    public static PrometheusEncryptedPair getField(final PrometheusEncryptedValues pValueSet) {
        return pValueSet.isDeletion()
                ? null
                : pValueSet.getEncryptedPair(PrometheusDataResource.DATAINFO_VALUE);
    }


    /**
     * Set InfoType.
     *
     * @param pValue the info Type
     */
    protected final void setValueInfoType(final PrometheusStaticDataItem pValue) {
        getValues().setUncheckedValue(PrometheusDataResource.DATAINFO_TYPE, pValue);
    }

    /**
     * Set InfoType Id.
     *
     * @param pId the info Type id
     */
    protected final void setValueInfoType(final Integer pId) {
        getValues().setUncheckedValue(PrometheusDataResource.DATAINFO_TYPE, pId);
    }

    /**
     * Set InfoType Name.
     *
     * @param pName the info Type name
     */
    protected final void setValueInfoType(final String pName) {
        getValues().setUncheckedValue(PrometheusDataResource.DATAINFO_TYPE, pName);
    }

    /**
     * Set Owner.
     *
     * @param pValue the owner
     */
    protected final void setValueOwner(final PrometheusDataItem pValue) {
        getValues().setUncheckedValue(PrometheusDataResource.DATAINFO_OWNER, pValue);
    }

    /**
     * Set Owner id.
     *
     * @param pId the owner id
     */
    protected final void setValueOwner(final Integer pId) {
        getValues().setUncheckedValue(PrometheusDataResource.DATAINFO_OWNER, pId);
    }

    /**
     * Set Owner name.
     *
     * @param pName the owner name
     */
    protected final void setValueOwner(final String pName) {
        getValues().setUncheckedValue(PrometheusDataResource.DATAINFO_OWNER, pName);
    }

    /**
     * Set Value.
     *
     * @param pValue the value
     * @throws OceanusException on error
     */
    protected void setValueValue(final Object pValue) throws OceanusException {
        getValues().setDeletion(false);
        setEncryptedValue(PrometheusDataResource.DATAINFO_VALUE, pValue);
    }

    /**
     * Set value.
     *
     * @param pValue the value
     */
    protected void setValueValue(final PrometheusEncryptedPair pValue) {
        final PrometheusEncryptedValues myValues = getValues();
        myValues.setDeletion(false);
        myValues.setUncheckedValue(PrometheusDataResource.DATAINFO_VALUE, pValue);
    }

    /**
     * Set Object Value.
     *
     * @param <X>    the object type
     * @param pBytes the value
     * @param pClass the object class
     * @throws OceanusException on error
     */
    protected <X> void setValueBytes(final byte[] pBytes,
                                     final Class<X> pClass) throws OceanusException {
        setEncryptedValue(PrometheusDataResource.DATAINFO_VALUE, pBytes, pClass);
    }

    /**
     * Set link.
     *
     * @param pLink the link
     */
    protected void setValueLink(final PrometheusDataItem pLink) {
        final PrometheusEncryptedValues myValues = getValues();
        myValues.setDeletion(false);
        myValues.setUncheckedValue(PrometheusDataResource.DATAINFO_LINK, pLink);
    }

    /**
     * Set link id.
     *
     * @param pId the linkId
     */
    private void setValueLink(final Integer pId) {
        final PrometheusEncryptedValues myValues = getValues();
        myValues.setUncheckedValue(PrometheusDataResource.DATAINFO_LINK, pId);
    }

    /**
     * Set link id.
     *
     * @param pId the linkId
     */
    private void setValueLink(final Long pId) {
        final PrometheusEncryptedValues myValues = getValues();
        myValues.setUncheckedValue(PrometheusDataResource.DATAINFO_LINK, pId);
    }

    /**
     * Set link name.
     *
     * @param pName the linkName
     */
    protected void setValueLink(final String pName) {
        final PrometheusEncryptedValues myValues = getValues();
        myValues.setUncheckedValue(PrometheusDataResource.DATAINFO_LINK, pName);
    }

    /**
     * Mark deleted.
     */
    public void markDeleted() {
        /* Set deletion indication */
        getValues().setDeletion(true);
    }

    @Override
    public void touchUnderlyingItems() {
        /* Pass call on */
        super.touchUnderlyingItems();

        /* Touch the info type */
        getInfoType().touchItem(this);
    }

    /**
     * Set Value.
     *
     * @param pValue the Value
     * @throws OceanusException on error
     */
    protected void setValue(final Object pValue) throws OceanusException {
        /* Access the info Type */
        final PrometheusStaticDataItem myType = getInfoType();
        final PrometheusDataInfoClass myClass = (PrometheusDataInfoClass) myType.getStaticClass();

        /* Access the DataSet and parser */
        final PrometheusDataSet myDataSet = getDataSet();
        final OceanusDataFormatter myFormatter = myDataSet.getDataFormatter();
        final OceanusDecimalParser myParser = myFormatter.getDecimalParser();

        /* Switch on Info Class */
        boolean bValueOK = false;
        switch (myClass.getDataType()) {
            case DATE:
                bValueOK = setDateValue(myFormatter.getDateFormatter(), pValue);
                break;
            case INTEGER:
                bValueOK = setIntegerValue(myFormatter, pValue);
                break;
            case LINK:
            case LINKSET:
                bValueOK = setLinkValue(pValue);
                break;
            case LINKPAIR:
                bValueOK = setLinkPairValue(pValue);
                break;
            case STRING:
                bValueOK = setStringValue(pValue);
                break;
            case CHARARRAY:
                bValueOK = setCharArrayValue(pValue);
                break;
            case MONEY:
                bValueOK = setMoneyValue(myParser, pValue);
                break;
            case RATE:
                bValueOK = setRateValue(myParser, pValue);
                break;
            case UNITS:
                bValueOK = setUnitsValue(myParser, pValue);
                break;
            case PRICE:
                bValueOK = setPriceValue(myParser, pValue);
                break;
            case RATIO:
                bValueOK = setRatioValue(myParser, pValue);
                break;
            default:
                break;
        }

        /* Reject invalid value */
        if (!bValueOK) {
            throw new PrometheusLogicException(this, ERROR_BADDATATYPE);
        }
    }

    /**
     * Set Date Value.
     *
     * @param pFormatter the date formatter
     * @param pValue     the Value
     * @return is value valid true/false
     * @throws OceanusException on error
     */
    private boolean setDateValue(final OceanusDateFormatter pFormatter,
                                 final Object pValue) throws OceanusException {
        try {
            /* Handle various forms */
            if (pValue instanceof Date d) {
                setValueValue(new OceanusDate(d));
                return true;
            } else if (pValue instanceof OceanusDate) {
                setValueValue(pValue);
                return true;
            } else if (pValue instanceof byte[] ba) {
                setValueBytes(ba, OceanusDate.class);
                return true;
            } else if (pValue instanceof String s) {
                setValueValue(pFormatter.parseDate(s));
                return true;
            }
        } catch (IllegalArgumentException e) {
            throw new PrometheusDataException(pValue, ERROR_BADDATA, e);
        }
        return false;
    }

    /**
     * Set Integer Value.
     *
     * @param pFormatter the data formatter
     * @param pValue     the Value
     * @return is value valid true/false
     * @throws OceanusException on error
     */
    private boolean setIntegerValue(final OceanusDataFormatter pFormatter,
                                    final Object pValue) throws OceanusException {
        try {
            /* Handle various forms */
            if (pValue instanceof Integer) {
                setValueValue(pValue);
                return true;
            } else if (pValue instanceof byte[] ba) {
                setValueBytes(ba, Integer.class);
                return true;
            } else if (pValue instanceof String s) {
                setValueValue(pFormatter.parseValue(s, Integer.class));
                return true;
            }
        } catch (IllegalArgumentException e) {
            throw new PrometheusDataException(pValue, ERROR_BADDATA, e);
        }
        return false;
    }

    /**
     * Set Link Value.
     *
     * @param pValue the Value
     * @return is value valid true/false
     * @throws OceanusException on error
     */
    private boolean setLinkValue(final Object pValue) throws OceanusException {
        try {
            /* Handle various forms */
            if (pValue instanceof Integer i) {
                setValueValue(pValue);
                setValueLink(i);
                return true;
            } else if (pValue instanceof byte[] ba) {
                setValueBytes(ba, Integer.class);
                setValueLink(getValue(Integer.class));
                return true;
            } else if (pValue instanceof String s) {
                setValueLink(s);
                return true;
            } else if (pValue instanceof PrometheusDataItem myItem) {
                setValueValue(myItem.getIndexedId());
                setValueLink(myItem);
                return true;
            }
        } catch (IllegalArgumentException e) {
            throw new PrometheusDataException(pValue, ERROR_BADDATA, e);
        }
        return false;
    }

    /**
     * Set Link Value.
     *
     * @param pValue the Value
     * @return is value valid true/false
     * @throws OceanusException on error
     */
    private boolean setLinkPairValue(final Object pValue) throws OceanusException {
        try {
            /* Handle various forms */
            if (pValue instanceof Long l) {
                setValueValue(pValue);
                setValueLink(l);
                return true;
            } else if (pValue instanceof byte[] ba) {
                setValueBytes(ba, Long.class);
                setValueLink(getValue(Long.class));
                return true;
            } else if (pValue instanceof String s) {
                setValueLink(s);
                return true;
            } else if (pValue instanceof PrometheusDataItem myItem) {
                setValueValue(myItem.getIndexedId());
                setValueLink(myItem);
                return true;
            }
        } catch (IllegalArgumentException e) {
            throw new PrometheusDataException(pValue, ERROR_BADDATA, e);
        }
        return false;
    }

    /**
     * Set String Value.
     *
     * @param pValue the Value
     * @return is value valid true/false
     * @throws OceanusException on error
     */
    private boolean setStringValue(final Object pValue) throws OceanusException {
        /* Handle various forms */
        if (pValue instanceof String) {
            setValueValue(pValue);
            return true;
        } else if (pValue instanceof byte[] ba) {
            setValueBytes(ba, String.class);
            return true;
        }
        return false;
    }

    /**
     * Set CharArray Value.
     *
     * @param pValue the Value
     * @return is value valid true/false
     * @throws OceanusException on error
     */
    private boolean setCharArrayValue(final Object pValue) throws OceanusException {
        /* Handle various forms */
        if (pValue instanceof char[]) {
            setValueValue(pValue);
            return true;
        } else if (pValue instanceof byte[] ba) {
            setValueBytes(ba, char[].class);
            return true;
        } else if (pValue instanceof String s) {
            setValueValue(s.toCharArray());
            return true;
        }
        return false;
    }

    /**
     * Set Money Value.
     *
     * @param pParser the parser
     * @param pValue  the Value
     * @return is value valid true/false
     * @throws OceanusException on error
     */
    private boolean setMoneyValue(final OceanusDecimalParser pParser,
                                  final Object pValue) throws OceanusException {
        /* Handle various forms */
        if (pValue instanceof OceanusMoney) {
            setValueValue(pValue);
            return true;
        } else if (pValue instanceof byte[] ba) {
            setValueBytes(ba, OceanusMoney.class);
            return true;
        } else if (pValue instanceof String s) {
            setValueValue(pParser.parseMoneyValue(s));
            return true;
        }
        return false;
    }

    /**
     * Set Rate Value.
     *
     * @param pParser the parser
     * @param pValue  the Value
     * @return is value valid true/false
     * @throws OceanusException on error
     */
    private boolean setRateValue(final OceanusDecimalParser pParser,
                                 final Object pValue) throws OceanusException {
        /* Handle various forms */
        if (pValue instanceof OceanusRate) {
            setValueValue(pValue);
            return true;
        } else if (pValue instanceof byte[] ba) {
            setValueBytes(ba, OceanusRate.class);
            return true;
        } else if (pValue instanceof String s) {
            setValueValue(pParser.parseRateValue(s));
            return true;
        }
        return false;
    }

    /**
     * Set Ratio Value.
     *
     * @param pParser the parser
     * @param pValue  the Value
     * @return is value valid true/false
     * @throws OceanusException on error
     */
    private boolean setRatioValue(final OceanusDecimalParser pParser,
                                  final Object pValue) throws OceanusException {
        /* Handle various forms */
        if (pValue instanceof OceanusRatio) {
            setValueValue(pValue);
            return true;
        } else if (pValue instanceof byte[] ba) {
            setValueBytes(ba, OceanusRatio.class);
            return true;
        } else if (pValue instanceof String s) {
            setValueValue(pParser.parseRatioValue(s));
            return true;
        }
        return false;
    }

    /**
     * Set Units Value.
     *
     * @param pParser the parser
     * @param pValue  the Value
     * @return is value valid true/false
     * @throws OceanusException on error
     */
    private boolean setUnitsValue(final OceanusDecimalParser pParser,
                                  final Object pValue) throws OceanusException {
        /* Handle various forms */
        if (pValue instanceof OceanusUnits) {
            setValueValue(pValue);
            return true;
        } else if (pValue instanceof byte[] ba) {
            setValueBytes(ba, OceanusUnits.class);
            return true;
        } else if (pValue instanceof String s) {
            setValueValue(pParser.parseUnitsValue(s));
            return true;
        }
        return false;
    }

    /**
     * Set Price Value.
     *
     * @param pParser the parser
     * @param pValue  the Value
     * @return is value valid true/false
     * @throws OceanusException on error
     */
    private boolean setPriceValue(final OceanusDecimalParser pParser,
                                  final Object pValue) throws OceanusException {
        /* Handle various forms */
        if (pValue instanceof OceanusPrice) {
            setValueValue(pValue);
            return true;
        } else if (pValue instanceof byte[] ba) {
            setValueBytes(ba, OceanusPrice.class);
            return true;
        } else if (pValue instanceof String s) {
            setValueValue(pParser.parsePriceValue(s));
            return true;
        }
        return false;
    }

    @Override
    public void rewindToVersion(final int pVersion) {
        /* Do the actual rewind */
        super.rewindToVersion(pVersion);

        /* If this is a linkSet type */
        if (getInfoClass().isLinkSet()) {
            /* Note the rewind */
            rewindInfoLinkSet();
        }
    }

    /**
     * rewind any infoSet links.
     */
    public void rewindInfoLinkSet() {
    }

    @Override
    public int compareValues(final PrometheusDataItem pThat) {
        /* Compare the owner and infoType */
        final PrometheusDataInfoItem myThat = (PrometheusDataInfoItem) pThat;
        int iDiff = getOwner().compareTo(myThat.getOwner());
        if (iDiff == 0) {
            iDiff = getInfoType().compareTo(myThat.getInfoType());
        }
        return iDiff;
    }

    /**
     * List class for DataInfo.
     *
     * @param <T> the DataType
     */
    public abstract static class PrometheusDataInfoList<T extends PrometheusDataInfoItem>
            extends PrometheusEncryptedList<T> {
        /*
         * Report fields.
         */
        static {
            MetisFieldSet.newFieldSet(PrometheusDataInfoList.class);
        }

        /**
         * Construct a generic data info list.
         *
         * @param pBaseClass the class of the underlying object
         * @param pData      the dataSet
         * @param pItemType  the list type
         * @param pStyle     the style of the list
         */
        protected PrometheusDataInfoList(final Class<T> pBaseClass,
                                         final PrometheusDataSet pData,
                                         final MetisListKey pItemType,
                                         final PrometheusListStyle pStyle) {
            super(pBaseClass, pData, pItemType, pStyle);
        }

        /**
         * Constructor for a cloned List.
         *
         * @param pSource the source List
         */
        protected PrometheusDataInfoList(final PrometheusDataInfoList<T> pSource) {
            super(pSource);
        }

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

        @Override
        protected abstract PrometheusDataInfoList<T> getEmptyList(PrometheusListStyle pStyle);

        /**
         * Add new item to the list.
         *
         * @param pOwner    the owner
         * @param pInfoType the information
         * @return the new info item
         */
        protected abstract T addNewItem(PrometheusDataItem pOwner,
                                        PrometheusStaticDataItem pInfoType);

        /**
         * Add an info Item to the list.
         *
         * @param pId        the Id
         * @param pOwner     the owner
         * @param pInfoClass the infoClass
         * @param pValue     the value
         * @throws OceanusException on error
         */
        public abstract void addInfoItem(Integer pId,
                                         PrometheusDataItem pOwner,
                                         PrometheusDataInfoClass pInfoClass,
                                         Object pValue) throws OceanusException;

        @Override
        public void updateMaps() {
            /* No activity, managed by owner */
        }

        @Override
        protected PrometheusDataMapItem allocateDataMap() {
            /* Unused */
            throw new UnsupportedOperationException();
        }
    }
}