Java tutorial
/** * This file is part of Pau's Asset Manager Project. * * Pau's Asset Manager Project 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. * * Pau's Asset Manager Project 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 Pau's Asset Manager Project. If not, see <http://www.gnu.org/licenses/>. */ package org.pau.assetmanager.viewmodel; import java.awt.Color; import java.text.NumberFormat; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; import java.util.Map; import java.util.Set; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.labels.StandardPieSectionLabelGenerator; import org.jfree.chart.plot.PiePlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.data.category.CategoryDataset; import org.jfree.data.general.PieDataset; import org.pau.assetmanager.business.AnnotationsBusiness; import org.pau.assetmanager.business.BooksBusiness; import org.pau.assetmanager.entities.Annotation; import org.pau.assetmanager.entities.Annotation.AnnotationType; import org.pau.assetmanager.entities.Book; import org.pau.assetmanager.entities.Client; import org.pau.assetmanager.entities.GeneralBook; import org.pau.assetmanager.entities.PropertyBook; import org.pau.assetmanager.entities.StockExpensesAnnotation; import org.pau.assetmanager.entities.StockIncomeAnnotation; import org.pau.assetmanager.entities.StocksBook; import org.pau.assetmanager.viewmodel.chart.ChartDataModel; import org.pau.assetmanager.viewmodel.chart.PrepareChart; import org.pau.assetmanager.viewmodel.chart.ResourceImageGenerator; import org.pau.assetmanager.viewmodel.comparator.AnnotationDateMontlyComparator; import org.pau.assetmanager.viewmodel.grouping.AnnotationDateMonthlyGroupingModel; import org.pau.assetmanager.viewmodel.stocks.StockConceptPerformance; import org.pau.assetmanager.viewmodel.stocks.StocksUtils; import org.pau.assetmanager.viewmodel.type.BookSelectionType; import org.pau.assetmanager.viewmodel.type.ClientDomainType; import org.pau.assetmanager.viewmodel.utils.AnnotationsFilter; import org.pau.assetmanager.viewmodel.utils.BookSelection; import org.pau.assetmanager.viewmodel.utils.NumberFomatter; import org.pau.assetmanager.viewmodel.utils.SortingCriteria; import org.zkoss.bind.annotation.BindingParam; import org.zkoss.bind.annotation.Command; import org.zkoss.bind.annotation.ContextParam; import org.zkoss.bind.annotation.ContextType; import org.zkoss.bind.annotation.DependsOn; import org.zkoss.bind.annotation.GlobalCommand; import org.zkoss.bind.annotation.Init; import org.zkoss.bind.annotation.NotifyChange; import org.zkoss.zk.ui.event.CheckEvent; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.util.Clients; import org.zkoss.zul.Checkbox; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import com.google.common.collect.Sets; /** * This class is the ViewModel of the report that displays the performance * information given a book selection and a year in a *monthly basis*. * * The ZUL related to this ViewModel is 'grouping_model_view_month.zul' * * @author Pau Carr Cardona * */ public class MonthlyReportViewModel { // book selection private BookSelection bookSelection = null; // year selected private Integer monthlyReportYear; // client domain type: the client of the book selection or all the clients private ClientDomainType clientType; @Init public void init() { Calendar calendar = GregorianCalendar.getInstance(); calendar.setTime(new Date()); monthlyReportYear = calendar.get(Calendar.YEAR); clientType = ClientDomainType.CURRENT_CLIENT; } public Collection<ClientDomainType> getClientDomainTypes() { return Arrays.asList(ClientDomainType.values()); } @DependsOn("bookSelection") public Boolean getIsAllBooks() { return bookSelection.getBookSelectionType().equals(BookSelectionType.ALL_BOOKS); } public ClientDomainType getClientType() { return clientType; } public void setClientType(ClientDomainType clientType) { this.clientType = clientType; } public BookSelectionType getBookSelectionType() { return bookSelection.getBookSelectionType(); } public Client getSelectedClient() { return bookSelection.getSelectedClient(); } /** * @param annotations * @return the multimap 'Year of Annotaion' --> 'Annotations Involved' */ private static Multimap<Integer, Annotation> getYearToAnnotationMultimapFromAnnotations( Collection<Annotation> annotations) { ImmutableMap<Annotation, Integer> annotationToYear = Maps.toMap(annotations, new Function<Annotation, Integer>() { @Override public Integer apply(Annotation input) { Calendar calendar = GregorianCalendar.getInstance(); calendar.setTime(input.getDate()); return calendar.get(Calendar.YEAR); } }); ListMultimap<Integer, Annotation> yearToAnnotationMultimap = Multimaps .invertFrom(Multimaps.forMap(annotationToYear), ArrayListMultimap.<Integer, Annotation>create()); return yearToAnnotationMultimap; } /** * This method generates an image of a chart of the evolution of the total * amount of money a book selection for a year in a monthly bases * * @return the relative random URL generated */ @DependsOn({ "selectedBook", "clientType", "monthlyReportYear" }) public String getBalanceChartURL() { if (bookSelection.getSelectedBook() == null) { return ""; } List<Annotation> anotations = getAnnotationsInAscendingDateOrder(Optional.<Integer>absent()); Multimap<Integer, Annotation> yearToAnnotationMultimap = getYearToAnnotationMultimapFromAnnotations( anotations); CategoryDataset categoryModel = ChartDataModel.getBalance(monthlyReportYear, yearToAnnotationMultimap, bookSelection, true, Optional.<String>absent()); JFreeChart jfchart = ChartFactory.createLineChart("Saldo Total", "Fecha", "Euros", categoryModel, PlotOrientation.VERTICAL, true, true, false); PrepareChart.prepareBalanceChart(jfchart); return ResourceImageGenerator.getFunction().apply(jfchart); } @DependsOn({ "selectedBook", "clientType", "monthlyReportYear" }) public String getBalanceChartURLForStock(final String stockLabel) { if (bookSelection.getSelectedBook() == null) { return ""; } List<Annotation> anotations = getAnnotationsInAscendingDateOrder(Optional.<Integer>absent()); List<Annotation> anotationsForStockLabel = Lists .newLinkedList(Collections2.filter(anotations, new Predicate<Annotation>() { @Override public boolean apply(Annotation annotation) { if (annotation instanceof StockIncomeAnnotation || annotation instanceof StockExpensesAnnotation) { return annotation.getConcept().equals(stockLabel); } else { return false; } } })); Multimap<Integer, Annotation> yearToAnnotationMultimap = getYearToAnnotationMultimapFromAnnotations( anotationsForStockLabel); CategoryDataset categoryModel = ChartDataModel.getBalance(monthlyReportYear, yearToAnnotationMultimap, bookSelection, true, Optional.<String>of(stockLabel)); JFreeChart jfchart = ChartFactory.createLineChart("Saldo Total para el valor " + stockLabel, "Fecha", "Euros", categoryModel, PlotOrientation.VERTICAL, true, true, false); PrepareChart.prepareBalanceChart(jfchart); return ResourceImageGenerator.getFunction().apply(jfchart); } @DependsOn({ "selectedBook", "clientType", "monthlyReportYear" }) public List<String> getStocks() { List<Annotation> allStocksAnnotationsFromDatabase = AnnotationsBusiness.getAllStocksAnnotationsFromDatabase( bookSelection, Optional.<Integer>absent(), clientType, SortingCriteria.DESCENDING); Set<String> allStocksConcepts = Sets.newHashSet( Collections2.transform(allStocksAnnotationsFromDatabase, new Function<Annotation, String>() { @Override public String apply(Annotation input) { return input.getConcept(); } })); List<String> allStocksConceptsList = Lists.newLinkedList(allStocksConcepts); Collections.sort(allStocksConceptsList); return allStocksConceptsList; } /** * This method generates an image of a chart of the evolution of the total * amount of money a properties book selection for a year in a monthly bases * * @return the relative random URL generated */ @DependsOn({ "selectedBook", "clientType", "monthlyReportYear" }) public String getBalancePropertiesChartURL() { if (bookSelection.getSelectedBook() == null || !getIsAllBooks()) { return ""; } List<Annotation> anotations = getAnnotationsInAscendingDateOrder(Optional.<Integer>absent()); Collection<Annotation> annotationsFromPropertiesBooks = Collections2.filter(anotations, new Predicate<Annotation>() { @Override public boolean apply(Annotation annotation) { return annotation.getBook() instanceof PropertyBook; } }); Multimap<Integer, Annotation> yearToBookAnnotationsMultimap = getYearToAnnotationMultimapFromAnnotations( annotationsFromPropertiesBooks); CategoryDataset categoryModel = ChartDataModel.getBalance(monthlyReportYear, yearToBookAnnotationsMultimap, bookSelection, false, Optional.<String>absent()); JFreeChart jfchart = ChartFactory.createLineChart("Saldo de propiedades (viviendas y locales)", "Fecha", "Euros", categoryModel, PlotOrientation.VERTICAL, true, true, false); PrepareChart.prepareBalanceChart(jfchart); return ResourceImageGenerator.getFunction().apply(jfchart); } /** * This method generates an image of a chart of the evolution of the total * amount of money for the stocks book selection for a year in a monthly basis * * @return the relative random URL generated */ @DependsOn({ "selectedBook", "clientType", "monthlyReportYear" }) public String getBalanceStocksChartURL() { if (bookSelection.getSelectedBook() == null) { return ""; } if (!getIsAllBooks()) { return ""; } List<Annotation> anotations = getAnnotationsInAscendingDateOrder(Optional.<Integer>absent()); Collection<Annotation> annotationsFromStocksBooks = Collections2.filter(anotations, new Predicate<Annotation>() { @Override public boolean apply(Annotation annotation) { return annotation.getBook() instanceof StocksBook; } }); Multimap<Integer, Annotation> yearToStocksBooksMultimap = getYearToAnnotationMultimapFromAnnotations( annotationsFromStocksBooks); CategoryDataset categoryModel = ChartDataModel.getBalance(monthlyReportYear, yearToStocksBooksMultimap, bookSelection, false, Optional.<String>absent()); JFreeChart jfchart = ChartFactory.createLineChart("Saldo de inversin en bolsa", "Fecha", "Euros", categoryModel, PlotOrientation.VERTICAL, true, true, false); PrepareChart.prepareBalanceChart(jfchart); return ResourceImageGenerator.getFunction().apply(jfchart); } /** * This method generates an image of a chart of the evolution of the total * amount of money a general book selection for a year in a monthly bases * * @return the relative random URL generated */ @DependsOn({ "selectedBook", "clientType", "monthlyReportYear" }) public String getBalanceOwnChartURL() { if (bookSelection.getSelectedBook() == null) { return ""; } if (!getIsAllBooks()) { return ""; } List<Annotation> anotations = getAnnotationsInAscendingDateOrder(Optional.<Integer>absent()); Collection<Annotation> annotationsFromGeneralBooks = Collections2.filter(anotations, new Predicate<Annotation>() { @Override public boolean apply(Annotation annotation) { return annotation.getBook() instanceof GeneralBook; } }); Multimap<Integer, Annotation> yearToGeneralBooksMultimap = getYearToAnnotationMultimapFromAnnotations( annotationsFromGeneralBooks); CategoryDataset categoryModel = ChartDataModel.getBalance(monthlyReportYear, yearToGeneralBooksMultimap, bookSelection, false, Optional.<String>absent()); JFreeChart jfchart = ChartFactory.createLineChart("Saldo de gastos e ingresos generales", "Fecha", "Euros", categoryModel, PlotOrientation.VERTICAL, true, true, false); PrepareChart.prepareBalanceChart(jfchart); return ResourceImageGenerator.getFunction().apply(jfchart); } /** * Generates the image of a pie chart that displays the percentage of * expenses of the different books which for a given year are loosing money * (not profitable) * * @return the relative random URL generated */ @DependsOn({ "selectedBook", "clientType", "monthlyReportYear" }) public String getDeficitPieChartURL() { if (bookSelection.getSelectedBook() == null) { return ""; } List<Annotation> anotations = getAnnotationsInAscendingDateOrder(Optional.of(monthlyReportYear)); PieDataset categoryModel = ChartDataModel.deficitsPieChartDataset(anotations); JFreeChart jfchart = ChartFactory.createPieChart( "Dficit ( ingresos menos gastos, siendo los gastos mayores a los ingresos )", categoryModel, true, true, false); PiePlot piePlot = ((PiePlot) jfchart.getPlot()); piePlot.setLabelGenerator(new StandardPieSectionLabelGenerator("{0} (-{1} , {2} )", NumberFomatter.PERCENTAGE_FORMATTER, NumberFormat.getPercentInstance())); piePlot.setBackgroundPaint(Color.white); return ResourceImageGenerator.getFunction().apply(jfchart); } /** * Generates the image of a pie chart that displays the percentage of income * of the different books which for a given year are profitable * * @return the relative random URL generated */ @DependsOn({ "selectedBook", "clientType", "monthlyReportYear" }) public String getIncomePieChartURL() { if (bookSelection.getSelectedBook() == null) { return ""; } List<Annotation> anotations = getAnnotationsInAscendingDateOrder(Optional.of(monthlyReportYear)); PieDataset categoryModel = ChartDataModel.incomesPieChartDataset(anotations); JFreeChart jfchart = ChartFactory.createPieChart( "Beneficios ( ingresos menos gastos, siendo los ingresos mayores a los gastos )", categoryModel, true, true, false); PiePlot piePlot = ((PiePlot) jfchart.getPlot()); piePlot.setLabelGenerator(new StandardPieSectionLabelGenerator("{0} ({1} , {2} )", NumberFomatter.PERCENTAGE_FORMATTER, NumberFormat.getPercentInstance())); piePlot.setBackgroundPaint(Color.white); return ResourceImageGenerator.getFunction().apply(jfchart); } /** * Generates the image of a pie chart that displays the percentage of * expenses of the different concept which for a given year are loosing * money (not profitable) * * NOTE: usually, a given concept is only related to either income or * expenses but this *not* always the case (for example, in stocks the * concept is the company involved and there can be incomes and expenses) * * @return the relative random URL generated */ @DependsOn({ "selectedBook", "clientType", "monthlyReportYear" }) public String getExpensesPieChartURL() { if (bookSelection.getSelectedBook() == null) { return ""; } Map<String, Double> expensesDataForBook = AnnotationsBusiness.getExpensesDataForBook(bookSelection, monthlyReportYear, clientType); PieDataset categoryModel = ChartDataModel.expensesPieChartDataset(expensesDataForBook); JFreeChart jfchart = ChartFactory.createPieChart("Gastos (nicamente gastos)", categoryModel, true, true, false); PiePlot piePlot = ((PiePlot) jfchart.getPlot()); piePlot.setLabelGenerator(new StandardPieSectionLabelGenerator("{0} ({1} , {2} )", NumberFomatter.PERCENTAGE_FORMATTER, NumberFormat.getPercentInstance())); piePlot.setBackgroundPaint(Color.white); return ResourceImageGenerator.getFunction().apply(jfchart); } /** * Generates the image of a time series in which there are four series -- * Income: the sum of amount of income annotations for a given month in a * year -- Expenses: the sum of amount of expenses annotations for a given * month in a year -- Monthly Total: the *difference* between Income and * Expenses (Monthly Total := Income - Expenses) -- Cumulative: the * cumulative sum over time of 'Monthly Total' * * @return the relative random URL generated */ @DependsOn({ "selectedBook", "clientType", "monthlyReportYear" }) public String getMonthlyReportSimpleChartURL() { if (bookSelection.getSelectedBook() == null) { return ""; } List<Annotation> anotations = getAnnotationsInAscendingDateOrder(Optional.of(monthlyReportYear)); CategoryDataset categoryModel = ChartDataModel.getIncomeExpensesSimpleOverTime(anotations, monthlyReportYear, bookSelection.getSelectedBook()); JFreeChart jfchart = ChartFactory.createLineChart("Ingresos vs. Gastos", "Fecha", "Euros", categoryModel, PlotOrientation.VERTICAL, true, true, false); PrepareChart.prepareSimpleJFreeChart(jfchart); return ResourceImageGenerator.getFunction().apply(jfchart); } /** * Generates the image of a bar chart with the performance of the sold * stocks. It computes the amount of money earned or lost given the price * per stock of the sold stocks. * * Note: The chart has into account that the stocks could have been bought * at different points in time and that the sold stocks can span different * different 'stock purchases' with different price per stock * * @return the relative random URL generated */ @DependsOn({ "selectedBook", "clientType", "monthlyReportYear" }) public String getStocksPerformanceURL() { if (bookSelection.getSelectedBook() == null) { return ""; } List<Annotation> annotations = AnnotationsBusiness.getAllStocksAnnotationsFromDatabase(bookSelection, Optional.<Integer>of(monthlyReportYear), clientType, SortingCriteria.ASCENDING); Map<String, StockConceptPerformance> stocksConceptToProfitLimitedToNumberOfSoldSlocks = StocksUtils .getStocksConceptToProfitLimitedToNumberOfSoldSlocks(annotations); CategoryDataset categoryModel = ChartDataModel .getStockConceptsPerformance(stocksConceptToProfitLimitedToNumberOfSoldSlocks); JFreeChart jfchart = ChartFactory.createBarChart( "Rendimiento de valores vendidos en Bolsa (slo valores vendidos)", "Valor", "Euros", categoryModel, PlotOrientation.VERTICAL, true, true, false); PrepareChart.prepareJFreeBarChartForStocks(jfchart, categoryModel); return ResourceImageGenerator.getFunction().apply(jfchart); } /** * Generates the image of a grouping bar for each month along a year. Each * monthly group contains two bars (Income and Expenses) for each of the * three types of books type (General, Stocks and Properties) * * @return the relative random URL generated */ @DependsOn({ "selectedBook", "clientType", "monthlyReportYear" }) public String getPropertiesVsOwnAllPropertiesURL() { if (bookSelection.getSelectedBook() == null) { return ""; } List<Annotation> anotations = getAnnotationsInAscendingDateOrder(Optional.of(monthlyReportYear)); CategoryDataset categoryModel = ChartDataModel.getIncomeExpensesOverTime(anotations, monthlyReportYear, bookSelection.getSelectedBook(), bookSelection.getBookSelectionType()); JFreeChart jfchart = ChartFactory.createBarChart("Propiedades vs. Generales vs. Bolsa", "Fecha", "Euros", categoryModel, PlotOrientation.VERTICAL, true, true, false); PrepareChart.preparePropertiesVsOwnAllPropertiesJFreeChart(jfchart, categoryModel); return ResourceImageGenerator.getFunction().apply(jfchart); } /** * Generates the image of a grouping bar for each month along a year. Each * monthly group contains a bar that represent the totals for each of the * selected properties (Incomes - Expenses) The selected property will be * the one from the properties book or all the properties for a client in * case the selection is for all the books * * @return the relative random URL generated */ @DependsOn({ "selectedBook", "clientType", "monthlyReportYear" }) public String getMonthlyReportPerBookChartURL() { if (bookSelection.getSelectedBook() == null) { return ""; } List<Annotation> anotations = getAnnotationsInAscendingDateOrder(Optional.of(monthlyReportYear)); List<PropertyBook> listOfPropertties = getPropertyBooksFromClient(); CategoryDataset categoryModel = ChartDataModel.getTotalsOverTimePerBook(anotations, monthlyReportYear, bookSelection.getSelectedBook(), listOfPropertties); JFreeChart jfchart = ChartFactory.createBarChart("Propiedades", "Fecha", "Euros", categoryModel, PlotOrientation.VERTICAL, true, true, false); PrepareChart.prepareJFreeBarChart(jfchart, listOfPropertties, categoryModel); return ResourceImageGenerator.getFunction().apply(jfchart); } /** * The method generated a grouping model to be displayed in a grid which * contains the all the annotations involved in the selected book for a * given year and a grouping for each month * * @return The grouping model for the monthly report */ @DependsOn({ "selectedBook", "clientType", "monthlyReportYear" }) public AnnotationDateMonthlyGroupingModel getAnnotationsDateMonthlyGroupingModel() { List<Annotation> anotations = getAnnotationsInAscendingDateOrder(Optional.of(monthlyReportYear)); return new AnnotationDateMonthlyGroupingModel(anotations, new AnnotationDateMontlyComparator(), true); } private List<PropertyBook> getPropertyBooksFromClient() { return BooksBusiness.getPropertyBooksFromClient(bookSelection.getSelectedBook().getClient()); } public Book getSelectedBook() { return bookSelection.getSelectedBook(); } @DependsOn({ "selectedBook" }) public Boolean getSelectedBookIsStocksOrAllProperties() { return bookSelection.getSelectedBook() != null && (getIsAllBooks() || bookSelection.getSelectedBook() instanceof StocksBook); } @NotifyChange({ "selectedBook", "bookSelectionType", "selectedClient", "clientType" }) @GlobalCommand public void updateBookSelection(@BindingParam("bookSelection") BookSelection bookSelection) { this.bookSelection = bookSelection; if (bookSelection.getBookSelectionType().equals(BookSelectionType.SINGLE_BOOK)) { clientType = ClientDomainType.CURRENT_CLIENT; } } @NotifyChange({ "selectedBook" }) @GlobalCommand public void updateReportAnnotations() { } public Integer getMonthlyReportYear() { return monthlyReportYear; } public void setMonthlyReportYear(Integer monthlyReportYear) { this.monthlyReportYear = monthlyReportYear; } @Command @NotifyChange({ "monthlyReportYear" }) public void refreshMonthlyReportYear(@ContextParam(ContextType.TRIGGER_EVENT) Event event) { String value = (String) event.getData(); this.monthlyReportYear = new Integer(value); } @Command @NotifyChange({ "clientType" }) public void refreshMonthlyReportClientType(@ContextParam(ContextType.TRIGGER_EVENT) Event event) { CheckEvent checkEvent = (CheckEvent) event.getData(); Checkbox checkbox = (Checkbox) checkEvent.getTarget(); clientType = checkbox.getValue(); Clients.clearBusy(); } private List<Annotation> getAnnotationsInAscendingDateOrder(Optional<Integer> optionalYear) { List<Annotation> listOfAnnotations = AnnotationsBusiness.getAnnotationsWithFilter( Optional.<AnnotationType>absent(), bookSelection, optionalYear, clientType, SortingCriteria.ASCENDING, AnnotationsFilter.emptyAnnotationsFilter()); return listOfAnnotations; } }