All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.arcusx.chrono.MonthSequence Maven / Gradle / Ivy

The newest version!
/**
 * This software is written by arcus(x) GmbH and subject 
 * to a contract between arcus(x) and its customer.
 *
 * This software stays property of arcus(x) unless differing
 * arrangements between arcus(x) and its customer apply.
 *
 * arcus(x) GmbH
 * Bergiusstrasse 27
 * D-22765 Hamburg, Germany
 * 
 * Tel.: +49 (0)40.333 102 92  
 * http://www.arcusx.com
 * mailto:[email protected]
 */

package com.arcusx.chrono;

import java.io.Serializable;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;

/**
 * Limited month based timespan.
 * 
 * Created 02.10.2003, 16:47:07.
 * 
 * @author conni
 * @version $Id$
 */
public class MonthSequence implements Serializable, Collection, Iterable
{
	private static final long serialVersionUID = 1L;

	private Month firstMonth;

	private int size;

	/**
	 * Get a months period.
	 * 
	 * @param firstMonth The first month in period. 
	 * @param lastMonth The last month in period, possibly null.
	 * @return A month period from firstMonth to lastMonth or an open period starting
	 * with firstMonth if lastMonth is null.
	 */
	public static MonthSequence valueOf(Month firstMonth, Month lastMonth)
	{
		if (lastMonth == null)
			return new OpenMonthSequence(firstMonth);

		return new MonthSequence(firstMonth, lastMonth);
	}

	/**
	 * A factory method to build this type via refelction
	 * from string.
	 * 
	 * @param s The string.
	 * @return The months.
	 */
	public static MonthSequence valueOf(String s)
	{
		if (s.startsWith("Months{") && s.endsWith("}"))
			s = s.substring("Months{".length(), s.length() - 1);

		return SimpleMonthSequenceFormat.INSTANCE.parse(s);
	}

	/**
	 * Create a new months period.
	 * 
	 * @param firstMonth First month in period.
	 * @param lastMonth Last month in period.
	 * @throws IllegalArgumentException if one is null or lastMonth is before firstMonth.
	 */
	public MonthSequence(Month firstMonth, Month lastMonth) throws IllegalArgumentException
	{
		if (firstMonth == null)
			throw new IllegalArgumentException("Start month may not be null.");

		if (lastMonth != null && lastMonth.before(firstMonth))
			throw new IllegalArgumentException("First month ("
					+ firstMonth
					+ ") must be before last month ("
					+ lastMonth
					+ ").");

		// FIXME broken with years <= 0
		if (firstMonth.getYearValue() <= 0)
			throw new UnsupportedOperationException("Calculating with years equal or less than zero is broken.");

		this.firstMonth = firstMonth;
		this.size = distanceBetween(firstMonth, lastMonth);
	}

	/**
	 * Create a new months period by start month and size.
	 * 
	 * @param firstMonth First month in period.
	 * @param size Count of months in period.
	 * @throws IllegalArgumentException if firstMonth is null or size < 0.
	 */
	public MonthSequence(Month firstMonth, int size) throws IllegalArgumentException
	{
		if (firstMonth == null)
			throw new IllegalArgumentException("Start month may not be null.");

		// FIXME broken with years <= 0
		if (firstMonth.getYearValue() <= 0)
			throw new UnsupportedOperationException("Calculating with years equal or less than zero is broken.");

		if (size < 0)
			throw new IllegalArgumentException("Size must be 0 or greater.");

		this.firstMonth = firstMonth;
		this.size = size;
	}

	/**
	 * Create a months instance withount last month.
	 * 
	 * For internal use only.
	 * 
	 * @param firstMonth 
	 */
	MonthSequence(Month firstMonth) throws IllegalArgumentException
	{
		if (firstMonth == null)
			throw new IllegalArgumentException("Start month may not be null.");

		// FIXME broken with years <= 0
		if (firstMonth.getYearValue() <= 0)
			throw new UnsupportedOperationException("Calculating with years equal or less than zero is broken.");

		this.firstMonth = firstMonth;
	}

	public boolean isOpen()
	{
		return false;
	}

	public Month getFirstMonth()
	{
		return this.firstMonth;
	}

	/**
	 * The last month or null if period has zero length or is open.
	 * 
	 * @return The last month or null.
	 */
	public Month getLastMonth()
	{
		return monthFor(this.firstMonth, this.size);
	}

	public boolean contains(Day day)
	{
		if (isEmpty())
			return false;

		return day.afterOrEqual(this.firstMonth.getFirstDay()) && day.beforeOrEqual(getLastMonth().getLastDay());
	}

	public boolean contains(Month month)
	{
		if (isEmpty())
			return false;

		return month.afterOrEqual(this.firstMonth) && month.beforeOrEqual(getLastMonth());
	}

	public boolean contains(MonthSequence months)
	{
		return !(months instanceof OpenMonthSequence)
				&& contains(months.getFirstMonth())
				&& contains(months.getLastMonth());
	}

	public MonthSequence limitBy(MonthSequence months)
	{
		return limitBy(months.getFirstMonth(), months.getLastMonth());
	}

	public Months toMonthSequence()
	{
		Months seq = new Months();
		seq.addMonths(this);

		return seq;
	}

	/**
	 * Limit the months period so it is between min and max.
	 * 
	 * @param min The minimum month, if null it means let period as is.
	 * @param max The maximum month the period may contain, if null it means let period as is.
	 * @return The new months period.
	 */
	public MonthSequence limitBy(Month min, Month max)
	{
		if (min == null && max == null)
			return this;

		if (min != null && max != null && !min.beforeOrEqual(max))
			throw new IllegalArgumentException("Min may not be after max.");

		if (min != null && min.after(getLastMonth()))
			return new MonthSequence(min, 0);

		if (max != null && max.before(this.firstMonth))
			return new MonthSequence(min, 0);

		// check if min is given and keep it if it is set after firstMonth
		Month newFirstMonth = null;
		if (min == null)
			newFirstMonth = this.firstMonth;
		else
			newFirstMonth = Month.maxOf(min, this.firstMonth);

		// check if max is given and keep it if it is set before lastMonth
		Month newLastMonth = null;
		if (max == null)
			newLastMonth = getLastMonth();
		else
			newLastMonth = max.before(getLastMonth()) ? max : getLastMonth();

		// create new months period if firstMonth and lastMonth changed
		if (this.firstMonth.equals(newFirstMonth) && getLastMonth().equals(newLastMonth))
			return this;

		return new MonthSequence(newFirstMonth, newLastMonth);
	}

	/**
	 * WARNING works only with a month sequence of the same kind (i.e. a closed sequence).  
	 * @param otherMonths
	 * @return true if month sequences overlap
	 */
	public boolean overlaps(MonthSequence otherMonths)
	{
		// sequences do not overlap, if this sequence starts after other ends
		if (otherMonths.getLastMonth().before(this.firstMonth))
			return false;

		// sequences do not overlap, if other sequence starts after this sequence end 
		if (otherMonths.firstMonth.after(getLastMonth()))
			return false;

		// otherwise sequences overlap
		return true;
	}

	public boolean equals(Object other)
	{
		if (other == null)
			return false;

		if (!MonthSequence.class.equals(other.getClass()))
			return false;

		MonthSequence otherMonths = (MonthSequence) other;

		if (size() == 0 && otherMonths.size() == 0)
			return this.firstMonth.equals(otherMonths.firstMonth);

		return this.firstMonth.equals(otherMonths.firstMonth) && this.size == otherMonths.size;
	}

	public int hashCode()
	{
		return this.firstMonth.hashCode() ^ size;
	}

	public String toString()
	{
		if ( this.size == 0)
			return "Months{}";
			
		return "Months{" + SimpleMonthSequenceFormat.INSTANCE.format(this) + "}";
	}

	//
	// from Collection
	//

	public Iterator iterator()
	{
		return new Iter(this.firstMonth, size);
	}

	public boolean add(Month o)
	{
		throw new UnsupportedOperationException("Immutable.");
	}

	public boolean addAll(Collection c)
	{
		throw new UnsupportedOperationException("Immutable.");
	}

	public void clear()
	{
		throw new UnsupportedOperationException("Immutable.");
	}

	public boolean contains(Object o)
	{
		if (o instanceof Month)
			return contains((Month) o);
		if (o instanceof MonthSequence)
			return contains((MonthSequence) o);

		return false;
	}

	public boolean containsAll(Collection c)
	{
		Iterator iter = c.iterator();
		while (iter.hasNext())
		{
			if (!contains(iter.next()))
				return false;
		}

		return true;
	}

	public boolean isEmpty()
	{
		return size() == 0;
	}

	public boolean remove(Object o)
	{
		throw new UnsupportedOperationException("Immutable.");
	}

	public boolean removeAll(Collection c)
	{
		throw new UnsupportedOperationException("Immutable.");
	}

	public boolean retainAll(Collection c)
	{
		throw new UnsupportedOperationException("Immutable.");
	}

	public int size()
	{
		return this.size;
	}

	public Object[] toArray()
	{
		return toArray(new Object[size()]);
	}

	public Object[] toArray(Object[] array)
	{
		Iterator iter = iterator();
		for (int i = 0; iter.hasNext(); ++i)
		{
			array[i] = iter.next();
		}

		return array;
	}

	private static Month monthFor(Month firstMonth, int count)
	{
		if (count == 0)
			return null;

		Calendar cal = new GregorianCalendar();
		cal.setTime(firstMonth.getFirstDay().toDate());
		cal.add(Calendar.MONTH, count - 1);

		return Month.valueOf(cal.getTime());
	}

	private static int distanceBetween(Month one, Month other)
	{
		if (one.equals(other))
			return 1;

		if (!one.before(other))
			throw new IllegalArgumentException("First month must be before or equal to last month.");

		Calendar cal = new GregorianCalendar();
		cal.setTime(one.getFirstDay().toDate());
		Date otherDate = other.getFirstDay().toDate();
		int count = 1;
		while (!cal.getTime().equals(otherDate))
		{
			cal.add(Calendar.MONTH, 1);
			count++;
		}

		return count;
	}

	private static final class Iter implements Iterator
	{

		private Calendar cal;

		private int count;

		private Iter(Month firstMonth, int count)
		{
			this.cal = firstMonth.getFirstDay().toCalendar();
			this.count = count;
		}

		public boolean hasNext()
		{
			return this.count > 0;
		}

		public Object next()
		{
			Month month = Month.valueOf(cal);
			cal.add(Calendar.MONTH, 1);
			count--;

			return month;
		}

		public void remove()
		{
			throw new UnsupportedOperationException("Cannot remove Month from Months.");
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy