package ru.orangesoftware.financisto2.graph; import java.util.ArrayList; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.List; import ru.orangesoftware.financisto2.R; import ru.orangesoftware.financisto2.db.CategoryRepository; import ru.orangesoftware.financisto2.db.CategoryRepository_; import ru.orangesoftware.financisto2.db.DatabaseAdapter; import ru.orangesoftware.financisto2.model.Currency; import ru.orangesoftware.financisto2.model.PeriodValue; import ru.orangesoftware.financisto2.model.ReportDataByPeriod; import android.content.Context; public abstract class Report2DChart { public static final String REPORT_TYPE = "report_type"; // position in ReportListActivity array public static final int REPORT_ACCOUNT_BY_PERIOD = 5; public static final int REPORT_CATEGORY_BY_PERIOD = 6; public static final int REPORT_PAYEE_BY_PERIOD = 7; public static final int REPORT_PROJECT_BY_PERIOD = 8; protected int level = 0; protected int chartWidth; protected int chartHeight; protected int currentFilterOrder; protected String columnFilter; protected List<Long> filterIds; protected String noFilterMessage; protected Currency currency; protected Calendar startPeriod; protected int periodLength; private String[] periodStrings; private int[] periods; protected ReportDataByPeriod data; protected List<Report2DPoint> points; protected int selectedPoint; protected DatabaseAdapter db; protected CategoryRepository categoryRepository; protected Context context; /** * Basic constructor * @param em entity manager to query data from database * @param periodLength The number of months to plot the chart * @param currency The reference currency to filter transactions in same currency */ public Report2DChart(Context context, DatabaseAdapter db, int periodLength, Currency currency) { setDefaultStartPeriod(periodLength); init(context, db, startPeriod, periodLength, currency); } /** * Constructor with a given first number * @param em entity manager to query data from database * @param startPeriod The first month of the period * @param periodLength The number of months to plot the chart * @param currency The reference currency to filter transactions in same currency */ public Report2DChart(Context context, DatabaseAdapter db, Calendar startPeriod, int periodLength, Currency currency) { init(context, db, startPeriod, periodLength, currency); } /** * Constructor for children charts, identifying the level in the hierarchy (0 = root) * @param em entity manager to query data from database * @param startPeriod The first month of the period * @param periodLength The number of months to plot the chart * @param currency The reference currency to filter transactions in same currency * @param level The level in the hierarchy (0 = root) */ public Report2DChart(Context context, DatabaseAdapter db, Calendar startPeriod, int periodLength, Currency currency, int level) { init(context, db, startPeriod, periodLength, currency); this.level = level; } /** * Rebuild data. * @param context The activity context. * @param em entity manager to query data from database * @param startPeriod The first month of the period * @param periodLength The number of months to plot the chart * @param currency The reference currency to filter transactions in same currency */ public void rebuild(Context context, DatabaseAdapter db, Calendar startPeriod, int periodLength, Currency currency) { init(context, db, startPeriod, periodLength, currency); } /** * Sets the first month of the period when the start period is not given. * Consider the chart for the LAST periodLength MONTHS. * @param periodLength The number of months to plot the chart */ private void setDefaultStartPeriod(int periodLength) { startPeriod = getDefaultStartPeriod(periodLength); } /** * Set the first month of the report period. * @param startPeriod The first month of the report period. */ public void setStartPeriod(Calendar startPeriod) { this.startPeriod = startPeriod; } /** * Get the default start period based on the periodLength, considering the current month the last month of the period. * @param periodLength Number of months to consider previous to the current month. * @return The start period based on the periodLength, assuming as reference the current month. */ public static Calendar getDefaultStartPeriod(int periodLength) { Calendar now = Calendar.getInstance(); Calendar dsp = new GregorianCalendar(now.get(Calendar.YEAR), now.get(Calendar.MONTH), 1); // move to start period (reference month - <periodLength> months) dsp.add(Calendar.MONTH, (-1)*periodLength+1); return dsp; } /** * Initialize parameters. * @param startPeriod * @param periodLength The number of months to plot the chart * @param currency */ private void init(Context context, DatabaseAdapter db, Calendar startPeriod, int periodLength, Currency currency) { this.context = context; this.db = db; this.categoryRepository = CategoryRepository_.getInstance_(context); this.startPeriod = startPeriod; this.periodLength = periodLength; this.currency = currency; periods = new int[22]; periodStrings = new String[22]; for (int i=3; i<=24; i++) { periods[i-3] = i; } // classes shall implement to determine query filters setFilterIds(); setColumnFilter(); if (filterIds!=null && filterIds.size()>0) { // get data currentFilterOrder = 0; build(); } // alert message in activity - no filter available } /** * Gets the message to display when there is no filter to build chart data. * @param context The activity context. * @return The message to display when there is no filter to build chart data. */ public abstract String getNoFilterMessage(Context context); /** * Required step (1) - set the resolution of the chart based on screen available space. * @param height * @param width */ public void setChartResolution(int height, int width) { this.chartHeight = height; this.chartWidth = width; } /** * * @return The list of points to plot. */ public List<Report2DPoint> getPoints() { return points; } /** * * @param location The order in list of points * @return The given point */ public Report2DPoint getPoint(int location) { if (points!=null && points.size()>0) { if (location>=0 && location<points.size()) { return points.get(location); } } return null; } /** * * @return */ public abstract List<Report2DChart> getChildrenCharts(); /** * * @return */ public boolean isRoot() { return level==0; } /** * @return */ public abstract String getFilterName(); /** * Move the cursor to next element of filters list, if not the last element. */ public boolean nextFilter() { if ((currentFilterOrder+1)<filterIds.size()) { currentFilterOrder++; build(); return true; } else { return false; } } /** * Move the pointer to previous element of filters list, if not the first element. */ public boolean previousFilter() { if (currentFilterOrder>0) { currentFilterOrder--; build(); return true; } else { return false; } } /** * * @param periodLength */ public void changePeriodLength(int periodLength) { this.periodLength = periodLength; build(); } /** * @return The period length */ public int getPeriodLength() { return periodLength; } /** * @return */ public String getPeriodLengthString(Context context) { return getPeriodString(context, periodLength); } /** * * @param currency */ public void changeCurrency(Currency currency) { this.currency = currency; build(); } /** * * @return The chart currency */ public Currency getCurrency() { return currency; } /** * * @return The currency symbol */ public String getCurrencySymbol() { return currency.symbol; } /** * Change the period reference by the first month of period given. * @param startPeriod The first month of the chart period. */ public void changeStartPeriod(Calendar startPeriod) { this.startPeriod = startPeriod; build(); } /** * @return The first month of the chart period. */ public Calendar getStartPeriod() { return startPeriod; } /** * Request data and fill data objects (list of points, max, min, etc.) */ protected void build() { data = new ReportDataByPeriod(context, startPeriod, periodLength, currency, columnFilter, filterIds.get(currentFilterOrder).intValue(),db); points = new ArrayList<Report2DPoint>(); List<PeriodValue> pvs = data.getPeriodValues(); for (int i=0; i<pvs.size(); i++) { points.add(new Report2DPoint(pvs.get(i))); } } /** * Set the name of Transaction column to filter on chart */ protected abstract void setColumnFilter(); /** * Fill filterIds with the list of Filter Object ids. * Ex.: In Category report, fill with category ids. */ public abstract void setFilterIds(); /** * Required when displaying a chart and its sub-elements. * Ex.: Category - level 0 (root). Sub-categories - level 1..n (children charts). * @return The Chart level. */ public int getLevel() { return level; } /** * Access to query results, such as min, max, mean and sum. * @return Object on which the data is stored */ public ReportDataByPeriod getDataBuilder() { return data; } /** * Check if there is data to plot. * @return True if there is data to plot or False if there is no points or if all the points have no value different of zero. */ public boolean hasDataToPlot() { if (data.getMaxValue()==data.getMinValue() && data.getMaxValue()==0) { //return false; } //return true; if (points!=null && points.size()>0) { for (int i=0; i<points.size(); i++) { if (points.get(i).getPointData().getValue()!=0) { return true; } } } // has no points return false; } /** * Flag that indicates if is possible to filter data or not. * @return True if the report data has a valid filter, false otherwise. */ public boolean hasFilter() { if (filterIds!=null && filterIds.size()>0) { return true; } else { return false; } } /** * The array of strings representing the periods. * @param context The activity context. * @return The array of strings representing the periods. */ public String[] getPeriodStrings(Context context) { for (int i=3; i<=24; i++) { periodStrings[i-3] = getPeriodString(context, i); } return periodStrings; } /** * @return The array of period options. */ public int[] getPeriodOptions() { return periods; } /** * Get the string that represents the periods. * @param context The activity context. * @param months The number of months of the period. * @return The string representing the given period. */ private String getPeriodString(Context context, int months) { switch (months) { case ReportDataByPeriod.LAST_QUARTER_PERIOD: return context.getString(R.string.report_last_quarter); case ReportDataByPeriod.LAST_HALF_YEAR_PERIOD: return context.getString(R.string.report_last_half_year); case ReportDataByPeriod.LAST_9_MONTHS_PERIOD: return context.getString(R.string.report_last_9_months); case ReportDataByPeriod.LAST_YEAR_PERIOD: return context.getString(R.string.report_last_year); default: String n = context.getString(R.string.report_n_months_var); return context.getString(R.string.report_last_n_months).replace(n, Integer.toString(months)); } } }