View Javadoc
1   /*
2    * Prometheus: Application Framework
3    * Copyright 2012-2026. Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6    * use this file except in compliance with the License.  You may obtain a copy
7    * of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
16   */
17  package io.github.tonywasher.joceanus.prometheus.data;
18  
19  import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataFieldId;
20  import io.github.tonywasher.joceanus.metis.data.MetisDataResource;
21  import io.github.tonywasher.joceanus.metis.field.MetisFieldItem.MetisFieldDef;
22  import io.github.tonywasher.joceanus.metis.field.MetisFieldItem.MetisFieldSetDef;
23  import io.github.tonywasher.joceanus.metis.field.MetisFieldItem.MetisFieldVersionedDef;
24  import io.github.tonywasher.joceanus.metis.field.MetisFieldVersionValues;
25  import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
26  import io.github.tonywasher.joceanus.prometheus.data.PrometheusData.PrometheusDataItemCtl;
27  import io.github.tonywasher.joceanus.prometheus.data.PrometheusData.PrometheusDataValuesCtl;
28  import io.github.tonywasher.joceanus.prometheus.data.PrometheusData.PrometheusGroupedItemCtl;
29  import io.github.tonywasher.joceanus.prometheus.data.PrometheusEncrypted.PrometheusDataInfoItemCtl;
30  import io.github.tonywasher.joceanus.prometheus.data.PrometheusEncrypted.PrometheusDataInfoSetCtl;
31  import io.github.tonywasher.joceanus.prometheus.data.PrometheusEncrypted.PrometheusDataInfoSetItemCtl;
32  import org.w3c.dom.Document;
33  import org.w3c.dom.Element;
34  import org.w3c.dom.Node;
35  
36  import java.util.ArrayList;
37  import java.util.Iterator;
38  import java.util.LinkedHashMap;
39  import java.util.List;
40  import java.util.Map;
41  import java.util.Map.Entry;
42  
43  /**
44   * Arguments class for DataItem.
45   *
46   * @author Tony Washer
47   */
48  public class PrometheusDataValues
49          implements PrometheusDataValuesCtl {
50      /**
51       * InfoSet Items tag.
52       */
53      private static final String TAG_INFOSET = PrometheusDataResource.DATAINFOSET_NAME.getValue();
54  
55      /**
56       * Children Items tag.
57       */
58      private static final String TAG_CHILDREN = PrometheusDataResource.DATAVALUES_CHILDREN.getValue();
59  
60      /**
61       * Child Item tag.
62       */
63      private static final String TAG_CHILD = PrometheusDataResource.DATAVALUES_CHILD.getValue();
64  
65      /**
66       * List type attribute.
67       */
68      protected static final String ATTR_TYPE = PrometheusDataResource.DATAVALUES_ATTRTYPE.getValue();
69  
70      /**
71       * List size attribute.
72       */
73      protected static final String ATTR_SIZE = PrometheusDataResource.DATAVALUES_ATTRSIZE.getValue();
74  
75      /**
76       * Data Version attribute.
77       */
78      protected static final String ATTR_VERS = PrometheusDataResource.DATAVALUES_ATTRVER.getValue();
79  
80      /**
81       * The item type.
82       */
83      private final String theItemType;
84  
85      /**
86       * Field Definitions.
87       */
88      private final Map<MetisDataFieldId, Object> theFields;
89  
90      /**
91       * InfoSet values.
92       */
93      private final List<PrometheusInfoItem> theInfoItems;
94  
95      /**
96       * Child values.
97       */
98      private final List<PrometheusDataValues> theChildren;
99  
100     /**
101      * Constructor.
102      *
103      * @param pItem     the Item to obtain values from
104      * @param pItemName the item name
105      */
106     protected PrometheusDataValues(final PrometheusDataItem pItem,
107                                    final String pItemName) {
108         /* Store Item type */
109         theItemType = pItemName;
110 
111         /* Create the map and list */
112         theFields = new LinkedHashMap<>();
113 
114         /* Store the id */
115         theFields.put(MetisDataResource.DATA_ID, pItem.getIndexedId());
116 
117         /* Access values */
118         final MetisFieldVersionValues myValues = pItem.getValues();
119 
120         /* Iterate through the fields */
121         final Iterator<MetisFieldDef> myIterator = pItem.getDataFieldSet().fieldIterator();
122         while (myIterator.hasNext()) {
123             final MetisFieldDef myField = myIterator.next();
124             final MetisDataFieldId myFieldId = myField.getFieldId();
125 
126             /* Ignore field if it is irrelevant */
127             if (!(myField instanceof MetisFieldVersionedDef myVersioned)
128                     || !myVersioned.isEquality()) {
129                 continue;
130             }
131 
132             /* If the field is to be included */
133             if (pItem.includeXmlField(myFieldId)) {
134                 /* Store the value if it is non-null */
135                 theFields.put(myFieldId, myValues.getValue(myField));
136             }
137         }
138 
139         /* If the item is an infoSet item */
140         if (pItem instanceof PrometheusDataInfoSetItemCtl myInfoItem) {
141             /* Access InfoSet */
142             final PrometheusDataInfoSetCtl myInfoSet = myInfoItem.getInfoSet();
143 
144             /* If the InfoSet is non-empty */
145             if (myInfoSet.isEmpty()) {
146                 /* No infoSet items */
147                 theInfoItems = null;
148             } else {
149                 /* Allocate infoItems list */
150                 theInfoItems = new ArrayList<>();
151 
152                 /* Iterator over the values */
153                 final Iterator<?> myInfoIterator = myInfoSet.iterator();
154                 while (myInfoIterator.hasNext()) {
155                     final Object myCurr = myInfoIterator.next();
156 
157                     /* If this is a DataInfo item */
158                     if (myCurr instanceof PrometheusDataInfoItemCtl myItem) {
159                         /* Add item to the list */
160                         final PrometheusInfoItem myInfo = new PrometheusInfoItem(myItem);
161                         theInfoItems.add(myInfo);
162                     }
163                 }
164             }
165 
166             /* Else not that we have no infoItems */
167         } else {
168             theInfoItems = null;
169         }
170 
171         /* If the item is a grouped item */
172         if (pItem instanceof PrometheusGroupedItemCtl myGrouped) {
173             /* Access child iterator */
174             final Iterator<PrometheusDataItemCtl> myChildIterator = myGrouped.childIterator();
175 
176             /* If there are no children */
177             if (myChildIterator == null) {
178                 theChildren = null;
179             } else {
180                 /* Allocate child list */
181                 theChildren = new ArrayList<>();
182 
183                 /* Iterator over the values */
184                 while (myChildIterator.hasNext()) {
185                     final PrometheusDataItem myCurr = (PrometheusDataItem) myChildIterator.next();
186 
187                     /* Add child to the list */
188                     final PrometheusDataValues myChild = new PrometheusDataValues(myCurr, TAG_CHILD);
189                     theChildren.add(myChild);
190                 }
191             }
192 
193             /* Else note that we have no children */
194         } else {
195             theChildren = null;
196         }
197     }
198 
199     /**
200      * Constructor.
201      *
202      * @param pOwner the Owner of the DataInfo Item
203      * @param pInfo  the values of the DataInfo Item
204      */
205     private PrometheusDataValues(final PrometheusDataItem pOwner,
206                                  final PrometheusInfoItem pInfo) {
207         /* Store Item type */
208         theItemType = "";
209 
210         /* Create the map and list */
211         theFields = new LinkedHashMap<>();
212 
213         /* Store the id if available */
214         final Integer myId = pInfo.getId();
215         if (myId != null) {
216             theFields.put(MetisDataResource.DATA_ID, myId);
217         }
218 
219         /* Store the Info Type */
220         theFields.put(PrometheusDataResource.DATAINFO_TYPE, pInfo.getName());
221 
222         /* Store the Owner */
223         theFields.put(PrometheusDataResource.DATAINFO_OWNER, pOwner.getIndexedId());
224 
225         /* Store the value */
226         theFields.put(PrometheusDataResource.DATAINFO_VALUE, pInfo.getValue());
227 
228         /* Set other fields to null */
229         theInfoItems = null;
230         theChildren = null;
231     }
232 
233     /**
234      * Constructor.
235      *
236      * @param pElement the Item to obtain values from
237      * @param pFields  the field definitions
238      */
239     public PrometheusDataValues(final Element pElement,
240                                 final MetisFieldSetDef pFields) {
241         this(pElement, pFields, pElement.getNodeName());
242     }
243 
244     /**
245      * Constructor.
246      *
247      * @param pElement  the Item to obtain values from
248      * @param pFields   the field definitions
249      * @param pItemName the item name
250      */
251     protected PrometheusDataValues(final Element pElement,
252                                    final MetisFieldSetDef pFields,
253                                    final String pItemName) {
254         /* Store Item type */
255         theItemType = pItemName;
256 
257         /* Create the map */
258         theFields = new LinkedHashMap<>();
259 
260         /* Declare the id if it exists */
261         final Integer myId = getId(pElement);
262         if (myId != null) {
263             theFields.put(MetisDataResource.DATA_ID, myId);
264         }
265 
266         /* Loop through the children */
267         final Iterator<MetisFieldDef> myIterator = pFields.fieldIterator();
268         while (myIterator.hasNext()) {
269             final MetisFieldDef myField = myIterator.next();
270 
271             /* If the field is an equality valueSet item */
272             if (myField instanceof MetisFieldVersionedDef myVersioned
273                     && myVersioned.isEquality()) {
274                 /* Access element */
275                 final Element myChild = getChild(pElement, myField.getFieldId().getId());
276                 if (myChild != null) {
277                     /* Put value */
278                     theFields.put(myField.getFieldId(), myChild.getTextContent());
279                 }
280             }
281         }
282 
283         /* Look for an InfoSet list */
284         final Element myInfoSet = getChild(pElement, TAG_INFOSET);
285         if (myInfoSet != null) {
286             /* Allocate infoItems list */
287             theInfoItems = new ArrayList<>();
288 
289             /* Loop through the child values */
290             for (Node myCurr = myInfoSet.getFirstChild(); myCurr != null; myCurr = myCurr.getNextSibling()) {
291                 /* If the child is an element */
292                 if (myCurr instanceof Element myChild) {
293                     /* Add item to the list */
294                     final PrometheusInfoItem myInfo = new PrometheusInfoItem(myChild);
295                     theInfoItems.add(myInfo);
296                 }
297             }
298 
299             /* Else not that we have no infoItems */
300         } else {
301             theInfoItems = null;
302         }
303 
304         /* Look for children */
305         final Element myChildren = getChild(pElement, TAG_CHILDREN);
306         if (myChildren != null) {
307             /* Allocate infoItems list */
308             theChildren = new ArrayList<>();
309 
310             /* Loop through the child values */
311             for (Node myCurr = myChildren.getFirstChild(); myCurr != null; myCurr = myCurr.getNextSibling()) {
312                 /* If the child is the correct element */
313                 if (myCurr instanceof Element myChild
314                         && TAG_CHILD.equals(myCurr.getNodeName())) {
315                     /* Add item to the list */
316                     final PrometheusDataValues myValues = new PrometheusDataValues(myChild, pFields, theItemType);
317                     theChildren.add(myValues);
318                 }
319             }
320 
321             /* Else not that we have no children */
322         } else {
323             theChildren = null;
324         }
325     }
326 
327     /**
328      * Constructor.
329      *
330      * @param pName the Item type
331      */
332     public PrometheusDataValues(final String pName) {
333         /* Store Item type */
334         theItemType = pName;
335 
336         /* Create the map */
337         theFields = new LinkedHashMap<>();
338 
339         /* No underlying arrays */
340         theInfoItems = null;
341         theChildren = null;
342     }
343 
344     /**
345      * Constructor.
346      *
347      * @param pItem the Item to obtain values from
348      */
349     public PrometheusDataValues(final PrometheusDataItem pItem) {
350         this(pItem, pItem.getDataFieldSet().getName());
351     }
352 
353     /**
354      * Obtain Item Type.
355      *
356      * @return the Item Type
357      */
358     public final String getItemType() {
359         return theItemType;
360     }
361 
362     /**
363      * Obtain Field iterator.
364      *
365      * @return the Field iterator
366      */
367     public final Iterator<Entry<MetisDataFieldId, Object>> fieldIterator() {
368         return theFields.entrySet().iterator();
369     }
370 
371     /**
372      * Does this item have InfoItems?
373      *
374      * @return true/false
375      */
376     public final boolean hasInfoItems() {
377         return theInfoItems != null;
378     }
379 
380     /**
381      * Obtain InfoItems iterator.
382      *
383      * @return the iterator
384      */
385     public final Iterator<PrometheusInfoItem> infoIterator() {
386         return theInfoItems.iterator();
387     }
388 
389     /**
390      * Does this item have children?
391      *
392      * @return true/false
393      */
394     public final boolean hasChildren() {
395         return theChildren != null;
396     }
397 
398     /**
399      * Obtain Child iterator.
400      *
401      * @return the iterator
402      */
403     public final Iterator<PrometheusDataValues> childIterator() {
404         return theChildren.iterator();
405     }
406 
407     /**
408      * Add value.
409      *
410      * @param pField the Field definition
411      * @param pValue the field value
412      */
413     public void addValue(final MetisDataFieldId pField,
414                          final Object pValue) {
415         /* If the value is non-null */
416         if (pValue != null) {
417             /* Add the field */
418             theFields.put(pField, pValue);
419         }
420     }
421 
422     /**
423      * Obtain value.
424      *
425      * @param pField the Field definition
426      * @return the field value
427      */
428     public Object getValue(final MetisDataFieldId pField) {
429         /* Return the field */
430         return theFields.get(pField);
431     }
432 
433     /**
434      * Obtain value of specified class.
435      *
436      * @param pField the Field definition
437      * @param pClass the class
438      * @param <T>    the item type
439      * @return the field value
440      */
441     public <T> T getValue(final MetisDataFieldId pField,
442                           final Class<T> pClass) {
443         /* Return the properly cast field */
444         return pClass.cast(getValue(pField));
445     }
446 
447     /**
448      * Obtain id from element.
449      *
450      * @param pElement the element.
451      * @return the id
452      */
453     private static Integer getId(final Element pElement) {
454         /* Access the id */
455         final String myId = pElement.getAttribute(MetisDataResource.DATA_ID.getId());
456         return !myId.isEmpty()
457                 ? Integer.parseInt(myId)
458                 : null;
459     }
460 
461     /**
462      * Obtain child element with given name.
463      *
464      * @param pParent the parent element
465      * @param pName   the element name
466      * @return the element
467      */
468     private static Element getChild(final Element pParent,
469                                     final String pName) {
470         /* Loop through the child values */
471         for (Node myCurr = pParent.getFirstChild(); myCurr != null; myCurr = myCurr.getNextSibling()) {
472             /* If the child is the correct element */
473             if (myCurr instanceof Element myElement
474                     && pName.equals(myCurr.getNodeName())) {
475                 /* Return the element */
476                 return myElement;
477             }
478         }
479 
480         /* Not found */
481         return null;
482     }
483 
484     /**
485      * Create XML element for item.
486      *
487      * @param pDocument  the document to hold the item.
488      * @param pFormatter the data formatter
489      * @param pStoreIds  do we include IDs in XML
490      * @return the new element
491      */
492     protected Element createXML(final Document pDocument,
493                                 final OceanusDataFormatter pFormatter,
494                                 final boolean pStoreIds) {
495         /* Create an element for the item */
496         final Element myElement = pDocument.createElement(theItemType);
497 
498         /* Loop through the values */
499         for (Entry<MetisDataFieldId, Object> myEntry : theFields.entrySet()) {
500             /* Access parts */
501             final MetisDataFieldId myFieldId = myEntry.getKey();
502             final Object myValue = myEntry.getValue();
503 
504             /* If this is the Id */
505             if (MetisDataResource.DATA_ID.equals(myFieldId)) {
506                 /* Add as an Attribute if required */
507                 if (pStoreIds) {
508                     myElement.setAttribute(myFieldId.getId(), myValue.toString());
509                 }
510 
511                 /* Skip to next field */
512                 continue;
513             }
514 
515             /* Create the child element */
516             final Element myChild = pDocument.createElement(myFieldId.getId());
517             myElement.appendChild(myChild);
518 
519             /* Store the value */
520             myChild.setTextContent(pFormatter.formatObject(myValue));
521         }
522 
523         /* If we have InfoSet items */
524         if (theInfoItems != null) {
525             /* Add infoSet */
526             final Element myInfoSet = pDocument.createElement(TAG_INFOSET);
527             myElement.appendChild(myInfoSet);
528 
529             /* Loop through the items */
530             for (PrometheusInfoItem myInfo : theInfoItems) {
531                 /* Create the element */
532                 final Element myItem = pDocument.createElement(myInfo.getName());
533                 myInfoSet.appendChild(myItem);
534 
535                 /* Set the id if required */
536                 if (pStoreIds) {
537                     myItem.setAttribute(MetisDataResource.DATA_ID.getValue(), myInfo.getId().toString());
538                 }
539 
540                 /* Set the value */
541                 myItem.setTextContent(pFormatter.formatObject(myInfo.getValue()));
542             }
543         }
544 
545         /* If we have children */
546         if (theChildren != null) {
547             /* Add children */
548             final Element myChildren = pDocument.createElement(TAG_CHILDREN);
549             myElement.appendChild(myChildren);
550 
551             /* Loop through the children */
552             final Iterator<PrometheusDataValues> myIterator = theChildren.iterator();
553             while (myIterator.hasNext()) {
554                 final PrometheusDataValues myValues = myIterator.next();
555 
556                 /* Create the subElement and append */
557                 final Element myChild = myValues.createXML(pDocument, pFormatter, pStoreIds);
558                 myChildren.appendChild(myChild);
559             }
560         }
561 
562         /* Return the element */
563         return myElement;
564     }
565 
566     /**
567      * InfoItem class.
568      */
569     public static final class PrometheusInfoItem {
570         /**
571          * Name of item.
572          */
573         private final String theName;
574 
575         /**
576          * Id of item.
577          */
578         private final Integer theId;
579 
580         /**
581          * Value of item.
582          */
583         private final Object theValue;
584 
585         /**
586          * Constructor.
587          *
588          * @param pInfo the info Item
589          */
590         private PrometheusInfoItem(final PrometheusDataInfoItemCtl pInfo) {
591             /* Access the infoClass */
592             final PrometheusDataInfoClass myClass = pInfo.getInfoClass();
593 
594             /* Store values */
595             theName = myClass.toString();
596             theId = pInfo.getIndexedId();
597             theValue = myClass.isLink()
598                     ? pInfo.getLink()
599                     : pInfo.getValue(Object.class);
600         }
601 
602         /**
603          * Constructor.
604          *
605          * @param pElement the XML element
606          */
607         private PrometheusInfoItem(final Element pElement) {
608             /* Store values */
609             theName = pElement.getNodeName();
610             theId = PrometheusDataValues.getId(pElement);
611             theValue = pElement.getTextContent();
612         }
613 
614         /**
615          * Obtain name of item.
616          *
617          * @return the name
618          */
619         public String getName() {
620             return theName;
621         }
622 
623         /**
624          * Obtain id of item.
625          *
626          * @return the id
627          */
628         public Integer getId() {
629             return theId;
630         }
631 
632         /**
633          * Obtain value of item.
634          *
635          * @return the value
636          */
637         public Object getValue() {
638             return theValue;
639         }
640 
641         @Override
642         public boolean equals(final Object pThat) {
643             /* Handle the trivial cases */
644             if (this == pThat) {
645                 return true;
646             }
647             if (pThat == null) {
648                 return false;
649             }
650 
651             /* Make sure that the object is the same class */
652             if (pThat.getClass() != getClass()) {
653                 return false;
654             }
655 
656             /* Access the object as an InfoItem */
657             final PrometheusInfoItem myItem = (PrometheusInfoItem) pThat;
658 
659             if (!theName.equals(myItem.getName())) {
660                 return false;
661             }
662             if (!theId.equals(myItem.getId())) {
663                 return false;
664             }
665             return theValue.equals(myItem.getValue());
666         }
667 
668         @Override
669         public int hashCode() {
670             return theName.hashCode() + theId + theValue.hashCode();
671         }
672 
673         @Override
674         public String toString() {
675             return theName + "=" + theValue;
676         }
677 
678         /**
679          * Obtain DataValues.
680          *
681          * @param pOwner the owner
682          * @return the dataValues
683          */
684         public PrometheusDataValues getValues(final PrometheusDataItem pOwner) {
685             return new PrometheusDataValues(pOwner, this);
686         }
687     }
688 }