content: package tim.prune.function.filesleuth.data;
  
  public class DateRange
  {
  	private final Date _dateFrom, _dateTo;
  
  	public static final DateRange EMPTY_RANGE = new DateRange(null, null);
  	public static final DateRange INVALID_RANGE = new DateRange(null, new Date(-1, -1, -1));
  
  
  	/** Constructor */
  	public DateRange(Date inFrom, Date inTo)
  	{
  		boolean inOrder = inFrom == null || inTo == null || inTo.isAfter(inFrom);
  		_dateFrom = inOrder ? inFrom : inTo;
  		_dateTo = inOrder ? inTo : inFrom;
  	}
  
  	/** @return a DateRange for a single day */
  	public static DateRange parseValues(int inYear, int inMonth, int inDay) {
  		return parseValues(inYear, inMonth, inDay, inYear, inMonth, inDay);
  	}
  
  	public static DateRange parseValues(int inYearFrom, int inMonthFrom, int inDayFrom,
  		int inYearTo, int inMonthTo, int inDayTo)
  	{
  		if (inYearFrom < 1000 || inYearFrom > 9999 || inYearTo < 1000 || inYearTo > 9999) {
  			return null;
  		}
  		// Whole year(s)
  		if (inMonthFrom <= 0 && inDayFrom <= 0 && inMonthTo <= 0 && inDayTo <= 0) {
  			return new DateRange(new Date(inYearFrom, 1, 1), new Date(inYearTo, 12, 31));
  		}
  		// Range of months
  		if (inMonthFrom > 0 && inDayFrom <= 0 && inMonthTo > 0 && inDayTo <= 0)
  		{
  			int daysInMonth = Date.getDaysInMonth(inYearTo, inMonthTo);
  			return new DateRange(new Date(inYearFrom, inMonthFrom, 1),
  					new Date(inYearTo, inMonthTo, daysInMonth));
  		}
  		// Range of days
  		if (inMonthFrom > 0 && inDayFrom > 0 && inMonthTo > 0 && inDayTo > 0)
  		{
  			return new DateRange(new Date(inYearFrom, inMonthFrom, inDayFrom),
  					new Date(inYearTo, inMonthTo, inDayTo));
  		}
  		return null;
  	}
  
  	public static DateRange parseString(String inValue)
  	{
  		final String value = (inValue == null ? "" : inValue.trim());
  		if (value.isEmpty()) {
  			return EMPTY_RANGE;
  		}
  		if (value.length() < 4) {
  			return INVALID_RANGE;
  		}
  		if (value.length() == 4)
  		{
  			// Single year
  			try {
  				final int year = Integer.parseInt(value);
  				if (year >= 1000) {
  					return new DateRange(new Date(year, 1, 1), new Date(year, 12, 31));
  				}
  			}
  			catch (NumberFormatException e) {}
  		}
  		else if (value.length() == 7 && Date.isSeparator(value.charAt(4)))
  		{
  			// single month
  			try {
  				final int year = Integer.parseInt(value.substring(0, 4));
  				final int month = Integer.parseInt(value.substring(5));
  				if (year >= 1000 && month >= 1 && month <= 12)
  				{
  					return new DateRange(new Date(year, month, 1),
  							new Date(year, month, Date.getDaysInMonth(year, month)));
  				}
  			}
  			catch (NumberFormatException e) {}
  		}
  		else if (value.length() == 10 && Date.isSeparator(value.charAt(4)) && Date.isSeparator(value.charAt(7)))
  		{
  			// single date
  			Date date = Date.parseString(value);
  			if (date != null) {
  				return new DateRange(date, date);
  			}
  		}
  		else if (value.length() >= 21) {
  			return parseDatePair(value);
  		}
  		return INVALID_RANGE;
  	}
  
  	public boolean isEmpty() {
  		return _dateFrom == null;
  	}
  
  	public boolean isValid()
  	{
  		return this != INVALID_RANGE
  			&& _dateFrom != null && _dateFrom.isValid()
  			&& _dateTo != null && _dateTo.isValid();
  	}
  
  	public Date getDateFrom() {
  		return _dateFrom;
  	}
  
  	public Date getDateTo() {
  		return _dateTo;
  	}
  
  	public boolean isYearMonthDay() {
  		return toShortString().length() <= 10;
  	}
  
  	private static DateRange parseDatePair(String value)
  	{
  		Date firstDate = Date.parseString(value.substring(0, 10));
  		if (firstDate == null || !firstDate.isValid()) {
  			return INVALID_RANGE;
  		}
  		int startOfSecondDate = value.length() - 10;
  		for (int pos = 11; pos < startOfSecondDate; pos++)
  		{
  			if (!Date.isSeparator(value.charAt(pos))) {
  				return INVALID_RANGE;
  			}
  		}
  		Date secondDate = Date.parseString(value.substring(startOfSecondDate));
  		if (secondDate == null || !secondDate.isValid()) {
  			return INVALID_RANGE;
  		}
  		if (firstDate.isAfter(secondDate)) {
  			return new DateRange(secondDate, firstDate); // reverse order
  		}
  		return new DateRange(firstDate, secondDate);
  	}
  
  	public String toString()
  	{
  		if (isEmpty()) {
  			return "";
  		}
  		if (_dateFrom.equals(_dateTo)) {
  			// single date
  			return _dateFrom.toString();
  		}
  		else {
  			// different start and end date
  			return String.format("%s - %s", _dateFrom.toString(), _dateTo.toString());
  		}
  	}
  
  	public String toShortString()
  	{
  		if (isEmpty()) {
  			return "";
  		}
  		final int year = _dateFrom.getYear();
  		if (_dateFrom.getYear() == _dateTo.getYear())
  		{
  			// is it a single year?
  			if (_dateFrom.getMonth() == 1 && _dateTo.getMonth() == 12
  				&& _dateFrom.getDay() == 1 && _dateTo.getDay() == 31)
  			{
  				return "" + year;
  			}
  			// is it a single month?
  			final int month = _dateFrom.getMonth();
  			if (month >= 1 && month == _dateTo.getMonth()
  				&& _dateFrom.getDay() == 1 && _dateTo.getDay() == Date.getDaysInMonth(year, month))
  			{
  				return String.format("%d-%02d", year, month);
  			}
  			// single day?
  			if (_dateFrom.equals(_dateTo)) {
  				return _dateFrom.toString();
  			}
  		}
  		return toString();
  	}
  
  	public boolean contains(Date date)
  	{
  		if (isEmpty()) {
  			return true; // no filter or invalid filter
  		}
  		if (date == null) {
  			return false; // filter but no date
  		}
  		if (_dateFrom.isAfter(date)) {
  			return false; // date is before filter
  		}
  		return _dateTo == null || !date.isAfter(_dateTo);
  	}
  
  	public boolean overlaps(DateRange inTrackRange)
  	{
  		if (isEmpty()) {
  			return true;	// no filter
  		}
  		if (inTrackRange == null || inTrackRange.isEmpty()) {
  			return false;	// no dates in track
  		}
  		if (_dateFrom.isAfter(inTrackRange._dateTo) || inTrackRange._dateFrom.isAfter(_dateTo)) {
  			return false;	// no overlap
  		}
  		return true;
  	}
  
  	/** Comparison with another DateRange */
  	public boolean equals(DateRange inOtherRange)
  	{
  		if (isEmpty()) {
  			return inOtherRange == null || inOtherRange.isEmpty();
  		}
  		return _dateFrom.equals(inOtherRange._dateFrom) && _dateTo.equals(inOtherRange._dateTo);
  	}
  
  	/** Comparison with an Object */
  	public boolean equals(Object inOther)
  	{
  		DateRange range = (inOther instanceof DateRange ? (DateRange) inOther : null);
  		return equals(range);
  	}
  
  	public boolean includes(DateRange inTrackRange)
  	{
  		if (isEmpty()) {
  			return inTrackRange == null || inTrackRange.isEmpty();
  		}
  		if (inTrackRange == null || inTrackRange.isEmpty()) {
  			return true;
  		}
  		return !_dateFrom.isAfter(inTrackRange._dateFrom) && !inTrackRange._dateTo.isAfter(_dateTo);
  	}
  }
  