jgnash.ui.report.compiled.RunningAccountBalanceChart.java Source code

Java tutorial

Introduction

Here is the source code for jgnash.ui.report.compiled.RunningAccountBalanceChart.java

Source

/*
 * jGnash, a personal finance application
 * Copyright (C) 2001-2016 Craig Cavanaugh
 *
 * This program 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.
 *
 *  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package jgnash.ui.report.compiled;

import java.awt.EventQueue;
import java.awt.event.ActionListener;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.Collections;
import java.util.List;
import java.util.ResourceBundle;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JPanel;

import jgnash.engine.Account;
import jgnash.engine.AccountType;
import jgnash.engine.Comparators;
import jgnash.engine.CurrencyNode;
import jgnash.ui.components.DatePanel;
import jgnash.ui.components.FilteredAccountListComboBox;
import jgnash.ui.components.GenericCloseDialog;
import jgnash.ui.register.AccountBalanceDisplayManager;
import jgnash.ui.util.IconUtils;
import jgnash.util.DateUtils;
import jgnash.util.ResourceUtils;

import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.forms.layout.RowSpec;

import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.DateTickMarkPosition;
import org.jfree.chart.axis.DateTickUnit;
import org.jfree.chart.axis.DateTickUnitType;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.data.time.Month;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;

/**
 * This displays a dialog that shows the monthly balance for a selected account
 * 
 * @author Craig Cavanaugh
 * @author Dany Veilleux
 * @author Peter Vida
 */
public class RunningAccountBalanceChart {

    private final ResourceBundle rb = ResourceUtils.getBundle();

    private final DatePanel startDateField = new DatePanel();

    private final DatePanel endDateField = new DatePanel();

    private JCheckBox subAccountCheckBox;

    private JCheckBox hideLockedAccountCheckBox;

    private JCheckBox hidePlaceholderAccountCheckBox;

    private final FilteredAccountListComboBox combo = new FilteredAccountListComboBox(false, false);

    public static void show() {

        EventQueue.invokeLater(() -> {
            RunningAccountBalanceChart chart = new RunningAccountBalanceChart();

            JPanel p = chart.createPanel();
            GenericCloseDialog d = new GenericCloseDialog(p, ResourceUtils.getString("Title.EndMonthBalance"));
            d.pack();
            d.setModal(false);
            d.setVisible(true);
        });
    }

    private JPanel createPanel() {
        LocalDate end = DateUtils.getLastDayOfTheMonth(endDateField.getLocalDate());
        LocalDate start = end.minusYears(1);

        startDateField.setDate(start);

        JButton refreshButton = new JButton(rb.getString("Button.Refresh"));
        refreshButton.setIcon(IconUtils.getIcon("/jgnash/resource/view-refresh.png"));

        subAccountCheckBox = new JCheckBox(rb.getString("Button.IncludeSubAccounts"));
        subAccountCheckBox.setSelected(true);

        hideLockedAccountCheckBox = new JCheckBox(rb.getString("Button.HideLockedAccount"));
        hidePlaceholderAccountCheckBox = new JCheckBox(rb.getString("Button.HidePlaceholderAccount"));

        JFreeChart chart = createVerticalXYBarChart(combo.getSelectedAccount());
        final ChartPanel chartPanel = new ChartPanel(chart);

        FormLayout layout = new FormLayout("p, 4dlu, p:g", "");
        DefaultFormBuilder builder = new DefaultFormBuilder(layout);

        FormLayout dLayout = new FormLayout("p, 4dlu, p, 8dlu, p, 4dlu, p, 8dlu, p", "");
        DefaultFormBuilder dBuilder = new DefaultFormBuilder(dLayout);

        dBuilder.append(rb.getString("Label.StartDate"), startDateField);
        dBuilder.append(rb.getString("Label.EndDate"), endDateField);
        dBuilder.append(refreshButton);

        FormLayout cbLayout = new FormLayout("p, 4dlu, p, 4dlu, p, 4dlu", "");
        DefaultFormBuilder cbBuilder = new DefaultFormBuilder(cbLayout);

        cbBuilder.append(subAccountCheckBox);
        cbBuilder.append(hideLockedAccountCheckBox);
        cbBuilder.append(hidePlaceholderAccountCheckBox);

        builder.append(rb.getString("Label.Account"), combo);
        builder.nextLine();
        builder.append(" ");
        builder.append(cbBuilder.getPanel());
        builder.nextLine();
        builder.appendRelatedComponentsGapRow();
        builder.nextLine();
        builder.append(dBuilder.getPanel(), 3);
        builder.nextLine();
        builder.appendUnrelatedComponentsGapRow();
        builder.nextLine();

        builder.appendRow(RowSpec.decode("fill:p:g"));
        builder.append(chartPanel, 3);

        final JPanel panel = builder.getPanel();

        ActionListener listener = e -> {
            updateSubAccountBox();
            Account a = combo.getSelectedAccount();
            if (a == null) {
                return;
            }
            chartPanel.setChart(createVerticalXYBarChart(a));
            panel.validate();
        };

        hideLockedAccountCheckBox.addActionListener(e -> {
            combo.setHideLocked(hideLockedAccountCheckBox.isSelected());
            updateSubAccountBox();
            Account a = combo.getSelectedAccount();
            if (a == null) {
                return;
            }
            chartPanel.setChart(createVerticalXYBarChart(a));
            panel.validate();
        });

        hidePlaceholderAccountCheckBox.addActionListener(e -> {
            combo.setHidePlaceholder(hidePlaceholderAccountCheckBox.isSelected());
            updateSubAccountBox();
            Account a = combo.getSelectedAccount();
            if (a == null) {
                return;
            }
            chartPanel.setChart(createVerticalXYBarChart(a));
            panel.validate();
        });

        updateSubAccountBox();

        combo.addActionListener(listener);
        refreshButton.addActionListener(listener);

        return panel;
    }

    private void updateSubAccountBox() {
        Account a = combo.getSelectedAccount();
        if (a == null) {
            return;
        }
        if (a.getChildCount() == 0) {
            subAccountCheckBox.setEnabled(false);
        } else {
            subAccountCheckBox.setEnabled(true);
        }
    }

    private JFreeChart createVerticalXYBarChart(Account a) {

        DateFormat df = new SimpleDateFormat("MM/yy");

        TimeSeriesCollection data = createTimeSeriesCollection(a);

        DateAxis dateAxis = new DateAxis(rb.getString("Column.Date"));
        dateAxis.setTickUnit(new DateTickUnit(DateTickUnitType.MONTH, 1, df));
        dateAxis.setTickMarkPosition(DateTickMarkPosition.MIDDLE);

        LocalDate start = DateUtils.getFirstDayOfTheMonth(startDateField.getLocalDate());
        LocalDate end = DateUtils.getLastDayOfTheMonth(endDateField.getLocalDate());

        dateAxis.setRange(DateUtils.asDate(start), DateUtils.asDate(end));

        NumberAxis valueAxis = new NumberAxis(rb.getString("Column.Balance"));
        StandardXYToolTipGenerator tooltipGenerator = new StandardXYToolTipGenerator("{1}, {2}", df,
                NumberFormat.getNumberInstance());

        XYBarRenderer renderer = new XYBarRenderer(0.2);
        renderer.setBaseToolTipGenerator(tooltipGenerator);

        XYPlot plot = new XYPlot(data, dateAxis, valueAxis, renderer);

        String title = rb.getString("Title.EndMonthBalance") + " - " + a.getPathName();

        JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, false);

        chart.setBackgroundPaint(null);

        return chart;
    }

    private TimeSeriesCollection createTimeSeriesCollection(final Account account) {

        if (subAccountCheckBox.isSelected()) {
            LocalDate start = DateUtils.getFirstDayOfTheMonth(startDateField.getLocalDate());
            LocalDate stop = DateUtils.getLastDayOfTheMonth(endDateField.getLocalDate());
            List<LocalDate> list = DateUtils.getLastDayOfTheMonths(start, stop);
            TimeSeries t = new TimeSeries(rb.getString("Column.Month"), rb.getString("Column.Month"),
                    rb.getString("Column.Balance"));

            // For every month, calculate the total amount
            for (LocalDate date : list) {
                LocalDate d = DateUtils.getLastDayOfTheMonth(date);

                // Get the total amount for the account and every sub accounts
                // for the specified date
                BigDecimal bd_TotalAmount = calculateTotal(d, account, account.getCurrencyNode());

                // Include it in the graph
                t.add(new Month(DateUtils.asDate(date)), bd_TotalAmount);
            }
            return new TimeSeriesCollection(t);
        }

        List<LocalDate> list = Collections.emptyList();

        int count = account.getTransactionCount();

        if (count > 0) {
            final LocalDate start = account.getTransactionAt(0).getLocalDate();
            final LocalDate stop = account.getTransactionAt(count - 1).getLocalDate();
            list = DateUtils.getLastDayOfTheMonths(start, stop);
        }

        TimeSeries t = new TimeSeries(rb.getString("Column.Month"), rb.getString("Column.Month"),
                rb.getString("Column.Balance"));

        AccountType type = account.getAccountType();

        for (final LocalDate date : list) {
            // get balance for the whole month
            LocalDate d = DateUtils.getLastDayOfTheMonth(date);

            BigDecimal balance = AccountBalanceDisplayManager.convertToSelectedBalanceMode(type,
                    account.getBalance(d));

            t.add(new Month(DateUtils.asDate(date)), balance);
        }

        return new TimeSeriesCollection(t);
    }

    private BigDecimal calculateTotal(final LocalDate d, final Account account, final CurrencyNode baseCurrency) {
        // get the amount for the account               
        BigDecimal total = AccountBalanceDisplayManager.convertToSelectedBalanceMode(account.getAccountType(),
                account.getBalance(d, baseCurrency));

        // add the amount of every child account
        for (int y = 0; y < account.getChildCount(); y++) {
            total = total.add(
                    calculateTotal(d, account.getChildren(Comparators.getAccountByCode()).get(y), baseCurrency));
        }
        return total;
    }

}