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.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 myPrice)) {
460             return false;
461         }
462 
463         /* Store the current detail into history */
464         pushHistory();
465 
466         /* Update the price if required */
467         if (!MetisDataDifference.isEqual(getPrice(), myPrice.getPrice())) {
468             setValuePrice(myPrice.getPriceField());
469         }
470 
471         /* Update the date if required */
472         if (!MetisDataDifference.isEqual(getDate(), myPrice.getDate())) {
473             setValueDate(myPrice.getDate());
474         }
475 
476         /* Check for changes */
477         return checkForHistory();
478     }
479 
480     @Override
481     public void adjustMapForItem() {
482         final MoneyWiseSecurityPriceBaseList<? extends MoneyWiseSecurityPrice> myList = getList();
483         final MoneyWiseSecurityPriceDataMap myMap = myList.getDataMap();
484         myMap.adjustForItem(this);
485     }
486 
487     /**
488      * Price List.
489      *
490      * @param <T> the data type
491      */
492     public abstract static class MoneyWiseSecurityPriceBaseList<T extends MoneyWiseSecurityPrice>
493             extends PrometheusEncryptedList<T> {
494         /*
495          * Report fields.
496          */
497         static {
498             MetisFieldSet.newFieldSet(MoneyWiseSecurityPriceBaseList.class);
499         }
500 
501         /**
502          * Construct an empty CORE Price list.
503          *
504          * @param pData     the DataSet for the list
505          * @param pClass    the class of the item
506          * @param pItemType the item type
507          */
508         protected MoneyWiseSecurityPriceBaseList(final MoneyWiseDataSet pData,
509                                                  final Class<T> pClass,
510                                                  final MoneyWiseBasicDataType pItemType) {
511             /* Call super-constructor */
512             super(pClass, pData, pItemType, PrometheusListStyle.CORE);
513         }
514 
515         /**
516          * Constructor for a cloned List.
517          *
518          * @param pSource the source List
519          */
520         protected MoneyWiseSecurityPriceBaseList(final MoneyWiseSecurityPriceBaseList<T> pSource) {
521             /* Call super-constructor */
522             super(pSource);
523         }
524 
525         @Override
526         public MoneyWiseSecurityPriceDataMap getDataMap() {
527             return (MoneyWiseSecurityPriceDataMap) super.getDataMap();
528         }
529 
530         @Override
531         protected MoneyWiseSecurityPriceDataMap allocateDataMap() {
532             return new MoneyWiseSecurityPriceDataMap();
533         }
534     }
535 
536     /**
537      * Price List.
538      */
539     public static class MoneyWiseSecurityPriceList
540             extends MoneyWiseSecurityPriceBaseList<MoneyWiseSecurityPrice> {
541         /**
542          * Report fields.
543          */
544         private static final MetisFieldSet<MoneyWiseSecurityPriceList> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseSecurityPriceList.class);
545 
546         /**
547          * Construct an empty CORE price list.
548          *
549          * @param pData the DataSet for the list
550          */
551         protected MoneyWiseSecurityPriceList(final MoneyWiseDataSet pData) {
552             super(pData, MoneyWiseSecurityPrice.class, MoneyWiseBasicDataType.SECURITYPRICE);
553         }
554 
555         /**
556          * Constructor for a cloned List.
557          *
558          * @param pSource the source List
559          */
560         private MoneyWiseSecurityPriceList(final MoneyWiseSecurityPriceList pSource) {
561             super(pSource);
562         }
563 
564         @Override
565         public MetisFieldSet<MoneyWiseSecurityPriceList> getDataFieldSet() {
566             return FIELD_DEFS;
567         }
568 
569         @Override
570         public String listName() {
571             return LIST_NAME;
572         }
573 
574         @Override
575         public MetisFieldSetDef getItemFields() {
576             return MoneyWiseSecurityPrice.FIELD_DEFS;
577         }
578 
579         @Override
580         public MoneyWiseDataSet getDataSet() {
581             return (MoneyWiseDataSet) super.getDataSet();
582         }
583 
584         @Override
585         protected MoneyWiseSecurityPriceList getEmptyList(final PrometheusListStyle pStyle) {
586             final MoneyWiseSecurityPriceList myList = new MoneyWiseSecurityPriceList(this);
587             myList.setStyle(pStyle);
588             return myList;
589         }
590 
591         /**
592          * Construct an edit extract of a Rate list.
593          *
594          * @param pEditSet the editSet
595          * @return the edit list
596          * @throws OceanusException on error
597          */
598         public MoneyWiseSecurityPriceList deriveEditList(final PrometheusEditSet pEditSet) throws OceanusException {
599             /* Build an empty List */
600             final MoneyWiseSecurityPriceList myList = getEmptyList(PrometheusListStyle.EDIT);
601             myList.ensureMap();
602             pEditSet.setEditEntryList(MoneyWiseBasicDataType.SECURITYPRICE, myList);
603 
604             /* Loop through the list */
605             final Iterator<MoneyWiseSecurityPrice> myIterator = iterator();
606             while (myIterator.hasNext()) {
607                 final MoneyWiseSecurityPrice myCurr = myIterator.next();
608 
609                 /* Copy the item */
610                 final MoneyWiseSecurityPrice myItem = new MoneyWiseSecurityPrice(myList, myCurr);
611                 myItem.resolveEditSetLinks(pEditSet);
612                 myList.add(myItem);
613 
614                 /* Adjust the map */
615                 myItem.adjustMapForItem();
616             }
617 
618             /* Return the List */
619             return myList;
620         }
621 
622         /**
623          * Add a new item to the core list.
624          *
625          * @param pPrice item
626          * @return the newly added item
627          */
628         @Override
629         public MoneyWiseSecurityPrice addCopyItem(final PrometheusDataItem pPrice) {
630             if (pPrice instanceof MoneyWiseSecurityPrice p) {
631                 final MoneyWiseSecurityPrice myPrice = new MoneyWiseSecurityPrice(this, p);
632                 add(myPrice);
633                 return myPrice;
634             } else {
635                 throw new UnsupportedOperationException();
636             }
637         }
638 
639         /**
640          * Add a new item to the edit list.
641          *
642          * @return the newly added item
643          */
644         @Override
645         public MoneyWiseSecurityPrice addNewItem() {
646             final MoneyWiseSecurityPrice myPrice = new MoneyWiseSecurityPrice(this);
647             add(myPrice);
648             return myPrice;
649         }
650 
651         @Override
652         public MoneyWiseSecurityPrice addValuesItem(final PrometheusDataValues pValues) throws OceanusException {
653             /* Create the price */
654             final MoneyWiseSecurityPrice myPrice = new MoneyWiseSecurityPrice(this, pValues);
655 
656             /* Check that this PriceId has not been previously added */
657             if (!isIdUnique(myPrice.getIndexedId())) {
658                 myPrice.addError(ERROR_DUPLICATE, MetisDataResource.DATA_ID);
659                 throw new MoneyWiseDataException(myPrice, ERROR_VALIDATION);
660             }
661 
662             /* Add to the list */
663             add(myPrice);
664 
665             /* Return it */
666             return myPrice;
667         }
668     }
669 
670     /**
671      * The dataMap class.
672      */
673     public static class MoneyWiseSecurityPriceDataMap
674             implements PrometheusDataMapItem, MetisFieldItem {
675         /**
676          * Report fields.
677          */
678         @SuppressWarnings("rawtypes")
679         private static final MetisFieldSet<MoneyWiseSecurityPriceDataMap> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseSecurityPriceDataMap.class);
680 
681         /*
682          * UnderlyingMap Field Id.
683          */
684         static {
685             FIELD_DEFS.declareLocalField(MoneyWiseBasicResource.MONEYWISEDATA_MAP_MAPOFMAPS, MoneyWiseSecurityPriceDataMap::getMapOfMaps);
686             FIELD_DEFS.declareLocalField(MoneyWiseBasicResource.SECURITYPRICE_MAP_MAPOFPRICES, MoneyWiseSecurityPriceDataMap::getMapOfPrices);
687         }
688 
689         /**
690          * Map of Maps.
691          */
692         private final Map<MoneyWiseSecurity, Map<OceanusDate, Integer>> theMapOfMaps;
693 
694         /**
695          * Map of Prices.
696          */
697         private final Map<MoneyWiseSecurity, MoneyWiseSecurityPriceList> theMapOfPrices;
698 
699         /**
700          * Constructor.
701          */
702         public MoneyWiseSecurityPriceDataMap() {
703             /* Create the maps */
704             theMapOfMaps = new HashMap<>();
705             theMapOfPrices = new HashMap<>();
706         }
707 
708         @SuppressWarnings("rawtypes")
709         @Override
710         public MetisFieldSet<MoneyWiseSecurityPriceDataMap> getDataFieldSet() {
711             return FIELD_DEFS;
712         }
713 
714         @Override
715         public String formatObject(final OceanusDataFormatter pFormatter) {
716             return FIELD_DEFS.getName();
717         }
718 
719         /**
720          * Obtain mapOfMaps.
721          *
722          * @return the map
723          */
724         private Map<MoneyWiseSecurity, Map<OceanusDate, Integer>> getMapOfMaps() {
725             return theMapOfMaps;
726         }
727 
728         /**
729          * Obtain mapOfPrices.
730          *
731          * @return the map
732          */
733         private Map<MoneyWiseSecurity, MoneyWiseSecurityPriceList> getMapOfPrices() {
734             return theMapOfPrices;
735         }
736 
737         @Override
738         public void resetMap() {
739             theMapOfMaps.clear();
740             theMapOfPrices.clear();
741         }
742 
743         @Override
744         public void adjustForItem(final PrometheusDataItem pItem) {
745             /* Access the Security Id */
746             final MoneyWiseSecurityPrice myItem = (MoneyWiseSecurityPrice) pItem;
747             final MoneyWiseSecurity mySecurity = myItem.getSecurity();
748             if (mySecurity == null) {
749                 return;
750             }
751 
752             /* Access the map */
753             final Map<OceanusDate, Integer> myMap = theMapOfMaps.computeIfAbsent(mySecurity, s -> new HashMap<>());
754 
755             /* Adjust price count */
756             final OceanusDate myDate = myItem.getDate();
757             final Integer myCount = myMap.get(myDate);
758             if (myCount == null) {
759                 myMap.put(myDate, PrometheusDataInstanceMap.ONE);
760             } else {
761                 myMap.put(myDate, myCount + 1);
762             }
763 
764             /* Access the list */
765             final MoneyWiseSecurityPriceList myList = theMapOfPrices.computeIfAbsent(mySecurity, MoneyWiseSecurityPriceList::new);
766 
767             /* Add element to the list */
768             myList.add(myItem);
769         }
770 
771         /**
772          * Check validity of Price.
773          *
774          * @param pItem the price
775          * @return true/false
776          */
777         public boolean validPriceCount(final MoneyWiseSecurityPrice pItem) {
778             /* Access the Details */
779             final MoneyWiseSecurity mySecurity = pItem.getSecurity();
780             final OceanusDate myDate = pItem.getDate();
781 
782             /* Access the map */
783             final Map<OceanusDate, Integer> myMap = theMapOfMaps.get(mySecurity);
784             if (myMap != null) {
785                 final Integer myResult = myMap.get(myDate);
786                 return PrometheusDataInstanceMap.ONE.equals(myResult);
787             }
788             return false;
789         }
790 
791         /**
792          * Check availability of date for a security.
793          *
794          * @param pSecurity the security
795          * @param pDate     the key to look up
796          * @return true/false
797          */
798         public boolean availableDate(final MoneyWiseSecurity pSecurity,
799                                      final OceanusDate pDate) {
800             /* Access the map */
801             final Map<OceanusDate, Integer> myMap = theMapOfMaps.get(pSecurity);
802             return myMap == null
803                     || myMap.get(pDate) == null;
804         }
805 
806         /**
807          * Obtain price for date.
808          *
809          * @param pSecurity the security
810          * @param pDate     the date
811          * @return the latest price for the date.
812          */
813         public OceanusPrice getPriceForDate(final MoneyWiseAssetBase pSecurity,
814                                             final OceanusDate pDate) {
815             /* Access as security */
816             final MoneyWiseSecurity mySecurity = MoneyWiseSecurity.class.cast(pSecurity);
817 
818             /* Access list for security */
819             final MoneyWiseSecurityPriceList myList = theMapOfPrices.get(mySecurity);
820             if (myList != null) {
821                 /* Loop through the prices */
822                 final Iterator<MoneyWiseSecurityPrice> myIterator = myList.iterator();
823                 while (myIterator.hasNext()) {
824                     final MoneyWiseSecurityPrice myCurr = myIterator.next();
825 
826                     /* Return this price if this is earlier or equal to the the date */
827                     if (pDate.compareTo(myCurr.getDate()) >= 0) {
828                         return myCurr.getPrice();
829                     }
830                 }
831             }
832 
833             /* return single unit price */
834             final Currency myCurrency = mySecurity.getCurrency();
835             return OceanusPrice.getWholeUnits(PrometheusDataInstanceMap.ONE, myCurrency);
836         }
837 
838         /**
839          * Obtain prices for range.
840          *
841          * @param pSecurity the security
842          * @param pRange    the date range
843          * @return the two deep array of prices for the range.
844          */
845         public OceanusPrice[] getPricesForRange(final MoneyWiseSecurity pSecurity,
846                                                 final OceanusDateRange pRange) {
847             /* Set price */
848             final Currency myCurrency = pSecurity.getCurrency();
849             OceanusPrice myFirst = OceanusPrice.getWholeUnits(PrometheusDataInstanceMap.ONE, myCurrency);
850             OceanusPrice myLatest = myFirst;
851             final OceanusDate myStart = pRange.getStart();
852 
853             /* Access list for security */
854             final MoneyWiseSecurityPriceList myList = theMapOfPrices.get(pSecurity);
855             if (myList != null) {
856                 /* Loop through the prices */
857                 final ListIterator<MoneyWiseSecurityPrice> myIterator = myList.listIterator(myList.size());
858                 while (myIterator.hasPrevious()) {
859                     final MoneyWiseSecurityPrice myCurr = myIterator.previous();
860 
861                     /* Check for the range of the date */
862                     final OceanusDate myDate = myCurr.getDate();
863                     final int iComp = pRange.compareToDate(myDate);
864 
865                     /* If this is later than the range we are finished */
866                     if (iComp < 0) {
867                         break;
868                     }
869 
870                     /* Record as best price */
871                     myLatest = myCurr.getPrice();
872 
873                     /* Record early price */
874                     if (iComp > 0
875                             || myDate.compareTo(myStart) == 0) {
876                         myFirst = myLatest;
877                     }
878                 }
879             }
880 
881             /* Return the prices */
882             return new OceanusPrice[]
883                     {myFirst, myLatest};
884         }
885 
886         /**
887          * Obtain priceList cursor.
888          *
889          * @param pSecurity the security
890          * @return the latest price for the date.
891          */
892         public ListIterator<MoneyWiseSecurityPrice> priceIterator(final MoneyWiseSecurity pSecurity) {
893             /* Access list for currency */
894             final MoneyWiseSecurityPriceList myList = theMapOfPrices.get(pSecurity);
895             return myList != null
896                     ? myList.listIterator(myList.size())
897                     : null;
898         }
899 
900         /**
901          * Price List class.
902          */
903         private static final class MoneyWiseSecurityPriceList
904                 implements MetisFieldItem, MetisDataList<MoneyWiseSecurityPrice> {
905             /**
906              * Report fields.
907              */
908             private static final MetisFieldSet<MoneyWiseSecurityPriceList> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseSecurityPriceList.class);
909 
910             /*
911              * UnderlyingMap Field Id.
912              */
913             static {
914                 FIELD_DEFS.declareLocalField(MetisDataResource.LIST_SIZE, MoneyWiseSecurityPriceList::size);
915             }
916 
917             /**
918              * The list.
919              */
920             private final List<MoneyWiseSecurityPrice> theList;
921 
922             /**
923              * The security.
924              */
925             private final MoneyWiseSecurity theSecurity;
926 
927             /**
928              * Constructor.
929              *
930              * @param pSecurity the security
931              */
932             private MoneyWiseSecurityPriceList(final MoneyWiseSecurity pSecurity) {
933                 theSecurity = pSecurity;
934                 theList = new ArrayList<>();
935             }
936 
937             @Override
938             public MetisFieldSet<MoneyWiseSecurityPriceList> getDataFieldSet() {
939                 return FIELD_DEFS;
940             }
941 
942             @Override
943             public String formatObject(final OceanusDataFormatter pFormatter) {
944                 return theSecurity.formatObject(pFormatter)
945                         + "("
946                         + size()
947                         + ")";
948             }
949 
950             @Override
951             public String toString() {
952                 return theSecurity.toString()
953                         + "("
954                         + size()
955                         + ")";
956             }
957 
958             @Override
959             public List<MoneyWiseSecurityPrice> getUnderlyingList() {
960                 return theList;
961             }
962         }
963     }
964 }