/*
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.ts;
import org.apache.log4j.Logger;
import com.jstatcom.component.IValTextField;
import com.jstatcom.component.MatrixBorder;
/**
* Component for display and selection of a TSDateRange. It
* interpretes an input string and validates it against a parser and a defined
* range. The range that is displayed in this text field always includes its
* bounds. This component can be costumized by setting an overall range that
* must enclose the range that is selected. This is ensured by the validation
* mechanism.
*
* This class fires a "TSDateRange" property change, if a new date was set.
*
* @see com.jstatcom.ts.TSDateRange
* @see com.jstatcom.parser.TSDateParser
* @author Markus Kraetzig
*/
public final class TSDateRangeSelector extends IValTextField {
private static final Logger log = Logger
.getLogger(TSDateRangeSelector.class);
/**
* Enclosing range that the input must be in with.
*/
private TSDateRange enclosingRange = null;
/**
* Currently displayed range.
*/
private TSDateRange tSDateRange = null;
/**
* Minimum observations to be available within the range.
*/
private int minNumberOfObs = 1;
/**
* Create a TSDateRangeSelectorwith the range set to
* [1960 q1,1982 q4].
*/
public TSDateRangeSelector() {
super();
TSDate t1 = TSDate.valueOf("1960 q1");
TSDate t2 = TSDate.valueOf("1982 q4");
enclosingRange = new TSDateRange(t1, t2);
setTSDateRange(enclosingRange);
setBorder(MatrixBorder.getInstanceSmall());
setSize(44, getHeight());
}
/**
* Gets the overall range that the selected range must be in with.
*
* @return the enclosing range
*/
public TSDateRange getEnclosingRange() {
return enclosingRange;
}
/**
* Gets the minimum number of observations selectable by this component.
*
* @return minimum number of observations
*/
public int getMinNumberOfObs() {
return minNumberOfObs;
}
/**
* Gets the currently displayed date range.
*
* @return the currently selected range
*/
public TSDateRange getTSDateRange() {
return tSDateRange;
}
/**
* Returns the currently displayed date range as an array of two indices
* relative to the lower bound of the overall range. The index starts from
* 0.
*
* @return int array with int[0] = index of lower bound, int[1] = index of
* upper bound
*/
public int[] getTSDateRangeAsIndices() {
return enclosingRange.indicesForRange(tSDateRange);
}
/**
* Error handling routine if validation failed. Displays an error message
* and sets back the range value to the last valid one.
*
* @param msg
* an error message
*/
public void handleError(String msg) {
super.handleError(msg + "\nThe previous value " + tSDateRange
+ " is restored.");
setTSDateRange(tSDateRange);
}
/**
* Sets the display to the maximum possible range, which is the enclosing
* range.
*/
public void maxRange() {
setTSDateRange(enclosingRange);
}
/**
* Sets the range that the selected range must be in with by setting an
* initial date and the desired number of available periods within the
* range.
*
* @param newStart
* new start of bounding range
* @param numberOfPeriods >
* 0 to define the end of the bounding range
*/
public void setEnclosingRange(TSDate newStart, int numberOfPeriods) {
setEnclosingRange(new TSDateRange(newStart, newStart
.addPeriods(numberOfPeriods - 1)));
}
/**
* Sets the overall range that the selected range must be in with. The
* selected range is not changed unless it violates the new range setting.
*
* @param newOverallRange
* the new bounding range
* @throws IllegalArgumentException
* if (newOverallRange == null)
*/
public void setEnclosingRange(TSDateRange newOverallRange) {
if (newOverallRange == null)
throw new IllegalArgumentException("Range cannot be null.");
enclosingRange = newOverallRange;
if (enclosingRange.encloses(tSDateRange) != null) {
// Try to get overlapping range.
TSDateRange newRange = enclosingRange.commonRange(tSDateRange);
if (newRange != null)
// Set overlapping range.
setTSDateRange(newRange);
else
// No overlapping range, set overall range.
setTSDateRange(enclosingRange);
}
}
/**
* Sets the minimum number of observations selectable by this component. It
* also sets back the currently displayed range to the overall range.
*
* @param newMinNumberOfObs
* any int value. If it is larger than the current range, then
* only the whole range is still valid. Otherwise no selection
* would be possible anymore.
*/
public void setMinNumberOfObs(int newMinNumberOfObs) {
minNumberOfObs = newMinNumberOfObs;
setTSDateRange(enclosingRange);
}
/**
* Sets the currently displayed date range if it conforms to the specified
* overall range. This method fires a "TSDateRange" property change with the
* old value and the changed value if a new date range was set.
*
* @param newTSDateRange
* the range to be displayed
* @throws IllegalArgumentException
* if newTSDateRange does not fit in overall range or is
* null
*/
public void setTSDateRange(TSDateRange newTSDateRange) {
if (newTSDateRange == null)
throw new IllegalArgumentException("Range is null.");
String msg;
if ((msg = enclosingRange.encloses(newTSDateRange)) != null)
throw new IllegalArgumentException(msg);
TSDateRange oldDateRange = tSDateRange;
tSDateRange = newTSDateRange;
String formattedRange = tSDateRange.lowerBound() + ", "
+ tSDateRange.upperBound();
setTextCarefully(formattedRange);
// fire property change
if (oldDateRange != null && !oldDateRange.equals(tSDateRange))
firePropertyChange("TSDateRange", oldDateRange, tSDateRange);
}
/**
* Validates input against the TSDateParser and the overall
* TSDateRange. It also checks the minimum number of
* observations restriction.
*
* @return error message if failure, null if ok
*/
public String validateInput() {
TSDateRange tmpRange = null;
TSDate t1, t2;
String inputString = getText().trim();
// Split input in 2 parts, separated by a komma
// and parse single date strings afterwards.
int kommaIndex = inputString.indexOf(",");
if (kommaIndex < 1 || kommaIndex > inputString.length() - 2)
// No komma within string, cannot be a valid range.
return "Input \"" + inputString + "\" is not a valid date range.";
String t1String = inputString.substring(0, kommaIndex);
String t2String = inputString.substring(kommaIndex + 1, inputString
.length());
try {
t1 = TSDate.valueOf(t1String);
t2 = TSDate.valueOf(t2String);
tmpRange = new TSDateRange(t1, t2);
} catch (Throwable ex) {
// Something went wrong during parsing or range construction.
// Wrong input must be considered exceptional behaviour.
String msg = "Input \"" + inputString
+ "\" is not a valid date range.";
log.warn(msg, ex);
return msg;
}
String error = enclosingRange.encloses(tmpRange);
// Check also against the minimum number of observations.
if (error == null && minNumberOfObs > 0) {
// If minNumber exceeds overall number of obs, reduce it.
int maxMin = enclosingRange.numOfObs();
int currentMin = (minNumberOfObs > maxMin) ? maxMin
: minNumberOfObs;
int num = tmpRange.numOfObs();
if (num < currentMin) {
String plural = (num == 1) ? "observation" : "observations";
return tmpRange + " contains only " + num + " " + plural
+ ".\nThis is less than the required minimum of "
+ currentMin + ".";
}
}
if (error == null)
setTSDateRange(tmpRange);
return error;
}
}
Ads help maintain this website.