/*
 This file is part of the software framework JStatCom, 
 a library for building applications for data based analysis.

 Copyright (C) 2000-2005  Markus Kraetzig

 This library is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License as
 published by the Free Software Foundation; either version 2.1 of the
 License, or (at your option) any later version.

 This library 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.

 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 USA
 */

package com.jstatcom.component;

import java.text.NumberFormat;

import com.jstatcom.model.NumberRange;
import com.jstatcom.model.NumberRangeTypes;
import com.jstatcom.util.UMath;
import com.jstatcom.util.UString;

/**
 * This component allows to input a range specified by two komma separated numbers
 * that is automatically checked according to an overall NumberRange object.
 * The overall range to validate against can be specified by constants.
 * 

* Example text input would be 1, 100. Only real numbers are allowed, * +/- Infinity or NaN are always invalid. This component always assumes that the * range type of the selected range is CLOSED, even if a different * range was set via the setNumberRange method. Therefore * getNumberRange always returns a CLOSED range. * The overall range does not have this restriction imposed. *

* Usage Note:
* If the number format type is set to NumberFormatTypes.DEFAULT, then * the precision set for this selector defines, how displayed numbers are rounded. * Any additional precision is lost, after a number was selected. If, for example, * only int numbers should be possible, the precision can be set to 0, * which is also the default. The resulting number is then stored as a * double that is equal to an int.
* If the number format type is set to NumberFormatTypes.SCIENTIFIC, * then the precision is used only for the display. No rounding is performed and * a precision of 0 does not mean that values are equal to an int. *

* This class fires a "numberRange" property change, if a new number range was set. * * @author Markus Kraetzig * @see com.jstatcom.model.NumberRange */ public final class NumRangeSelector extends IValTextField { private int precision = 0; private NumberFormat numberFormat = NumberFormatTypes.DEFAULT.getInstance(); private NumberFormatTypes numberFormatType = NumberFormatTypes.DEFAULT; private NumberRange currentNumberRange = NumberRange.valueOf("[1,100]"); private NumberRange overallNumberRange = null; private final NumberRange defaultOverallRange = new NumberRange( Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, NumberRangeTypes.OPEN); /** * Default constructor initializes * overall range to (-Infinity,+Infinity) and sets the displayed * range to value to 1, 100. */ public NumRangeSelector() { super(); setOverallNumberRange(defaultOverallRange); setBorder(MatrixBorder.getInstanceSmall()); setSize(100, 20); } /** * Gets the current number format type. * * @return the format type in use */ public NumberFormatTypes getNumberFormatType() { return numberFormatType; } /** * Gets the currently displayed number range. This range is always * of type CLOSED an can never have +/- Infinity bounds. * * @return the (closed) selected number range */ public NumberRange getNumberRange() { return currentNumberRange; } /** * Gets the overall range that the range input is checked against. * * @return current overall number range */ public NumberRange getOverallNumberRange() { return overallNumberRange; } /** * Gets the current overall number range displayed as a string, for * example [1,100] or ("lowerBound_p", 20]. * * @return string representation of overall number range */ public String getOverallRangeExpr() { return overallNumberRange.toString(); } /** * Gets the precision for the displayed numbers. * * @return number count right to the decimal point */ public int getPrecision() { return precision; } /** * Error handling routine if validation failed. Displays error message * and sets back number value to the last valid one.. * * @param msg an error message */ public void handleError(String msg) { super.handleError( msg + "\nThe previous value " + currentNumberRange + " is restored."); setNumberRange(currentNumberRange); } /** * Sets the current number format type. * * @param newNumberFormatType format type to use */ public void setNumberFormatType( com.jstatcom.component.NumberFormatTypes newNumberFormatType) { numberFormatType = newNumberFormatType; numberFormat = newNumberFormatType.getInstance(); } /** * Sets the currently displayed number range with checking whether it conforms * to the specified overall range. The number range is always set to range type * CLOSED, even if newRange has a different type. * This is possible, because any legal range can also be a closed range. Be aware * that +/- Infinity is not allowed for the selected range bounds. * This method fires a "numberRange" property change with the old value and the changed value, * if a new number range was set. * * @param newRange the new selected number range * @throws IllegalArgumentException if newRange is not enclosed * in overall range */ public void setNumberRange(NumberRange newRange) { String msg; if ((msg = overallNumberRange.encloses(newRange)) != null) throw new IllegalArgumentException(msg); NumberRange oldRange = currentNumberRange; // Always set selected range to type CLOSED. if (newRange.rangeType == NumberRangeTypes.CLOSED) currentNumberRange = newRange; else currentNumberRange = new NumberRange( newRange.lowerBound, newRange.upperBound, NumberRangeTypes.CLOSED); setTextCarefully( UString.formatNumber( currentNumberRange.lowerBound, numberFormat, precision) + ", " + UString.formatNumber( currentNumberRange.upperBound, numberFormat, precision)); if (!oldRange.equals(newRange)) firePropertyChange("numberRange", oldRange, newRange); } /** * Sets the overall range that the range input is checked against. * The currently displayed range is automatically validated * against the new overall range. If the current range is not valid, * it automatically set to a legal value within * newNumberRange. * * @param newNumberRange the overall number range for validation, * if null then a range of (-Infinity,+Infinity) * is set. */ public void setOverallNumberRange(NumberRange newNumberRange) { if (newNumberRange == null) newNumberRange = new NumberRange(); // default constructor creates(-inf,inf) overallNumberRange = newNumberRange; if (overallNumberRange.encloses(currentNumberRange) == null) { setNumberRange(currentNumberRange); // number is valid return; } // set to default range in case of problem setNumberRange(overallNumberRange.defaultNumberRange()); } /** * Sets the range to be checked against by specifying a * string of the form ["(", "["] lowerBound "," upperBound + ["]", ")"], * where lowerbound/upperBound are either a number or a * symbolname, for example * [lowerBound_p, upperBound_p] or (0, upperBound_x] or [1,10]. *

* If the range is specified without symbol names, it is set * immediately. If the range depends on symbols to be loaded from * a symbol table, this is postponed to the next FOCUS_GAINED event. * In the meantime, the default non-validating (-Infinity,Infinity) * range is used. * * @param expr the expression to be parsed * @throws IllegalArgumentException if expr == null * or if it cannot be parsed to a valid range */ public void setOverallRangeExpr(String expr) { if (expr == null || expr.length() == 0) throw new IllegalArgumentException("Range expression is empty."); setOverallNumberRange(NumberRange.valueOf(expr)); } /** * Sets number of digits right to decimal point. * * @param newPrecision new number precision * @throws IllegalArgumentException if newPrecision < 0 */ public void setPrecision(int newPrecision) { if (newPrecision < 0) throw new IllegalArgumentException("Negative precision argument."); this.precision = newPrecision; } /** * Sets the currently selected range by specifying a * string with lower bound and upper bound seperated by * a komma, for example 1, 100. * Symbol names are not allowed for the bounds but have to be * specified separately by the respective accessor methods. * * @param expr string containing komma separated bounds * @throws IllegalArgumentException if bounds are not enclosed * in overall range or expr could not be parsed to a * valid range of type CLOSED */ public void setRangeBounds(String expr) { if (expr == null || expr.length() == 0) throw new IllegalArgumentException("Range expression is empty."); expr = NumberRangeTypes.CLOSED.leftBracket + expr + NumberRangeTypes.CLOSED.rightBracket; setNumberRange(NumberRange.valueOf(expr)); } /** * Validates input against the specified range whenever necessary. * This method is invoked by the superclass and does not need to be * called manually. * * @return null if ok, an error message otherwise */ public String validateInput() { String exp = getText().trim(); String error = "Input \"" + exp + "\" is not a valid number range."; // Split input in 2 parts, separated by a komma // and parse single number strings afterwards. int kommaIndex = exp.indexOf(","); if (kommaIndex < 1 || kommaIndex > exp.length() - 2) // No komma within string, cannot be a valid range. throw new IllegalArgumentException(error); String lString = exp.substring(0, kommaIndex); String uString = exp.substring(kommaIndex + 1, exp.length()); Double lBound = UString.parseToNumber(lString); Double uBound = UString.parseToNumber(uString); if (lBound == null || uBound == null) return "Input \"" + exp + "\" is not a valid number range."; double lower = lBound.doubleValue(); double upper = uBound.doubleValue(); if (numberFormatType == NumberFormatTypes.DEFAULT) lower = UMath.round(lower, precision); upper = UMath.round(upper, precision); NumberRange newRange = new NumberRange(lower, upper, NumberRangeTypes.CLOSED); error = overallNumberRange.encloses(newRange); if (error == null) { setNumberRange(newRange); // number range is valid return null; } return newRange.formattedString(numberFormat) + " is not contained in " + overallNumberRange.formattedString(numberFormat) + "."; } }

Ads help maintain this website.