View Javadoc
1   /*
2    * MoneyWise: Finance Application
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.moneywise.data.basic;
18  
19  import io.github.tonywasher.joceanus.metis.data.MetisDataDifference;
20  import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataFieldId;
21  import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataList;
22  import io.github.tonywasher.joceanus.metis.data.MetisDataResource;
23  import io.github.tonywasher.joceanus.metis.field.MetisFieldItem;
24  import io.github.tonywasher.joceanus.metis.field.MetisFieldSet;
25  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurity.MoneyWiseSecurityList;
26  import io.github.tonywasher.joceanus.moneywise.exc.MoneyWiseDataException;
27  import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
28  import io.github.tonywasher.joceanus.oceanus.date.OceanusDate;
29  import io.github.tonywasher.joceanus.oceanus.date.OceanusDateFormatter;
30  import io.github.tonywasher.joceanus.oceanus.date.OceanusDateRange;
31  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusPrice;
32  import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
33  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataInstanceMap;
34  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataItem;
35  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataMapItem;
36  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataSet;
37  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataValues;
38  import io.github.tonywasher.joceanus.prometheus.data.PrometheusEncryptedDataItem;
39  import io.github.tonywasher.joceanus.prometheus.data.PrometheusEncryptedFieldSet;
40  import io.github.tonywasher.joceanus.prometheus.data.PrometheusEncryptedPair;
41  import io.github.tonywasher.joceanus.prometheus.data.PrometheusEncryptedValues;
42  import io.github.tonywasher.joceanus.prometheus.data.PrometheusListStyle;
43  import io.github.tonywasher.joceanus.prometheus.views.PrometheusEditSet;
44  
45  import java.util.ArrayList;
46  import java.util.Currency;
47  import java.util.HashMap;
48  import java.util.Iterator;
49  import java.util.List;
50  import java.util.ListIterator;
51  import java.util.Map;
52  
53  /**
54   * SecurityPrice data type.
55   *
56   * @author Tony Washer
57   */
58  public class MoneyWiseSecurityPrice
59          extends PrometheusEncryptedDataItem {
60      /**
61       * Object name.
62       */
63      public static final String OBJECT_NAME = MoneyWiseBasicDataType.SECURITYPRICE.getItemName();
64  
65      /**
66       * List name.
67       */
68      public static final String LIST_NAME = MoneyWiseBasicDataType.SECURITYPRICE.getListName();
69  
70      /**
71       * Report fields.
72       */
73      private static final PrometheusEncryptedFieldSet<MoneyWiseSecurityPrice> FIELD_DEFS = PrometheusEncryptedFieldSet.newEncryptedFieldSet(MoneyWiseSecurityPrice.class);
74  
75      /*
76       * FieldIds.
77       */
78      static {
79          FIELD_DEFS.declareLinkField(MoneyWiseBasicDataType.SECURITY);
80          FIELD_DEFS.declareDateField(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_DATE);
81          FIELD_DEFS.declareEncryptedPriceField(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_PRICE);
82      }
83  
84      /**
85       * Invalid currency error.
86       */
87      public static final String ERROR_CURRENCY = MoneyWiseBasicResource.MONEYWISEDATA_ERROR_CURRENCY.getValue();
88  
89      /**
90       * Copy Constructor.
91       *
92       * @param pList  the list
93       * @param pPrice The Price
94       */
95      protected MoneyWiseSecurityPrice(final MoneyWiseSecurityPriceBaseList<? extends MoneyWiseSecurityPrice> pList,
96                                       final MoneyWiseSecurityPrice pPrice) {
97          /* Set standard values */
98          super(pList, pPrice);
99      }
100 
101     /**
102      * Edit Constructor.
103      *
104      * @param pList the list
105      */
106     public MoneyWiseSecurityPrice(final MoneyWiseSecurityPriceBaseList<? extends MoneyWiseSecurityPrice> pList) {
107         super(pList, 0);
108         setNextDataKeySet();
109     }
110 
111     /**
112      * Values constructor.
113      *
114      * @param pList   the List to add to
115      * @param pValues the values constructor
116      * @throws OceanusException on error
117      */
118     private MoneyWiseSecurityPrice(final MoneyWiseSecurityPriceList pList,
119                                    final PrometheusDataValues pValues) throws OceanusException {
120         /* Initialise the item */
121         super(pList, pValues);
122 
123         /* Access formatter */
124         final OceanusDataFormatter myFormatter = getDataSet().getDataFormatter();
125 
126         /* Protect against exceptions */
127         try {
128             /* Store the Date */
129             Object myValue = pValues.getValue(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_DATE);
130             if (myValue instanceof OceanusDate d) {
131                 setValueDate(d);
132             } else if (myValue instanceof String s) {
133                 final OceanusDateFormatter myParser = myFormatter.getDateFormatter();
134                 setValueDate(myParser.parseDate(s));
135             }
136 
137             /* Store the Security */
138             myValue = pValues.getValue(MoneyWiseBasicDataType.SECURITY);
139             if (myValue instanceof Integer i) {
140                 setValueSecurity(i);
141             } else if (myValue instanceof String s) {
142                 setValueSecurity(s);
143             }
144 
145             /* Store the Price */
146             myValue = pValues.getValue(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_PRICE);
147             if (myValue instanceof OceanusPrice p) {
148                 setValuePrice(p);
149             } else if (myValue instanceof byte[] ba) {
150                 setValuePrice(ba);
151             } else if (myValue instanceof String myString) {
152                 setValuePrice(myString);
153                 setValuePrice(myFormatter.parseValue(myString, OceanusPrice.class));
154             }
155 
156             /* Catch Exceptions */
157         } catch (IllegalArgumentException
158                  | OceanusException e) {
159             /* Pass on exception */
160             throw new MoneyWiseDataException(this, ERROR_CREATEITEM, e);
161         }
162     }
163 
164     @Override
165     public MetisFieldSetDef getDataFieldSet() {
166         return FIELD_DEFS;
167     }
168 
169     @Override
170     public boolean includeXmlField(final MetisDataFieldId pField) {
171         /* Determine whether fields should be included */
172         if (MoneyWiseBasicDataType.SECURITY.equals(pField)) {
173             return true;
174         }
175         if (MoneyWiseBasicResource.MONEYWISEDATA_FIELD_DATE.equals(pField)) {
176             return true;
177         }
178         if (MoneyWiseBasicResource.MONEYWISEDATA_FIELD_PRICE.equals(pField)) {
179             return true;
180         }
181 
182         /* Pass call on */
183         return super.includeXmlField(pField);
184     }
185 
186     @Override
187     public String toString() {
188         /* Access Key Values */
189         final PrometheusEncryptedValues myValues = getValues();
190         final Object mySecurity = myValues.getValue(MoneyWiseBasicDataType.SECURITY);
191         final Object myDate = myValues.getValue(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_DATE);
192         final Object myPrice = myValues.getValue(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_PRICE, OceanusPrice.class);
193 
194         /* Access formatter */
195         final OceanusDataFormatter myFormatter = getDataSet().getDataFormatter();
196 
197         /* Create string builder */
198         final StringBuilder myBuilder = new StringBuilder();
199         myBuilder.append(myFormatter.formatObject(mySecurity));
200         myBuilder.append(": ");
201         myBuilder.append(myFormatter.formatObject(myPrice));
202         myBuilder.append('@');
203         myBuilder.append(myFormatter.formatObject(myDate));
204 
205         /* return it */
206         return myBuilder.toString();
207     }
208 
209     @Override
210     public String formatObject(final OceanusDataFormatter pFormatter) {
211         return toString();
212     }
213 
214     /**
215      * Obtain Price.
216      *
217      * @return the price
218      */
219     public OceanusPrice getPrice() {
220         return getValues().getValue(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_PRICE, OceanusPrice.class);
221     }
222 
223     /**
224      * Obtain Encrypted Price.
225      *
226      * @return the Bytes
227      */
228     public byte[] getPriceBytes() {
229         return getValues().getEncryptedBytes(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_PRICE);
230     }
231 
232     /**
233      * Obtain Encrypted Price Field.
234      *
235      * @return the field
236      */
237     public PrometheusEncryptedPair getPriceField() {
238         return getValues().getEncryptedPair(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_PRICE);
239     }
240 
241     /**
242      * Obtain Date.
243      *
244      * @return the date
245      */
246     public OceanusDate getDate() {
247         return getValues().getValue(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_DATE, OceanusDate.class);
248     }
249 
250     /**
251      * Obtain Security.
252      *
253      * @return the security
254      */
255     public MoneyWiseSecurity getSecurity() {
256         return getValues().getValue(MoneyWiseBasicDataType.SECURITY, MoneyWiseSecurity.class);
257     }
258 
259     /**
260      * Obtain SecurityId.
261      *
262      * @return the securityId
263      */
264     public Integer getSecurityId() {
265         final MoneyWiseSecurity mySecurity = getSecurity();
266         return mySecurity == null
267                 ? null
268                 : mySecurity.getIndexedId();
269     }
270 
271     /**
272      * Obtain SecurityName.
273      *
274      * @return the securityName
275      */
276     public String getSecurityName() {
277         final MoneyWiseSecurity mySecurity = getSecurity();
278         return mySecurity == null
279                 ? null
280                 : mySecurity.getName();
281     }
282 
283     /**
284      * Set the security.
285      *
286      * @param pValue the security
287      */
288     private void setValueSecurity(final MoneyWiseSecurity pValue) {
289         getValues().setUncheckedValue(MoneyWiseBasicDataType.SECURITY, pValue);
290     }
291 
292     /**
293      * Set the security id.
294      *
295      * @param pId the security id
296      */
297     private void setValueSecurity(final Integer pId) {
298         getValues().setUncheckedValue(MoneyWiseBasicDataType.SECURITY, pId);
299     }
300 
301     /**
302      * Set the security name.
303      *
304      * @param pName the security name
305      */
306     private void setValueSecurity(final String pName) {
307         getValues().setUncheckedValue(MoneyWiseBasicDataType.SECURITY, pName);
308     }
309 
310     /**
311      * Set the price.
312      *
313      * @param pValue the price
314      * @throws OceanusException on error
315      */
316     private void setValuePrice(final OceanusPrice pValue) throws OceanusException {
317         setEncryptedValue(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_PRICE, pValue);
318     }
319 
320     /**
321      * Set the encrypted price.
322      *
323      * @param pBytes the encrypted price
324      * @throws OceanusException on error
325      */
326     private void setValuePrice(final byte[] pBytes) throws OceanusException {
327         setEncryptedValue(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_PRICE, pBytes, OceanusPrice.class);
328     }
329 
330     /**
331      * Set the price.
332      *
333      * @param pValue the price
334      */
335     private void setValuePrice(final String pValue) {
336         getValues().setUncheckedValue(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_PRICE, pValue);
337     }
338 
339     /**
340      * Set the price.
341      *
342      * @param pValue the price
343      */
344     public void setValuePrice(final PrometheusEncryptedPair pValue) {
345         getValues().setUncheckedValue(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_PRICE, pValue);
346     }
347 
348     /**
349      * Set the date.
350      *
351      * @param pValue the date
352      */
353     private void setValueDate(final OceanusDate pValue) {
354         getValues().setUncheckedValue(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_DATE, pValue);
355     }
356 
357     @Override
358     @SuppressWarnings("unchecked")
359     public MoneyWiseSecurityPriceBaseList<? extends MoneyWiseSecurityPrice> getList() {
360         return (MoneyWiseSecurityPriceBaseList<? extends MoneyWiseSecurityPrice>) super.getList();
361     }
362 
363     @Override
364     public MoneyWiseSecurityPrice getBase() {
365         return (MoneyWiseSecurityPrice) super.getBase();
366     }
367 
368     @Override
369     public int compareValues(final PrometheusDataItem pThat) {
370         /* Access as SecurityPrice */
371         final MoneyWiseSecurityPrice myThat = (MoneyWiseSecurityPrice) pThat;
372 
373         /* If header settings differ */
374         if (isHeader() != pThat.isHeader()) {
375             return isHeader()
376                     ? -1
377                     : 1;
378         }
379 
380         /* If the date differs */
381         final int iDiff = MetisDataDifference.compareObject(getDate(), myThat.getDate());
382         if (iDiff != 0) {
383             /* Sort in reverse date order !! */
384             return -iDiff;
385         }
386 
387         /* Compare the securities */
388         return getSecurity().compareTo(myThat.getSecurity());
389     }
390 
391     @Override
392     public void resolveDataSetLinks() throws OceanusException {
393         /* Update the Encryption details */
394         super.resolveDataSetLinks();
395 
396         /* Resolve data links */
397         resolveDataLink(MoneyWiseBasicDataType.SECURITY, MoneyWiseBasicDataType.SECURITY);
398     }
399 
400     /**
401      * Resolve links in an editSet.
402      *
403      * @param pEditSet the edit Set
404      * @throws OceanusException on error
405      */
406     protected void resolveEditSetLinks(final PrometheusEditSet pEditSet) throws OceanusException {
407         /* Resolve parent within list */
408         final MoneyWiseSecurityList mySecurities = pEditSet.getDataList(MoneyWiseBasicDataType.SECURITY, MoneyWiseSecurityList.class);
409         resolveDataLink(MoneyWiseBasicDataType.SECURITY, mySecurities);
410     }
411 
412     /**
413      * Set the security.
414      *
415      * @param pValue the security
416      */
417     public void setSecurity(final MoneyWiseSecurity pValue) {
418         setValueSecurity(pValue);
419     }
420 
421     /**
422      * Set a new price.
423      *
424      * @param pPrice the price
425      * @throws OceanusException on error
426      */
427     public void setPrice(final OceanusPrice pPrice) throws OceanusException {
428         setValuePrice(pPrice);
429     }
430 
431     /**
432      * Set a new date.
433      *
434      * @param pDate the new date
435      */
436     public void setDate(final OceanusDate pDate) {
437         setValueDate(pDate);
438     }
439 
440     @Override
441     public void touchUnderlyingItems() {
442         /* touch the underlying security */
443         getSecurity().touchItem(this);
444     }
445 
446     @Override
447     public void touchOnUpdate() {
448         /* Touch security */
449         getSecurity().touchItem(this);
450     }
451 
452     @Override
453     public boolean applyChanges(final PrometheusDataItem pPrice) {
454         /* Can only update from a SecurityPrice */
455         if (!(pPrice instanceof MoneyWiseSecurityPrice myPrice)) {
456             return false;
457         }
458 
459         /* Store the current detail into history */
460         pushHistory();
461 
462         /* Update the price if required */
463         if (!MetisDataDifference.isEqual(getPrice(), myPrice.getPrice())) {
464             setValuePrice(myPrice.getPriceField());
465         }
466 
467         /* Update the date if required */
468         if (!MetisDataDifference.isEqual(getDate(), myPrice.getDate())) {
469             setValueDate(myPrice.getDate());
470         }
471 
472         /* Check for changes */
473         return checkForHistory();
474     }
475 
476     @Override
477     public void adjustMapForItem() {
478         final MoneyWiseSecurityPriceBaseList<? extends MoneyWiseSecurityPrice> myList = getList();
479         final MoneyWiseSecurityPriceDataMap myMap = myList.getDataMap();
480         myMap.adjustForItem(this);
481     }
482 
483     /**
484      * Price List.
485      *
486      * @param <T> the data type
487      */
488     public abstract static class MoneyWiseSecurityPriceBaseList<T extends MoneyWiseSecurityPrice>
489             extends PrometheusEncryptedList<T> {
490         /*
491          * Report fields.
492          */
493         static {
494             MetisFieldSet.newFieldSet(MoneyWiseSecurityPriceBaseList.class);
495         }
496 
497         /**
498          * Construct an empty CORE Price list.
499          *
500          * @param pData     the DataSet for the list
501          * @param pClass    the class of the item
502          * @param pItemType the item type
503          */
504         protected MoneyWiseSecurityPriceBaseList(final PrometheusDataSet pData,
505                                                  final Class<T> pClass,
506                                                  final MoneyWiseBasicDataType pItemType) {
507             /* Call super-constructor */
508             super(pClass, pData, pItemType, PrometheusListStyle.CORE);
509         }
510 
511         /**
512          * Constructor for a cloned List.
513          *
514          * @param pSource the source List
515          */
516         protected MoneyWiseSecurityPriceBaseList(final MoneyWiseSecurityPriceBaseList<T> pSource) {
517             /* Call super-constructor */
518             super(pSource);
519         }
520 
521         @Override
522         public MoneyWiseSecurityPriceDataMap getDataMap() {
523             return (MoneyWiseSecurityPriceDataMap) super.getDataMap();
524         }
525 
526         @Override
527         protected MoneyWiseSecurityPriceDataMap allocateDataMap() {
528             return new MoneyWiseSecurityPriceDataMap();
529         }
530     }
531 
532     /**
533      * Price List.
534      */
535     public static class MoneyWiseSecurityPriceList
536             extends MoneyWiseSecurityPriceBaseList<MoneyWiseSecurityPrice> {
537         /**
538          * Report fields.
539          */
540         private static final MetisFieldSet<MoneyWiseSecurityPriceList> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseSecurityPriceList.class);
541 
542         /**
543          * Construct an empty CORE price list.
544          *
545          * @param pData the DataSet for the list
546          */
547         protected MoneyWiseSecurityPriceList(final PrometheusDataSet pData) {
548             super(pData, MoneyWiseSecurityPrice.class, MoneyWiseBasicDataType.SECURITYPRICE);
549         }
550 
551         /**
552          * Constructor for a cloned List.
553          *
554          * @param pSource the source List
555          */
556         private MoneyWiseSecurityPriceList(final MoneyWiseSecurityPriceList pSource) {
557             super(pSource);
558         }
559 
560         @Override
561         public MetisFieldSet<MoneyWiseSecurityPriceList> getDataFieldSet() {
562             return FIELD_DEFS;
563         }
564 
565         @Override
566         public String listName() {
567             return LIST_NAME;
568         }
569 
570         @Override
571         public MetisFieldSetDef getItemFields() {
572             return MoneyWiseSecurityPrice.FIELD_DEFS;
573         }
574 
575         @Override
576         protected MoneyWiseSecurityPriceList getEmptyList(final PrometheusListStyle pStyle) {
577             final MoneyWiseSecurityPriceList myList = new MoneyWiseSecurityPriceList(this);
578             myList.setStyle(pStyle);
579             return myList;
580         }
581 
582         /**
583          * Construct an edit extract of a Rate list.
584          *
585          * @param pEditSet the editSet
586          * @return the edit list
587          * @throws OceanusException on error
588          */
589         public MoneyWiseSecurityPriceList deriveEditList(final PrometheusEditSet pEditSet) throws OceanusException {
590             /* Build an empty List */
591             final MoneyWiseSecurityPriceList myList = getEmptyList(PrometheusListStyle.EDIT);
592             myList.ensureMap();
593             pEditSet.setEditEntryList(MoneyWiseBasicDataType.SECURITYPRICE, myList);
594 
595             /* Loop through the list */
596             final Iterator<MoneyWiseSecurityPrice> myIterator = iterator();
597             while (myIterator.hasNext()) {
598                 final MoneyWiseSecurityPrice myCurr = myIterator.next();
599 
600                 /* Copy the item */
601                 final MoneyWiseSecurityPrice myItem = new MoneyWiseSecurityPrice(myList, myCurr);
602                 myItem.resolveEditSetLinks(pEditSet);
603                 myList.add(myItem);
604 
605                 /* Adjust the map */
606                 myItem.adjustMapForItem();
607             }
608 
609             /* Return the List */
610             return myList;
611         }
612 
613         /**
614          * Add a new item to the core list.
615          *
616          * @param pPrice item
617          * @return the newly added item
618          */
619         @Override
620         public MoneyWiseSecurityPrice addCopyItem(final PrometheusDataItem pPrice) {
621             if (pPrice instanceof MoneyWiseSecurityPrice p) {
622                 final MoneyWiseSecurityPrice myPrice = new MoneyWiseSecurityPrice(this, p);
623                 add(myPrice);
624                 return myPrice;
625             } else {
626                 throw new UnsupportedOperationException();
627             }
628         }
629 
630         /**
631          * Add a new item to the edit list.
632          *
633          * @return the newly added item
634          */
635         @Override
636         public MoneyWiseSecurityPrice addNewItem() {
637             final MoneyWiseSecurityPrice myPrice = new MoneyWiseSecurityPrice(this);
638             add(myPrice);
639             return myPrice;
640         }
641 
642         @Override
643         public MoneyWiseSecurityPrice addValuesItem(final PrometheusDataValues pValues) throws OceanusException {
644             /* Create the price */
645             final MoneyWiseSecurityPrice myPrice = new MoneyWiseSecurityPrice(this, pValues);
646 
647             /* Check that this PriceId has not been previously added */
648             if (!isIdUnique(myPrice.getIndexedId())) {
649                 myPrice.addError(ERROR_DUPLICATE, MetisDataResource.DATA_ID);
650                 throw new MoneyWiseDataException(myPrice, ERROR_VALIDATION);
651             }
652 
653             /* Add to the list */
654             add(myPrice);
655 
656             /* Return it */
657             return myPrice;
658         }
659     }
660 
661     /**
662      * The dataMap class.
663      */
664     public static class MoneyWiseSecurityPriceDataMap
665             implements PrometheusDataMapItem, MetisFieldItem {
666         /**
667          * Report fields.
668          */
669         @SuppressWarnings("rawtypes")
670         private static final MetisFieldSet<MoneyWiseSecurityPriceDataMap> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseSecurityPriceDataMap.class);
671 
672         /*
673          * UnderlyingMap Field Id.
674          */
675         static {
676             FIELD_DEFS.declareLocalField(MoneyWiseBasicResource.MONEYWISEDATA_MAP_MAPOFMAPS, MoneyWiseSecurityPriceDataMap::getMapOfMaps);
677             FIELD_DEFS.declareLocalField(MoneyWiseBasicResource.SECURITYPRICE_MAP_MAPOFPRICES, MoneyWiseSecurityPriceDataMap::getMapOfPrices);
678         }
679 
680         /**
681          * Map of Maps.
682          */
683         private final Map<MoneyWiseSecurity, Map<OceanusDate, Integer>> theMapOfMaps;
684 
685         /**
686          * Map of Prices.
687          */
688         private final Map<MoneyWiseSecurity, MoneyWiseSecurityPriceList> theMapOfPrices;
689 
690         /**
691          * Constructor.
692          */
693         public MoneyWiseSecurityPriceDataMap() {
694             /* Create the maps */
695             theMapOfMaps = new HashMap<>();
696             theMapOfPrices = new HashMap<>();
697         }
698 
699         @SuppressWarnings("rawtypes")
700         @Override
701         public MetisFieldSet<MoneyWiseSecurityPriceDataMap> getDataFieldSet() {
702             return FIELD_DEFS;
703         }
704 
705         @Override
706         public String formatObject(final OceanusDataFormatter pFormatter) {
707             return FIELD_DEFS.getName();
708         }
709 
710         /**
711          * Obtain mapOfMaps.
712          *
713          * @return the map
714          */
715         private Map<MoneyWiseSecurity, Map<OceanusDate, Integer>> getMapOfMaps() {
716             return theMapOfMaps;
717         }
718 
719         /**
720          * Obtain mapOfPrices.
721          *
722          * @return the map
723          */
724         private Map<MoneyWiseSecurity, MoneyWiseSecurityPriceList> getMapOfPrices() {
725             return theMapOfPrices;
726         }
727 
728         @Override
729         public void resetMap() {
730             theMapOfMaps.clear();
731             theMapOfPrices.clear();
732         }
733 
734         @Override
735         public void adjustForItem(final PrometheusDataItem pItem) {
736             /* Access the Security Id */
737             final MoneyWiseSecurityPrice myItem = (MoneyWiseSecurityPrice) pItem;
738             final MoneyWiseSecurity mySecurity = myItem.getSecurity();
739             if (mySecurity == null) {
740                 return;
741             }
742 
743             /* Access the map */
744             final Map<OceanusDate, Integer> myMap = theMapOfMaps.computeIfAbsent(mySecurity, s -> new HashMap<>());
745 
746             /* Adjust price count */
747             final OceanusDate myDate = myItem.getDate();
748             final Integer myCount = myMap.get(myDate);
749             if (myCount == null) {
750                 myMap.put(myDate, PrometheusDataInstanceMap.ONE);
751             } else {
752                 myMap.put(myDate, myCount + 1);
753             }
754 
755             /* Access the list */
756             final MoneyWiseSecurityPriceList myList = theMapOfPrices.computeIfAbsent(mySecurity, MoneyWiseSecurityPriceList::new);
757 
758             /* Add element to the list */
759             myList.add(myItem);
760         }
761 
762         /**
763          * Check validity of Price.
764          *
765          * @param pItem the price
766          * @return true/false
767          */
768         public boolean validPriceCount(final MoneyWiseSecurityPrice pItem) {
769             /* Access the Details */
770             final MoneyWiseSecurity mySecurity = pItem.getSecurity();
771             final OceanusDate myDate = pItem.getDate();
772 
773             /* Access the map */
774             final Map<OceanusDate, Integer> myMap = theMapOfMaps.get(mySecurity);
775             if (myMap != null) {
776                 final Integer myResult = myMap.get(myDate);
777                 return PrometheusDataInstanceMap.ONE.equals(myResult);
778             }
779             return false;
780         }
781 
782         /**
783          * Check availability of date for a security.
784          *
785          * @param pSecurity the security
786          * @param pDate     the key to look up
787          * @return true/false
788          */
789         public boolean availableDate(final MoneyWiseSecurity pSecurity,
790                                      final OceanusDate pDate) {
791             /* Access the map */
792             final Map<OceanusDate, Integer> myMap = theMapOfMaps.get(pSecurity);
793             return myMap == null
794                     || myMap.get(pDate) == null;
795         }
796 
797         /**
798          * Obtain price for date.
799          *
800          * @param pSecurity the security
801          * @param pDate     the date
802          * @return the latest price for the date.
803          */
804         public OceanusPrice getPriceForDate(final MoneyWiseAssetBase pSecurity,
805                                             final OceanusDate pDate) {
806             /* Access as security */
807             final MoneyWiseSecurity mySecurity = MoneyWiseSecurity.class.cast(pSecurity);
808 
809             /* Access list for security */
810             final MoneyWiseSecurityPriceList myList = theMapOfPrices.get(mySecurity);
811             if (myList != null) {
812                 /* Loop through the prices */
813                 final Iterator<MoneyWiseSecurityPrice> myIterator = myList.iterator();
814                 while (myIterator.hasNext()) {
815                     final MoneyWiseSecurityPrice myCurr = myIterator.next();
816 
817                     /* Return this price if this is earlier or equal to the the date */
818                     if (pDate.compareTo(myCurr.getDate()) >= 0) {
819                         return myCurr.getPrice();
820                     }
821                 }
822             }
823 
824             /* return single unit price */
825             final Currency myCurrency = mySecurity.getCurrency();
826             return OceanusPrice.getWholeUnits(PrometheusDataInstanceMap.ONE, myCurrency);
827         }
828 
829         /**
830          * Obtain prices for range.
831          *
832          * @param pSecurity the security
833          * @param pRange    the date range
834          * @return the two deep array of prices for the range.
835          */
836         public OceanusPrice[] getPricesForRange(final MoneyWiseSecurity pSecurity,
837                                                 final OceanusDateRange pRange) {
838             /* Set price */
839             final Currency myCurrency = pSecurity.getCurrency();
840             OceanusPrice myFirst = OceanusPrice.getWholeUnits(PrometheusDataInstanceMap.ONE, myCurrency);
841             OceanusPrice myLatest = myFirst;
842             final OceanusDate myStart = pRange.getStart();
843 
844             /* Access list for security */
845             final MoneyWiseSecurityPriceList myList = theMapOfPrices.get(pSecurity);
846             if (myList != null) {
847                 /* Loop through the prices */
848                 final ListIterator<MoneyWiseSecurityPrice> myIterator = myList.listIterator(myList.size());
849                 while (myIterator.hasPrevious()) {
850                     final MoneyWiseSecurityPrice myCurr = myIterator.previous();
851 
852                     /* Check for the range of the date */
853                     final OceanusDate myDate = myCurr.getDate();
854                     final int iComp = pRange.compareToDate(myDate);
855 
856                     /* If this is later than the range we are finished */
857                     if (iComp < 0) {
858                         break;
859                     }
860 
861                     /* Record as best price */
862                     myLatest = myCurr.getPrice();
863 
864                     /* Record early price */
865                     if (iComp > 0
866                             || myDate.compareTo(myStart) == 0) {
867                         myFirst = myLatest;
868                     }
869                 }
870             }
871 
872             /* Return the prices */
873             return new OceanusPrice[]
874                     {myFirst, myLatest};
875         }
876 
877         /**
878          * Obtain priceList cursor.
879          *
880          * @param pSecurity the security
881          * @return the latest price for the date.
882          */
883         public ListIterator<MoneyWiseSecurityPrice> priceIterator(final MoneyWiseSecurity pSecurity) {
884             /* Access list for currency */
885             final MoneyWiseSecurityPriceList myList = theMapOfPrices.get(pSecurity);
886             return myList != null
887                     ? myList.listIterator(myList.size())
888                     : null;
889         }
890 
891         /**
892          * Price List class.
893          */
894         private static final class MoneyWiseSecurityPriceList
895                 implements MetisFieldItem, MetisDataList<MoneyWiseSecurityPrice> {
896             /**
897              * Report fields.
898              */
899             private static final MetisFieldSet<MoneyWiseSecurityPriceList> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseSecurityPriceList.class);
900 
901             /*
902              * UnderlyingMap Field Id.
903              */
904             static {
905                 FIELD_DEFS.declareLocalField(MetisDataResource.LIST_SIZE, MoneyWiseSecurityPriceList::size);
906             }
907 
908             /**
909              * The list.
910              */
911             private final List<MoneyWiseSecurityPrice> theList;
912 
913             /**
914              * The security.
915              */
916             private final MoneyWiseSecurity theSecurity;
917 
918             /**
919              * Constructor.
920              *
921              * @param pSecurity the security
922              */
923             private MoneyWiseSecurityPriceList(final MoneyWiseSecurity pSecurity) {
924                 theSecurity = pSecurity;
925                 theList = new ArrayList<>();
926             }
927 
928             @Override
929             public MetisFieldSet<MoneyWiseSecurityPriceList> getDataFieldSet() {
930                 return FIELD_DEFS;
931             }
932 
933             @Override
934             public String formatObject(final OceanusDataFormatter pFormatter) {
935                 return theSecurity.formatObject(pFormatter)
936                         + "("
937                         + size()
938                         + ")";
939             }
940 
941             @Override
942             public String toString() {
943                 return theSecurity.toString()
944                         + "("
945                         + size()
946                         + ")";
947             }
948 
949             @Override
950             public List<MoneyWiseSecurityPrice> getUnderlyingList() {
951                 return theList;
952             }
953         }
954     }
955 }