DataLimit.java [tools/WepsReportData/src/usda/weru/util/table] Revision:   Date:
/*
 * DataLimit.java
 *
 * Created on June 21, 2006, 4:29 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package usda.weru.util.table;

import org.jdom.Element;

/**
 * Wrapper class to handle upper and lower data limits.  It will replace the data with another value if it is outside the bounds it is configured for.
 * @author Joseph Levin
 */
 public class DataLimit implements XmlObject{
    private String c_system;
    private double c_lower = Double.NaN;
    private boolean c_lowerInclusive = false;
    private boolean c_lowerAllowZero = false;
    private String c_lowerText = null;
    
    private double c_upper = Double.NaN;
    private boolean c_upperInclusive = false;
    private boolean c_upperAllowZero = false;
    private String c_upperText = null;

    /**
     * Create a new DataLimit with no limits in place.
     */
    public DataLimit(){
    }


    /**
     * Load the limits from a jdom Element.
     * @param node An org.jdom.Element containing the limit information.
     */
    public void fromXml(Element node){
        loadSystem(node);
        loadLower(node);
        loadUpper(node);
    }

    /**
     * Load the unit system to be used by this DataLimit from a jdom Element.
     * @param node An org.jdom.Element containing the unit system to be used for this DataLimit.
     */
    public void loadSystem(Element node){
        c_system = node.getAttributeValue(WepsTableEnum.XML_system);
    }

    /**
     * Load a lower limit from a jdom Element.
     * @param node An org.jdom.Element containing limit information for a lower limit.
     */
    public void loadLower(Element node){
        Element lower = node.getChild(WepsTableEnum.XML_lower);
        if (lower != null){
            try{
                c_lower = Double.parseDouble(lower.getValue());
            }
            catch (NumberFormatException nfe){
                c_lower = Double.NaN;
                return;
            }
            String inclusiveText = lower.getAttributeValue(WepsTableEnum.XML_inclusive);
            c_lowerInclusive = Helper.isTrue(inclusiveText);
            String allowZeroText = lower.getAttributeValue(WepsTableEnum.XML_allowzero);
            c_lowerAllowZero = Helper.isTrue(allowZeroText);
            c_lowerText = lower.getAttributeValue(WepsTableEnum.XML_text);

        }
        else{
            c_lower = Double.NaN;
            c_lowerInclusive = false;
            c_lowerText = null;
            c_lowerAllowZero = false;
        }
    }

    /**
     * Load a upper limit from a jdom Element.
     * @param node An org.jdom.Element containing limit information for a upper limit.
     */
    public void loadUpper(Element node){
        Element upper = node.getChild(WepsTableEnum.XML_upper);
        if (upper != null){
            try{
                c_upper = Double.parseDouble(upper.getValue());
            }
            catch (NumberFormatException nfe){
                c_upper = Double.NaN;
                return;
            }
            String inclusiveText = upper.getAttributeValue(WepsTableEnum.XML_inclusive);
            c_upperInclusive = Helper.isTrue(inclusiveText);
            String allowZeroText = upper.getAttributeValue(WepsTableEnum.XML_allowzero);
            c_upperAllowZero = Helper.isTrue(allowZeroText);
            c_upperText = upper.getAttributeValue(WepsTableEnum.XML_text);
        }
        else{
            c_upper = Double.NaN;
            c_upperInclusive = false;
            c_upperText = null;
            c_upperAllowZero = false;
        }
    }

    /**
     * Apply the available limits to an Object.
     * @param o The object to be limited.
     * @return The limited Object.
     */
    public Object limit(Object o){
        try{
            double d;
            if (o instanceof Number){
                d = ((Number) o).doubleValue();
            }
            else{
                d = Double.parseDouble(o.toString());
            }
                
            if (excedingThreshold(d)){
                return getText(d);
            }
            else{
                return o;
            }
        }
        catch (Exception e){
            return o;
        }        
    }
    
    /**
     * Return whether or not a given value has exceeded the thresholds set by this DataLimit.
     * @param value The value to be tested.
     * @return <B>true</B> if it is outside the threshold, <B>false</B> otherwise.
     */
    public boolean excedingThreshold(double value){
        return outisdeLowerBound(value) || outisdeUpperBound (value);                
    }

    /**
     * Test whether the given value is below the lower bound.
     * @param value The value to be tested.
     * @return <B>true</B> if the value is below the lower bound, <B>false</B> otherwise.
     */
    public boolean outisdeLowerBound(double value){
        //Special case for zeros
        if (c_lowerAllowZero && value == 0) return false;

        if (isLowerBoundInclusive() && value <= c_lower) return true;
        else if(value < c_lower) return true;
        else return false;
    }

    /**
     * Test whether the given value is above the upper bound.
     * @param value The value to be tested.
     * @return <B>true</B> if the value is above the upper bound, <B>false</B> otherwise.
     */
    public boolean outisdeUpperBound(double value){
        //Special case for zeros
        if (c_upperAllowZero && value == 0) return false;
        
        if (isUpperBoundInclusive() && value >= c_upper) return true;
        else if(value > c_upper) return true;
        else return false;
    }

    /**
     * Return the value to be printed if the supplied value is outside the bounds.
     * @param value The value to be tested.
     * @return The upper limit text if the value is above the upper limit, the lower limit text if the value is below the lower limit, or the value if it is not outside the bounds.
     */
    public String getText(double value){
        if (outisdeLowerBound(value)){
            return getLowerBoundText();
        }
        else if (outisdeUpperBound(value)){
            return this.getUpperBoundText();
        }
        else return Double.toString(value);
    }

    /**
     * Return the unit system used by this DataLimit.
     * @return The unit system, or WepsTableEnum.NO_UNITS_SYSTEM if there is none defined.
     */
    public String getSystem(){
        if (c_system != null){
            return c_system;
        }
        else{
            return WepsTableEnum.NO_UNITS_SYSTEM;
        }
    }

    /**
     * Set the unit system to be used by this DataLimit.
     * @param system The unit system to be used.
     */
    public void setSystem(String system){
        c_system = system;
    }

    /**
     * Return whether or not there is a lower bound specified.
     * @return <B>true</B> if there is a lower bound specified, <B>false</B> otherwise.
     */
    public boolean hasLowerBound(){
        return !Double.isNaN(c_lower);
    }                

    /**
     * Return the lower bound.
     * @return The lower bound.
     */
    public double getLowerBound(){
        return c_lower;
    }

    /**
     * Set the lower bound.
     * @param bound The lower bound.
     */
    public void setLowerBound(double bound){
        c_lower = bound;
    }

    /**
     * Return whether or not the lower bound is inclusive.
     * @return <B>true</B> if the lower bound is inclusive, <B>false</B> otherwise.
     */
    public boolean isLowerBoundInclusive(){
        return c_lowerInclusive;
    }

    /**
     * Set whether or not the lower bound as inclusive.
     * @param inclusive <B>true</B> if the lower bound should be inclusive, <B>false</B> if it should not be.
     */
    public void setLowerBoundInclusive(boolean inclusive){
        c_lowerInclusive = inclusive;
    }

    /**
     * Return whether or not a lower bound text has been specified.
     * @return The lower bound text.
     */
    public boolean hasLowerBoundText(){
        return c_lowerText != null;
    }                

    /**
     * Return the text that will replace the value if the value is below the lower bound.
     * @return The text to be displayed.
     */
    public String getLowerBoundText(){
        if (hasLowerBoundText()){
            return c_lowerText;
        }
        else{
            String symbol = (isLowerBoundInclusive()) ? "<" : "<";
            return symbol + Double.toString(getLowerBound());

        }
    }

    /**
     * Set the text to be displayed if the value is below the lower bound.
     * @param text The text to be displayed.
     */
    public void setLowerBoundText(String text){
        c_lowerText = text;
    }

    /**
     * Return whether or not there is an upper bound.
     * @return <B>true</B> if there is an upper bound, <B>false</B> otherwise.
     */
    public boolean hasUpperBound(){
        return !Double.isNaN(c_upper);
    }

    /**
     * Return the upper bound.
     * @return The value of the upper bound.
     */
    public double getUpperBound(){
        return c_upper;
    }

    /**
     * Set the upper bound.
     * @param bound The value of the upper bound.
     */
    public void setUpperBound(double bound){
        c_upper = bound;
    }

    /**
     * Return whether or not the upper bound is inclusive.
     * @return <B>true</B> if the upper bound is inclusive, <B>false</B> otherwise.
     */
    public boolean isUpperBoundInclusive(){
        return c_upperInclusive;
    }

    /**
     * Set whether or not the upper bound is inclusive.
     * @param inclusive Whether or not the bound is to be inclusive.
     */
    public void setUpperBoundInclusive(boolean inclusive){
        c_upperInclusive = inclusive;
    }

    /**
     * Return whether or not there is text that will replace the value if the value is above the upper bound.
     * @return <B>true</B> if there is, <B>false</B> otherwise.
     */
    public boolean hasUpperBoundText(){
        return c_upperText != null;
    }

    /**
     * Return the text that replaces the value if the value is above the upper bound.
     * @return The text that replaces the value.
     */
    public String getUpperBoundText(){
        if (hasUpperBoundText()){
            return c_upperText;
        }
        else{
            String symbol = (isUpperBoundInclusive()) ? ">" : ">";
            return symbol + Double.toString(getUpperBound());

        }
    }

    /**
     * Set the text that will replace the value if it is above the upper bound.
     * @param text The text used if the value exceeds the upper bound.
     */
    public void setUpperBoundText(String text){
        c_upperText = text;
    }


}