/*
 * @(#)MinimumBoundFilter.java    1.0  9 February 2001
 *
 * Copyright 2004
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs.filter;

import edu.neu.ccs.*;
import java.math.*;

/**
 * <P>Filter that enforces an inclusive or exclusive
 * numeric lower bound by throwing an exception
 * when the data to be filtered violates the bound.
 *
 * Inner classes provide the functionality of both
 * integer and real number lower bounds
 * of primitive or arbitrary precision.</P>
 *
 * @author  Jeff Raab
 * @version 2.2
 * @since   1.0
 * @see MaximumBoundFilter
 */
public class MinimumBoundFilter extends BoundFilter {

    /** Bound property name for the minimum property. */
    public static final String MINIMUM = "minimum";

    //////////////////
    // Constructors //
    //////////////////
    
    /**
     * Constructs an inclusive or exclusive 
     * minimum bound filter.
     *
     * @param inclusive whether or not the bound is inclusive
     */
    public MinimumBoundFilter(boolean inclusive) {
        super(inclusive);
    }

    ///////////////////
    // Inner classes //
    ///////////////////

    /**
     * <P>Minimum bound filter representing
     * an integer lower bound of primitive precision.</P>
     *
     * @author  Jeff Raab
     * @version 2.1
     */
    public static class Long extends MinimumBoundFilter {

        /** The minimum bound for the range. */
        protected long minimum = 0l;

        //////////////////
        // Constructors //
        //////////////////
    
        /**
         * Constructs an inclusive range 
         * bounded by the given minimum value.
         *
         * @param min the minimum value for the range
         */
        public Long(long min) {
            this(min, true);
        }

        /**
         * Constructs an inclusive or exclusive range
         * bounded by the given minimum value.
         *
         * @param min the minimum value for the range
         * @param isInclusive whether or not the range is inclusive
         */
        public Long(long min, boolean isInclusive) {
            super(isInclusive);
            setMinimum(min);
        }

        //////////////////////
        // StringableFilter //
        //////////////////////
        
        /**
         * Returns the given object 
         * if it satisfies the bound for this range.
         *
         * @param obj the object to be filtered
         * @throws FilterException if the object 
         *      is out of bounds
         */
        public Stringable filterStringable(Stringable obj) 
            throws FilterException 
        {
            // superclass filters out objects not
            // assignable from the Number type
            XNumber n = (XNumber)super.filterStringable(obj);
            long l = n.longValue();
                
            // ensure that the object satisfies bound
            if ((getMinimum() < l) ||
                (isInclusive() && (getMinimum() == l)))
            {
                return obj;
            }
            
            // otherwise build an exception message
            String message = "Value not within the range ";
            message += (super.isInclusive() ? "[" : "(");
            message += getMinimum() + ", inf.]";

            // and throw an exception
            throw new FilterException(obj, message);
        }   

        ////////////////
        // Public API //
        ////////////////

        /**
         * Sets the minimum bound for the range
         * to the given value.
         *
         * @param min the desired minimum bound
         * @see #getMinimum()
         */
        public void setMinimum(long min) {
            long oldMin = getMinimum();
            minimum = min;
        
            // if the value has changed
            if (getMinimum() != oldMin) {
            
                // notify listeners of property change
                changeAdapter.firePropertyChange(
                    MINIMUM,
                    new java.lang.Long(oldMin),
                    new java.lang.Long(getMinimum()));
            }
        }

        /**
         * Returns the minimum bound for this range.
         *
         * @see #setMinimum(long)
         */
        public long getMinimum() {
            return minimum;
        }
    }

    /**
     * <P>Minimum bound filter representing
     * a real number lower bound of primitive precision.</P>
     *
     * @author  Jeff Raab
     * @version 2.1
     * @since   1.0
     */
    public static class Double extends MinimumBoundFilter {

        /** The minimum bound for the range. */
        protected double minimum = 0.0;

        //////////////////
        // Constructors //
        //////////////////
    
        /**
         * Constructs an inclusive range 
         * bounded by the given minimum value.
         *
         * @param min the minimum value for the range
         */
        public Double(double min) {
            this(min, true);
        }

        /**
         * Constructs an inclusive or exclusive range
         * bounded by the given minimum value.
         *
         * @param min the minimum value for the range
         * @param isInclusive whether or not the range is inclusive
         */
        public Double(double min, boolean isInclusive) {
            super(isInclusive);
            setMinimum(min);
        }

        //////////////////////
        // StringableFilter //
        //////////////////////
        
        /**
         * Returns the given object 
         * if it satisfies the bound for this range.
         *
         * @param obj the object to be filtered
         * @throws FilterException if the object 
         *      is out of bounds
         */
        public Stringable filterStringable(Stringable obj) 
            throws FilterException 
        {
            // superclass filters out objects not
            // assignable from the Number type
            XNumber n = (XNumber)super.filterStringable(obj);
            double d = n.doubleValue();
                
            // ensure that the object satisfies bound
            if ((getMinimum() < d) ||
                (isInclusive() && (getMinimum() == d)))
            {
                return obj;
            }
            
            // otherwise build an exception message
            String message = "Value not within the range ";
            message += (super.isInclusive() ? "[" : "(");
            message += getMinimum() + ", inf.]";

            // and throw an exception
            throw new FilterException(obj, message);
        }   

        ////////////////
        // Public API //
        ////////////////

        /**
         * Sets the minimum bound for the range
         * to the given value.
         *
         * @param min the desired minimum bound
         * @see #getMinimum()
         */
        public void setMinimum(double min) {
            double oldMin = getMinimum();
            minimum = min;
        
            // if the value has changed
            if (getMinimum() != oldMin) {
            
                // notify listeners of property change
                changeAdapter.firePropertyChange(
                    MINIMUM,
                    new java.lang.Double(oldMin),
                    new java.lang.Double(getMinimum()));
            }
        }

        /**
         * Returns the minimum bound for this range.
         *
         * @see #setMinimum(double)
         */
        public double getMinimum() {
            return minimum;
        }
    }

    /**
     * <P>Minimum bound filter representing
     * an integer lower bound of arbitrary precision.</P>
     *
     * @author  Jeff Raab
     * @version 2.1
     * @since   1.0
     */
    public static class BigInteger extends MinimumBoundFilter {

        /** The minimum bound for the range. */
        protected java.math.BigInteger minimum = null;

        //////////////////
        // Constructors //
        //////////////////
    
        /**
         * Constructs an inclusive range 
         * bounded by the given minimum value.
         *
         * @param min the minimum value for the range
         */
        public BigInteger(java.math.BigInteger min) {
            this(min, true);
        }

        /**
         * Constructs an inclusive or exclusive range
         * bounded by the given minimum value.
         *
         * @param min the minimum value for the range
         * @param isInclusive whether or not the range is inclusive
         */
        public BigInteger(java.math.BigInteger min, boolean isInclusive) {
            super(isInclusive);
            setMinimum(min);
        }

        //////////////////////
        // StringableFilter //
        //////////////////////
        
        /**
         * Returns the given object 
         * if it satisfies the bound for this range.
         *
         * @param obj the object to be filtered
         * @throws FilterException if the object 
         *      is out of bounds
         */
        public Stringable filterStringable(Stringable obj) 
            throws FilterException 
        {
            // superclass filters out objects not
            // assignable from the Number type
            XNumber n = (XNumber)super.filterStringable(obj);
            java.math.BigInteger b = new java.math.BigInteger(
                (new java.lang.Long(n.longValue())).toString());
                
            // ensure that the object satisfies bound
            if ((b.compareTo(getMinimum()) > 0) ||
                (isInclusive() && (b.compareTo(getMinimum()) == 0)))
            {
                return obj;
            }
            
            // otherwise build an exception message
            String message = "Value not within the range ";
            message += (super.isInclusive() ? "[" : "(");
            message += getMinimum() + ", inf.]";

            // and throw an exception
            throw new FilterException(obj, message);
        }   

        ////////////////
        // Public API //
        ////////////////

        /**
         * Sets the minimum bound for the range
         * to the given value.
         *
         * If <CODE>null</CODE> the value is not changed.
         *
         * @param min the desired minimum bound
         * @see #getMinimum()
         */
        public void setMinimum(java.math.BigInteger min) {
            if (min == null)
                return;
            
            java.math.BigInteger oldMin = getMinimum();
            minimum = min;
        
            // if the value has changed
            if (!getMinimum().equals(oldMin)) {
            
                // notify listeners of property change
                changeAdapter.firePropertyChange(
                    MINIMUM,
                    oldMin,
                    getMinimum());
            }
        }

        /**
         * Returns the minimum bound for this range.
         *
         * @see #setMinimum(BigInteger)
         */
        public java.math.BigInteger getMinimum() {
            return minimum;
        }
    }

    /**
     * <P>Minimum bound filter representing
     * a real number lower bound of arbitrary precision.</P>
     *
     * @author  Jeff Raab
     * @version 2.1
     * @since   1.0
     */
    public static class BigDecimal extends MinimumBoundFilter {

        /** The minimum bound for the range. */
        protected java.math.BigDecimal minimum = null;

        //////////////////
        // Constructors //
        //////////////////
    
        /**
         * Constructs an inclusive range 
         * bounded by the given minimum value.
         *
         * @param min the minimum value for the range
         */
        public BigDecimal(java.math.BigDecimal min) {
            this(min, true);
        }

        /**
         * Constructs an inclusive or exclusive range
         * bounded by the given minimum value.
         *
         * @param min the minimum value for the range
         * @param isInclusive whether or not the range is inclusive
         */
        public BigDecimal(
            java.math.BigDecimal min, 
            boolean isInclusive) 
        {
            super(isInclusive);
            setMinimum(min);
        }

        //////////////////////
        // StringableFilter //
        //////////////////////
        
        /**
         * Returns the given object 
         * if it satisfies the bound for this range.
         *
         * @param obj the object to be filtered
         * @throws FilterException if the object 
         *      is out of bounds
         */
        public Stringable filterStringable(Stringable obj) 
            throws FilterException 
        {
            // superclass filters out objects not
            // assignable from the Number type
            XNumber n = (XNumber)super.filterStringable(obj);
            java.math.BigDecimal d = 
                new java.math.BigDecimal(n.doubleValue());
                
            // ensure that the object satisfies bound
            if ((d.compareTo(getMinimum()) > 0) ||
                (isInclusive() && (d.compareTo(getMinimum()) == 0)))
            {
                return obj;
            }
            
            // otherwise build an exception message
            String message = "Value not within the range ";
            message += (super.isInclusive() ? "[" : "(");
            message += getMinimum() + ", inf.]";

            // and throw an exception
            throw new FilterException(obj, message);
        }   

        ////////////////
        // Public API //
        ////////////////

        /**
         * Sets the minimum bound for the range
         * to the given value.
         *
         * If <CODE>null</CODE>, the value is not changed.
         *
         * @param min the desired minimum bound
         * @see #getMinimum()
         */
        public void setMinimum(java.math.BigDecimal min) {
            if (min == null)
                return;
            
            java.math.BigDecimal oldMin = getMinimum();
            minimum = min;
        
            // if the value has changed
            if (!getMinimum().equals(oldMin)) {
            
                // notify listeners of property change
                changeAdapter.firePropertyChange(
                    MINIMUM,
                    oldMin,
                    getMinimum());
            }
        }

        /**
         * Returns the minimum bound for this range.
         *
         * @see #setMinimum(java.math.BigDecimal)
         */
        public java.math.BigDecimal getMinimum() {
            return minimum;
        }
    }
}
