de.dekarlab.moneybuilder.view.AnalyticsView.java Source code

Java tutorial

Introduction

Here is the source code for de.dekarlab.moneybuilder.view.AnalyticsView.java

Source

/**
* Copyright (C) 2016,  Denis Karlow, DekarLab.de/BehindTheStrategy.com
*  
* This file is part of Dekar Lab Money Builder Software.
* 
* Dekar Lab Money Builder Software is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* 
* Dekar Lab Money Builder Software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DEKAR Lab CapBuilder Software.  If not, see <http://www.gnu.org/licenses/>.
*
* Diese Datei ist Teil von Dekar Lab Money Builder Software.
* 
* Dekar Lab Money Builder ist Freie Software: Sie knnen es unter den Bedingungen
* der GNU General Public License, wie von der Free Software Foundation,
* Version 3 der Lizenz oder (nach Ihrer Wahl) jeder spteren
* verffentlichten Version, weiterverbreiten und/oder modifizieren.
* 
* Dekar Lab Money Builder Software wird in der Hoffnung, dass es ntzlich sein wird, aber
* OHNE JEDE GEWHRLEISTUNG, bereitgestellt; sogar ohne die implizite
* Gewhrleistung der MARKTFHIGKEIT oder EIGNUNG FR EINEN BESTIMMTEN ZWECK.
* Siehe die GNU General Public License fr weitere Details.
    
* Sie sollten eine Kopie der GNU General Public License zusammen mit diesem
* Programm erhalten haben. Wenn nicht, siehe <http://www.gnu.org/licenses/>.
*/

package de.dekarlab.moneybuilder.view;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;
import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PiePlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.CategoryItemRenderer;
import org.jfree.chart.renderer.category.StandardBarPainter;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.data.general.PieDataset;
import org.jfree.experimental.chart.swt.ChartComposite;

import de.dekarlab.moneybuilder.App;
import de.dekarlab.moneybuilder.model.Account;
import de.dekarlab.moneybuilder.model.Book;
import de.dekarlab.moneybuilder.model.Folder;
import de.dekarlab.moneybuilder.model.Period;
import de.dekarlab.moneybuilder.model.util.Formatter;

/**
 * Analytics view.
 * 
 * @author dk
 * 
 */
public class AnalyticsView extends Composite {
    /**
     * Colors. http://www.mulinblog.com/a-color-palette-optimized-for-data-
     * visualization/
     * 
     */
    private Color[] COLORS = new Color[] { new Color(93, 165, 218), // 5DA5DA
            // (blue)
            new Color(241, 88, 84), // F15854 (red)
            new Color(222, 207, 63), // DECF3F (yellow)
            new Color(250, 164, 58), // FAA43A (orange)
            new Color(96, 189, 104), // 60BD68 (green)
            new Color(77, 77, 77), // 4D4D4D
            // (gray)
            new Color(241, 124, 176), // F17CB0 (pink)
            new Color(178, 145, 47), // B2912F (brown)
            new Color(178, 118, 178) // B276B2 (purple)
    };
    /**
     * Reports.
     */
    private Combo cmbReports;
    /**
     * Chart composite.
     */
    private ChartComposite chartComp;

    /**
     * Constructor.
     * 
     * @param parent
     * @param style
     */
    public AnalyticsView(Composite parent, int style) {
        super(parent, style);
    }

    /**
     * Create GUI method.
     */
    protected void initView() throws Exception {
        // init layout
        GridLayout layout = new GridLayout();
        layout.numColumns = 2;
        setLayout(layout);
        Label lblReport = new Label(this, SWT.NONE);
        lblReport.setText(App.getGuiProp("analytics.reports"));

        cmbReports = new Combo(this, SWT.READ_ONLY);
        cmbReports.add(App.getGuiProp("report.allperiods.capital"));
        cmbReports.add(App.getGuiProp("report.allperiods.pl"));
        cmbReports.add(App.getGuiProp("report.income"));
        cmbReports.add(App.getGuiProp("report.expenses"));
        cmbReports.add(App.getGuiProp("report.assets"));
        cmbReports.add(App.getGuiProp("report.liability"));
        cmbReports.add(App.getGuiProp("report.profit"));
        cmbReports.select(0);
        createChart();
        // select by default
        cmbReports.addListener(SWT.Selection, new Listener() {
            @Override
            public void handleEvent(Event arg0) {
                createChart();
            }
        });
    }

    /**
     * Create pie chart.
     * 
     * @param dataset
     * @param title
     * @return
     */
    protected JFreeChart createPieChart(final PieDataset dataset, final String title) {
        final JFreeChart chart = ChartFactory.createPieChart(title, dataset, true, true, false);
        final PiePlot plot = (PiePlot) chart.getPlot();
        plot.setNoDataMessage(App.getGuiProp("report.nodata.msg"));
        plot.setCircular(true);
        // plot.set
        plot.setLabelGap(0.02);
        plot.setBackgroundPaint(Color.white);
        chart.removeLegend();
        plot.setBackgroundPaint(Color.white);
        Iterator<?> it = dataset.getKeys().iterator();
        int color = 0;
        while (it.hasNext()) {
            plot.setSectionPaint((String) it.next(), COLORS[color]);
            color++;
            if (COLORS.length == color) {
                color = 0;
            }
        }
        plot.setLabelBackgroundPaint(Color.white);
        StandardPieSectionLabelGenerator slbl = new StandardPieSectionLabelGenerator("{0} {2} ({1})",
                new DecimalFormat("#,##0"), new DecimalFormat("0%"));
        plot.setLabelGenerator(slbl);
        plot.setLabelFont(new Font("Helvetica", Font.PLAIN, 14));
        plot.setLabelOutlinePaint(Color.white);
        plot.setLabelShadowPaint(Color.white);
        plot.setShadowPaint(Color.white);
        plot.setIgnoreZeroValues(true);
        return chart;
    }

    /**
     * Create pie chart.
     * 
     * @param dataset
     * @param title
     * @return
     */
    protected JFreeChart createLineChart(final CategoryDataset dataset, final String title) {
        final JFreeChart chart = ChartFactory.createLineChart("", // chart title
                App.getGuiProp("report.period.lbl"), // domain axis label
                App.getGuiProp("report.value.lbl"), // range axis label
                dataset, // data
                PlotOrientation.VERTICAL, // orientation
                true, // include legend
                true, // tooltips
                false // urls
        );
        final CategoryPlot plot = (CategoryPlot) chart.getPlot();
        plot.setNoDataMessage(App.getGuiProp("report.nodata.msg"));
        plot.setBackgroundPaint(Color.white);
        plot.setBackgroundPaint(Color.white);
        ((NumberAxis) plot.getRangeAxis()).setAutoRangeIncludesZero(false);
        ((CategoryAxis) plot.getDomainAxis()).setMaximumCategoryLabelLines(10);
        ((CategoryAxis) plot.getDomainAxis()).setCategoryLabelPositions(CategoryLabelPositions.DOWN_90);
        plot.setDomainGridlinesVisible(true);
        plot.setDomainGridlinePaint(Color.gray);
        plot.setRangeGridlinePaint(Color.gray);
        plot.setRangeGridlinesVisible(true);
        plot.setRangeZeroBaselinePaint(Color.black);
        plot.setRangeZeroBaselineVisible(true);
        int color = 0;
        CategoryItemRenderer renderer = plot.getRenderer();
        for (int ser = 0; ser < dataset.getColumnCount(); ser++) {
            renderer.setSeriesPaint(ser, COLORS[color]);
            renderer.setSeriesStroke(ser, new BasicStroke(4));
            StandardCategoryItemLabelGenerator gen = new StandardCategoryItemLabelGenerator("{2}",
                    NumberFormat.getInstance(Locale.GERMAN)) {
                private static final long serialVersionUID = 1L;

                public String generateLabel(CategoryDataset dataset, int series, int item) {
                    if (item % 3 == 0) {
                        return super.generateLabelString(dataset, series, item);
                    } else {
                        return null;
                    }
                }
            };

            renderer.setSeriesItemLabelGenerator(ser, gen);
            renderer.setSeriesItemLabelsVisible(ser, true);

            color++;
            if (COLORS.length == color) {
                color = 0;
            }
        }
        return chart;
    }

    /**
     * Create pie chart.
     * 
     * @param dataset
     * @param title
     * @return
     */
    protected JFreeChart createBarChart(final CategoryDataset dataset, final String title) {
        final JFreeChart chart = ChartFactory.createBarChart("", // chart title
                App.getGuiProp("report.period.lbl"), // domain axis label
                App.getGuiProp("report.value.lbl"), // range axis label
                dataset, // data
                PlotOrientation.VERTICAL, // orientation
                false, // include legend
                true, // tooltips
                false // urls
        );

        final CategoryPlot plot = (CategoryPlot) chart.getPlot();
        plot.setNoDataMessage(App.getGuiProp("report.nodata.msg"));
        plot.setBackgroundPaint(Color.white);
        ((NumberAxis) plot.getRangeAxis()).setAutoRangeIncludesZero(false);
        ((CategoryAxis) plot.getDomainAxis()).setMaximumCategoryLabelLines(10);
        ((CategoryAxis) plot.getDomainAxis()).setCategoryLabelPositions(CategoryLabelPositions.DOWN_90);
        plot.setDomainGridlinesVisible(true);
        plot.setDomainGridlinePaint(Color.gray);
        plot.setRangeGridlinePaint(Color.gray);
        plot.setRangeGridlinesVisible(true);
        plot.setRangeZeroBaselinePaint(Color.black);
        plot.setRangeZeroBaselineVisible(true);
        int color = 0;
        ((BarRenderer) plot.getRenderer()).setBarPainter(new StandardBarPainter());
        CategoryItemRenderer renderer = plot.getRenderer();
        for (int ser = 0; ser < dataset.getColumnCount(); ser++) {
            renderer.setSeriesPaint(ser, COLORS[color]);
            renderer.setSeriesItemLabelGenerator(ser,
                    new StandardCategoryItemLabelGenerator("{2}", NumberFormat.getInstance(Locale.GERMAN)));
            renderer.setSeriesItemLabelsVisible(ser, true);
            color++;
            if (COLORS.length == color) {
                color = 0;
            }
        }
        return chart;
    }

    /**
     * Create chart.
     */
    protected void createChart() {
        if (chartComp != null) {
            chartComp.dispose();
        }
        JFreeChart chart = null;
        String reportSel = cmbReports.getItem(cmbReports.getSelectionIndex());
        Book book = App.getApp().getBookFile().getBook();
        Period period = book.getCurrPeriod();
        if (reportSel.equals(App.getGuiProp("report.expenses"))) {
            chart = createPieChart(createDatasetSnapshot(Account.TYPE_EXPENSE),
                    // cmbReports.getItem(cmbReports.getSelectionIndex())
                    Formatter.formatValue(book.getExpenseList().getValue(period.getId()).getCredit()));
        } else if (reportSel.equals(App.getGuiProp("report.income"))) {
            chart = createPieChart(createDatasetSnapshot(Account.TYPE_INCOME),
                    // cmbReports.getItem(cmbReports.getSelectionIndex())
                    Formatter.formatValue(book.getIncomeList().getValue(period.getId()).getDebit()));
        } else if (reportSel.equals(App.getGuiProp("report.assets"))) {
            chart = createPieChart(createDatasetSnapshot(Account.TYPE_ASSET),
                    // cmbReports.getItem(cmbReports.getSelectionIndex())
                    Formatter.formatValue(book.getAssetList().getValue(period.getId()).getEndOfPeriod()));
        } else if (reportSel.equals(App.getGuiProp("report.liability"))) {
            chart = createPieChart(createDatasetSnapshot(Account.TYPE_LIABILITY),
                    // cmbReports.getItem(cmbReports.getSelectionIndex())
                    Formatter.formatValue(book.getLiabilityList().getValue(period.getId()).getEndOfPeriod()));
        } else if (reportSel.equals(App.getGuiProp("report.allperiods.capital"))) {
            chart = createLineChart(createDatasetPeriod(0), cmbReports.getItem(cmbReports.getSelectionIndex()));
        } else if (reportSel.equals(App.getGuiProp("report.allperiods.pl"))) {
            chart = createLineChart(createDatasetPeriod(1), cmbReports.getItem(cmbReports.getSelectionIndex()));
        } else if (reportSel.equals(App.getGuiProp("report.profit"))) {
            chart = createBarChart(createDatasetPeriod(2), cmbReports.getItem(cmbReports.getSelectionIndex()));
        }
        if (chart == null) {
            return;
        }
        GridData chartGd = new GridData(GridData.FILL_BOTH);
        chartGd.horizontalSpan = 2;
        chartComp = new ChartComposite(this, SWT.NONE, chart, false);
        chartComp.setLayoutData(chartGd);
        // layout of parent works
        getParent().layout(true, true);
        // marks the composite's screen are as invalidates, which will force a
        getParent().redraw();
        // tells the application to do all outstanding paint requests
        // immediately
        getParent().update();
    }

    /**
     * Load book from model.
     * 
     * @param book
     */
    protected void loadGuiFromModel() {
        createChart();
    }

    /**
     * Dataset expenses.
     * 
     * @return
     */
    protected PieDataset createDatasetSnapshot(int type) {
        Book book = App.getApp().getBookFile().getBook();
        Period period = book.getCurrPeriod();
        Folder folder = book.getExpenseList();
        if (type == Account.TYPE_ASSET) {
            folder = book.getAssetList();
        } else if (type == Account.TYPE_EXPENSE) {
            folder = book.getExpenseList();
        } else if (type == Account.TYPE_LIABILITY) {
            folder = book.getLiabilityList();
        } else if (type == Account.TYPE_INCOME) {
            folder = book.getIncomeList();
        }
        final DefaultPieDataset result = new DefaultPieDataset();
        for (Account acnt : folder.getList()) {
            double value = 0.0;
            if (type == Account.TYPE_ASSET || type == Account.TYPE_LIABILITY) {
                value = acnt.getValue(period.getId()).getEndOfPeriod();
            } else if (type == Account.TYPE_EXPENSE) {
                value = acnt.getValue(period.getId()).getCredit();

            } else if (type == Account.TYPE_INCOME) {
                value = acnt.getValue(period.getId()).getDebit();
            }
            result.setValue(acnt.getName(), value);
        }
        return result;
    }

    /**
     * Dataset expenses.
     * 
     * @return
     */
    protected CategoryDataset createDatasetPeriod(int type) {
        Book book = App.getApp().getBookFile().getBook();
        final DefaultCategoryDataset dataset = new DefaultCategoryDataset();
        List<Period> periodList = book.getPeriodList();
        List<Double> movingAverage = new ArrayList<Double>();
        double MA_SIZE = 12.0;
        for (Period period : periodList) {
            Folder assets = book.getAssetList();
            double assetValue = assets.getValue(period.getId()).getEndOfPeriod();
            Folder liability = book.getLiabilityList();
            double liabilityValue = liability.getValue(period.getId()).getEndOfPeriod();
            Folder income = book.getIncomeList();
            double incomeValue = income.getValue(period.getId()).getDebit()
                    - income.getValue(period.getId()).getCredit();
            Folder expense = book.getExpenseList();
            double expenseValue = expense.getValue(period.getId()).getCredit()
                    - expense.getValue(period.getId()).getDebit();
            if (type == 0) {// capital
                if (assetValue != 0.0 || liabilityValue != 0.0) {
                    dataset.addValue(assetValue - liabilityValue, assets.getName() + " - " + liability.getName(),
                            Formatter.formatDateShort(period.getDate()));
                }
            } else if (type == 1) {// income and expenses
                if (incomeValue != 0.0 || expenseValue != 0.0) {
                    dataset.addValue(incomeValue, income.getName(), Formatter.formatDateShort(period.getDate()));
                    dataset.addValue(expenseValue, expense.getName(), Formatter.formatDateShort(period.getDate()));
                    movingAverage.add(incomeValue - expenseValue);
                    double sum = 0.0;
                    for (double val : movingAverage) {
                        sum += val;
                    }
                    dataset.addValue(sum / MA_SIZE, (int) MA_SIZE + "m-Moving Average",
                            Formatter.formatDateShort(period.getDate()));
                    if (movingAverage.size() == MA_SIZE) {
                        movingAverage.remove(0);
                    }
                }
            } else if (type == 2) {// profit and loss
                if (incomeValue != 0.0 || expenseValue != 0.0) {
                    dataset.addValue(incomeValue - expenseValue, income.getName(),
                            Formatter.formatDateShort(period.getDate()));
                }
            }
        }
        return dataset;
    }

}