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