/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program 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 Lesser General Public License for more details.
*
* Copyright (c) 2001 - 2009 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core.function;
import java.math.BigDecimal;
/**
* Computes the percentage for a column in relation to a base column.
* <p/>
* The function undestands two parameters. The <code>dividend</code> parameter is required and denotes the name of an
* ItemBand-field which is used as dividend. The <code>divisor</code> parameter is required and denotes the name of an
* ItemBand-field which is uses as divisor.
* <p/>
* If either the divident or the divisor are not numeric, the expression will return <code>null</code>.
* <p/>
* The formula used is as follows:
* <pre>
* Percent := divident / divisor
* </pre>
* <p/>
* If the flag <code>useDifference</code> is set, the difference between base and subject is used instead.
* <pre>
* Percent := (divisor - divident) / divisor
* </pre>
*
* @author Heiko Evermann
* @author Thomas Morgner
* @deprecated The same can be achieved using a simple ValueExpression.
*/
public class PercentageExpression extends AbstractExpression
{
/**
* A constant for the value ZERO.
*/
private static final BigDecimal ZERO = new BigDecimal(0);
/**
* the field used as dividend by the function.
*/
private String dividend;
/**
* the field used as divisor by the function.
*/
private String divisor;
/**
* A flag indicating whether the difference between divident and divisor should be used as real divisor.
*/
private boolean useDifference;
/**
* The scale-property defines the precission of the divide-operation.
*/
private int scale;
/**
* The rounding-property defines the precission of the divide-operation.
*/
private int roundingMode;
/**
* Constructs a new function. <P> Initially the function has no name...be sure to assign one before using the
* function.
*/
public PercentageExpression()
{
scale = 14;
roundingMode = BigDecimal.ROUND_HALF_UP;
}
/**
* Returns whether the difference between divident and divisor should be used as real divisor.
*
* @return true, if the difference is used, false if the divident is used directly.
*/
public boolean isUseDifference()
{
return useDifference;
}
/**
* Defines whether the difference between divident and divisor should be used as real divisor.
*
* @param useDifference true, if the difference is used, false if the divident is used directly.
*/
public void setUseDifference(final boolean useDifference)
{
this.useDifference = useDifference;
}
/**
* Returns the defined rounding mode. This influences the precision of the divide-operation.
*
* @return the rounding mode.
* @see java.math.BigDecimal#divide(java.math.BigDecimal,int)
*/
public int getRoundingMode()
{
return roundingMode;
}
/**
* Defines the rounding mode. This influences the precision of the divide-operation.
*
* @param roundingMode the rounding mode.
* @see java.math.BigDecimal#divide(java.math.BigDecimal,int)
*/
public void setRoundingMode(final int roundingMode)
{
this.roundingMode = roundingMode;
}
/**
* Returns the scale for the divide-operation. The scale influences the precision of the division.
*
* @return the scale.
*/
public int getScale()
{
return scale;
}
/**
* Defines the scale for the divide-operation. The scale influences the precision of the division.
*
* @param scale the scale.
*/
public void setScale(final int scale)
{
this.scale = scale;
}
/**
* Returns the field used as dividend by the function. <P> The field name corresponds to a column name in the report's
* data-row.
*
* @return The field name.
*/
public String getDividend()
{
return dividend;
}
/**
* Returns the field used as divisor by the function. <P> The field name corresponds to a column name in the report's
* data-row.
*
* @return The field name.
*/
public String getDivisor()
{
return divisor;
}
/**
* Sets the field name to be used as dividend for the function. <P> The field name corresponds to a column name in the
* report's data-row.
*
* @param dividend the field name.
*/
public void setDividend(final String dividend)
{
this.dividend = dividend;
}
/**
* Sets the field name to be used as divisor for the function. <P> The field name corresponds to a column name in the
* report's data-row.
*
* @param divisor the field name.
*/
public void setDivisor(final String divisor)
{
this.divisor = divisor;
}
/**
* Return the current function value. <P> The value is calculated as the quotient of two columns: the dividend column
* and the divisor column. If the divisor is zero, the return value is "n/a";
*
* @return The quotient
*/
public Object getValue()
{
if (dividend == null || divisor == null)
{
return null;
}
final Object dividentFieldValue = getDataRow().get(getDividend());
// do not add when field is null or no number
final BigDecimal dividend;
if (dividentFieldValue instanceof BigDecimal)
{
dividend = (BigDecimal) dividentFieldValue;
}
else if (dividentFieldValue instanceof Number == false)
{
return null;
}
else
{
dividend = new BigDecimal(dividentFieldValue.toString());
}
final Object divisorFieldValue = getDataRow().get(getDivisor());
// do not add when field is null or no number
final BigDecimal divisor;
if (divisorFieldValue instanceof BigDecimal)
{
divisor = (BigDecimal) divisorFieldValue;
}
else if (divisorFieldValue instanceof Number == false)
{
return null;
}
else
{
divisor = new BigDecimal(divisorFieldValue.toString());
}
if (PercentageExpression.ZERO.compareTo(divisor) == 0)
{
return null;
}
if (useDifference)
{
final BigDecimal delta = dividend.subtract(divisor);
return delta.divide(divisor, scale, roundingMode);
}
else
{
return dividend.divide(divisor, scale, roundingMode);
}
}
}