Java tutorial
/* * GraphAccountsBalancesTopComponent.java * * Copyright (C) 2009 Francois Duchemin * * This file is part of GrisbiGraphs. * * GrisbiGraphs 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 2 of the License, or * (at your option) any later version. * * GrisbiGraphs 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 GrisbiGraphs; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package gg.view.accountsbalances; import gg.db.datamodel.SearchCriteria; import gg.db.entities.Account; import gg.db.entities.MoneyContainer; import gg.utilities.Utilities; import java.awt.BorderLayout; import java.awt.Color; import java.math.BigDecimal; import java.util.Collection; import java.util.Map; import java.util.Properties; import java.util.SortedSet; import java.util.TreeSet; import java.util.logging.Logger; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartPanel; 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.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.renderer.category.LineAndShapeRenderer; import org.jfree.data.category.DefaultCategoryDataset; import org.jfree.util.ShapeUtilities; import org.netbeans.api.settings.ConvertAsProperties; import org.openide.util.ImageUtilities; import org.openide.util.Lookup; import org.openide.util.LookupEvent; import org.openide.util.LookupListener; import org.openide.util.NbBundle; import org.openide.windows.TopComponent; import org.openide.windows.WindowManager; /** * Top component which displays a graph showing the accounts' balances evolution over time. */ @ConvertAsProperties(dtd = "-//gg.view.accountsbalances//GraphAccountsBalances//EN", autostore = false) public final class GraphAccountsBalancesTopComponent extends TopComponent implements LookupListener { /** Singleton instance of the topcomponent */ private static GraphAccountsBalancesTopComponent instance; /** Path to the icon used by the component and its open action */ private static final String ICON_PATH = "gg/resources/icons/GraphAccountsBalances.png"; /** ID of the component */ private static final String PREFERRED_ID = "GraphAccountsBalancesTopComponent"; /** Result for the lookup listener */ private Lookup.Result result = null; /** Logger */ private Logger log = Logger.getLogger(this.getClass().getName()); /** Creates a new instance of GraphAccountsBalancesTopComponent */ public GraphAccountsBalancesTopComponent() { initComponents(); setName(NbBundle.getMessage(GraphAccountsBalancesTopComponent.class, "CTL_GraphAccountsBalancesTopComponent")); setToolTipText(NbBundle.getMessage(GraphAccountsBalancesTopComponent.class, "HINT_GraphAccountsBalancesTopComponent")); setIcon(ImageUtilities.loadImage(ICON_PATH, true)); putClientProperty(TopComponent.PROP_CLOSING_DISABLED, Boolean.TRUE); putClientProperty(TopComponent.PROP_DRAGGING_DISABLED, Boolean.TRUE); putClientProperty(TopComponent.PROP_UNDOCKING_DISABLED, Boolean.TRUE); } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { jPanelAccountsBalances = new javax.swing.JPanel(); jPanelAccountsBalances.setLayout(new java.awt.BorderLayout()); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout .createSequentialGroup().addContainerGap().addComponent(jPanelAccountsBalances, javax.swing.GroupLayout.DEFAULT_SIZE, 380, Short.MAX_VALUE) .addContainerGap())); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout .createSequentialGroup().addContainerGap().addComponent(jPanelAccountsBalances, javax.swing.GroupLayout.DEFAULT_SIZE, 278, Short.MAX_VALUE) .addContainerGap())); }// </editor-fold>//GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JPanel jPanelAccountsBalances; // End of variables declaration//GEN-END:variables /** * Gets default instance. Do not use directly: reserved for *.settings files only, * i.e. deserialization routines; otherwise you could get a non-deserialized instance. * To obtain the singleton instance, use {@link #findInstance}. * @return Default instance */ public static synchronized GraphAccountsBalancesTopComponent getDefault() { if (instance == null) { instance = new GraphAccountsBalancesTopComponent(); } return instance; } /** * Obtain the GraphAccountsBalancesTopComponent instance. Never call {@link #getDefault} directly! * @return GraphAccountsBalancesTopComponent instance */ public static synchronized GraphAccountsBalancesTopComponent findInstance() { TopComponent win = WindowManager.getDefault().findTopComponent(PREFERRED_ID); if (win == null) { Logger.getLogger(GraphAccountsBalancesTopComponent.class.getName()).warning("Cannot find " + PREFERRED_ID + " component. It will not be located properly in the window system."); return getDefault(); } if (win instanceof GraphAccountsBalancesTopComponent) { return (GraphAccountsBalancesTopComponent) win; } Logger.getLogger(GraphAccountsBalancesTopComponent.class.getName()) .warning("There seem to be multiple components with the '" + PREFERRED_ID + "' ID. That is a potential source of errors and unexpected behavior."); return getDefault(); } /** * Gets the persistence type * @return Persistence type */ @Override public int getPersistenceType() { return TopComponent.PERSISTENCE_ALWAYS; } /** * Saves properties * @param p Properties to save */ public void writeProperties(Properties p) { p.setProperty("version", "1.0"); } /** * Reads properties * @param p properties to save * @return TopComponent with loaded properties */ public Object readProperties(Properties p) { GraphAccountsBalancesTopComponent singleton = GraphAccountsBalancesTopComponent.getDefault(); singleton.readPropertiesImpl(p); return singleton; } /** * Reads properties * @param p Properties to read */ private void readPropertiesImpl(Properties p) { String version = p.getProperty("version"); } /** * Gets the topcomponent's ID * @return Topcomponent's ID */ @Override protected String preferredID() { return PREFERRED_ID; } /** * Registers a lookup listener on the Accounts' balances table topcomponent * when the topcomponent is activated so the updating the table automatically updates the graph */ @Override public void componentOpened() { if (result == null) { // Register lookup listener on the accounts' balance table top component result = WindowManager.getDefault().findTopComponent("AccountsBalancesTopComponent").getLookup() .lookupResult(Map.class); result.addLookupListener(this); result.allInstances(); // Display the graph resultChanged(null); } } /** Unregisters the lookup listener when the topcomponent is closed */ @Override public void componentClosed() { if (result != null) { result.removeLookupListener(this); result = null; } } /** Called when the lookup content is changed (content of table changed)*/ @Override public void resultChanged(LookupEvent ev) { Collection instances = result.allInstances(); if (!instances.isEmpty()) { // Get the currency/account balances by search criteria @SuppressWarnings("unchecked") Map<MoneyContainer, Map<SearchCriteria, BigDecimal>> balances = (Map<MoneyContainer, Map<SearchCriteria, BigDecimal>>) instances .iterator().next(); // Display the content of the table in the graph displayData(balances); } } /** * Displays the accounts' balances by period * @param balances Accounts' balances */ private void displayData(Map<MoneyContainer, Map<SearchCriteria, BigDecimal>> balances) { log.info("Accounts' balances graph computed and displayed"); // Display hourglass cursor Utilities.changeCursorWaitStatus(true); // Create the dataset (that will contain the accounts' balances) DefaultCategoryDataset dataset = new DefaultCategoryDataset(); // Create an empty chart JFreeChart chart = ChartFactory.createLineChart("", // chart title "", // x axis label NbBundle.getMessage(GraphAccountsBalancesTopComponent.class, "AccountsBalancesTopComponent.Amount"), // y axis label dataset, // data displayed in the chart PlotOrientation.VERTICAL, true, // include legend true, // tooltips false // urls ); // Chart color chart.setBackgroundPaint(jPanelAccountsBalances.getBackground()); CategoryPlot plot = (CategoryPlot) chart.getPlot(); plot.setBackgroundPaint(Color.WHITE); // Grid lines plot.setDomainGridlinesVisible(true); plot.setDomainGridlinePaint(Color.LIGHT_GRAY); plot.setRangeGridlinesVisible(true); plot.setRangeGridlinePaint(Color.LIGHT_GRAY); // Set the orientation of the categories on the domain axis (X axis) CategoryAxis domainAxis = plot.getDomainAxis(); domainAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_45); // Add the series on the chart for each account displayed in the table for (MoneyContainer moneyContainer : balances.keySet()) { if (moneyContainer instanceof Account) { Account account = (Account) moneyContainer; SortedSet<SearchCriteria> sortedSearchCriteria = new TreeSet<SearchCriteria>( balances.get(account).keySet()); for (SearchCriteria searchCriteria : sortedSearchCriteria) { if (!searchCriteria.hasAccountsFilter() || searchCriteria.getAccounts().contains(account)) { dataset.addValue(balances.get(moneyContainer).get(searchCriteria), account.toString(), searchCriteria.getPeriod()); } } } } // Series' shapes LineAndShapeRenderer renderer = (LineAndShapeRenderer) plot.getRenderer(); for (int i = 0; i < dataset.getRowCount(); i++) { renderer.setSeriesShapesVisible(i, true); renderer.setSeriesShape(i, ShapeUtilities.createDiamond(2F)); } // Set the scale of the chart NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis(); rangeAxis.setAutoRange(true); rangeAxis.setAutoRangeIncludesZero(false); rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); // Create the chart panel that contains the chart ChartPanel chartPanel = new ChartPanel(chart); // Display the chart jPanelAccountsBalances.removeAll(); jPanelAccountsBalances.add(chartPanel, BorderLayout.CENTER); jPanelAccountsBalances.updateUI(); // Display normal cursor Utilities.changeCursorWaitStatus(false); } }