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