Java tutorial
/* * This file is part of the ZoRa project: http://www.photozora.org. * * ZoRa 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. * * ZoRa 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 ZoRa; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * (c) 2017 Berthold Daum */ package com.bdaum.zoom.report.internal.wizards; import java.awt.Color; import java.awt.Font; import java.awt.Paint; import java.awt.Stroke; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TimeZone; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.dialogs.ProgressIndicator; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StackLayout; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.Axis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PiePlot; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.category.CategoryStepRenderer; import org.jfree.chart.swt.ChartComposite; import org.jfree.chart.title.TextTitle; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.DefaultCategoryDataset; import org.jfree.data.general.Dataset; import org.jfree.data.general.DefaultPieDataset; import org.jfree.data.general.PieDataset; import org.jfree.data.time.Day; import org.jfree.data.time.Month; import org.jfree.data.time.Quarter; import org.jfree.data.time.RegularTimePeriod; import org.jfree.data.time.TimeSeries; import org.jfree.data.time.TimeSeriesCollection; import org.jfree.data.time.Week; import org.jfree.data.time.Year; import org.jfree.ui.RectangleInsets; import org.jfree.util.Rotation; import org.jfree.util.TableOrder; import com.bdaum.zoom.cat.model.asset.Asset; import com.bdaum.zoom.cat.model.group.SmartCollectionImpl; import com.bdaum.zoom.cat.model.report.Report; import com.bdaum.zoom.core.Core; import com.bdaum.zoom.core.IVolumeManager; import com.bdaum.zoom.core.QueryField; import com.bdaum.zoom.core.db.IDbManager; import com.bdaum.zoom.report.internal.ReportActivator; import com.bdaum.zoom.report.internal.jfree.custom.CylinderRenderer; import com.bdaum.zoom.report.internal.jfree.custom.SparseCategoryAxis; public class ReportComponent extends Composite { private static final String VALUE = Messages.ReportComponent_value; private static final long ONEDAY = 86400000L; private static final String OTHERS = Messages.ReportComponent_others; private static final String EARNINGS = Messages.ReportComponent_earnings; private static final String SALES = Messages.ReportComponent_sales; private static final String COUNT = Messages.ReportComponent_count; // Propertys keys private static final String TITLE = "title"; //$NON-NLS-1$ private static final String TITLEFONT = "titleFont"; //$NON-NLS-1$ private static final String TITLECOLOR = "titleColor"; //$NON-NLS-1$ private static final String BGCOLOR = "bgColor"; //$NON-NLS-1$ private static final String OUTLINEPAINT = "outlinePaint"; //$NON-NLS-1$ private static final String OUTLINESTROKE = "outlineStroke"; //$NON-NLS-1$ private static final String ORIENTATION = "orientation"; //$NON-NLS-1$ private static final String AXISLABEL = "AxisLabel"; //$NON-NLS-1$ private static final String AXISLABELFONT = "AxisLabelFont"; //$NON-NLS-1$ private static final String AXISLABELPAINT = "AxisLabelPaint"; //$NON-NLS-1$ private static final String TICKMARKSVISIBLE = "TickmarksVisble"; //$NON-NLS-1$ private static final String TICKLABELSVISIBLE = "TicklabelsVisible"; //$NON-NLS-1$ private static final String TICKLABELFONT = "TicklabelFont"; //$NON-NLS-1$ private static final String TICKLABELPAINT = "TicklabelPaint"; //$NON-NLS-1$ private static final String ANTIALIAS = "antialias"; //$NON-NLS-1$ private static final String CANVASPAINT = "canvasPaint"; //$NON-NLS-1$ public class Cumulated { private int count; private int sales; private double earnings; public Cumulated(int count, int sales, double earnings) { this.count = count; this.sales = sales; this.earnings = earnings; } public void count() { ++count; } public void addSales(int sales) { this.sales += sales; } public void addEarnings(double earnings) { this.earnings += earnings; } public int getCount() { return count; } public int getSales() { return sales; } public double getEarnings() { return earnings; } } public class ReportJob extends Job { private Report report; private ChartComposite target; private int preview; private int interval; private long lower; private long upper; private long range; private QueryField[] qfields; private int mode; private boolean doCount; private boolean doSales; private boolean doEarnings; private SimpleDateFormat df; public ReportJob(Report report, ChartComposite target, int preview) { super(Messages.ReportComponent_generator); this.report = report; this.target = target; this.preview = preview; mode = report.getMode(); if ((mode & ReportWizard.DAYTIME) != 0) { lower = 0; upper = 96; interval = report.getDayInterval(); df = new SimpleDateFormat( interval == 96 ? Messages.ReportComponent_Hmm : Messages.ReportComponent_H); df.setTimeZone(TimeZone.getTimeZone("Z")); //$NON-NLS-1$ } else if ((mode & ReportWizard.TIME) != 0) { lower = report.getTimeLower(); upper = report.getTimeUpper(); interval = report.getTimeInterval(); } else { lower = report.getValueLower(); upper = report.getValueUpper(); interval = report.getValueInterval(); } range = upper - lower; qfields = resolveField(report); doCount = (mode & ReportWizard.IMAGECOUNT) != 0; doSales = (mode & ReportWizard.SALES) != 0; doEarnings = (mode & ReportWizard.EARNINGS) != 0; setSystem(true); setPriority(Job.LONG); } @Override public boolean belongsTo(Object family) { return family == ReportComponent.this; } @Override protected IStatus run(IProgressMonitor monitor) { IProgressMonitor monitorWrapper = new IProgressMonitor() { public void worked(int work) { monitor.worked(work); ReportComponent.this.worked(work); } public void subTask(String name) { monitor.subTask(name); } public void setTaskName(String name) { monitor.setTaskName(name); } public void setCanceled(boolean value) { monitor.setCanceled(value); } public boolean isCanceled() { return monitor.isCanceled(); } public void internalWorked(double work) { monitor.internalWorked(work); } public void done() { monitor.done(); ReportComponent.this.endTask(); } public void beginTask(String name, int totalWork) { monitor.beginTask(name, totalWork); ReportComponent.this.beginTask(totalWork); } }; return runJob(monitorWrapper); } private IStatus runJob(IProgressMonitor monitor) { final Dataset dataset = createDataset(monitor); if (monitor.isCanceled()) return Status.CANCEL_STATUS; JFreeChart chart = createChart(dataset); if (!target.isDisposed()) target.getDisplay().asyncExec(new Runnable() { @Override public void run() { target.setChart(chart); target.forceRedraw(); } }); monitor.done(); return Status.OK_STATUS; } private QueryField[] resolveField(Report report) { String field = report.getField(); if (field == null) return null; return QueryField.findQuerySubField(field); } private Dataset createDataset(IProgressMonitor monitor) { Dataset result = null; if ((mode & ReportWizard.PIE) != 0 && !ReportWizard.isMultiple(mode)) { result = new DefaultPieDataset(); collectDiscreteValues((DefaultPieDataset) result, monitor); } else if ((mode & ReportWizard.DISCRETE) != 0) { result = new DefaultCategoryDataset(); collectDiscreteValues((DefaultCategoryDataset) result, monitor); } else if ((mode & ReportWizard.DAYTIME) != 0) { result = new DefaultCategoryDataset(); collectDayValues((DefaultCategoryDataset) result, monitor); } else if ((mode & ReportWizard.TIME) != 0) { result = new TimeSeriesCollection(); collectTimeValues((TimeSeriesCollection) result, monitor); } else { result = new DefaultCategoryDataset(); collectNumericValues((DefaultCategoryDataset) result, monitor); } return result; } private void collectDayValues(DefaultCategoryDataset result, IProgressMonitor monitor) { List<Asset> set = fetchAssets(report, preview); if (set != null) { int size = set.size(); int incr = Math.max(1, size / 100); monitor.beginTask(Messages.ReportComponent_collecting, size); int[] counts = new int[interval]; int[] sales = new int[interval]; double[] earnings = new double[interval]; int div = 1440 / interval; int cnt = 0; for (Asset asset : set) { Integer minutes = (Integer) QueryField.TIMEOFDAY.obtainFieldValue(asset); if (minutes != null) { int i = minutes / div; if (doCount) ++counts[i]; if (doSales) sales[i] += asset.getSales(); if (doEarnings) earnings[i] += asset.getEarnings(); if (++cnt % incr == 0) monitor.worked(incr); if (cnt > preview) break; } } cumulate(counts); cumulate(sales); cumulate(earnings); for (int i = 0; i < earnings.length; i++) { String label; if (interval == 24) label = String.valueOf(i); else if (i % 4 == 0) label = String.valueOf(i / 4); else label = "_" + i; //$NON-NLS-1$ if (doCount) result.addValue((Integer) counts[i], COUNT, label); if (doSales) result.addValue((Integer) sales[i], SALES, label); if (doEarnings) result.addValue(earnings[i], EARNINGS, label); } } } private void collectTimeValues(TimeSeriesCollection result, IProgressMonitor monitor) { List<Asset> set = fetchAssets(report, preview); if (set != null) { int size = set.size(); int incr = Math.max(1, size / 100); monitor.beginTask(Messages.ReportComponent_collecting, size); GregorianCalendar lowerCal = new GregorianCalendar(); lowerCal.setTimeInMillis(lower); lowerCal.set(GregorianCalendar.HOUR, 0); lowerCal.set(GregorianCalendar.MINUTE, 0); lowerCal.set(GregorianCalendar.SECOND, 0); lowerCal.set(GregorianCalendar.MILLISECOND, 0); GregorianCalendar upperCal = new GregorianCalendar(); upperCal.setTimeInMillis(upper); upperCal.set(GregorianCalendar.HOUR, 0); upperCal.set(GregorianCalendar.MINUTE, 0); upperCal.set(GregorianCalendar.SECOND, 0); upperCal.set(GregorianCalendar.MILLISECOND, 0); int startYear = 0; int startQuarter = 0; int startMonth = 0; long lowerday = 0; long lowerWeek = 0; int dim; switch (interval) { case ReportWizard.T_YEAR: startYear = lowerCal.get(GregorianCalendar.YEAR); dim = upperCal.get(GregorianCalendar.YEAR) - startYear + 1; lowerCal.set(GregorianCalendar.DAY_OF_YEAR, 1); break; case ReportWizard.T_QUARTER: startQuarter = lowerCal.get(GregorianCalendar.MONTH) / 3 + lowerCal.get(GregorianCalendar.YEAR) * 4; int uq = upperCal.get(GregorianCalendar.MONTH) / 3 + upperCal.get(GregorianCalendar.YEAR) * 4; dim = uq - startQuarter + 1; lowerCal.set(GregorianCalendar.DAY_OF_MONTH, 1); int m = lowerCal.get(GregorianCalendar.MONTH); lowerCal.add(GregorianCalendar.MONTH, -(m % 3)); break; case ReportWizard.T_MONTH: startMonth = lowerCal.get(GregorianCalendar.MONTH) + lowerCal.get(GregorianCalendar.YEAR) * 12; int um = upperCal.get(GregorianCalendar.MONTH) + upperCal.get(GregorianCalendar.YEAR) * 12; dim = um - startMonth + 1; lowerCal.set(GregorianCalendar.DAY_OF_MONTH, 1); break; case ReportWizard.T_WEEK: int firstDay = lowerCal.get(GregorianCalendar.DAY_OF_WEEK); int weekStart = lowerCal.getFirstDayOfWeek(); int adjust = (firstDay - weekStart + 7) % 7; lowerCal.add(GregorianCalendar.DAY_OF_YEAR, -adjust); lowerWeek = lowerCal.getTimeInMillis(); firstDay = upperCal.get(GregorianCalendar.DAY_OF_WEEK); weekStart = upperCal.getFirstDayOfWeek(); adjust = (firstDay - weekStart + 7) % 7; upperCal.add(GregorianCalendar.DAY_OF_YEAR, -adjust); long diff = upperCal.getTimeInMillis() - lowerWeek; dim = (int) (diff / (7 * ONEDAY)) + 1; break; default: lowerCal.set(GregorianCalendar.HOUR, 0); lowerCal.set(GregorianCalendar.MINUTE, 0); lowerCal.set(GregorianCalendar.SECOND, 0); lowerCal.set(GregorianCalendar.MILLISECOND, 0); lowerday = lowerCal.getTimeInMillis(); upperCal.set(GregorianCalendar.HOUR, 0); upperCal.set(GregorianCalendar.MINUTE, 0); upperCal.set(GregorianCalendar.SECOND, 0); upperCal.set(GregorianCalendar.MILLISECOND, 0); diff = upperCal.getTimeInMillis() - lowerday; dim = (int) (diff / ONEDAY) + 1; break; } int vmode = report.getMode() & ReportWizard.ALLVALUES; int[] counts = new int[dim]; int[] sales = new int[dim]; double[] earnings = new double[dim]; boolean doCount = (vmode & ReportWizard.IMAGECOUNT) != 0; boolean doSales = (vmode & ReportWizard.SALES) != 0; boolean doEarnings = (vmode & ReportWizard.EARNINGS) != 0; GregorianCalendar cal = new GregorianCalendar(); int cnt = 0; for (Asset asset : set) { Date date = (Date) QueryField.DATE.obtainFieldValue(asset); if (date != null) { cal.setTime(date); if (cal.compareTo(lowerCal) >= 0 && cal.compareTo(upperCal) < 0) { int i; switch (interval) { case ReportWizard.T_YEAR: i = cal.get(GregorianCalendar.YEAR) - startYear; break; case ReportWizard.T_QUARTER: int uq = cal.get(GregorianCalendar.MONTH) / 3 + cal.get(GregorianCalendar.YEAR) * 4; i = uq - startQuarter; break; case ReportWizard.T_MONTH: int um = cal.get(GregorianCalendar.MONTH) + cal.get(GregorianCalendar.YEAR) * 12; i = um - startMonth; break; case ReportWizard.T_WEEK: cal.set(GregorianCalendar.HOUR, 0); cal.set(GregorianCalendar.MINUTE, 0); cal.set(GregorianCalendar.SECOND, 0); cal.set(GregorianCalendar.MILLISECOND, 0); int firstDay = cal.get(GregorianCalendar.DAY_OF_WEEK); int weekStart = cal.getFirstDayOfWeek(); int adjust = (firstDay - weekStart + 7) % 7; cal.add(GregorianCalendar.DAY_OF_YEAR, -adjust); long diff = cal.getTimeInMillis() - lowerWeek; i = (int) (diff / (7 * ONEDAY)); break; default: cal.set(GregorianCalendar.HOUR, 0); cal.set(GregorianCalendar.MINUTE, 0); cal.set(GregorianCalendar.SECOND, 0); cal.set(GregorianCalendar.MILLISECOND, 0); diff = cal.getTimeInMillis() - lowerday; i = (int) (diff / ONEDAY); break; } if (doCount) ++counts[i]; if (doSales) sales[i] += asset.getSales(); if (doEarnings) earnings[i] += asset.getEarnings(); } } if (++cnt % incr == 0) monitor.worked(incr); if (cnt > preview) break; } cumulate(counts); cumulate(sales); cumulate(earnings); TimeSeries countSeries = doCount ? new TimeSeries(COUNT) : null; TimeSeries salesSeries = doSales ? new TimeSeries(SALES) : null; TimeSeries earningsSeries = doEarnings ? new TimeSeries(EARNINGS) : null; for (int i = 0; i < earnings.length; i++) { Date time = lowerCal.getTime(); RegularTimePeriod period; switch (interval) { case ReportWizard.T_YEAR: period = new Year(time); lowerCal.add(GregorianCalendar.YEAR, 1); break; case ReportWizard.T_QUARTER: period = new Quarter(time); lowerCal.add(GregorianCalendar.MONTH, 3); break; case ReportWizard.T_MONTH: period = new Month(time); lowerCal.add(GregorianCalendar.MONTH, 1); break; case ReportWizard.T_WEEK: period = new Week(time); lowerCal.add(GregorianCalendar.WEEK_OF_YEAR, 1); break; default: period = new Day(time); lowerCal.add(GregorianCalendar.DAY_OF_YEAR, 1); break; } if (countSeries != null) countSeries.add(period, counts[i]); if (salesSeries != null) salesSeries.add(period, sales[i]); if (earningsSeries != null) earningsSeries.add(period, earnings[i]); } if (countSeries != null) result.addSeries(countSeries); if (salesSeries != null) result.addSeries(salesSeries); if (earningsSeries != null) result.addSeries(earningsSeries); } } private void collectDiscreteValues(DefaultPieDataset result, IProgressMonitor monitor) { if (qfields == null) return; Map<String, Cumulated> histMap = collectValues(monitor); Cumulated others = computeOthers(histMap); int vmode = mode & ReportWizard.ALLVALUES; String[] enumLabels = computeDiscreteLabels(histMap, others); for (String key : enumLabels) { Cumulated cumulated = histMap.get(key); if (cumulated != null) { switch (vmode) { case ReportWizard.IMAGECOUNT: result.setValue(key, cumulated.getCount()); break; case ReportWizard.SALES: result.setValue(key, cumulated.getSales()); break; case ReportWizard.EARNINGS: result.setValue(key, cumulated.getEarnings()); break; } } } } private void collectNumericValues(DefaultCategoryDataset result, IProgressMonitor monitor) { if (qfields == null) return; List<Asset> set = fetchAssets(report, preview); if (set != null) { int size = set.size(); int incr = Math.max(1, size / 100); monitor.beginTask(Messages.ReportComponent_collecting, size); int[] counts = new int[interval]; int[] sales = new int[interval]; double[] earnings = new double[interval]; boolean day = (mode & ReportWizard.DAYTIME) != 0; int i; int cnt = 0; for (Asset asset : set) { Object value = QueryField.obtainFieldValue(asset, qfields[0], qfields[1]); if (value instanceof Integer) { int v = (Integer) value; if (v >= lower && v < upper) { i = (int) (((v - lower) * interval) / range); if (doCount) ++counts[i]; if (doSales) sales[i] += asset.getSales(); if (doEarnings) earnings[i] += asset.getEarnings(); } } else if (value instanceof Double) { double v = (Double) value; if (v >= lower && v < upper) { i = (int) (((v - lower) * interval) / range); if (doCount) ++counts[i]; if (doSales) sales[i] += asset.getSales(); if (doEarnings) earnings[i] += asset.getEarnings(); } } if (++cnt % incr == 0) monitor.worked(incr); if (cnt > preview) break; } cumulate(counts); cumulate(sales); cumulate(earnings); for (i = 0; i < earnings.length; i++) { String label = day ? df.format(new Date(i * ONEDAY / interval)) : String.valueOf((int) ((i * range + interval / 2) / interval + lower)); if (doCount) result.addValue((Integer) counts[i], COUNT, label); if (doSales) result.addValue((Integer) sales[i], SALES, label); if (doEarnings) result.addValue(earnings[i], EARNINGS, label); } } } private String[] computeDiscreteLabels(Map<String, Cumulated> histMap, Cumulated others) { String[] enumLabels = qfields[1].getEnumLabels(); boolean nameSort = false; if (enumLabels == null) { nameSort = true; Set<String> keySet = histMap.keySet(); enumLabels = keySet.toArray(new String[keySet.size()]); } if (others != null) { String[] newLabels = new String[enumLabels.length + 1]; System.arraycopy(enumLabels, 0, newLabels, 0, enumLabels.length); newLabels[enumLabels.length] = OTHERS; enumLabels = newLabels; histMap.put(OTHERS, others); } int sortField = report.getSortField(); boolean descending = report.getDescending(); if (sortField == 0 && nameSort) { sortField = ReportWizard.NAME; descending = false; } if (sortField != 0) { final int field = sortField; final boolean desc = descending; Arrays.sort(enumLabels, new Comparator<String>() { @Override public int compare(String s1, String s2) { if ((field & ReportWizard.NAME) != 0) return desc ? s2.compareTo(s1) : s1.compareTo(s2); Cumulated c1 = histMap.get(s1); Cumulated c2 = histMap.get(s2); int result = 0; if ((field & ReportWizard.SALES) != 0) { int sales1 = c1 == null ? 0 : c1.sales; int sales2 = c2 == null ? 0 : c2.sales; result = sales1 == sales2 ? 0 : sales1 < sales2 ? -1 : 1; } else if ((field & ReportWizard.EARNINGS) != 0) { double earnings1 = c1 == null ? 0d : c1.earnings; double earnings2 = c2 == null ? 0d : c2.earnings; result = earnings1 == earnings2 ? 0 : earnings1 < earnings2 ? -1 : 1; } else { int count1 = c1 == null ? 0 : c1.count; int count2 = c2 == null ? 0 : c2.count; result = count1 == count2 ? 0 : count1 < count2 ? -1 : 1; } return desc ? -result : result; } }); } return enumLabels; } private void collectDiscreteValues(DefaultCategoryDataset result, IProgressMonitor monitor) { if (qfields == null) return; Map<String, Cumulated> histMap = collectValues(monitor); Cumulated others = computeOthers(histMap); String[] enumLabels = computeDiscreteLabels(histMap, others); for (String key : enumLabels) { Cumulated cumulated = histMap.get(key); if (cumulated != null) { if ((mode & ReportWizard.IMAGECOUNT) != 0) result.setValue(cumulated.getCount(), COUNT, key); if ((mode & ReportWizard.SALES) != 0) result.setValue(cumulated.getSales(), SALES, key); if ((mode & ReportWizard.EARNINGS) != 0) result.setValue(cumulated.getEarnings(), EARNINGS, key); } } } protected Map<String, Cumulated> collectValues(IProgressMonitor monitor) { Map<String, Cumulated> histMap = new HashMap<>(50); String[] filter = report.getFilter(); Set<String> hiddenValues = filter != null && filter.length > 0 ? new HashSet<>(Arrays.asList(filter)) : null; List<Asset> set = fetchAssets(report, preview); if (set != null) { int size = set.size(); int incr = Math.max(1, size / 100); monitor.beginTask(Messages.ReportComponent_collecting, size); int cnt = 0; for (Asset asset : set) { Object value = QueryField.obtainFieldValue(asset, qfields[0], qfields[1]); String text = qfields[1].value2text(value, null); if (hiddenValues == null || !hiddenValues.contains(text)) { Cumulated cumulated = histMap.get(text); if (cumulated == null) { cumulated = new Cumulated(1, asset.getSales(), asset.getEarnings()); histMap.put(text, cumulated); } else { cumulated.count(); cumulated.addSales(asset.getSales()); cumulated.addEarnings(asset.getEarnings()); } } if (++cnt % incr == 0) monitor.worked(incr); if (cnt > preview) break; } } return histMap; } private Cumulated computeOthers(Map<String, Cumulated> histMap) { int totalCount = 0; int totalSales = 0; double totalEarnings = 0d; for (Cumulated cumulated : histMap.values()) { totalCount += cumulated.getCount(); totalSales += cumulated.getSales(); totalEarnings += cumulated.getEarnings(); } double t = report.getThreshold(); double countThreshold = totalCount * t / 100; double salesThreshold = totalSales * t / 100; double earningsThreshold = totalEarnings * t / 100; List<String> toBeDeleted = new ArrayList<>(); for (Entry<String, Cumulated> entry : histMap.entrySet()) { Cumulated cumulated = entry.getValue(); if ((totalCount == 0 || cumulated.getCount() < countThreshold) && (totalSales == 0 || cumulated.getSales() < salesThreshold) && (totalEarnings == 0 || cumulated.getEarnings() < earningsThreshold)) { toBeDeleted.add(entry.getKey()); } } int otherCounts = 0; int otherSales = 0; double otherEarnings = 0; if (toBeDeleted.size() > 1) for (String key : toBeDeleted) { Cumulated removed = histMap.remove(key); otherCounts += removed.getCount(); otherSales += removed.getSales(); otherEarnings += removed.getEarnings(); } return otherCounts > 0 || otherSales > 0 || otherEarnings != 0 ? new Cumulated(otherCounts, otherSales, otherEarnings) : null; } protected List<Asset> fetchAssets(Report report, int preview) { List<Asset> assets = null; IDbManager dbManager = Core.getCore().getDbManager(); String sourceId = report.getSource(); if (sourceId != null) { SmartCollectionImpl sm = dbManager.obtainById(SmartCollectionImpl.class, sourceId); assets = sm != null ? dbManager.createCollectionProcessor(sm).select(false) : null; } if (assets == null) assets = dbManager.obtainObjects(Asset.class); if (report.getSkipOrphans()) { int cnt = 0; List<Asset> nonOrphans = new ArrayList<>(Math.min(1000, preview)); IVolumeManager volumeManager = Core.getCore().getVolumeManager(); Iterator<Asset> it = assets.iterator(); while (it.hasNext()) { Asset asset = it.next(); if (volumeManager.findExistingFile(asset, false) != null || volumeManager.isOffline(asset.getVolume())) { nonOrphans.add(asset); ++cnt; } if (cnt >= preview) break; } return nonOrphans; } return assets; } private void cumulate(double[] values) { if ((mode & ReportWizard.CUMULATE) != 0) for (int i = 1; i < values.length; i++) values[i] += values[i - 1]; } private void cumulate(int[] values) { if ((mode & ReportWizard.CUMULATE) != 0) for (int i = 1; i < values.length; i++) values[i] += values[i - 1]; } @SuppressWarnings("unchecked") private JFreeChart createChart(Dataset dataset) { boolean multiple = ReportWizard.isMultiple(mode); JFreeChart chart = null; String name = report.getName(); String vTitle = computeValueLabel(mode); String dTitle = qfields[1].getLabel(); boolean tooltips = preview == Integer.MAX_VALUE; if (dataset instanceof PieDataset) { if ((mode & ReportWizard.THREEDIM) != 0) chart = ChartFactory.createPieChart3D(name, (PieDataset) dataset, multiple, tooltips, false); else chart = ChartFactory.createPieChart(name, (PieDataset) dataset, multiple, tooltips, false); } else if (dataset instanceof CategoryDataset) { if ((mode & ReportWizard.AREA) != 0) chart = ChartFactory.createAreaChart(name, dTitle, vTitle, (CategoryDataset) dataset, PlotOrientation.VERTICAL, multiple, tooltips, false); else if ((mode & ReportWizard.BAR) != 0) chart = createBarChart(name, dTitle, vTitle, (CategoryDataset) dataset, multiple, tooltips); else if ((mode & (ReportWizard.LINE | ReportWizard.STEP)) != 0) chart = createLineChart(name, dTitle, vTitle, (CategoryDataset) dataset, multiple, tooltips); else if ((mode & ReportWizard.PIE) != 0) chart = ChartFactory.createMultiplePieChart(name, (CategoryDataset) dataset, TableOrder.BY_COLUMN, multiple, tooltips, false); } else if (dataset instanceof TimeSeriesCollection) { dataset = toCategoryDataset((TimeSeriesCollection) dataset); if ((mode & ReportWizard.AREA) != 0) chart = ChartFactory.createAreaChart(name, dTitle, vTitle, (CategoryDataset) dataset, PlotOrientation.VERTICAL, multiple, tooltips, false); else if ((mode & ReportWizard.BAR) != 0) chart = createBarChart(name, dTitle, vTitle, (CategoryDataset) dataset, multiple, tooltips); else if ((mode & (ReportWizard.LINE | ReportWizard.STEP)) != 0) chart = createLineChart(name, dTitle, vTitle, (CategoryDataset) dataset, multiple, tooltips); } customize(chart, dataset); applyProperties(chart, (Map<String, Object>) report.getProperties()); return chart; } protected JFreeChart createLineChart(String name, String dTitle, String vTitle, CategoryDataset dataset, boolean multiple, boolean tooltips) { JFreeChart chart; if ((report.getMode() & ReportWizard.THREEDIM) != 0) chart = ChartFactory.createLineChart3D(name, dTitle, vTitle, dataset, PlotOrientation.VERTICAL, multiple, tooltips, false); else chart = ChartFactory.createLineChart(name, dTitle, vTitle, dataset, PlotOrientation.VERTICAL, multiple, tooltips, false); return chart; } protected JFreeChart createBarChart(String name, String dTitle, String vTitle, CategoryDataset dataset, boolean multiple, boolean tooltips) { JFreeChart chart; if ((mode & ReportWizard.THREEDIM) != 0) chart = ChartFactory.createBarChart3D(name, dTitle, vTitle, dataset, PlotOrientation.VERTICAL, multiple, tooltips, false); else chart = ChartFactory.createBarChart(name, dTitle, vTitle, dataset, PlotOrientation.VERTICAL, multiple, tooltips, false); return chart; } private CategoryDataset toCategoryDataset(TimeSeriesCollection dataset) { DefaultCategoryDataset result = new DefaultCategoryDataset(); // String pattern; // switch (interval) { // case ReportWizard.T_YEAR: // pattern = "yyyy"; // break; // case ReportWizard.T_QUARTER: // pattern = "yyyy-MM"; // break; // case ReportWizard.T_MONTH: // pattern = "yyyy-MM"; // break; // case ReportWizard.T_WEEK: // pattern = "YYYY/ww"; // break; // default: // pattern = "yyyy-MM-dd"; // break; // } // SimpleDateFormat sdf = new SimpleDateFormat(pattern); int n = dataset.getSeriesCount(); TimeSeries[] series = new TimeSeries[n]; Comparable<?>[] keys = new Comparable<?>[n]; for (int i = 0; i < n; i++) { series[i] = dataset.getSeries(i); keys[i] = series[i].getKey(); } for (int j = 0; j < series[0].getItemCount(); j++) { for (int i = 0; i < n; i++) { Number v = series[i].getValue(j); RegularTimePeriod timePeriod = series[i].getTimePeriod(j); // long time = timePeriod.getFirstMillisecond(); // String label = sdf.format(new Date(time)); result.addValue(v, keys[i], timePeriod.toString()); } } return result; } private void customize(JFreeChart chart, Dataset dataset) { if (chart != null) { String name = report.getName(); if (name != null && !name.isEmpty()) { TextTitle t = new TextTitle(name); chart.setTitle(t); } String description = report.getDescription(); if (description != null && !description.isEmpty()) { TextTitle t = new TextTitle(description); chart.addSubtitle(t); } Plot plot = chart.getPlot(); if ((mode & ReportWizard.BAR) != 0 && (mode & ReportWizard.CYLINDER) != 0) { if (plot instanceof CategoryPlot) { CylinderRenderer cylinderRenderer = new CylinderRenderer(); CategoryPlot categoryPlot = (CategoryPlot) plot; // BarRenderer3D barRenderer3D = // (BarRenderer3D)categoryPlot.getRenderer(); // set all needed renderer properties here; for // instance: // cylinderRenderer.setItemMargin(barRenderer3D.getItemMargin()); // etc categoryPlot.setRenderer(cylinderRenderer); } } else if ((mode & ReportWizard.STEP) != 0) { if (plot instanceof CategoryPlot) { CategoryStepRenderer stepRenderer = new CategoryStepRenderer(); CategoryPlot categoryPlot = (CategoryPlot) plot; categoryPlot.setRenderer(stepRenderer); } } if ((mode & ReportWizard.PIE) != 0) { PiePlot piePlot = (PiePlot) plot; piePlot.setStartAngle(290); piePlot.setDirection(Rotation.CLOCKWISE); piePlot.setForegroundAlpha(0.5f); piePlot.setLabelFont(new Font("Arial", Font.PLAIN, 11)); //$NON-NLS-1$ piePlot.setLabelPadding(new RectangleInsets(0, 4, 5, 1)); } else { if ((mode & ReportWizard.EARNINGS) == 0) { if (plot instanceof CategoryPlot) { ValueAxis rangeAxis = ((CategoryPlot) plot).getRangeAxis(); rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); } } } if (plot instanceof CategoryPlot) { SparseCategoryAxis xAxis = null; if ((mode & ReportWizard.DAYTIME) != 0 && report.getDayInterval() == 96) xAxis = new SparseCategoryAxis(4); else if ((mode & ReportWizard.TIME) != 0) { int columnCount = ((CategoryDataset) dataset).getColumnCount(); if (columnCount > 15) { switch (interval) { case ReportWizard.T_YEAR: xAxis = new SparseCategoryAxis(5); break; case ReportWizard.T_QUARTER: xAxis = new SparseCategoryAxis(4); break; case ReportWizard.T_MONTH: xAxis = new SparseCategoryAxis(6); break; case ReportWizard.T_WEEK: xAxis = new SparseCategoryAxis(4); break; case ReportWizard.T_DAY: xAxis = new SparseCategoryAxis(7); break; } } } if (xAxis != null) { Font font = new Font("Arial", Font.PLAIN, 9); //$NON-NLS-1$ xAxis.setTickLabelFont(font); ((CategoryPlot) plot).setDomainAxis(xAxis); ((CategoryPlot) plot).getRangeAxis().setTickLabelFont(font); } else { Font font = new Font("Arial", Font.PLAIN, 11); //$NON-NLS-1$ ((CategoryPlot) plot).getDomainAxis().setTickLabelFont(font); ((CategoryPlot) plot).getRangeAxis().setTickLabelFont(font); } } chart.setAntiAlias(true); chart.setTextAntiAlias(true); } } protected String computeValueLabel(int mode) { if (ReportWizard.isMultiple(mode)) return VALUE; switch (mode & ReportWizard.ALLVALUES) { case ReportWizard.SALES: return SALES; case ReportWizard.EARNINGS: return EARNINGS; default: return COUNT; } } } private static final int PROGRESS_THICKNESS = 5; private ChartComposite chartComposite; private int preview; private Composite stack; private StackLayout stackLayout; private Composite pendingComp; private ProgressIndicator progressBar; public ReportComponent(Composite parent, int style, int preview) { super(parent, style); this.preview = preview; setLayout(new FillLayout()); stack = new Composite(this, SWT.NONE); stackLayout = new StackLayout(); stack.setLayout(stackLayout); chartComposite = new ChartComposite(stack, SWT.NONE, null, preview == Integer.MAX_VALUE, preview == Integer.MAX_VALUE, preview == Integer.MAX_VALUE, preview == Integer.MAX_VALUE, preview == Integer.MAX_VALUE); chartComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); pendingComp = new Composite(stack, SWT.NONE); pendingComp.setLayout(new GridLayout(1, false)); Label label = new Label(pendingComp, SWT.NONE); label.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); label.setText(Messages.ReportPage_pending); if (preview == Integer.MAX_VALUE) { progressBar = new ProgressIndicator(pendingComp, SWT.BORDER); GridData data = new GridData(SWT.FILL, SWT.BEGINNING, true, false); data.heightHint = PROGRESS_THICKNESS; progressBar.setLayoutData(data); } stackLayout.topControl = pendingComp; } public void saveAs() { try { Job.getJobManager().join(this, null); } catch (OperationCanceledException | InterruptedException e) { // do nothing } try { chartComposite.doSaveAs(); } catch (IOException e) { ReportActivator.getDefault().logError(Messages.ReportComponent_io_error, e); } } public void print() { try { Job.getJobManager().join(this, null); } catch (OperationCanceledException | InterruptedException e) { // do nothing } chartComposite.createChartPrintJob(); } public void setReport(Report report) { stackLayout.topControl = pendingComp; stack.layout(true, true); Job.getJobManager().cancel(this); if (report != null) new ReportJob(report, chartComposite, preview).schedule(); } protected void endTask() { if (progressBar != null) { if (!progressBar.isDisposed()) { progressBar.getDisplay().asyncExec(() -> { if (!progressBar.isDisposed()) { progressBar.done(); } }); } } if (!stack.isDisposed()) { stack.getDisplay().asyncExec(() -> { stackLayout.topControl = chartComposite; stack.layout(true, true); }); } } protected void worked(int work) { if (progressBar != null && !progressBar.isDisposed()) { progressBar.getDisplay().asyncExec(() -> { if (!progressBar.isDisposed()) progressBar.worked(work); }); } } protected void beginTask(int totalWork) { if (progressBar != null && !progressBar.isDisposed()) { progressBar.getDisplay().asyncExec(() -> { if (!progressBar.isDisposed()) progressBar.beginTask(totalWork); }); } } @Override public void dispose() { Job.getJobManager().cancel(this); super.dispose(); } public void saveChartProperties(Report report) { JFreeChart chart = chartComposite.getChart(); if (chart != null) { Map<String, Object> properties = new HashMap<>(); TextTitle title = chart.getTitle(); if (title != null) { properties.put(TITLE, title.getText()); properties.put(TITLEFONT, title.getFont()); properties.put(TITLECOLOR, title.getPaint()); } Plot plot = chart.getPlot(); properties.put(BGCOLOR, plot.getBackgroundPaint()); properties.put(OUTLINEPAINT, plot.getOutlinePaint()); properties.put(OUTLINESTROKE, plot.getOutlineStroke()); Axis domainAxis = null; Axis rangeAxis = null; if (plot instanceof CategoryPlot) { CategoryPlot p = (CategoryPlot) plot; domainAxis = p.getDomainAxis(); rangeAxis = p.getRangeAxis(); properties.put(ORIENTATION, p.getOrientation()); } else if (plot instanceof XYPlot) { XYPlot p = (XYPlot) plot; domainAxis = p.getDomainAxis(); rangeAxis = p.getRangeAxis(); properties.put(ORIENTATION, p.getOrientation()); } if (domainAxis != null) saveAxisProperties(domainAxis, "x", properties); //$NON-NLS-1$ if (rangeAxis != null) saveAxisProperties(rangeAxis, "y", properties); //$NON-NLS-1$ properties.put(ANTIALIAS, chart.getAntiAlias()); properties.put(CANVASPAINT, chart.getBackgroundPaint()); report.setProperties(properties); } } private static void saveAxisProperties(Axis axis, String prefix, Map<String, Object> properties) { properties.put(prefix + AXISLABEL, axis.getLabel()); properties.put(prefix + AXISLABELFONT, axis.getLabelFont()); properties.put(prefix + AXISLABELPAINT, axis.getLabelPaint()); properties.put(prefix + TICKMARKSVISIBLE, axis.isTickMarksVisible()); properties.put(prefix + TICKLABELSVISIBLE, axis.isTickLabelsVisible()); properties.put(prefix + TICKLABELFONT, axis.getTickLabelFont()); properties.put(prefix + TICKLABELPAINT, axis.getLabelPaint()); } private static void applyProperties(JFreeChart chart, Map<String, Object> properties) { if (properties != null) { String text = (String) properties.get(TITLE); if (text != null) { TextTitle title = chart.getTitle(); if (title == null) { title = new TextTitle(); chart.setTitle(title); } title.setText(text); Font titleFont = (Font) properties.get(TITLEFONT); if (titleFont != null) title.setFont(titleFont); Paint paint = (Paint) properties.get(TITLECOLOR); if (paint != null) title.setPaint(paint); } else chart.setTitle((TextTitle) null); Plot plot = chart.getPlot(); Paint paint = (Paint) properties.get(BGCOLOR); if (paint != null) plot.setBackgroundPaint(paint); paint = (Color) properties.get(OUTLINEPAINT); if (paint != null) plot.setOutlinePaint(paint); Stroke stroke = (Stroke) properties.get(OUTLINESTROKE); if (stroke != null) plot.setOutlineStroke(stroke); PlotOrientation orientation = (PlotOrientation) properties.get(ORIENTATION); Axis domainAxis = null; Axis rangeAxis = null; if (plot instanceof CategoryPlot) { CategoryPlot p = (CategoryPlot) plot; domainAxis = p.getDomainAxis(); rangeAxis = p.getRangeAxis(); if (orientation != null) p.setOrientation(orientation); } else if (plot instanceof XYPlot) { XYPlot p = (XYPlot) plot; domainAxis = p.getDomainAxis(); rangeAxis = p.getRangeAxis(); if (orientation != null) p.setOrientation(orientation); } if (domainAxis != null) applyAxisProperties(domainAxis, "x", properties); //$NON-NLS-1$ if (rangeAxis != null) applyAxisProperties(rangeAxis, "y", properties); //$NON-NLS-1$ Boolean anti = (Boolean) properties.get(ANTIALIAS); if (anti != null) chart.setAntiAlias(anti); paint = (Paint) properties.get(CANVASPAINT); if (paint != null) chart.setBackgroundPaint(paint); } } private static void applyAxisProperties(Axis axis, String prefix, Map<String, Object> properties) { axis.setLabel((String) properties.get(prefix + AXISLABEL)); Font font = (Font) properties.get(prefix + AXISLABELFONT); if (font != null) axis.setLabelFont(font); Paint paint = (Paint) properties.get(prefix + AXISLABELPAINT); if (paint != null) axis.setLabelPaint(paint); Boolean visible = (Boolean) properties.get(prefix + TICKMARKSVISIBLE); if (visible != null) axis.setTickMarksVisible(visible); visible = (Boolean) properties.get(prefix + TICKLABELSVISIBLE); if (visible != null) axis.setTickLabelsVisible(visible); font = (Font) properties.get(prefix + TICKLABELFONT); if (font != null) axis.setTickLabelFont(font); paint = (Paint) properties.get(prefix + TICKLABELPAINT); if (paint != null) axis.setTickLabelPaint(paint); } }