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.lethe.ui.controls;
18  
19  import io.github.tonywasher.joceanus.oceanus.date.OceanusDateRange;
20  import io.github.tonywasher.joceanus.oceanus.event.OceanusEventManager;
21  import io.github.tonywasher.joceanus.oceanus.event.OceanusEventRegistrar;
22  import io.github.tonywasher.joceanus.oceanus.event.OceanusEventRegistrar.OceanusEventProvider;
23  import io.github.tonywasher.joceanus.metis.data.MetisDataDifference;
24  import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicDataType;
25  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysis;
26  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisPortfolioBucket;
27  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisPortfolioBucket.MoneyWiseAnalysisPortfolioBucketList;
28  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisSecurityBucket;
29  import io.github.tonywasher.joceanus.moneywise.lethe.views.MoneyWiseAnalysisFilter;
30  import io.github.tonywasher.joceanus.moneywise.lethe.views.MoneyWiseAnalysisFilter.MoneyWiseAnalysisSecurityFilter;
31  import io.github.tonywasher.joceanus.prometheus.views.PrometheusDataEvent;
32  import io.github.tonywasher.joceanus.tethys.api.base.TethysUIComponent;
33  import io.github.tonywasher.joceanus.tethys.api.base.TethysUIConstant;
34  import io.github.tonywasher.joceanus.tethys.api.base.TethysUIEvent;
35  import io.github.tonywasher.joceanus.tethys.api.button.TethysUIButtonFactory;
36  import io.github.tonywasher.joceanus.tethys.api.button.TethysUIScrollButtonManager;
37  import io.github.tonywasher.joceanus.tethys.api.control.TethysUIControlFactory;
38  import io.github.tonywasher.joceanus.tethys.api.control.TethysUILabel;
39  import io.github.tonywasher.joceanus.tethys.api.factory.TethysUIFactory;
40  import io.github.tonywasher.joceanus.tethys.api.menu.TethysUIScrollItem;
41  import io.github.tonywasher.joceanus.tethys.api.menu.TethysUIScrollMenu;
42  import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIBoxPaneManager;
43  
44  import java.util.Iterator;
45  
46  /**
47   * Security Analysis Selection.
48   */
49  public class MoneyWiseSecurityAnalysisSelect
50          implements MoneyWiseAnalysisFilterSelection, OceanusEventProvider<PrometheusDataEvent> {
51      /**
52       * Text for Portfolio Label.
53       */
54      private static final String NLS_PORTFOLIO = MoneyWiseBasicDataType.PORTFOLIO.getItemName();
55  
56      /**
57       * Text for Security Label.
58       */
59      private static final String NLS_SECURITY = MoneyWiseBasicDataType.SECURITY.getItemName();
60  
61      /**
62       * The Event Manager.
63       */
64      private final OceanusEventManager<PrometheusDataEvent> theEventManager;
65  
66      /**
67       * The panel.
68       */
69      private final TethysUIBoxPaneManager thePanel;
70  
71      /**
72       * The security button.
73       */
74      private final TethysUIScrollButtonManager<MoneyWiseAnalysisSecurityBucket> theSecButton;
75  
76      /**
77       * The portfolio button.
78       */
79      private final TethysUIScrollButtonManager<MoneyWiseAnalysisPortfolioBucket> thePortButton;
80  
81      /**
82       * Portfolio menu.
83       */
84      private final TethysUIScrollMenu<MoneyWiseAnalysisPortfolioBucket> thePortfolioMenu;
85  
86      /**
87       * Security menu.
88       */
89      private final TethysUIScrollMenu<MoneyWiseAnalysisSecurityBucket> theSecurityMenu;
90  
91      /**
92       * The active portfolio bucket list.
93       */
94      private MoneyWiseAnalysisPortfolioBucketList thePortfolios;
95  
96      /**
97       * The state.
98       */
99      private MoneyWiseSecurityState theState;
100 
101     /**
102      * The savePoint.
103      */
104     private MoneyWiseSecurityState theSavePoint;
105 
106     /**
107      * Constructor.
108      *
109      * @param pFactory the GUI factory
110      */
111     protected MoneyWiseSecurityAnalysisSelect(final TethysUIFactory<?> pFactory) {
112         /* Create the security button */
113         final TethysUIButtonFactory<?> myButtons = pFactory.buttonFactory();
114         theSecButton = myButtons.newScrollButton(MoneyWiseAnalysisSecurityBucket.class);
115 
116         /* Create the portfolio button */
117         thePortButton = myButtons.newScrollButton(MoneyWiseAnalysisPortfolioBucket.class);
118 
119         /* Create Event Manager */
120         theEventManager = new OceanusEventManager<>();
121 
122         /* Create the labels */
123         final TethysUIControlFactory myControls = pFactory.controlFactory();
124         final TethysUILabel myPortLabel = myControls.newLabel(NLS_PORTFOLIO + TethysUIConstant.STR_COLON);
125         final TethysUILabel mySecLabel = myControls.newLabel(NLS_SECURITY + TethysUIConstant.STR_COLON);
126 
127         /* Define the layout */
128         thePanel = pFactory.paneFactory().newHBoxPane();
129         thePanel.addSpacer();
130         thePanel.addNode(myPortLabel);
131         thePanel.addNode(thePortButton);
132         thePanel.addStrut();
133         thePanel.addNode(mySecLabel);
134         thePanel.addNode(theSecButton);
135 
136         /* Create initial state */
137         theState = new MoneyWiseSecurityState();
138         theState.applyState();
139 
140         /* Access the menus */
141         thePortfolioMenu = thePortButton.getMenu();
142         theSecurityMenu = theSecButton.getMenu();
143 
144         /* Create the listener */
145         OceanusEventRegistrar<TethysUIEvent> myRegistrar = thePortButton.getEventRegistrar();
146         myRegistrar.addEventListener(TethysUIEvent.NEWVALUE, e -> handleNewPortfolio());
147         thePortButton.setMenuConfigurator(e -> buildPortfolioMenu());
148         myRegistrar = theSecButton.getEventRegistrar();
149         myRegistrar.addEventListener(TethysUIEvent.NEWVALUE, e -> handleNewSecurity());
150         theSecButton.setMenuConfigurator(e -> buildSecurityMenu());
151     }
152 
153     @Override
154     public TethysUIComponent getUnderlying() {
155         return thePanel;
156     }
157 
158     @Override
159     public OceanusEventRegistrar<PrometheusDataEvent> getEventRegistrar() {
160         return theEventManager.getEventRegistrar();
161     }
162 
163     @Override
164     public MoneyWiseAnalysisSecurityFilter getFilter() {
165         return theState.getFilter();
166     }
167 
168     @Override
169     public boolean isAvailable() {
170         return thePortfolios != null
171                 && !thePortfolios.isEmpty();
172     }
173 
174     /**
175      * Create SavePoint.
176      */
177     public void createSavePoint() {
178         /* Create the savePoint */
179         theSavePoint = new MoneyWiseSecurityState(theState);
180     }
181 
182     /**
183      * Restore SavePoint.
184      */
185     public void restoreSavePoint() {
186         /* Restore the savePoint */
187         theState = new MoneyWiseSecurityState(theSavePoint);
188 
189         /* Apply the state */
190         theState.applyState();
191     }
192 
193     @Override
194     public void setEnabled(final boolean bEnabled) {
195         /* Determine whether there are any Securities to select */
196         final boolean secAvailable = bEnabled && isAvailable();
197 
198         /* Pass call on to buttons */
199         theSecButton.setEnabled(secAvailable);
200         thePortButton.setEnabled(secAvailable);
201     }
202 
203     @Override
204     public void setVisible(final boolean pVisible) {
205         thePanel.setVisible(pVisible);
206     }
207 
208     /**
209      * Set analysis.
210      *
211      * @param pAnalysis the analysis.
212      */
213     public void setAnalysis(final MoneyWiseAnalysis pAnalysis) {
214         /* Access buckets */
215         thePortfolios = pAnalysis.getPortfolios();
216 
217         /* Obtain the current security */
218         MoneyWiseAnalysisSecurityBucket mySecurity = theState.getSecurity();
219 
220         /* Switch to versions from the analysis */
221         mySecurity = mySecurity != null
222                 ? thePortfolios.getMatchingSecurityHolding(mySecurity.getSecurityHolding())
223                 : thePortfolios.getDefaultSecurityHolding();
224         final MoneyWiseAnalysisPortfolioBucket myPortfolio = mySecurity != null
225                 ? thePortfolios.getMatchingPortfolio(mySecurity.getPortfolio())
226                 : thePortfolios.getDefaultPortfolio();
227 
228         /* Set the security */
229         theState.setTheSecurity(myPortfolio, mySecurity);
230         theState.setDateRange(pAnalysis.getDateRange());
231         theState.applyState();
232     }
233 
234     @Override
235     public void setFilter(final MoneyWiseAnalysisFilter<?, ?> pFilter) {
236         /* If this is the correct filter type */
237         if (pFilter instanceof MoneyWiseAnalysisSecurityFilter) {
238             /* Access filter */
239             final MoneyWiseAnalysisSecurityFilter myFilter = (MoneyWiseAnalysisSecurityFilter) pFilter;
240 
241             /* Obtain the filter buckets */
242             MoneyWiseAnalysisSecurityBucket mySecurity = myFilter.getBucket();
243 
244             /* Look for the equivalent buckets */
245             mySecurity = thePortfolios.getMatchingSecurityHolding(mySecurity.getSecurityHolding());
246 
247             /* Determine the matching portfolio bucket */
248             final MoneyWiseAnalysisPortfolioBucket myPortfolio = thePortfolios.getMatchingPortfolio(mySecurity.getPortfolio());
249 
250             /* Set the security */
251             theState.setTheSecurity(myPortfolio, mySecurity);
252             theState.setDateRange(myFilter.getDateRange());
253             theState.applyState();
254         }
255     }
256 
257     /**
258      * Handle new Portfolio.
259      */
260     private void handleNewPortfolio() {
261         /* Select the new portfolio */
262         if (theState.setPortfolio(thePortButton.getValue())) {
263             theState.applyState();
264             theEventManager.fireEvent(PrometheusDataEvent.SELECTIONCHANGED);
265         }
266     }
267 
268     /**
269      * Handle new Security.
270      */
271     private void handleNewSecurity() {
272         /* Select the new security */
273         if (theState.setSecurity(theSecButton.getValue())) {
274             theState.applyState();
275             theEventManager.fireEvent(PrometheusDataEvent.SELECTIONCHANGED);
276         }
277     }
278 
279     /**
280      * Build Portfolio menu.
281      */
282     private void buildPortfolioMenu() {
283         /* Reset the popUp menu */
284         thePortfolioMenu.removeAllItems();
285 
286         /* Record active item */
287         TethysUIScrollItem<MoneyWiseAnalysisPortfolioBucket> myActive = null;
288         final MoneyWiseAnalysisPortfolioBucket myCurr = theState.getPortfolio();
289 
290         /* Loop through the available portfolio values */
291         final Iterator<MoneyWiseAnalysisPortfolioBucket> myIterator = thePortfolios.iterator();
292         while (myIterator.hasNext()) {
293             final MoneyWiseAnalysisPortfolioBucket myBucket = myIterator.next();
294 
295             /* Create a new MenuItem and add it to the popUp */
296             final TethysUIScrollItem<MoneyWiseAnalysisPortfolioBucket> myItem = thePortfolioMenu.addItem(myBucket);
297 
298             /* If this is the active bucket */
299             if (myBucket.equals(myCurr)) {
300                 /* Record it */
301                 myActive = myItem;
302             }
303         }
304 
305         /* Ensure active item is visible */
306         if (myActive != null) {
307             myActive.scrollToItem();
308         }
309     }
310 
311     /**
312      * Build Security menu.
313      */
314     private void buildSecurityMenu() {
315         /* Reset the popUp menu */
316         theSecurityMenu.removeAllItems();
317 
318         /* Access current portfolio */
319         final MoneyWiseAnalysisPortfolioBucket myPortfolio = theState.getPortfolio();
320         final MoneyWiseAnalysisSecurityBucket myCurr = theState.getSecurity();
321         TethysUIScrollItem<MoneyWiseAnalysisSecurityBucket> myActive = null;
322 
323         /* Loop through the available security values */
324         final Iterator<MoneyWiseAnalysisSecurityBucket> myIterator = myPortfolio.securityIterator();
325         while (myIterator.hasNext()) {
326             final MoneyWiseAnalysisSecurityBucket myBucket = myIterator.next();
327 
328             /* Create a new MenuItem and add it to the popUp */
329             final TethysUIScrollItem<MoneyWiseAnalysisSecurityBucket> myItem = theSecurityMenu.addItem(myBucket, myBucket.getSecurityName());
330 
331             /* If this is the active bucket */
332             if (myBucket.equals(myCurr)) {
333                 /* Record it */
334                 myActive = myItem;
335             }
336         }
337 
338         /* Ensure active item is visible */
339         if (myActive != null) {
340             myActive.scrollToItem();
341         }
342     }
343 
344     /**
345      * SavePoint values.
346      */
347     private final class MoneyWiseSecurityState {
348         /**
349          * The active Portfolio.
350          */
351         private MoneyWiseAnalysisPortfolioBucket thePortfolio;
352 
353         /**
354          * The active SecurityBucket.
355          */
356         private MoneyWiseAnalysisSecurityBucket theSecurity;
357 
358         /**
359          * The dateRange.
360          */
361         private OceanusDateRange theDateRange;
362 
363         /**
364          * The filter.
365          */
366         private MoneyWiseAnalysisSecurityFilter theFilter;
367 
368         /**
369          * Constructor.
370          */
371         private MoneyWiseSecurityState() {
372         }
373 
374         /**
375          * Constructor.
376          *
377          * @param pState state to copy from
378          */
379         private MoneyWiseSecurityState(final MoneyWiseSecurityState pState) {
380             /* Initialise state */
381             theSecurity = pState.getSecurity();
382             thePortfolio = pState.getPortfolio();
383             theDateRange = pState.getDateRange();
384             theFilter = pState.getFilter();
385         }
386 
387         /**
388          * Obtain the Security Bucket.
389          *
390          * @return the Security
391          */
392         private MoneyWiseAnalysisSecurityBucket getSecurity() {
393             return theSecurity;
394         }
395 
396         /**
397          * Obtain the Portfolio.
398          *
399          * @return the portfolio
400          */
401         private MoneyWiseAnalysisPortfolioBucket getPortfolio() {
402             return thePortfolio;
403         }
404 
405         /**
406          * Obtain the dateRange.
407          *
408          * @return the dateRange
409          */
410         private OceanusDateRange getDateRange() {
411             return theDateRange;
412         }
413 
414         /**
415          * Obtain the Filter.
416          *
417          * @return the filter
418          */
419         private MoneyWiseAnalysisSecurityFilter getFilter() {
420             return theFilter;
421         }
422 
423         /**
424          * Set new Security.
425          *
426          * @param pSecurity the Security
427          * @return true/false did a change occur
428          */
429         private boolean setSecurity(final MoneyWiseAnalysisSecurityBucket pSecurity) {
430             /* Adjust the selected security */
431             if (!MetisDataDifference.isEqual(pSecurity, theSecurity)) {
432                 /* Store the security */
433                 setTheSecurity(thePortfolio, pSecurity);
434                 return true;
435             }
436             return false;
437         }
438 
439         /**
440          * Set new Security.
441          *
442          * @param pPortfolio the Portfolio
443          * @param pSecurity  the Security
444          */
445         private void setTheSecurity(final MoneyWiseAnalysisPortfolioBucket pPortfolio,
446                                     final MoneyWiseAnalysisSecurityBucket pSecurity) {
447             /* Store the portfolio and security */
448             thePortfolio = pPortfolio;
449             theSecurity = pSecurity;
450             if (theSecurity != null) {
451                 theFilter = new MoneyWiseAnalysisSecurityFilter(theSecurity);
452                 theFilter.setDateRange(theDateRange);
453             } else {
454                 theFilter = null;
455             }
456         }
457 
458         /**
459          * Set new Portfolio.
460          *
461          * @param pPortfolio the Portfolio
462          * @return true/false did a change occur
463          */
464         private boolean setPortfolio(final MoneyWiseAnalysisPortfolioBucket pPortfolio) {
465             /* Adjust the selected portfolio */
466             if (!MetisDataDifference.isEqual(pPortfolio, thePortfolio)) {
467                 setTheSecurity(pPortfolio, getFirstSecurity(pPortfolio));
468                 return true;
469             }
470             return false;
471         }
472 
473         /**
474          * Obtain first security for portfolio.
475          *
476          * @param pPortfolio the portfolio
477          * @return the first security
478          */
479         private MoneyWiseAnalysisSecurityBucket getFirstSecurity(final MoneyWiseAnalysisPortfolioBucket pPortfolio) {
480             /* Loop through the available security values */
481             final Iterator<MoneyWiseAnalysisSecurityBucket> myIterator = pPortfolio.securityIterator();
482             return myIterator.hasNext()
483                     ? myIterator.next()
484                     : null;
485         }
486 
487         /**
488          * Set the dateRange.
489          *
490          * @param pRange the dateRange
491          */
492         private void setDateRange(final OceanusDateRange pRange) {
493             /* Store the dateRange */
494             theDateRange = pRange;
495             if (theFilter != null) {
496                 theFilter.setDateRange(theDateRange);
497             }
498         }
499 
500         /**
501          * Apply the State.
502          */
503         private void applyState() {
504             /* Adjust the lock-down */
505             setEnabled(true);
506             theSecButton.setValue(theSecurity, theSecurity == null
507                     ? null
508                     : theSecurity.getSecurityName());
509             thePortButton.setValue(thePortfolio);
510         }
511     }
512 }