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