/*
* This file is part of LibrePlan
*
* Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e
* Desenvolvemento Tecnolóxico de Galicia
* Copyright (C) 2010-2011 Igalia, S.L.
*
* 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 org.zkoss.ganttz.data;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import org.apache.commons.lang3.Validate;
import org.joda.time.LocalDate;
import org.zkoss.ganttz.IDatesMapper;
/**
* <p>
* It's a special purpose date type for ganttzk library. Since ganttzk could be
* used by several implementations it must have freedom to allow different
* underlying implementations.
* </p>
* <p>
* There are two base cases:
* <ul>
* <li>the {@link LocalDateBased} one that is basically a LocalDate date. It's
* normally used to receive values from interface. For example when moving a
* Task a {@link LocalDateBased} date is generated with the position the task
* has been moved.</li>
* <li>the {@link CustomDate}. This is one intended to be overridden by the user
* of ganttzk.
* </ul>
* These base cases are used as a basis for a form of degenerate pattern
* matching in {@link #byCases(ICases)}, resembling the visitor pattern. This
* scheme allows ganttzk to work with both kinds of dates and perform different
* operations depending on the type.
* </p>
* <p>
* Please note that ganttzk is intended to work with only one type of
* {@link CustomDate} at the same time. It's a bad idea to mix several types of
* CustomDate.
* </p>
*
* @author Óscar González Fernández
*/
public abstract class GanttDate implements Comparable<GanttDate> {
public static LocalDateBased createFrom(Date date) {
if ( date == null ) {
return null;
}
return createFrom(LocalDate.fromDateFields(date));
}
public static LocalDateBased createFrom(LocalDate localDate) {
if ( localDate == null ) {
return null;
}
return new LocalDateBased(localDate);
}
public static GanttDate min(GanttDate... dates) {
return Collections.min(Arrays.asList(dates));
}
public static GanttDate max(GanttDate... dates) {
return Collections.max(Arrays.asList(dates));
}
@Override
public final boolean equals(Object obj) {
if ( obj == this ) {
return true;
}
if ( obj instanceof GanttDate ) {
GanttDate other = (GanttDate) obj;
return isEqualsTo(other);
}
return false;
}
protected abstract boolean isEqualsTo(GanttDate other);
@Override
public abstract int hashCode();
public interface ICases<R> {
R on(LocalDateBased localDateBased);
R on(CustomDate customType);
}
public static abstract class Cases<T extends CustomDate, R> implements ICases<R> {
private final Class<T> klass;
public Cases(Class<T> klass) {
Validate.notNull(klass);
this.klass = klass;
}
@Override
public R on(CustomDate customType) {
return onCustom(klass.cast(customType));
}
protected abstract R onCustom(T customType);
}
public abstract <R> R byCases(ICases<R> cases);
/**
* Converts this {@link GanttDate} to a date that is the start of the day
* represented by this {@link GanttDate}
*/
public abstract Date toDayRoundedDate();
public abstract LocalDate toLocalDate();
public abstract LocalDate asExclusiveEnd();
public abstract int toPixels(IDatesMapper datesMapper);
public static class LocalDateBased extends GanttDate {
private final LocalDate localDate;
public LocalDateBased(LocalDate localDate) {
Validate.notNull(localDate);
this.localDate = localDate;
}
public LocalDate getLocalDate() {
return localDate;
}
public <R> R byCases(ICases<R> cases) {
return cases.on(this);
}
@Override
public int compareTo(GanttDate o) {
return o.byCases(new ICases<Integer>() {
@Override
public Integer on(LocalDateBased localDateBased) {
return localDate.compareTo(localDateBased.localDate);
}
@Override
public Integer on(CustomDate customType) {
return -customType.compareToLocalDate(localDate);
}
});
}
@Override
public Date toDayRoundedDate() {
return localDate.toDateTimeAtStartOfDay().toDate();
}
@Override
public LocalDate toLocalDate() {
return localDate;
}
@Override
public LocalDate asExclusiveEnd() {
return localDate;
}
@Override
public boolean isEqualsTo(GanttDate other) {
return other.byCases(new ICases<Boolean>() {
@Override
public Boolean on(LocalDateBased localDateBased) {
return localDate.equals(localDateBased.localDate);
}
@Override
public Boolean on(CustomDate customType) {
return false;
}
});
}
@Override
public int hashCode() {
return localDate.hashCode();
}
@Override
public int toPixels(IDatesMapper datesMapper) {
return datesMapper.toPixels(localDate);
}
}
public static abstract class CustomDate extends GanttDate {
public CustomDate() {
}
protected abstract boolean isEqualsToCustom(CustomDate customType);
@Override
protected boolean isEqualsTo(GanttDate other) {
return other.byCases(new ICases<Boolean>() {
@Override
public Boolean on(LocalDateBased localDateBased) {
return false;
}
@Override
public Boolean on(CustomDate customType) {
return isEqualsToCustom(customType);
}
});
}
@Override
public <R> R byCases(ICases<R> cases) {
return cases.on(this);
}
@Override
public int compareTo(GanttDate o) {
return o.byCases(new ICases<Integer>() {
@Override
public Integer on(LocalDateBased localDateBased) {
return compareToLocalDate(localDateBased.localDate);
}
@Override
public Integer on(CustomDate customType) {
return compareToCustom(customType);
}
});
}
protected abstract int compareToCustom(CustomDate customType);
protected abstract int compareToLocalDate(LocalDate localDate);
}
public boolean after(GanttDate other) {
return compareTo(other) > 0;
}
}