Java tutorial
/* * 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(); } }