no.ntnu.mmfplanner.ui.graph.NpvChart.java Source code

Java tutorial

Introduction

Here is the source code for no.ntnu.mmfplanner.ui.graph.NpvChart.java

Source

/*
 * Copyright (C) 2007 Snorre Gylterud, Stein Magnus Jodal, Johannes Knutsen,
 * Erik Bagge Ottesen, Ralf Bjarne Taraldset, and Iterate AS
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.
 */

package no.ntnu.mmfplanner.ui.graph;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Paint;
import java.awt.Stroke;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import no.ntnu.mmfplanner.model.Project;
import no.ntnu.mmfplanner.model.ProjectRoi;

import org.jfree.chart.ChartColor;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.util.ShapeUtilities;

/**
 * Draws the NPV Chart for the development process. Along the X-axis goes the
 * periods (time) and along the Y-axis Discounted Cash. Uses the JFreeChart
 * library.
 */

public class NpvChart extends ChartPanel implements PropertyChangeListener {
    private static final long serialVersionUID = 1L;
    private Project project;
    private boolean waterfall;
    private boolean modelValid;
    private static Paint[] chartColor = ChartColor.createDefaultPaintArray();
    private static Stroke STROKE_LINE = new BasicStroke(2);

    public NpvChart(boolean waterfall) {
        super(null);
        this.waterfall = waterfall;
        createChart();
    }

    /**
     * Creates the main chart, but does not fill inn any data
     */
    private void createChart() {
        JFreeChart chart = ChartFactory.createXYLineChart(null, // chart title
                "Period", // x axis label
                "Discounted Cash", // y axis label
                null, // data
                PlotOrientation.VERTICAL, true, // include legend
                true, // tooltips
                false // urls
        );

        XYPlot plot = (XYPlot) chart.getPlot();
        plot.getDomainAxis().setLowerMargin(0.0);
        plot.getDomainAxis().setUpperMargin(0.0);
        plot.setRangeAxisLocation(1, AxisLocation.BOTTOM_OR_LEFT);

        // change the auto tick unit selection to integer units only...
        NumberAxis rangeAxis = (NumberAxis) plot.getDomainAxis();
        rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());

        plot.getDomainAxis().setTickMarksVisible(false);

        setChart(chart);
        setMouseZoomable(false);
    }

    /**
     * Sets the model used for the project and invalidates the current chart
     */
    public void setModel(Project project) {
        if (null != this.project) {
            this.project.removePropertyChangeListener(this);
        }
        this.project = project;
        if (null != project) {
            project.addPropertyChangeListener(this);
        }
        invalidateModel();
    }

    /**
     * Checks if the model is valid, and if not updates the model.
     */
    @Override
    public void paint(Graphics g) {
        if (!modelValid) {
            updateModel();
        }
        super.paint(g);
    }

    /**
     * Invalidates the model and requests a repaint. Will cause updateModel() to
     * be called on the next paint.
     */
    private void invalidateModel() {
        modelValid = false;
        repaint();
    }

    /**
     * Updates the chart with data from the model.
     */
    private void updateModel() {
        modelValid = true;

        XYSeriesCollection dataset = new XYSeriesCollection();
        XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();

        renderer.setLegendLine(new Rectangle2D.Double(0.0, 0.0, 6.0, 0.0));
        renderer.setUseFillPaint(true);

        // the x=0 line
        addLineX(dataset, renderer);

        // rolling npv line (iterative)
        ProjectRoi projectRoi = ProjectRoi.getRoiTable(project, project.getInterestRate(), false);
        addNpvLine(projectRoi, "NPV Iterative", chartColor[0], dataset, renderer);

        // rolling npv line (waterfall)
        if (waterfall) {
            ProjectRoi projectRoiWaterfall = ProjectRoi.getRoiTable(project, project.getInterestRate(), true);
            addNpvLine(projectRoiWaterfall, "NPV Waterfall", chartColor[1], dataset, renderer);
        }

        // legend (break even and self funding)
        addLegendElements(dataset, renderer);

        XYPlot plot = ((XYPlot) getChart().getPlot());
        plot.setDataset(dataset);
        plot.setRenderer(renderer);

        plot.setDomainGridlinesVisible(false);
    }

    /**
     * Helper method for updateModel(). Adds the legend elements for self
     * funding and break even.
     */
    private void addLegendElements(XYSeriesCollection dataset, XYLineAndShapeRenderer renderer) {
        // self funding legend
        XYSeries selfFundingLegend = new XYSeries("Self Funding");
        int series = dataset.getSeriesCount();
        dataset.addSeries(selfFundingLegend);
        renderer.setSeriesPaint(series, Color.black);
        renderer.setSeriesShape(series, ShapeUtilities.createUpTriangle(3.5f));

        // break even legend
        XYSeries breakEvenLegend = new XYSeries("Break Even");
        series = dataset.getSeriesCount();
        dataset.addSeries(breakEvenLegend);
        renderer.setSeriesPaint(series, Color.black);
        renderer.setSeriesShape(series, ShapeUtilities.createDiamond(3.5f));
    }

    /**
     * Helper method for updateModel(). Adds a rolling npv line with self
     * funding and break even, as well as adding legend elements
     */
    private void addNpvLine(ProjectRoi projectRoi, String caption, Paint paint, XYSeriesCollection dataset,
            XYLineAndShapeRenderer renderer) {
        // adds the rolling npvseries and sets approperiate render properties
        XYSeries rollingNpv = new XYSeries(caption);
        rollingNpv.add(0.5, 0.0);
        for (int i = 0; i < projectRoi.rollingNpv.length; i++) {
            rollingNpv.add(i + 1.5, projectRoi.rollingNpv[i]);
        }

        int series = dataset.getSeriesCount();
        dataset.addSeries(rollingNpv);
        renderer.setSeriesShapesVisible(series, false);
        renderer.setSeriesStroke(series, STROKE_LINE);
        renderer.setSeriesPaint(series, paint);
        renderer.setSeriesVisibleInLegend(series, true);

        // break even
        if (projectRoi.breakevenPeriod > 0) {
            XYSeries breakEven = new XYSeries("Break Even");
            breakEven.add(projectRoi.breakevenRegression - 0.5, 0.0);

            series = dataset.getSeriesCount();
            dataset.addSeries(breakEven);
            renderer.setSeriesLinesVisible(series, false);
            renderer.setSeriesPaint(series, paint);
            renderer.setSeriesVisibleInLegend(series, false);
            renderer.setSeriesShape(series, ShapeUtilities.createDiamond(3.5f));
        }

        // selfFunding
        if (projectRoi.selfFundingPeriod > 1) {
            XYSeries selfFunding = new XYSeries("Self Funding");
            double x = projectRoi.selfFundingPeriod - 0.5;
            double y = projectRoi.rollingNpv[projectRoi.selfFundingPeriod - 2];
            selfFunding.add(x, y);

            series = dataset.getSeriesCount();
            dataset.addSeries(selfFunding);
            renderer.setSeriesLinesVisible(series, false);
            renderer.setSeriesPaint(series, paint);
            renderer.setSeriesVisibleInLegend(series, false);
            renderer.setSeriesShape(series, ShapeUtilities.createUpTriangle(3.5f));
        }

    }

    /**
     * Helper method for updateModel(). Adds the gray line at x=0.
     */
    private void addLineX(XYSeriesCollection dataset, XYLineAndShapeRenderer renderer) {
        XYSeries line = new XYSeries("");
        line.add(0.5, 0.0);
        line.add(project.getPeriods() + 0.5, 0.0);

        int series = dataset.getSeriesCount();
        dataset.addSeries(line);
        renderer.setSeriesPaint(series, Color.GRAY);
        renderer.setSeriesShapesVisible(series, false);
        renderer.setSeriesLinesVisible(series, true);
        renderer.setSeriesVisibleInLegend(series, false);
    }

    /**
     * Invalidates the model when a change has occured
     */
    public void propertyChange(PropertyChangeEvent evt) {
        invalidateModel();
    }
}