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.panel;
18  
19  import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
20  import io.github.tonywasher.joceanus.oceanus.date.OceanusDateRange;
21  import io.github.tonywasher.joceanus.oceanus.event.OceanusEvent;
22  import io.github.tonywasher.joceanus.oceanus.event.OceanusEventManager;
23  import io.github.tonywasher.joceanus.oceanus.event.OceanusEventRegistrar;
24  import io.github.tonywasher.joceanus.oceanus.event.OceanusEventRegistrar.OceanusEventProvider;
25  import io.github.tonywasher.joceanus.oceanus.profile.OceanusProfile;
26  import io.github.tonywasher.joceanus.metis.report.MetisReportEvent;
27  import io.github.tonywasher.joceanus.metis.report.MetisReportHTMLBuilder;
28  import io.github.tonywasher.joceanus.metis.report.MetisReportManager;
29  import io.github.tonywasher.joceanus.metis.ui.MetisErrorPanel;
30  import io.github.tonywasher.joceanus.metis.viewer.MetisViewerEntry;
31  import io.github.tonywasher.joceanus.metis.viewer.MetisViewerManager;
32  import io.github.tonywasher.joceanus.moneywise.exc.MoneyWiseDataException;
33  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysis;
34  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisManager;
35  import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisSecurityBucket;
36  import io.github.tonywasher.joceanus.moneywise.lethe.reports.MoneyWiseReportBuilder;
37  import io.github.tonywasher.joceanus.moneywise.lethe.reports.MoneyWiseReportStyleSheet;
38  import io.github.tonywasher.joceanus.moneywise.lethe.reports.MoneyWiseReportType;
39  import io.github.tonywasher.joceanus.moneywise.lethe.ui.controls.MoneyWiseAnalysisSelect.MoneyWiseStatementSelect;
40  import io.github.tonywasher.joceanus.moneywise.lethe.ui.controls.MoneyWiseReportSelect;
41  import io.github.tonywasher.joceanus.moneywise.lethe.views.MoneyWiseAnalysisFilter;
42  import io.github.tonywasher.joceanus.moneywise.lethe.views.MoneyWiseAnalysisFilter.MoneyWiseAnalysisSecurityFilter;
43  import io.github.tonywasher.joceanus.moneywise.ui.MoneyWiseGoToId;
44  import io.github.tonywasher.joceanus.moneywise.ui.MoneyWiseUIResource;
45  import io.github.tonywasher.joceanus.moneywise.views.MoneyWiseView;
46  import io.github.tonywasher.joceanus.prometheus.ui.PrometheusGoToEvent;
47  import io.github.tonywasher.joceanus.prometheus.views.PrometheusDataEvent;
48  import io.github.tonywasher.joceanus.prometheus.views.PrometheusViewerEntryId;
49  import io.github.tonywasher.joceanus.tethys.api.base.TethysUIComponent;
50  import io.github.tonywasher.joceanus.tethys.api.base.TethysUIEvent;
51  import io.github.tonywasher.joceanus.tethys.api.button.TethysUIDateRangeSelector;
52  import io.github.tonywasher.joceanus.tethys.api.control.TethysUIHTMLManager;
53  import io.github.tonywasher.joceanus.tethys.api.factory.TethysUIFactory;
54  import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIBorderPaneManager;
55  import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIPaneFactory;
56  import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIScrollPaneManager;
57  import org.w3c.dom.Document;
58  
59  /**
60   * Report panel.
61   */
62  public class MoneyWiseReportTab
63          implements OceanusEventProvider<PrometheusDataEvent>, TethysUIComponent {
64      /**
65       * Text for DataEntry Title.
66       */
67      private static final String NLS_DATAENTRY = MoneyWiseUIResource.REPORT_DATAENTRY.getValue();
68  
69      /**
70       * The Event Manager.
71       */
72      private final OceanusEventManager<PrometheusDataEvent> theEventManager;
73  
74      /**
75       * The Data View.
76       */
77      private final MoneyWiseView theView;
78  
79      /**
80       * The Panel.
81       */
82      private final TethysUIBorderPaneManager thePanel;
83  
84      /**
85       * The HTML pane.
86       */
87      private final TethysUIHTMLManager theHTMLPane;
88  
89      /**
90       * The Report selection Panel.
91       */
92      private final MoneyWiseReportSelect theSelect;
93  
94      /**
95       * The Spot Analysis Entry.
96       */
97      private final MetisViewerEntry theSpotEntry;
98  
99      /**
100      * The Error Panel.
101      */
102     private final MetisErrorPanel theError;
103 
104     /**
105      * The Report Manager.
106      */
107     private final MetisReportManager<MoneyWiseAnalysisFilter<?, ?>> theManager;
108 
109     /**
110      * The ReportBuilder.
111      */
112     private final MoneyWiseReportBuilder theBuilder;
113 
114     /**
115      * Constructor for Report Window.
116      *
117      * @param pView the data view
118      * @throws OceanusException on error
119      */
120     public MoneyWiseReportTab(final MoneyWiseView pView) throws OceanusException {
121         /* Store the view */
122         theView = pView;
123 
124         /* Access GUI Factory */
125         final TethysUIFactory<?> myFactory = pView.getGuiFactory();
126 
127         /* Create the event manager */
128         theEventManager = new OceanusEventManager<>();
129 
130         /* Create the Panel */
131         final TethysUIPaneFactory myPanes = myFactory.paneFactory();
132         thePanel = myPanes.newBorderPane();
133 
134         /* Create the top level debug entry for this view */
135         final MetisViewerManager myDataMgr = theView.getViewerManager();
136         final MetisViewerEntry mySection = theView.getViewerEntry(PrometheusViewerEntryId.VIEW);
137         final MetisViewerEntry myReport = myDataMgr.newEntry(mySection, NLS_DATAENTRY);
138         theSpotEntry = myDataMgr.newEntry(myReport, PrometheusViewerEntryId.ANALYSIS.toString());
139         theSpotEntry.setVisible(false);
140 
141         /* Create the HTML Pane */
142         theHTMLPane = myFactory.controlFactory().newHTMLManager();
143 
144         /* Create Report Manager */
145         theManager = new MetisReportManager<>(new MetisReportHTMLBuilder(pView.getDataFormatter()));
146 
147         /* Create the report builder */
148         theBuilder = new MoneyWiseReportBuilder(theManager);
149 
150         /* Create the Report Selection panel */
151         theSelect = new MoneyWiseReportSelect(myFactory);
152 
153         /* Create the error panel for this view */
154         theError = theView.getToolkit().getToolkit().newErrorPanel(myReport);
155 
156         /* Create a scroll pane */
157         final TethysUIScrollPaneManager myHTMLScroll = myPanes.newScrollPane();
158         myHTMLScroll.setContent(theHTMLPane);
159 
160         /* Create the header panel */
161         final TethysUIBorderPaneManager myHeader = myPanes.newBorderPane();
162         myHeader.setCentre(theSelect);
163         myHeader.setNorth(theError);
164 
165         /* Now define the panel */
166         thePanel.setNorth(myHeader);
167         thePanel.setCentre(myHTMLScroll);
168 
169         /* Load the CSS */
170         theHTMLPane.setCSSContent(MoneyWiseReportStyleSheet.CSS_REPORT);
171 
172         /* Create listeners */
173         theView.getEventRegistrar().addEventListener(e -> refreshData());
174         theManager.getEventRegistrar().addEventListener(this::handleGoToRequest);
175         theError.getEventRegistrar().addEventListener(e -> handleErrorPane());
176         final OceanusEventRegistrar<PrometheusDataEvent> myRegistrar = theSelect.getEventRegistrar();
177         myRegistrar.addEventListener(PrometheusDataEvent.SELECTIONCHANGED, e -> handleReportRequest());
178         myRegistrar.addEventListener(PrometheusDataEvent.PRINT, e -> theHTMLPane.printIt());
179         myRegistrar.addEventListener(PrometheusDataEvent.SAVETOFILE, e -> theHTMLPane.saveToFile());
180         theHTMLPane.getEventRegistrar().addEventListener(TethysUIEvent.BUILDPAGE, e -> {
181             theManager.processReference(e.getDetails(String.class), theHTMLPane);
182             e.consume();
183         });
184     }
185 
186     @Override
187     public TethysUIComponent getUnderlying() {
188         return thePanel;
189     }
190 
191     @Override
192     public OceanusEventRegistrar<PrometheusDataEvent> getEventRegistrar() {
193         return theEventManager.getEventRegistrar();
194     }
195 
196     @Override
197     public void setEnabled(final boolean pEnabled) {
198         /* Pass on to important elements */
199         theSelect.setEnabled(pEnabled);
200         theError.setEnabled(pEnabled);
201         theHTMLPane.setEnabled(pEnabled);
202     }
203 
204     @Override
205     public void setVisible(final boolean pVisible) {
206         thePanel.setVisible(pVisible);
207     }
208 
209     /**
210      * Refresh views/controls after a load/update of underlying data.
211      */
212     private void refreshData() {
213         /* Obtain the active profile */
214         OceanusProfile myTask = theView.getActiveTask();
215         myTask = myTask.startTask("Reports");
216 
217         /* Protect against exceptions */
218         try {
219             /* Hide the instant debug since it is now invalid */
220             theSpotEntry.setVisible(false);
221 
222             /* Refresh the data */
223             theSelect.setRange(theView.getRange());
224             theSelect.setSecurities(theView.hasActiveSecurities());
225             buildReport();
226 
227             /* Create SavePoint */
228             theSelect.createSavePoint();
229         } catch (OceanusException e) {
230             /* Show the error */
231             theView.addError(e);
232 
233             /* Restore SavePoint */
234             theSelect.restoreSavePoint();
235         }
236 
237         /* Complete the task */
238         myTask.end();
239     }
240 
241     /**
242      * Build the report.
243      *
244      * @throws OceanusException on error
245      */
246     private void buildReport() throws OceanusException {
247         /* Access the values from the selection */
248         final MoneyWiseReportType myReportType = theSelect.getReportType();
249         final OceanusDateRange myRange = theSelect.getDateRange();
250         final MoneyWiseAnalysisManager myManager = theView.getAnalysisManager();
251         final MoneyWiseAnalysisSecurityBucket mySecurity = theSelect.getSecurity();
252 
253         /* set lockDown of selection */
254         theSelect.setEnabled(true);
255 
256         /* Skip if we have no analysis */
257         if (myManager.isIdle()) {
258             theHTMLPane.setHTMLContent("", "");
259             return;
260         }
261 
262         /* Access the appropriate analysis */
263         final MoneyWiseAnalysis myAnalysis = myReportType.isPointInTime()
264                 ? myManager.getDatedAnalysis(myRange.getEnd())
265                 : myManager.getRangedAnalysis(myRange);
266 
267         /* Record analysis and build report */
268         theSelect.setAnalysis(myAnalysis);
269         final Document myDoc = theBuilder.createReport(myAnalysis, myReportType, mySecurity);
270 
271         /* Declare to debugger */
272         theSpotEntry.setObject(myAnalysis);
273         theSpotEntry.setVisible(true);
274 
275         /* Declare the document */
276         theManager.setDocument(myDoc);
277 
278         /* Create initial display version */
279         final String myText = theManager.formatXML();
280         theHTMLPane.setHTMLContent(myText, "");
281     }
282 
283     /**
284      * handleErrorPane.
285      */
286     private void handleErrorPane() {
287         /* Determine whether we have an error */
288         final boolean isError = theError.hasError();
289 
290         /* Hide selection panel on error */
291         theSelect.setVisible(!isError);
292 
293         /* Lock HTML area */
294         theHTMLPane.setEnabled(!isError);
295     }
296 
297     /**
298      * handleGoToRequest.
299      *
300      * @param pEvent the event
301      */
302     private void handleGoToRequest(final OceanusEvent<MetisReportEvent> pEvent) {
303         /* Access the filter */
304         final MoneyWiseAnalysisFilter<?, ?> myFilter = pEvent.getDetails(MoneyWiseAnalysisFilter.class);
305 
306         /* If we are currently showing Asset Gains */
307         if (MoneyWiseReportType.ASSETGAINS.equals(theSelect.getReportType())) {
308             /* Select the capital gains report */
309             final MoneyWiseAnalysisSecurityBucket myBucket = ((MoneyWiseAnalysisSecurityFilter) myFilter).getBucket();
310             theSelect.setSecurity(myBucket);
311 
312             /* else we are selecting a statement */
313         } else {
314             /* Create the details of the report */
315             final TethysUIDateRangeSelector mySelect = theSelect.getDateRangeSelector();
316             final MoneyWiseStatementSelect myStatement = new MoneyWiseStatementSelect(mySelect, myFilter);
317 
318             /* Request the action */
319             theEventManager.fireEvent(PrometheusDataEvent.GOTOWINDOW, new PrometheusGoToEvent<>(MoneyWiseGoToId.STATEMENT, myStatement));
320         }
321     }
322 
323     /**
324      * handleReportRequest.
325      */
326     private void handleReportRequest() {
327         /* Protect against exceptions */
328         try {
329             /* build the report */
330             buildReport();
331 
332             /* Create SavePoint */
333             theSelect.createSavePoint();
334 
335             /* Catch Exceptions */
336         } catch (OceanusException e) {
337             /* Build the error */
338             final OceanusException myError = new MoneyWiseDataException("Failed to change selection", e);
339 
340             /* Show the error */
341             theError.addError(myError);
342             handleErrorPane();
343 
344             /* Restore SavePoint */
345             theSelect.restoreSavePoint();
346         }
347     }
348 }