/**
* Copyright (C) 2001-2017 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU Affero General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* 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
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.gui.new_plotter.utility;
import com.rapidminer.RapidMiner;
import com.rapidminer.datatable.DataTableRow;
import com.rapidminer.gui.new_plotter.listener.events.ValueRangeChangeEvent;
import com.rapidminer.gui.new_plotter.listener.events.ValueRangeChangeEvent.ValueRangeChangeType;
import com.rapidminer.tools.I18N;
import com.rapidminer.tools.ParameterService;
import java.sql.Date;
import java.text.DateFormat;
/**
* A numerical range. By default the lower bound is included (">=" comparison),
* the upper bound is not included in the range ("<" comparison).
* This behavior can be changed by the properties includeLowerBound and includeUpperBound.
*
* @author Marius Helf, Nils Woehler
*
*/
public class NumericalValueRange extends AbstractValueRange {
private double lowerBound;
private double upperBound;
private int columnIdx;
private boolean includeLowerBound;
private boolean includeUpperBound;
private int upperPrecision = -Integer.parseInt(ParameterService
.getParameterValue(RapidMiner.PROPERTY_RAPIDMINER_GENERAL_FRACTIONDIGITS_NUMBERS));
private int lowerPrecision = upperPrecision;
private DateFormat dateFormat;
public NumericalValueRange(double lowerBound, double upperBound, int columnIdx) {
this(lowerBound, upperBound, columnIdx, true, false);
}
/**
* Creates a new {@link NumericalValueRange}.
*
* If isDate, the {@link #toString()} method converts the lower and upper bound to dates.
*/
public NumericalValueRange(double lowerBound, double upperBound, int columnIdx, DateFormat dateFormat,
boolean includeLowerBound, boolean includeUpperBound) {
this.lowerBound = lowerBound;
this.upperBound = upperBound;
this.includeLowerBound = includeLowerBound;
this.includeUpperBound = includeUpperBound;
this.columnIdx = columnIdx;
this.dateFormat = dateFormat;
}
public NumericalValueRange(double lowerBound, double upperBound, int columnIdx, boolean includeLowerBound,
boolean includeUpperBound) {
this(lowerBound, upperBound, columnIdx, null, includeLowerBound, includeUpperBound);
}
@Override
public double getLowerBound() {
return lowerBound;
}
public void setLowerBound(double lowerBound) {
if (lowerBound != this.lowerBound) {
this.lowerBound = lowerBound;
fireValueRangeChanged(new ValueRangeChangeEvent(this, ValueRangeChangeType.LOWER_BOUND, lowerBound));
}
}
@Override
public double getUpperBound() {
return upperBound;
}
public void setUpperBound(double upperBound) {
if (upperBound != this.upperBound) {
this.upperBound = upperBound;
fireValueRangeChanged(new ValueRangeChangeEvent(this, ValueRangeChangeType.UPPER_BOUND, upperBound));
}
}
public boolean includesLowerBound() {
return includeLowerBound;
}
public boolean includesUpperBound() {
return includeUpperBound;
}
public int getColumnIdx() {
return columnIdx;
}
public void setColumnIdx(int index) {
if (index != columnIdx) {
this.columnIdx = index;
fireValueRangeChanged(new ValueRangeChangeEvent(this, ValueRangeChangeType.RESET));
}
}
@Override
public boolean keepRow(DataTableRow row) {
double value = row.getValue(columnIdx);
boolean belowLowerBound = false;
if (includeLowerBound && value < lowerBound) {
belowLowerBound = true;
} else if (!includeLowerBound && value <= lowerBound) {
belowLowerBound = true;
}
if (belowLowerBound) {
return false;
}
boolean aboveUpperBound = false;
if (includeUpperBound && value > upperBound) {
aboveUpperBound = true;
} else if (!includeUpperBound && value >= upperBound) {
aboveUpperBound = true;
}
if (aboveUpperBound) {
return false;
}
return true;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if (!includesLowerBound() || Double.isInfinite(getLowerBound())) {
builder.append("(");
} else {
builder.append("[");
}
if (dateFormat != null) {
builder.append(getDateStringForValue(getLowerBound()));
builder.append(", ");
builder.append(getDateStringForValue(getUpperBound()));
} else {
double min = DataStructureUtils.roundToPowerOf10(getLowerBound(), lowerPrecision);
double max = DataStructureUtils.roundToPowerOf10(getUpperBound(), upperPrecision);
builder.append(DataStructureUtils.getRoundedString(min, lowerPrecision));
builder.append(", ");
builder.append(DataStructureUtils.getRoundedString(max, upperPrecision));
}
if (!includesUpperBound() || Double.isInfinite(getUpperBound())) {
builder.append(")");
} else {
builder.append("]");
}
return builder.toString();
}
private String getDateStringForValue(double value) {
if (Double.isNaN(value)) {
return I18N.getGUILabel("plotter.unknown_value_label");
} else if (Double.isInfinite(value)) {
return Double.toString(value);
} else {
return dateFormat.format(new Date((long) value));
}
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!(obj instanceof NumericalValueRange)) {
return false;
}
NumericalValueRange otherRange = (NumericalValueRange) obj;
if (otherRange.lowerBound != this.lowerBound) {
return false;
}
if (otherRange.upperBound != this.upperBound) {
return false;
}
if (otherRange.includeLowerBound != this.includeLowerBound) {
return false;
}
if (otherRange.includeUpperBound != this.includeUpperBound) {
return false;
}
return true;
}
@Override
public int hashCode() {
Double upperBoundDouble = this.upperBound;
Double lowerBoundDouble = this.lowerBound;
return upperBoundDouble.hashCode() + lowerBoundDouble.hashCode();
}
@Override
public double getValue() {
return (getUpperBound() + getLowerBound()) / 2.0;
}
public void setVisualPrecision(int lowerPrecision, int upperPrecision) {
this.lowerPrecision = lowerPrecision;
this.upperPrecision = upperPrecision;
}
@Override
public NumericalValueRange clone() {
return new NumericalValueRange(getLowerBound(), getUpperBound(), getColumnIdx(), includesLowerBound(),
includesUpperBound());
}
@Override
public boolean definesUpperLowerBound() {
return true;
}
public int getVisualUpperPrecision() {
return upperPrecision;
}
public int getVisualLowerPrecision() {
return lowerPrecision;
}
public void setVisualUpperPrecision(int upperPrecision) {
this.upperPrecision = upperPrecision;
}
public void setVisualLowerPrecision(int lowerPrecision) {
this.lowerPrecision = lowerPrecision;
}
public boolean isDate() {
return dateFormat != null;
}
public DateFormat getDateFormat() {
return this.dateFormat;
}
public void setDateFormat(DateFormat dateFormat) {
this.dateFormat = dateFormat;
}
}