/*
This file is part of Cyclos (www.cyclos.org).
A project of the Social Trade Organisation (www.socialtrade.org).
Cyclos 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.
Cyclos 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 Cyclos; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package nl.strohalm.cyclos.entities.reports;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import nl.strohalm.cyclos.entities.accounts.SystemAccountType;
import nl.strohalm.cyclos.entities.accounts.transactions.PaymentFilter;
import nl.strohalm.cyclos.entities.groups.Group;
import nl.strohalm.cyclos.entities.groups.GroupFilter;
import nl.strohalm.cyclos.utils.Month;
import nl.strohalm.cyclos.utils.NamedPeriod;
import nl.strohalm.cyclos.utils.Period;
import nl.strohalm.cyclos.utils.Quarter;
import nl.strohalm.cyclos.utils.query.QueryParameters;
/**
* parameters for Statistical queries. This is the base type; each specific query for the statistics is a child class of this.
* <p>
* In order to have correct form validation, the child classes of <code>StatisticalQuery</code> <b>MUST</b> declare the getters of the form checkboxes
* as a method starting the name with "is", amd returning a boolean. Any method like this will be considered as a getter for a checkbox element on the
* form. Graph checkboxes must have a getter returning a boolean, and a method name starting with "is" and ending with "Graph".
* <p>
* <p>
* Note that there is a problem with using <code>paymentFilter<b>s</b></code> in a form. This is a drop down where a user can select one or more
* <code>paymentFilter</code>s. However, <code>paymentFilter</code>s are not mutually exclusive, and two different <code>paymentFilter</code>s may
* show overlap, when they are both sharing one and the same <code>transferType</code>.<br>
* This would create at best misleading graphs, at worst (for example in pie charts) it would make graph rendering impossible. Therefore, the
* following business rules apply for the use of the multi drop down <code>paymentFilters</code>:
* <ul>
* <li>When more than one item is selected, <code>paymentFilter</code>s will be tested on mutual exclusivity. If there is overlap the items in the
* selection, then the form should not accept this.
* <li>Selecting one item will result in splitting up the <code>paymentFilter</code> to its containing <code>TransferType</code>s.
* </ul>
*
* @author Rinke
*
*/
public class StatisticalQuery extends QueryParameters {
private static final long serialVersionUID = 30870554769291883L;
private NamedPeriod periodMain;
private NamedPeriod periodComparedTo;
private StatisticsWhatToShow whatToShow;
private ThroughTimeRange throughTimeRange;
private Month initialMonth;
private Month finalMonth;
private Quarter initialQuarter;
private Quarter finalQuarter;
private Integer initialYear;
private Integer initialMonthYear;
private Integer initialQuarterYear;
private Integer finalYear;
private Integer finalMonthYear;
private Integer finalQuarterYear;
private PaymentFilter paymentFilter;
private Collection<PaymentFilter> paymentFilters;
private Collection<GroupFilter> groupFilters;
private Collection<Group> groups;
private SystemAccountType systemAccountFilter;
private Period[] periods;
/**
* checks if any graph is checked. The method tests for any of the following conditions on a method:
* <ul>
* <li>method is declared, and not inherited from a super class
* <li>mthod name starting with "is"
* <li>method name ending with "Graph"
* <li>return type is boolean
* <li>no parameters accepted
* <li>method must return true.
* </ul>
* .
* <p>
* If there is at least one of such a method, the <code>anyGraphChecked()</code> method will return true. (Calls the to this super class not yet
* known subclass methods via reflection).
*
* @return true if any graph is checked
*/
public boolean anyGraphChecked() {
final Class<? extends StatisticalQuery> cl = this.getClass();
for (final Method m : cl.getDeclaredMethods()) {
final String methodName = m.getName();
final Class<?> rt = m.getReturnType();
if (methodName.startsWith("is") && methodName.endsWith("Graph") && rt == boolean.class && m.getParameterTypes().length == 0) {
try {
// in case of any exception connected to this invocation, be strict and return true.
if ((Boolean) m.invoke(this)) {
return true;
}
} catch (final IllegalArgumentException e) {
// as it is checked for zero params, this cannot happen, so exception is swallowed.
} catch (final IllegalAccessException e) {
return true;
} catch (final InvocationTargetException e) {
// normally, a simple graph getter would not throw an exception, but to be on the safe side, just act like it is a graph setter if
// this exception occurs
return true;
}
}
}
return false;
}
/**
* counts how many items (= subjects to calculate stats upon, like "gross product", "number of members", etc) are checked for this statistics, by
* running over all declared methods. If the following conditions are met for the method, it is considered a checked item:
* <ul>
* <li>method name starts with "is"
* <li>method name does not end with "Graph"
* <li>method does not accept params
* <li>method return type is boolean
* <li>method returns true
* </ul>
*
* @return the number of items requested, according to the above conditions.
* @throws IllegalAccessException if something goes wrong with the invokation of methods via reflection
*/
public int countItemsChecked() throws IllegalAccessException {
final Class<? extends StatisticalQuery> cl = this.getClass();
int itemsCheckedCounter = 0;
for (final Method m : cl.getDeclaredMethods()) {
final String methodName = m.getName();
final Class<?> rt = m.getReturnType();
if (methodName.startsWith("is") && !methodName.endsWith("Graph") && rt == boolean.class && m.getParameterTypes().length == 0) {
try {
if ((Boolean) m.invoke(this)) {
itemsCheckedCounter++;
}
} catch (final IllegalArgumentException e) {
// as no params are passed, and as this is checked for, this cannot happen, so the exception is swallowed
} catch (final InvocationTargetException e) {
// as a normal simple getter does not throw an exception, it is assumed that this was not a getter. So don't count it, swallow,
// and continue the loop
}
}
}
return itemsCheckedCounter;
}
public Month getFinalMonth() {
return finalMonth;
}
public Integer getFinalMonthYear() {
return finalMonthYear;
}
public Quarter getFinalQuarter() {
return finalQuarter;
}
public Integer getFinalQuarterYear() {
return finalQuarterYear;
}
public Integer getFinalYear() {
return finalYear;
}
public Collection<Group> getGroups() {
return groups;
}
public Month getInitialMonth() {
return initialMonth;
}
public Integer getInitialMonthYear() {
return initialMonthYear;
}
public Quarter getInitialQuarter() {
return initialQuarter;
}
public Integer getInitialQuarterYear() {
return initialQuarterYear;
}
public Integer getInitialYear() {
return initialYear;
}
public PaymentFilter getPaymentFilter() {
return paymentFilter;
}
public Collection<PaymentFilter> getPaymentFilters() {
return paymentFilters;
}
public NamedPeriod getPeriodComparedTo() {
return periodComparedTo;
}
public NamedPeriod getPeriodMain() {
return periodMain;
}
public Period[] getPeriods() {
return periods;
}
public SystemAccountType getSystemAccountFilter() {
return systemAccountFilter;
}
public ThroughTimeRange getThroughTimeRange() {
return throughTimeRange;
}
public StatisticsWhatToShow getWhatToShow() {
return whatToShow;
}
public void setFinalMonth(final Month finalMonth) {
this.finalMonth = finalMonth;
}
public void setFinalMonthYear(final Integer finalMonthYear) {
this.finalMonthYear = finalMonthYear;
}
public void setFinalQuarter(final Quarter finalQuarter) {
this.finalQuarter = finalQuarter;
}
public void setFinalQuarterYear(final Integer finalQuarterYear) {
this.finalQuarterYear = finalQuarterYear;
}
public void setFinalYear(final Integer finalYear) {
this.finalYear = finalYear;
}
public void setGroups(final Collection<Group> groups) {
this.groups = groups;
}
public void setInitialMonth(final Month initialMonth) {
this.initialMonth = initialMonth;
}
public void setInitialMonthYear(final Integer initialMonthYear) {
this.initialMonthYear = initialMonthYear;
}
public void setInitialQuarter(final Quarter initialQuarter) {
this.initialQuarter = initialQuarter;
}
public void setInitialQuarterYear(final Integer initialQuarterYear) {
this.initialQuarterYear = initialQuarterYear;
}
public void setInitialYear(final Integer initialYear) {
this.initialYear = initialYear;
}
public void setPaymentFilter(final PaymentFilter paymentFilter) {
this.paymentFilter = paymentFilter;
}
public void setPaymentFilters(final Collection<PaymentFilter> paymentFilters) {
this.paymentFilters = paymentFilters;
}
public void setPeriodComparedTo(final NamedPeriod periodComparedTo) {
this.periodComparedTo = periodComparedTo;
}
public void setPeriodMain(final NamedPeriod periodMain) {
this.periodMain = periodMain;
}
public void setPeriods(final Period[] periods) {
this.periods = periods;
}
public void setSystemAccountFilter(final SystemAccountType systemAccountFilter) {
this.systemAccountFilter = systemAccountFilter;
}
public void setThroughTimeRange(final ThroughTimeRange throughTimeRange) {
this.throughTimeRange = throughTimeRange;
}
public void setWhatToShow(final StatisticsWhatToShow whatToShow) {
this.whatToShow = whatToShow;
}
public Collection<GroupFilter> getGroupFilters() {
return groupFilters;
}
public void setGroupFilters(Collection<GroupFilter> groupFilters) {
this.groupFilters = groupFilters;
}
}