PrometheusDataValues.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.format.OceanusDataFormatter;
import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataFieldId;
import io.github.tonywasher.joceanus.metis.data.MetisDataResource;
import io.github.tonywasher.joceanus.metis.field.MetisFieldItem.MetisFieldDef;
import io.github.tonywasher.joceanus.metis.field.MetisFieldItem.MetisFieldSetDef;
import io.github.tonywasher.joceanus.metis.field.MetisFieldItem.MetisFieldVersionedDef;
import io.github.tonywasher.joceanus.metis.field.MetisFieldVersionValues;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* Arguments class for DataItem.
*
* @author Tony Washer
*/
public class PrometheusDataValues {
/**
* Interface for an infoSet item.
*/
@FunctionalInterface
public interface PrometheusInfoSetItem {
/**
* Obtain infoSet.
*
* @return the infoSet
*/
PrometheusDataInfoSet<?> getInfoSet();
}
/**
* Interface for a grouped item.
*/
public interface PrometheusGroupedItem {
/**
* Is the item a child.
*
* @return true/false
*/
boolean isChild();
/**
* Obtain the child iterator.
*
* @return the iterator
*/
Iterator<? extends PrometheusDataItem> childIterator();
}
/**
* InfoSet Items tag.
*/
private static final String TAG_INFOSET = PrometheusDataResource.DATAINFOSET_NAME.getValue();
/**
* Children Items tag.
*/
private static final String TAG_CHILDREN = PrometheusDataResource.DATAVALUES_CHILDREN.getValue();
/**
* Child Item tag.
*/
private static final String TAG_CHILD = PrometheusDataResource.DATAVALUES_CHILD.getValue();
/**
* List type attribute.
*/
protected static final String ATTR_TYPE = PrometheusDataResource.DATAVALUES_ATTRTYPE.getValue();
/**
* List size attribute.
*/
protected static final String ATTR_SIZE = PrometheusDataResource.DATAVALUES_ATTRSIZE.getValue();
/**
* Data Version attribute.
*/
protected static final String ATTR_VERS = PrometheusDataResource.DATAVALUES_ATTRVER.getValue();
/**
* The item type.
*/
private final String theItemType;
/**
* Field Definitions.
*/
private final Map<MetisDataFieldId, Object> theFields;
/**
* InfoSet values.
*/
private final List<PrometheusInfoItem> theInfoItems;
/**
* Child values.
*/
private final List<PrometheusDataValues> theChildren;
/**
* Constructor.
*
* @param pItem the Item to obtain values from
* @param pItemName the item name
*/
protected PrometheusDataValues(final PrometheusDataItem pItem,
final String pItemName) {
/* Store Item type */
theItemType = pItemName;
/* Create the map and list */
theFields = new LinkedHashMap<>();
/* Store the id */
theFields.put(MetisDataResource.DATA_ID, pItem.getIndexedId());
/* Access values */
final MetisFieldVersionValues myValues = pItem.getValues();
/* Iterate through the fields */
final Iterator<MetisFieldDef> myIterator = pItem.getDataFieldSet().fieldIterator();
while (myIterator.hasNext()) {
final MetisFieldDef myField = myIterator.next();
final MetisDataFieldId myFieldId = myField.getFieldId();
/* Ignore field if it is irrelevant */
if (!(myField instanceof MetisFieldVersionedDef myVersioned)
|| !myVersioned.isEquality()) {
continue;
}
/* If the field is to be included */
if (pItem.includeXmlField(myFieldId)) {
/* Store the value if it is non-null */
theFields.put(myFieldId, myValues.getValue(myField));
}
}
/* If the item is an infoSet item */
if (pItem instanceof PrometheusInfoSetItem myInfoItem) {
/* Access InfoSet */
final PrometheusDataInfoSet<?> myInfoSet = myInfoItem.getInfoSet();
/* If the InfoSet is non-empty */
if (myInfoSet.isEmpty()) {
/* No infoSet items */
theInfoItems = null;
} else {
/* Allocate infoItems list */
theInfoItems = new ArrayList<>();
/* Iterator over the values */
final Iterator<?> myInfoIterator = myInfoSet.iterator();
while (myInfoIterator.hasNext()) {
final Object myCurr = myInfoIterator.next();
/* If this is a DataInfo item */
if (myCurr instanceof PrometheusDataInfoItem myItem) {
/* Add item to the list */
final PrometheusInfoItem myInfo = new PrometheusInfoItem(myItem);
theInfoItems.add(myInfo);
}
}
}
/* Else not that we have no infoItems */
} else {
theInfoItems = null;
}
/* If the item is a grouped item */
if (pItem instanceof PrometheusGroupedItem myGrouped) {
/* Access child iterator */
final Iterator<? extends PrometheusDataItem> myChildIterator = myGrouped.childIterator();
/* If there are no children */
if (myChildIterator == null) {
theChildren = null;
} else {
/* Allocate child list */
theChildren = new ArrayList<>();
/* Iterator over the values */
while (myChildIterator.hasNext()) {
final PrometheusDataItem myCurr = myChildIterator.next();
/* Add child to the list */
final PrometheusDataValues myChild = new PrometheusDataValues(myCurr, TAG_CHILD);
theChildren.add(myChild);
}
}
/* Else note that we have no children */
} else {
theChildren = null;
}
}
/**
* Constructor.
*
* @param pOwner the Owner of the DataInfo Item
* @param pInfo the values of the DataInfo Item
*/
private PrometheusDataValues(final PrometheusDataItem pOwner,
final PrometheusInfoItem pInfo) {
/* Store Item type */
theItemType = "";
/* Create the map and list */
theFields = new LinkedHashMap<>();
/* Store the id if available */
final Integer myId = pInfo.getId();
if (myId != null) {
theFields.put(MetisDataResource.DATA_ID, myId);
}
/* Store the Info Type */
theFields.put(PrometheusDataResource.DATAINFO_TYPE, pInfo.getName());
/* Store the Owner */
theFields.put(PrometheusDataResource.DATAINFO_OWNER, pOwner.getIndexedId());
/* Store the value */
theFields.put(PrometheusDataResource.DATAINFO_VALUE, pInfo.getValue());
/* Set other fields to null */
theInfoItems = null;
theChildren = null;
}
/**
* Constructor.
*
* @param pElement the Item to obtain values from
* @param pFields the field definitions
*/
public PrometheusDataValues(final Element pElement,
final MetisFieldSetDef pFields) {
this(pElement, pFields, pElement.getNodeName());
}
/**
* Constructor.
*
* @param pElement the Item to obtain values from
* @param pFields the field definitions
* @param pItemName the item name
*/
protected PrometheusDataValues(final Element pElement,
final MetisFieldSetDef pFields,
final String pItemName) {
/* Store Item type */
theItemType = pItemName;
/* Create the map */
theFields = new LinkedHashMap<>();
/* Declare the id if it exists */
final Integer myId = getId(pElement);
if (myId != null) {
theFields.put(MetisDataResource.DATA_ID, myId);
}
/* Loop through the children */
final Iterator<MetisFieldDef> myIterator = pFields.fieldIterator();
while (myIterator.hasNext()) {
final MetisFieldDef myField = myIterator.next();
/* If the field is an equality valueSet item */
if (myField instanceof MetisFieldVersionedDef myVersioned
&& myVersioned.isEquality()) {
/* Access element */
final Element myChild = getChild(pElement, myField.getFieldId().getId());
if (myChild != null) {
/* Put value */
theFields.put(myField.getFieldId(), myChild.getTextContent());
}
}
}
/* Look for an InfoSet list */
final Element myInfoSet = getChild(pElement, TAG_INFOSET);
if (myInfoSet != null) {
/* Allocate infoItems list */
theInfoItems = new ArrayList<>();
/* Loop through the child values */
for (Node myCurr = myInfoSet.getFirstChild(); myCurr != null; myCurr = myCurr.getNextSibling()) {
/* If the child is an element */
if (myCurr instanceof Element myChild) {
/* Add item to the list */
final PrometheusInfoItem myInfo = new PrometheusInfoItem(myChild);
theInfoItems.add(myInfo);
}
}
/* Else not that we have no infoItems */
} else {
theInfoItems = null;
}
/* Look for children */
final Element myChildren = getChild(pElement, TAG_CHILDREN);
if (myChildren != null) {
/* Allocate infoItems list */
theChildren = new ArrayList<>();
/* Loop through the child values */
for (Node myCurr = myChildren.getFirstChild(); myCurr != null; myCurr = myCurr.getNextSibling()) {
/* If the child is the correct element */
if (myCurr instanceof Element myChild
&& TAG_CHILD.equals(myCurr.getNodeName())) {
/* Add item to the list */
final PrometheusDataValues myValues = new PrometheusDataValues(myChild, pFields, theItemType);
theChildren.add(myValues);
}
}
/* Else not that we have no children */
} else {
theChildren = null;
}
}
/**
* Constructor.
*
* @param pName the Item type
*/
public PrometheusDataValues(final String pName) {
/* Store Item type */
theItemType = pName;
/* Create the map */
theFields = new LinkedHashMap<>();
/* No underlying arrays */
theInfoItems = null;
theChildren = null;
}
/**
* Constructor.
*
* @param pItem the Item to obtain values from
*/
public PrometheusDataValues(final PrometheusDataItem pItem) {
this(pItem, pItem.getDataFieldSet().getName());
}
/**
* Obtain Item Type.
*
* @return the Item Type
*/
public final String getItemType() {
return theItemType;
}
/**
* Obtain Field iterator.
*
* @return the Field iterator
*/
public final Iterator<Entry<MetisDataFieldId, Object>> fieldIterator() {
return theFields.entrySet().iterator();
}
/**
* Does this item have InfoItems?
*
* @return true/false
*/
public final boolean hasInfoItems() {
return theInfoItems != null;
}
/**
* Obtain InfoItems iterator.
*
* @return the iterator
*/
public final Iterator<PrometheusInfoItem> infoIterator() {
return theInfoItems.iterator();
}
/**
* Does this item have children?
*
* @return true/false
*/
public final boolean hasChildren() {
return theChildren != null;
}
/**
* Obtain Child iterator.
*
* @return the iterator
*/
public final Iterator<PrometheusDataValues> childIterator() {
return theChildren.iterator();
}
/**
* Add value.
*
* @param pField the Field definition
* @param pValue the field value
*/
public void addValue(final MetisDataFieldId pField,
final Object pValue) {
/* If the value is non-null */
if (pValue != null) {
/* Add the field */
theFields.put(pField, pValue);
}
}
/**
* Obtain value.
*
* @param pField the Field definition
* @return the field value
*/
public Object getValue(final MetisDataFieldId pField) {
/* Return the field */
return theFields.get(pField);
}
/**
* Obtain value of specified class.
*
* @param pField the Field definition
* @param pClass the class
* @param <T> the item type
* @return the field value
*/
public <T> T getValue(final MetisDataFieldId pField,
final Class<T> pClass) {
/* Return the properly cast field */
return pClass.cast(getValue(pField));
}
/**
* Obtain id from element.
*
* @param pElement the element.
* @return the id
*/
private static Integer getId(final Element pElement) {
/* Access the id */
final String myId = pElement.getAttribute(MetisDataResource.DATA_ID.getId());
return !myId.isEmpty()
? Integer.parseInt(myId)
: null;
}
/**
* Obtain child element with given name.
*
* @param pParent the parent element
* @param pName the element name
* @return the element
*/
private static Element getChild(final Element pParent,
final String pName) {
/* Loop through the child values */
for (Node myCurr = pParent.getFirstChild(); myCurr != null; myCurr = myCurr.getNextSibling()) {
/* If the child is the correct element */
if (myCurr instanceof Element myElement
&& pName.equals(myCurr.getNodeName())) {
/* Return the element */
return myElement;
}
}
/* Not found */
return null;
}
/**
* Create XML element for item.
*
* @param pDocument the document to hold the item.
* @param pFormatter the data formatter
* @param pStoreIds do we include IDs in XML
* @return the new element
*/
protected Element createXML(final Document pDocument,
final OceanusDataFormatter pFormatter,
final boolean pStoreIds) {
/* Create an element for the item */
final Element myElement = pDocument.createElement(theItemType);
/* Loop through the values */
for (Entry<MetisDataFieldId, Object> myEntry : theFields.entrySet()) {
/* Access parts */
final MetisDataFieldId myFieldId = myEntry.getKey();
final Object myValue = myEntry.getValue();
/* If this is the Id */
if (MetisDataResource.DATA_ID.equals(myFieldId)) {
/* Add as an Attribute if required */
if (pStoreIds) {
myElement.setAttribute(myFieldId.getId(), myValue.toString());
}
/* Skip to next field */
continue;
}
/* Create the child element */
final Element myChild = pDocument.createElement(myFieldId.getId());
myElement.appendChild(myChild);
/* Store the value */
myChild.setTextContent(pFormatter.formatObject(myValue));
}
/* If we have InfoSet items */
if (theInfoItems != null) {
/* Add infoSet */
final Element myInfoSet = pDocument.createElement(TAG_INFOSET);
myElement.appendChild(myInfoSet);
/* Loop through the items */
for (PrometheusInfoItem myInfo : theInfoItems) {
/* Create the element */
final Element myItem = pDocument.createElement(myInfo.getName());
myInfoSet.appendChild(myItem);
/* Set the id if required */
if (pStoreIds) {
myItem.setAttribute(MetisDataResource.DATA_ID.getValue(), myInfo.getId().toString());
}
/* Set the value */
myItem.setTextContent(pFormatter.formatObject(myInfo.getValue()));
}
}
/* If we have children */
if (theChildren != null) {
/* Add children */
final Element myChildren = pDocument.createElement(TAG_CHILDREN);
myElement.appendChild(myChildren);
/* Loop through the children */
final Iterator<PrometheusDataValues> myIterator = theChildren.iterator();
while (myIterator.hasNext()) {
final PrometheusDataValues myValues = myIterator.next();
/* Create the subElement and append */
final Element myChild = myValues.createXML(pDocument, pFormatter, pStoreIds);
myChildren.appendChild(myChild);
}
}
/* Return the element */
return myElement;
}
/**
* InfoItem class.
*/
public static final class PrometheusInfoItem {
/**
* Name of item.
*/
private final String theName;
/**
* Id of item.
*/
private final Integer theId;
/**
* Value of item.
*/
private final Object theValue;
/**
* Constructor.
*
* @param pInfo the info Item
*/
private PrometheusInfoItem(final PrometheusDataInfoItem pInfo) {
/* Access the infoClass */
final PrometheusDataInfoClass myClass = pInfo.getInfoClass();
/* Store values */
theName = myClass.toString();
theId = pInfo.getIndexedId();
theValue = myClass.isLink()
? pInfo.getLink()
: pInfo.getValue(Object.class);
}
/**
* Constructor.
*
* @param pElement the XML element
*/
private PrometheusInfoItem(final Element pElement) {
/* Store values */
theName = pElement.getNodeName();
theId = PrometheusDataValues.getId(pElement);
theValue = pElement.getTextContent();
}
/**
* Obtain name of item.
*
* @return the name
*/
public String getName() {
return theName;
}
/**
* Obtain id of item.
*
* @return the id
*/
public Integer getId() {
return theId;
}
/**
* Obtain value of item.
*
* @return the value
*/
public Object getValue() {
return theValue;
}
@Override
public boolean equals(final Object pThat) {
/* Handle the trivial cases */
if (this == pThat) {
return true;
}
if (pThat == null) {
return false;
}
/* Make sure that the object is the same class */
if (pThat.getClass() != getClass()) {
return false;
}
/* Access the object as an InfoItem */
final PrometheusInfoItem myItem = (PrometheusInfoItem) pThat;
if (!theName.equals(myItem.getName())) {
return false;
}
if (!theId.equals(myItem.getId())) {
return false;
}
return theValue.equals(myItem.getValue());
}
@Override
public int hashCode() {
return theName.hashCode() + theId + theValue.hashCode();
}
@Override
public String toString() {
return theName + "=" + theValue;
}
/**
* Obtain DataValues.
*
* @param pOwner the owner
* @return the dataValues
*/
public PrometheusDataValues getValues(final PrometheusDataItem pOwner) {
return new PrometheusDataValues(pOwner, this);
}
}
}