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.views;
18  
19  import io.github.tonywasher.joceanus.metis.data.MetisDataEditState;
20  import io.github.tonywasher.joceanus.metis.data.MetisDataState;
21  import io.github.tonywasher.joceanus.metis.field.MetisFieldSet;
22  import io.github.tonywasher.joceanus.metis.field.MetisFieldVersionValues;
23  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicDataType;
24  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicResource;
25  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDataSet;
26  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseExchangeRate;
27  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseCurrency;
28  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseCurrency.MoneyWiseCurrencyList;
29  import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseStaticDataType;
30  import io.github.tonywasher.joceanus.oceanus.date.OceanusDate;
31  import io.github.tonywasher.joceanus.oceanus.decimal.OceanusRatio;
32  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataItem;
33  import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataValues;
34  import io.github.tonywasher.joceanus.prometheus.data.PrometheusListStyle;
35  
36  import java.util.Iterator;
37  import java.util.ListIterator;
38  
39  /**
40   * Extension of ExchangeRate to cater for spot rates.
41   */
42  public final class MoneyWiseSpotExchangeRate
43          extends MoneyWiseExchangeRate {
44      /**
45       * Object name.
46       */
47      public static final String OBJECT_NAME = MoneyWiseSpotExchangeRate.class.getSimpleName();
48  
49      /**
50       * List name.
51       */
52      public static final String LIST_NAME = OBJECT_NAME + "s";
53  
54      /**
55       * Report fields.
56       */
57      @SuppressWarnings("rawtypes")
58      private static final MetisFieldSet<MoneyWiseSpotExchangeRate> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseSpotExchangeRate.class);
59  
60      /*
61       * The fields.
62       */
63      static {
64          FIELD_DEFS.declareLocalField(MoneyWiseViewResource.SPOTEVENT_PREVDATE, MoneyWiseSpotExchangeRate::getPrevDate);
65          FIELD_DEFS.declareLocalField(MoneyWiseViewResource.SPOTRATE_PREVRATE, MoneyWiseSpotExchangeRate::getPrevRate);
66      }
67  
68      /**
69       * the previous date.
70       */
71      private OceanusDate thePrevDate;
72  
73      /**
74       * the previous rate.
75       */
76      private OceanusRatio thePrevRate;
77  
78      /**
79       * Constructor for a new SpotRate where no rate data exists.
80       *
81       * @param pList     the Spot Rate List
82       * @param pCurrency the currency
83       */
84      private MoneyWiseSpotExchangeRate(final MoneyWiseSpotExchangeList pList,
85                                        final MoneyWiseCurrency pCurrency) {
86          super(pList);
87  
88          /* Store base values */
89          setDate(pList.theDate);
90          setFromCurrency(pList.getCurrency());
91          setToCurrency(pCurrency);
92      }
93  
94      @Override
95      public MetisFieldSetDef getDataFieldSet() {
96          return FIELD_DEFS;
97      }
98  
99      /**
100      * Obtain previous rate.
101      *
102      * @return the rate.
103      */
104     public OceanusRatio getPrevRate() {
105         return thePrevRate;
106     }
107 
108     /**
109      * Obtain previous date.
110      *
111      * @return the date.
112      */
113     public OceanusDate getPrevDate() {
114         return thePrevDate;
115     }
116 
117     /**
118      * Validate the line.
119      */
120     @Override
121     public void validate() {
122         setValidEdit();
123     }
124 
125     /* Is this row locked */
126     @Override
127     public boolean isLocked() {
128         return isDeleted();
129     }
130 
131     /**
132      * Note that this item has been validated.
133      */
134     @Override
135     public void setValidEdit() {
136         setEditState(hasHistory()
137                 ? MetisDataEditState.VALID
138                 : MetisDataEditState.CLEAN);
139     }
140 
141     @Override
142     public OceanusRatio getExchangeRate() {
143         /* Switch on state */
144         switch (getState()) {
145             case NEW:
146             case CHANGED:
147             case RECOVERED:
148             case CLEAN:
149                 return super.getExchangeRate();
150             default:
151                 return null;
152         }
153     }
154 
155     @Override
156     public MetisDataState getState() {
157         final MetisFieldVersionValues myCurr = getValues();
158         final MetisFieldVersionValues myBase = getOriginalValues();
159 
160         /* If we have no changes we are CLEAN */
161         if (myCurr.getVersion() == 0) {
162             return MetisDataState.CLEAN;
163         }
164 
165         /* If the original rate is Null */
166         if (myBase.getValue(MoneyWiseBasicResource.XCHGRATE_RATE) == null) {
167             /* Return status */
168             return myCurr.getValue(MoneyWiseBasicResource.XCHGRATE_RATE) == null
169                     ? MetisDataState.DELNEW
170                     : MetisDataState.NEW;
171         }
172 
173         /* If we are deleted return so */
174         return myCurr.getValue(MoneyWiseBasicResource.XCHGRATE_RATE) == null
175                 ? MetisDataState.DELETED
176                 : MetisDataState.CHANGED;
177     }
178 
179     /**
180      * The Spot Rates List class.
181      */
182     public static class MoneyWiseSpotExchangeList
183             extends MoneyWiseExchangeRateBaseList<MoneyWiseSpotExchangeRate> {
184         /**
185          * Report fields.
186          */
187         @SuppressWarnings("rawtypes")
188         private static final MetisFieldSet<MoneyWiseSpotExchangeList> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseSpotExchangeList.class);
189 
190         /*
191          * The currency field Id.
192          */
193         static {
194             FIELD_DEFS.declareLocalField(MoneyWiseStaticDataType.CURRENCY, MoneyWiseSpotExchangeList::getCurrency);
195             FIELD_DEFS.declareLocalField(MoneyWiseBasicResource.MONEYWISEDATA_FIELD_DATE, MoneyWiseSpotExchangeList::getDate);
196             FIELD_DEFS.declareLocalField(MoneyWiseViewResource.SPOTEVENT_NEXTDATE, MoneyWiseSpotExchangeList::getNext);
197             FIELD_DEFS.declareLocalField(MoneyWiseViewResource.SPOTEVENT_PREVDATE, MoneyWiseSpotExchangeList::getPrev);
198         }
199 
200         /**
201          * The date.
202          */
203         private final OceanusDate theDate;
204 
205         /**
206          * The view.
207          */
208         private final MoneyWiseView theView;
209 
210         /**
211          * The currency.
212          */
213         private final MoneyWiseCurrency theCurrency;
214 
215         /**
216          * The next date.
217          */
218         private OceanusDate theNext;
219 
220         /**
221          * The previous date.
222          */
223         private OceanusDate thePrev;
224 
225         /**
226          * Constructor.
227          *
228          * @param pView the view
229          * @param pDate the date
230          */
231         public MoneyWiseSpotExchangeList(final MoneyWiseView pView,
232                                          final OceanusDate pDate) {
233             /* Build initial list */
234             super(pView.getData(), MoneyWiseSpotExchangeRate.class, MoneyWiseBasicDataType.SECURITYPRICE);
235             setStyle(PrometheusListStyle.EDIT);
236             ensureMap();
237 
238             /* Store parameters */
239             theDate = pDate;
240             theView = pView;
241 
242             /* Obtain the portfolio bucket */
243             final MoneyWiseDataSet myData = theView.getData();
244             theCurrency = myData.getReportingCurrency();
245             final MoneyWiseCurrencyList myCurrencies = myData.getAccountCurrencies();
246 
247             /* Loop through the Currencies */
248             final OceanusDate myDate = new OceanusDate(theDate);
249             final Iterator<MoneyWiseCurrency> myCurIterator = myCurrencies.iterator();
250             while (myCurIterator.hasNext()) {
251                 final MoneyWiseCurrency myCurrency = myCurIterator.next();
252 
253                 /* Ignore deleted/disabled and default currency */
254                 boolean bIgnore = myCurrency.isDeleted() || myCurrency.isDisabled();
255                 bIgnore |= myCurrency.equals(theCurrency);
256                 if (bIgnore) {
257                     continue;
258                 }
259 
260                 /* Create a SpotRate entry */
261                 final MoneyWiseSpotExchangeRate mySpot = new MoneyWiseSpotExchangeRate(this, myCurrency);
262                 mySpot.setIndexedId(myCurrency.getIndexedId());
263                 mySpot.setDate(myDate);
264                 add(mySpot);
265             }
266 
267             /* Set the base for this list */
268             final MoneyWiseExchangeRateList myRates = myData.getExchangeRates();
269             setBase(myRates);
270 
271             /* Loop through the rates */
272             final ListIterator<MoneyWiseExchangeRate> myIterator = myRates.listIterator(myRates.size());
273             while (myIterator.hasPrevious()) {
274                 final MoneyWiseExchangeRate myRate = myIterator.previous();
275 
276                 /* Ignore deleted rates */
277                 if (myRate.isDeleted()) {
278                     continue;
279                 }
280 
281                 /* Test the Date */
282                 final int iDiff = theDate.compareTo(myRate.getDate());
283 
284                 /* If we are past the date */
285                 if (iDiff < 0) {
286                     /* Record the next date and break the loop */
287                     theNext = myRate.getDate();
288                     break;
289                 }
290 
291                 /* Access the Spot Rate and ignore if not relevant */
292                 final MoneyWiseCurrency myCurrency = myRate.getToCurrency();
293                 final MoneyWiseSpotExchangeRate mySpot = findItemById(myCurrency.getIndexedId());
294                 if (mySpot == null) {
295                     continue;
296                 }
297 
298                 /* If we are exactly the date */
299                 if (iDiff == 0) {
300                     /* Set rate */
301                     mySpot.setValueExchangeRate(myRate.getExchangeRate());
302 
303                     /* Link to base and re-establish state */
304                     mySpot.setBase(myRate);
305 
306                     /* else we are a previous date */
307                 } else {
308                     /* Set previous date and value */
309                     mySpot.thePrevDate = myRate.getDate();
310                     mySpot.thePrevRate = myRate.getExchangeRate();
311 
312                     /* Record the latest previous date */
313                     thePrev = myRate.getDate();
314                 }
315             }
316         }
317 
318         @SuppressWarnings("rawtypes")
319         @Override
320         public MetisFieldSet<MoneyWiseSpotExchangeList> getDataFieldSet() {
321             return FIELD_DEFS;
322         }
323 
324         @Override
325         public String listName() {
326             return MoneyWiseSpotExchangeList.class.getSimpleName();
327         }
328 
329         @Override
330         public MetisFieldSetDef getItemFields() {
331             return MoneyWiseSpotExchangeRate.FIELD_DEFS;
332         }
333 
334         @Override
335         protected MoneyWiseSpotExchangeList getEmptyList(final PrometheusListStyle pStyle) {
336             throw new UnsupportedOperationException();
337         }
338 
339         @Override
340         public MoneyWiseDataSet getDataSet() {
341             return (MoneyWiseDataSet) super.getDataSet();
342         }
343 
344         /**
345          * Obtain the currency.
346          *
347          * @return the currency
348          */
349         private MoneyWiseCurrency getCurrency() {
350             return theCurrency;
351         }
352 
353         /**
354          * Obtain the date.
355          *
356          * @return the date
357          */
358         private OceanusDate getDate() {
359             return theDate;
360         }
361 
362         /**
363          * Obtain the next date.
364          *
365          * @return the date
366          */
367         public OceanusDate getNext() {
368             return theNext;
369         }
370 
371         /**
372          * Obtain the previous date.
373          *
374          * @return the date
375          */
376         public OceanusDate getPrev() {
377             return thePrev;
378         }
379 
380         /* Is this list locked */
381         @Override
382         public boolean isLocked() {
383             return false;
384         }
385 
386         /* Disable Add a new item */
387         @Override
388         public MoneyWiseSpotExchangeRate addCopyItem(final PrometheusDataItem pElement) {
389             throw new UnsupportedOperationException();
390         }
391 
392         @Override
393         public MoneyWiseSpotExchangeRate addNewItem() {
394             throw new UnsupportedOperationException();
395         }
396 
397         @Override
398         public MoneyWiseSpotExchangeRate addValuesItem(final PrometheusDataValues pValues) {
399             throw new UnsupportedOperationException();
400         }
401     }
402 }