package edu.umd.rhsmith.diads.meater.util;
/**
* Represents a range of integers, inclusive on either end. Allows this range to be converted to
* text and SQL. Capable of extracting ranges from strings such as <code>13-21</code> or
* <code>[8-44]</code>.
*
* @author dmonner
*/
public class NumberRange
{
/**
* The smallest number included in the range
*/
public final int min;
/**
* The largest number included in the range
*/
public final int max;
/**
* The original string source of the range, if any
*/
public final String from;
/**
* Creates a new number range based on two integer end points.
*
* @param min
* @param max
* @throws IllegalArgumentException
* If min > max
*/
public NumberRange(final int min, final int max)
{
if(min > max)
throw new IllegalArgumentException("Min cannot be greater than max.");
this.min = min;
this.max = max;
this.from = null;
}
/**
* Creates a new number range based on a string such as <code>13-21</code> or <code>[8-44]</code>.
*
* @param from
* @throws IllegalArgumentException
* If the string has mismatched brackets
* @throws NumberFormatException
* If any number in the string fails to parse
*/
public NumberRange(final String from)
{
String str = from.trim();
final boolean sw = str.startsWith("[");
final boolean ew = str.endsWith("]");
if(sw != ew)
throw new IllegalArgumentException("Mismatched []s: " + from);
// trim the string by removing brackets if they exist
if(sw && ew)
str = str.substring(1, str.length() - 1).trim();
// find the first hyphen
final int hyphen = str.indexOf('-');
if(hyphen >= 0)
{
// split the string on the hyphen
this.min = Integer.parseInt(str.substring(0, hyphen).trim());
this.max = Integer.parseInt(str.substring(hyphen + 1).trim());
}
else
{
// if no hyphen exists, this is a singular set
this.min = this.max = Integer.parseInt(str);
}
this.from = from;
}
/**
* Determines whether an arbitrary number falls inside the range defined by this object.
*
* @param num
* @return <code>true</code> iff num falls inside the defined range
*/
public boolean contains(final int num)
{
return min <= num && num <= max;
}
/**
* Determines whether the given range has a non-null intersection with this range.
*
* @param that
* @return <code>true</code> iff the two ranges have numbers in common
*/
public boolean overlaps(final NumberRange that)
{
return !(this.max < that.min || that.max < this.min);
}
/**
* Builds a string representing the range as a SQL <code>WHERE</code> condition on the given
* variable name.
*
* @param varname
* The variable name whose range is defined by this object
* @return SQL code to put in a <code>WHERE</code> condition that restricts the domain of
* <code>varname</code> to the values represented by this object
*/
public String toSQL(final String varname)
{
final StringBuilder sb = new StringBuilder();
if(min < max)
{
sb.append(min);
sb.append(" <= ");
sb.append(varname);
sb.append(" AND ");
sb.append(varname);
sb.append(" <= ");
sb.append(max);
}
else
{
sb.append(varname);
sb.append(" = ");
sb.append(min);
}
return sb.toString();
}
/**
* @return This range represented as an unbracketed string
*/
@Override
public String toString()
{
return toString(false);
}
/**
* @param bracketed
* Whether or not to add enclosing square brackets around the result string.
* @return This range represented as a string
*/
public String toString(final boolean bracketed)
{
final StringBuilder sb = new StringBuilder();
if(bracketed)
sb.append("[");
sb.append(min);
if(min < max)
{
sb.append(", ");
sb.append(max);
}
if(bracketed)
sb.append("]");
return sb.toString();
}
}