PrometheusStaticDataItem.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.MetisDataDifference;
import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataFieldId;
import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataNamedItem;
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 java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Template for a Static Data item and List.
*
* @author Tony Washer
*/
public abstract class PrometheusStaticDataItem
extends PrometheusEncryptedDataItem
implements MetisDataNamedItem {
/**
* Report fields.
*/
private static final PrometheusEncryptedFieldSet<PrometheusStaticDataItem> FIELD_DEFS = PrometheusEncryptedFieldSet.newEncryptedFieldSet(PrometheusStaticDataItem.class);
/*
* FieldIds.
*/
static {
FIELD_DEFS.declareEncryptedStringField(PrometheusDataResource.DATAITEM_FIELD_NAME, NAMELEN);
FIELD_DEFS.declareEncryptedStringField(PrometheusDataResource.DATAITEM_FIELD_DESC, DESCLEN);
FIELD_DEFS.declareBooleanField(PrometheusDataResource.STATICDATA_ENABLED);
FIELD_DEFS.declareIntegerField(PrometheusDataResource.STATICDATA_SORT);
FIELD_DEFS.declareEnumField(PrometheusDataResource.STATICDATA_CLASS);
}
/**
* BadId error.
*/
public static final String ERROR_BADID = PrometheusDataResource.STATICDATA_ERROR_ID.getValue();
/**
* BadName error.
*/
public static final String ERROR_BADNAME = PrometheusDataResource.STATICDATA_ERROR_NAME.getValue();
/**
* The Enum Class for this Static Data.
*/
private final Class<? extends PrometheusStaticDataClass> theEnumClass;
/**
* Copy Constructor.
*
* @param pList The list to associate the Static Data with
* @param pSource The static data to copy
*/
protected PrometheusStaticDataItem(final PrometheusStaticList<?> pList,
final PrometheusStaticDataItem pSource) {
super(pList, pSource);
theEnumClass = pSource.getEnumClass();
setIndexedId(pSource.getIndexedId());
}
/**
* Basic Constructor.
*
* @param pList The list to associate the Static Data with
* @param pValue the name of the new item
* @throws OceanusException on error
*/
protected PrometheusStaticDataItem(final PrometheusStaticList<?> pList,
final String pValue) throws OceanusException {
/* Call super constructor */
super(pList, 0);
/* Determine the class */
theEnumClass = pList.getEnumClass();
parseEnumValue(pValue);
/* Record the name */
setValueName(pValue);
setValueEnabled(Boolean.TRUE);
}
/**
* Basic Constructor.
*
* @param pList The list to associate the Static Data with
* @param pClass the class of the new item
* @throws OceanusException on error
*/
protected PrometheusStaticDataItem(final PrometheusStaticList<?> pList,
final PrometheusStaticDataClass pClass) throws OceanusException {
/* Call super constructor */
super(pList, 0);
/* Determine the class */
theEnumClass = pList.getEnumClass();
/* Store the class */
setValueClass(pClass);
/* Set encryption */
setNextDataKeySet();
/* Access classId and order */
setIndexedId(pClass.getClassId());
setValueOrder(pClass.getOrder());
/* Record the name */
setValueName(pClass.toString());
setValueEnabled(Boolean.TRUE);
/* Set the DataKeySet */
setNextDataKeySet();
}
/**
* Values constructor.
*
* @param pList the List to add to
* @param pValues the values constructor
* @throws OceanusException on error
*/
protected PrometheusStaticDataItem(final PrometheusStaticList<?> pList,
final PrometheusDataValues pValues) throws OceanusException {
/* Initialise the item */
super(pList, pValues);
/* Access formatter */
final OceanusDataFormatter myFormatter = getDataSet().getDataFormatter();
/* Protect against exceptions */
try {
/* Determine the class */
theEnumClass = pList.getEnumClass();
/* Store the Name */
Object myValue = pValues.getValue(PrometheusDataResource.DATAITEM_FIELD_NAME);
if (myValue instanceof String s) {
setValueName(s);
} else if (myValue instanceof byte[] ba) {
setValueName(ba);
}
/* Store the Description */
myValue = pValues.getValue(PrometheusDataResource.DATAITEM_FIELD_DESC);
if (myValue instanceof String s) {
setValueDesc(s);
} else if (myValue instanceof byte[] ba) {
setValueDesc(ba);
}
/* Store the class */
myValue = pValues.getValue(PrometheusDataResource.STATICDATA_CLASS);
if (myValue instanceof String s) {
parseEnumValue(s);
} else {
parseEnumValue(getIndexedId());
}
/* Store the Order */
myValue = pValues.getValue(PrometheusDataResource.STATICDATA_SORT);
if (myValue instanceof Integer i) {
setValueOrder(i);
} else if (myValue instanceof String s) {
setValueOrder(myFormatter.parseValue(s, Integer.class));
}
/* Store the Enabled flag */
myValue = pValues.getValue(PrometheusDataResource.STATICDATA_ENABLED);
if (myValue instanceof Boolean b) {
setValueEnabled(b);
} else if (myValue instanceof String s) {
setValueEnabled(myFormatter.parseValue(s, Boolean.class));
} else {
setValueEnabled(Boolean.TRUE);
}
/* Catch Exceptions */
} catch (NumberFormatException
| OceanusException e) {
/* Pass on exception */
throw new PrometheusDataException(this, ERROR_CREATEITEM, e);
}
}
@Override
public MetisFieldSetDef getDataFieldSet() {
return FIELD_DEFS;
}
@Override
public String formatObject(final OceanusDataFormatter pFormatter) {
return toString();
}
@Override
public String toString() {
return getName();
}
@Override
public boolean includeXmlField(final MetisDataFieldId pField) {
/* Determine whether fields should be included */
if (PrometheusDataResource.DATAITEM_FIELD_NAME.equals(pField)) {
return true;
}
if (PrometheusDataResource.DATAITEM_FIELD_DESC.equals(pField)) {
return getDesc() != null;
}
if (PrometheusDataResource.STATICDATA_ENABLED.equals(pField)) {
return !getEnabled();
}
if (PrometheusDataResource.STATICDATA_CLASS.equals(pField)) {
return !getName().equalsIgnoreCase(getStaticClass().name());
}
/* Pass call on */
return super.includeXmlField(pField);
}
@Override
public final String getName() {
return getValues().getValue(PrometheusDataResource.DATAITEM_FIELD_NAME, String.class);
}
/**
* Return the encrypted name of the Static Data.
*
* @return the encrypted name
*/
public final byte[] getNameBytes() {
return getValues().getEncryptedBytes(PrometheusDataResource.DATAITEM_FIELD_NAME);
}
/**
* Return the encrypted name field of the Static Data.
*
* @return the encrypted field
*/
private PrometheusEncryptedPair getNameField() {
return getValues().getEncryptedPair(PrometheusDataResource.DATAITEM_FIELD_NAME);
}
/**
* Return the description of the Static Data.
*
* @return the description
*/
public final String getDesc() {
return getValues().getValue(PrometheusDataResource.DATAITEM_FIELD_DESC, String.class);
}
/**
* Return the encrypted description of the Static Data.
*
* @return the encrypted description
*/
public final byte[] getDescBytes() {
return getValues().getEncryptedBytes(PrometheusDataResource.DATAITEM_FIELD_DESC);
}
/**
* Return the encrypted description field of the Static Data.
*
* @return the encrypted name
*/
private PrometheusEncryptedPair getDescField() {
return getValues().getEncryptedPair(PrometheusDataResource.DATAITEM_FIELD_DESC);
}
/**
* Return the sort order of the Static Data.
*
* @return the order
*/
public final Integer getOrder() {
return getValues().getValue(PrometheusDataResource.STATICDATA_SORT, Integer.class);
}
/**
* Return the Static class of the Static Data.
*
* @return the class
*/
public final PrometheusStaticDataClass getStaticClass() {
return getValues().getValue(PrometheusDataResource.STATICDATA_CLASS, PrometheusStaticDataClass.class);
}
/**
* Is the Static item enabled.
*
* @return <code>true/false</code>
*/
public final boolean getEnabled() {
return getValues().getValue(PrometheusDataResource.STATICDATA_ENABLED, Boolean.class);
}
@Override
public boolean isDisabled() {
return !getEnabled();
}
/**
* Obtain the Enum class of this Static Data.
*
* @return the class
*/
protected final Class<? extends PrometheusStaticDataClass> getEnumClass() {
return theEnumClass;
}
/**
* Set the Name.
*
* @param pValue the name
* @throws OceanusException on error
*/
private void setValueName(final String pValue) throws OceanusException {
setEncryptedValue(PrometheusDataResource.DATAITEM_FIELD_NAME, pValue);
}
/**
* Set the Name.
*
* @param pBytes the encrypted name
* @throws OceanusException on error
*/
private void setValueName(final byte[] pBytes) throws OceanusException {
setEncryptedValue(PrometheusDataResource.DATAITEM_FIELD_NAME, pBytes, String.class);
}
/**
* Set the Name field.
*
* @param pField the encrypted name
*/
private void setValueName(final PrometheusEncryptedPair pField) {
getValues().setUncheckedValue(PrometheusDataResource.DATAITEM_FIELD_NAME, pField);
}
/**
* Set the Description.
*
* @param pValue the description
* @throws OceanusException on error
*/
protected final void setValueDesc(final String pValue) throws OceanusException {
setEncryptedValue(PrometheusDataResource.DATAITEM_FIELD_DESC, pValue);
}
/**
* Set the Description.
*
* @param pBytes the encrypted description
* @throws OceanusException on error
*/
private void setValueDesc(final byte[] pBytes) throws OceanusException {
setEncryptedValue(PrometheusDataResource.DATAITEM_FIELD_DESC, pBytes, String.class);
}
/**
* Set the Description field.
*
* @param pField the encrypted description
*/
private void setValueDesc(final PrometheusEncryptedPair pField) {
getValues().setUncheckedValue(PrometheusDataResource.DATAITEM_FIELD_DESC, pField);
}
/**
* Set the Enabled flag.
*
* @param isEnabled TRUE/FALSE
*/
protected final void setValueEnabled(final Boolean isEnabled) {
getValues().setUncheckedValue(PrometheusDataResource.STATICDATA_ENABLED, isEnabled);
}
/**
* Set the Order.
*
* @param pOrder the order
*/
private void setValueOrder(final Integer pOrder) {
getValues().setUncheckedValue(PrometheusDataResource.STATICDATA_SORT, pOrder);
}
/**
* Set the Class.
*
* @param pClass the class
*/
private void setValueClass(final PrometheusStaticDataClass pClass) {
getValues().setUncheckedValue(PrometheusDataResource.STATICDATA_CLASS, pClass);
}
@Override
public int compareValues(final PrometheusDataItem pThat) {
/* Access as StaticDataItem */
final PrometheusStaticDataItem myThat = (PrometheusStaticDataItem) pThat;
/* Make sure that the object is the same enumeration class */
if (!MetisDataDifference.isEqual(getEnumClass(), myThat.getEnumClass())) {
/* Order the classes by canonical name */
return getEnumClass().getCanonicalName().compareTo(myThat.getEnumClass().getCanonicalName());
}
/* Compare on order and name */
final int iDiff = getOrder() - myThat.getOrder();
return iDiff != 0 ? iDiff : MetisDataDifference.compareObject(getName(), myThat.getName());
}
@Override
public PrometheusStaticList<?> getList() {
return (PrometheusStaticList<?>) super.getList();
}
/**
* Parse enum type.
*
* @param pValue the value
* @throws OceanusException on error
*/
private void parseEnumValue(final String pValue) throws OceanusException {
final Class<? extends PrometheusStaticDataClass> myClass = getEnumClass();
final PrometheusStaticDataClass[] myEnums = myClass.getEnumConstants();
/* Loop through the enum constants */
for (PrometheusStaticDataClass myValue : myEnums) {
/* If this is the desired value */
if (myValue.toString().equalsIgnoreCase(pValue)) {
/* Store the class */
setValueClass(myValue);
/* Access classId and order */
setIndexedId(myValue.getClassId());
setValueOrder(myValue.getOrder());
break;
}
}
/* Reject if we didn't find the class */
if (getStaticClass() == null) {
throw new PrometheusDataException(ERROR_BADNAME + " " + myClass.getSimpleName() + ": " + pValue);
}
}
/**
* Parse enum type.
*
* @param pValue the value
* @throws OceanusException on error
*/
private void parseEnumValue(final Integer pValue) throws OceanusException {
final Class<? extends PrometheusStaticDataClass> myClass = getEnumClass();
final PrometheusStaticDataClass[] myEnums = myClass.getEnumConstants();
/* Loop through the enum constants */
for (PrometheusStaticDataClass myValue : myEnums) {
/* If this is the desired value */
if (pValue.equals(myValue.getClassId())) {
/* Store the class */
setValueClass(myValue);
/* Access classId and order */
setIndexedId(myValue.getClassId());
setValueOrder(myValue.getOrder());
break;
}
}
/* Reject if we didn't find the class */
if (getStaticClass() == null) {
throw new PrometheusDataException(ERROR_BADNAME + " " + myClass.getSimpleName() + ": " + pValue);
}
}
/**
* Set a new name.
*
* @param pName the name
* @throws OceanusException on error
*/
public void setName(final String pName) throws OceanusException {
setValueName(pName);
}
/**
* Set a new description.
*
* @param pDesc the description
* @throws OceanusException on error
*/
public void setDescription(final String pDesc) throws OceanusException {
/* Set the appropriate value */
setValueDesc(pDesc);
}
/**
* Set Enabled indicator.
*
* @param isEnabled TRUE/FALSE
*/
public void setEnabled(final boolean isEnabled) {
/* Set the appropriate value */
setValueEnabled(isEnabled);
}
/**
* Set Order indicator.
*
* @param iOrder the order
*/
public void setOrder(final int iOrder) {
/* Set the appropriate value */
setValueOrder(iOrder);
}
@Override
public boolean applyChanges(final PrometheusDataItem pData) {
/* Can only apply changes for Static Data */
if (!(pData instanceof PrometheusStaticDataItem)) {
return false;
}
/* Access the data */
final PrometheusStaticDataItem myData = (PrometheusStaticDataItem) pData;
/* Store the current detail into history */
pushHistory();
/* Apply basic changes */
applyBasicChanges(myData);
/* Check for changes */
return checkForHistory();
}
/**
* Apply basic changes.
*
* @param pData the changed element
*/
protected void applyBasicChanges(final PrometheusStaticDataItem pData) {
/* Update the name if required */
if (!MetisDataDifference.isEqual(getName(), pData.getName())) {
setValueName(pData.getNameField());
}
/* Update the description if required */
if (!MetisDataDifference.isEqual(getDesc(), pData.getDesc())) {
setValueDesc(pData.getDescField());
}
/* Update the enabled indication if required */
if (!MetisDataDifference.isEqual(getEnabled(), pData.getEnabled())) {
setEnabled(pData.getEnabled());
}
/* Update the order indication if required */
if (!MetisDataDifference.isEqual(getOrder(), pData.getOrder())) {
setOrder(pData.getOrder());
}
}
@Override
public void adjustMapForItem() {
final PrometheusStaticList<?> myList = getList();
final PrometheusStaticDataMap<?> myMap = myList.getDataMap();
myMap.adjustForItem(myList.getBaseClass().cast(this));
}
/**
* Represents a list of StaticData objects.
*
* @param <T> the item type
*/
public abstract static class PrometheusStaticList<T extends PrometheusStaticDataItem>
extends PrometheusEncryptedList<T> {
/*
* Report fields.
*/
static {
MetisFieldSet.newFieldSet(PrometheusStaticList.class);
}
/**
* Construct a generic static data list.
*
* @param pBaseClass the class of the underlying object
* @param pData the dataSet
* @param pItemType the item type
* @param pStyle the style of the list
*/
protected PrometheusStaticList(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 PrometheusStaticList(final PrometheusStaticList<T> pSource) {
super(pSource);
}
/**
* Obtain the enumClass.
*
* @return the enumClass
*/
protected abstract Class<? extends PrometheusStaticDataClass> getEnumClass();
@Override
@SuppressWarnings("unchecked")
public PrometheusStaticDataMap<T> getDataMap() {
return (PrometheusStaticDataMap<T>) super.getDataMap();
}
/**
* Search for a particular item by class.
*
* @param eClass The class of the item to search for
* @return The Item if present (or <code>null</code> if not found)
*/
public T findItemByClass(final PrometheusStaticDataClass eClass) {
/* Look for item by class Id */
return findItemById(eClass.getClassId());
}
@Override
public T findItemByName(final String pName) {
/* look up the name in the map */
return getDataMap().findItemByName(pName);
}
/**
* Is the list full?
*
* @return true/false
*/
public boolean isFull() {
/* We can only be full with the correct number of items */
if (size() < getEnumClass().getEnumConstants().length) {
return false;
}
/* Loop through all elements */
final Iterator<T> myIterator = iterator();
while (myIterator.hasNext()) {
final T myCurr = myIterator.next();
/* If the item is deleted */
if (myCurr.isDeleted()) {
/* Not full */
return false;
}
}
/* Must be full */
return true;
}
/**
* Obtain a list of classes that are missing/deleted.
*
* @return The List of classes
*/
public List<PrometheusStaticDataClass> getMissingClasses() {
/* Allocate the list */
final List<PrometheusStaticDataClass> myList = new ArrayList<>();
/* Loop through all elements */
for (PrometheusStaticDataClass myClass : getEnumClass().getEnumConstants()) {
/* Locate the element */
final T myItem = findItemById(myClass.getClassId());
/* If the item is missing or deleted */
if ((myItem == null)
|| myItem.isDeleted()) {
/* Add it to the list */
myList.add(myClass);
}
}
/* Return the list */
return myList;
}
/**
* Add Item for class.
*
* @param pClass the class to add
* @return the added class
* @throws OceanusException on error
*/
public T addNewItem(final PrometheusStaticDataClass pClass) throws OceanusException {
/* Create the new item */
return newItem(pClass);
}
/**
* Create new Item for class.
*
* @param pClass the class to create
* @return the created class
* @throws OceanusException on error
*/
protected abstract T newItem(PrometheusStaticDataClass pClass) throws OceanusException;
/**
* Populate default values.
*
* @throws OceanusException on error
*/
public void populateDefaults() throws OceanusException {
/* Loop through all elements */
for (PrometheusStaticDataClass myClass : getEnumClass().getEnumConstants()) {
/* Create new element */
final T myItem = newItem(myClass);
/* Validate the item */
myItem.validate();
/* Handle validation failure */
if (myItem.hasErrors()) {
throw new PrometheusDataException(myItem, ERROR_VALIDATION);
}
}
/* Ensure that the list is sorted */
reSort();
}
@Override
protected PrometheusDataMapItem allocateDataMap() {
return new PrometheusStaticDataMap<>();
}
}
/**
* The dataMap class.
*
* @param <T> the item type
*/
public static class PrometheusStaticDataMap<T extends PrometheusStaticDataItem>
extends PrometheusDataInstanceMap<T, String> {
/**
* Report fields.
*/
@SuppressWarnings("rawtypes")
private static final MetisFieldSet<PrometheusStaticDataMap> FIELD_DEFS = MetisFieldSet.newFieldSet(PrometheusStaticDataMap.class);
/*
* Declare Fields.
*/
static {
FIELD_DEFS.declareLocalField(PrometheusDataResource.STATICDATAMAP_ORDERCOUNTS, PrometheusStaticDataMap::getOrderCountMap);
}
/**
* Map of order counts.
*/
private final Map<Integer, Integer> theOrderCountMap;
/**
* Constructor.
*/
public PrometheusStaticDataMap() {
/* Create the maps */
theOrderCountMap = new HashMap<>();
}
@SuppressWarnings("rawtypes")
@Override
public MetisFieldSet<? extends PrometheusStaticDataMap> getDataFieldSet() {
return FIELD_DEFS;
}
@Override
public String formatObject(final OceanusDataFormatter pFormatter) {
return FIELD_DEFS.getName();
}
/**
* Obtain the keyMap.
*
* @return the map
*/
private Map<Integer, Integer> getOrderCountMap() {
return theOrderCountMap;
}
@Override
public void resetMap() {
super.resetMap();
theOrderCountMap.clear();
}
@Override
@SuppressWarnings("unchecked")
public void adjustForItem(final PrometheusDataItem pItem) {
/* Access item */
final T myItem = (T) pItem;
/* Adjust order count */
final Integer myOrder = myItem.getOrder();
final Integer myCount = theOrderCountMap.get(myOrder);
if (myCount == null) {
theOrderCountMap.put(myOrder, ONE);
} else {
theOrderCountMap.put(myOrder, myCount + 1);
}
/* Adjust name count */
adjustForItem(myItem, myItem.getName());
}
/**
* find item by name.
*
* @param pName the name to look up
* @return the matching item
*/
public T findItemByName(final String pName) {
return findItemByKey(pName);
}
/**
* Check validity of name.
*
* @param pName the name to look up
* @return true/false
*/
public boolean validNameCount(final String pName) {
return validKeyCount(pName);
}
/**
* Check availability of name.
*
* @param pName the key to look up
* @return true/false
*/
public boolean availableName(final String pName) {
return availableKey(pName);
}
/**
* Check validity of order.
*
* @param pOrder the order to look up
* @return true/false
*/
public boolean validOrderCount(final Integer pOrder) {
final Integer myResult = theOrderCountMap.get(pOrder);
return ONE.equals(myResult);
}
}
}