Pull up TimeTrackerStateUsingJodaTime to TimeTrackerState

Now all zoom levels are based on the same superclass

FEA: ItEr61S08TimeUnitConfigurablePlanning
This commit is contained in:
Óscar González Fernández 2010-09-29 23:39:19 +02:00
parent 0e8a61715f
commit 55e4c8a7c6
8 changed files with 260 additions and 463 deletions

View file

@ -30,7 +30,7 @@ import org.joda.time.ReadablePeriod;
* @author Óscar González Fernández <ogonzalez@igalia.com>
* @author Lorenzo Tilve Álvaro <ltilve@igalia.com>
*/
public class DetailFiveTimeTrackerState extends TimeTrackerStateUsingJodaTime {
public class DetailFiveTimeTrackerState extends TimeTrackerState {
private static final int NUMBER_OF_DAYS_MINIMUM = 50;
public static final int FIRST_LEVEL_SIZE = 210;

View file

@ -40,7 +40,7 @@ import org.zkoss.ganttz.util.Interval;
* @author Óscar González Fernández <ogonzalez@igalia.com>
* @author Lorenzo Tilve Álvaro <ltilve@igalia.com>
*/
public class DetailFourTimeTrackerState extends TimeTrackerStateUsingJodaTime {
public class DetailFourTimeTrackerState extends TimeTrackerState {
private static final int NUMBER_OF_WEEKS_MINIMUM = 40;

View file

@ -20,11 +20,12 @@
package org.zkoss.ganttz.timetracker.zoom;
import java.util.Collection;
import java.util.Vector;
import org.joda.time.DateTime;
import org.zkoss.ganttz.util.Interval;
import org.joda.time.LocalDate;
import org.joda.time.Months;
import org.joda.time.ReadablePeriod;
import org.joda.time.Years;
/**
* Zoom level with years in the first level and semesters in the second level
@ -33,6 +34,8 @@ import org.zkoss.ganttz.util.Interval;
*/
public class DetailOneTimeTrackerState extends TimeTrackerState {
public static final Period MINIMUN_PERIOD = PeriodType.YEARS.amount(4);
private static final int FIRST_LEVEL_ITEM_SIZE = 200;
private static final int SECOND_LEVEL_ITEM_SIZE = 100;
@ -45,74 +48,6 @@ public class DetailOneTimeTrackerState extends TimeTrackerState {
super(firstLevelModificator, secondLevelModificator);
}
private Collection<DetailItem> buildCollectionDetailsFirstLevel(
int initialYear, int endYear) {
Collection<DetailItem> detailsVector = new Vector<DetailItem>();
for (int i = initialYear; i <= endYear; i++) {
DetailItem d = new DetailItem(FIRST_LEVEL_ITEM_SIZE, String
.valueOf(i), new DateTime(i, 1, 1, 0, 0, 0, 0),
new DateTime(i, 12, 31, 0, 0, 0, 0));
detailsVector.add(d);
}
return detailsVector;
}
/**
* Creates secondary level DetailItems, adding currentDay tag to the
* proper interval (Bank holidays function call pending).
*/
private Collection<DetailItem> buildCollectionDetailsSecondLevel(
int initialYear, int endYear) {
Collection<DetailItem> detailsVector = new Vector<DetailItem>();
DateTime beginDate = new DateTime(initialYear, 1, 1, 0, 0, 0, 0);
DateTime endDate = new DateTime(initialYear, 7, 1, 0, 0, 0, 0);
for (int i = initialYear; i <= endYear; i++) {
DetailItem d1 = new DetailItem(SECOND_LEVEL_ITEM_SIZE, "H1",
beginDate, endDate);
DetailItem d2 = new DetailItem(SECOND_LEVEL_ITEM_SIZE, "H2",
endDate, endDate.plusMonths(6));
detailsVector.add(d1);
detailsVector.add(d2);
beginDate = beginDate.plusYears(1);
endDate = endDate.plusYears(1);
}
return detailsVector;
}
@Override
protected Collection<DetailItem> createDetailsForFirstLevel(
Interval interval) {
int[] pairYears = calculateInitialEndYear(interval.getStart(), interval
.getFinish());
return buildCollectionDetailsFirstLevel(pairYears[0], pairYears[1]);
}
@Override
protected Collection<DetailItem> createDetailsForSecondLevel(
Interval interval) {
int[] pairYears = calculateInitialEndYear(interval.getStart(), interval
.getFinish());
return buildCollectionDetailsSecondLevel(pairYears[0], pairYears[1]);
}
public Interval getRealIntervalFor(Interval interval) {
int[] pairYears = calculateInitialEndYear(interval.getStart(), interval
.getFinish());
return new Interval(year(pairYears[0]), year(pairYears[1] + 1));
}
@Override
protected ZoomLevel getZoomLevel() {
return ZoomLevel.DETAIL_ONE;
@ -123,4 +58,55 @@ public class DetailOneTimeTrackerState extends TimeTrackerState {
return SECOND_LEVEL_ITEM_SIZE;
}
@Override
protected IDetailItemCreator getDetailItemCreatorFirstLevel() {
return new IDetailItemCreator() {
@Override
public DetailItem create(DateTime start) {
int year = start.getYear();
DateTime end = new LocalDate(year + 1, 1, 1)
.toDateTimeAtStartOfDay();
return new DetailItem(FIRST_LEVEL_ITEM_SIZE, start.getYear()
+ "", start, end);
}
};
}
@Override
protected ReadablePeriod getPeriodFirstLevel() {
return Years.ONE;
}
@Override
protected IDetailItemCreator getDetailItemCreatorSecondLevel() {
return new IDetailItemCreator() {
@Override
public DetailItem create(DateTime dateTime) {
return new DetailItem(SECOND_LEVEL_ITEM_SIZE,
dateTime.getMonthOfYear() == 1 ? "H1" : "H2", dateTime,
dateTime.plusMonths(6));
}
};
}
@Override
protected ReadablePeriod getPeriodSecondLevel() {
return Months.SIX;
}
@Override
protected LocalDate round(LocalDate date, boolean down) {
return doYearRound(date, down);
}
public static LocalDate doYearRound(LocalDate date, boolean down) {
return new LocalDate(date.getYear() + (down?0:1), 1, 1);
}
@Override
protected Period getMinimumPeriod() {
return MINIMUN_PERIOD;
}
}

View file

@ -30,7 +30,7 @@ import org.joda.time.ReadablePeriod;
* @author Óscar González Fernández <ogonzalez@igalia.com>
* @author Lorenzo Tilve Álvaro <ltilve@igalia.com>
*/
public class DetailSixTimeTrackerState extends TimeTrackerStateUsingJodaTime {
public class DetailSixTimeTrackerState extends TimeTrackerState {
private static final int NUMBER_OF_DAYS_MINIMUM = 50;
public static final int FIRST_LEVEL_SIZE = 672;

View file

@ -31,7 +31,7 @@ import org.zkoss.util.Locales;
* @author Óscar González Fernández <ogonzalez@igalia.com>
* @author Lorenzo Tilve Álvaro <ltilve@igalia.com>
*/
public class DetailThreeTimeTrackerState extends TimeTrackerStateUsingJodaTime {
public class DetailThreeTimeTrackerState extends TimeTrackerState {
private static final int NUMBER_OF_MONTHS_MINIMUM = 20;

View file

@ -20,14 +20,11 @@
package org.zkoss.ganttz.timetracker.zoom;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Vector;
import org.joda.time.DateTime;
import org.zkoss.ganttz.util.Interval;
import org.joda.time.LocalDate;
import org.joda.time.Months;
import org.joda.time.ReadablePeriod;
import org.joda.time.Years;
/**
* Zoom level with years in the first level and quarters in the second level
@ -45,142 +42,55 @@ public class DetailTwoTimeTrackerState extends TimeTrackerState {
super(firstLevelModificator, secondLevelModificator);
}
public final double daysPerPixel() {
@Override
protected IDetailItemCreator getDetailItemCreatorFirstLevel() {
return new IDetailItemCreator() {
@Override
public DetailItem create(DateTime dateTime) {
return new DetailItem(FIRST_LEVEL_ITEM_SIZE, dateTime.getYear()
+ "", dateTime, dateTime);
}
};
}
@Override
protected ReadablePeriod getPeriodFirstLevel() {
return Years.ONE;
}
@Override
protected IDetailItemCreator getDetailItemCreatorSecondLevel() {
return new IDetailItemCreator() {
@Override
public DetailItem create(DateTime dateTime) {
int quarterNumber = dateTime.getMonthOfYear() / 3 + 1;
String quarterCaption = "Q" + quarterNumber;
return new DetailItem(SECOND_LEVEL_ITEM_SIZE, quarterCaption,
dateTime, dateTime.plusMonths(3));
}
};
}
@Override
protected ReadablePeriod getPeriodSecondLevel() {
return Months.THREE;
}
@Override
protected LocalDate round(LocalDate date, boolean down) {
return DetailOneTimeTrackerState.doYearRound(date, down);
}
@Override
protected Period getMinimumPeriod() {
return DetailOneTimeTrackerState.MINIMUN_PERIOD;
}
@Override
public double daysPerPixel() {
return ((double) 365 / FIRST_LEVEL_ITEM_SIZE);
}
public Interval getRealIntervalFor(Interval interval) {
int[] pairYears = calculateInitialEndYear(interval.getStart(), interval
.getFinish());
int startQuarter = calculateInQuarterPeriodDateInYear(interval
.getStart(), pairYears[0]);
int endQuarter = calculateInQuarterPeriodDateInYear(interval
.getFinish(), pairYears[1]);
return new Interval(quarterAt(startQuarter - 1, year(pairYears[0])),
quarterAt(endQuarter, year(pairYears[1])));
}
private static Date quarterAt(int quarter, Date date) {
int year = from(date).get(Calendar.YEAR);
Calendar calendar = from(year(year));
calendar.add(Calendar.MONTH, 3 * quarter);
return calendar.getTime();
}
static Calendar from(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
return calendar;
}
private Collection<DetailItem> buildCollectionDetailsFirstLevel(
Date initialDate, Date endDate, int initialYear, int endYear) {
Collection<DetailItem> detailsVector = new Vector<DetailItem>();
// Calculate the size of the first detail of the first level
int quarter = calculateInQuarterPeriodDateInYear(initialDate,
initialYear);
detailsVector.add(new DetailItem((4 - (quarter - 1))
* FIRST_LEVEL_ITEM_SIZE / 4, String.valueOf(initialYear),
new DateTime(initialYear, 1, 1, 0, 0, 0, 0), new DateTime(
initialYear, 1, 1, 0, 0, 0, 0)));
for (int i = (initialYear + 1); i < endYear; i++) {
DetailItem d = new DetailItem(FIRST_LEVEL_ITEM_SIZE, String
.valueOf(i), new DateTime(i, 1, 1, 0, 0, 0, 0),
new DateTime(i, 12, 31, 0, 0, 0, 0));
detailsVector.add(d);
}
// Calculate the size of the last detail of the first level
int endQuarter = calculateInQuarterPeriodDateInYear(endDate, endYear);
detailsVector
.add(new DetailItem(endQuarter * FIRST_LEVEL_ITEM_SIZE / 4,
String.valueOf(endYear)));
return detailsVector;
}
private Collection<DetailItem> buildCollectionDetailsSecondLevel(
Date initialDate, Date endDate, int initialYear, int endYear) {
ArrayList<DetailItem> result = new ArrayList<DetailItem>();
DateTime tempDate = new DateTime(initialDate);
DateTime beginInterval = new DateTime(tempDate.year().get(), tempDate
.monthOfYear().get(), 1, 0, 0, 0, 0);
DateTime endInterval = beginInterval.plusMonths(3);
int startDateQuarter = calculateInQuarterPeriodDateInYear(initialDate,
initialYear);
int quarterEndDate = calculateInQuarterPeriodDateInYear(endDate,endYear);
for (int j = initialYear; j <= endYear; j++) {
final int initialQuarter = (j == initialYear) ? startDateQuarter - 1
: 0;
final int endQuarter = (j == endYear) ? quarterEndDate : 4;
for (int i = initialQuarter; i < endQuarter; i++) {
DetailItem quarter = new DetailItem(SECOND_LEVEL_ITEM_SIZE, "Q"
+ (i + 1),
beginInterval, endInterval);
result.add(quarter);
beginInterval = beginInterval.plusMonths(3);
endInterval = endInterval.plusMonths(3);
}
}
return result;
}
/**
* @param date
* @param year
* @return a number from 1(quarter until to 1st April) to 4(quarter until
* 1st January of the next year) showing the quarter in which the
* date is for the year
*/
private int calculateInQuarterPeriodDateInYear(Date date, int year) {
Date[] quarters = createQuartersForYear(year);
for (int i = 0; i < quarters.length; i++) {
if (date.before(quarters[i])) {
return i + 1;
}
}
throw new IllegalArgumentException("date " + date + " is not in year "
+ year);
}
private static Date[] createQuartersForYear(int year) {
Date yearDate = year(year);
Date[] result = new Date[4];
for (int i = 0; i < result.length; i++) {
result[i] = quarterAt(i + 1, yearDate);
}
return result;
}
@Override
protected Collection<DetailItem> createDetailsForFirstLevel(
Interval interval) {
int[] pairYears = calculateInitialEndYear(interval.getStart(), interval
.getFinish());
return buildCollectionDetailsFirstLevel(interval.getStart(), interval
.getFinish(), pairYears[0], pairYears[1]);
}
@Override
protected Collection<DetailItem> createDetailsForSecondLevel(
Interval interval) {
int[] pairYears = calculateInitialEndYear(interval.getStart(), interval
.getFinish());
return buildCollectionDetailsSecondLevel(interval.getStart(), interval
.getFinish(), pairYears[0], pairYears[1]);
}
@Override
protected ZoomLevel getZoomLevel() {
return ZoomLevel.DETAIL_TWO;

View file

@ -24,9 +24,16 @@ import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import org.joda.time.DateTime;
import org.joda.time.Days;
import org.joda.time.LocalDate;
import org.joda.time.Months;
import org.joda.time.ReadablePeriod;
import org.joda.time.Weeks;
import org.joda.time.Years;
import org.joda.time.base.BaseSingleFieldPeriod;
import org.zkoss.ganttz.util.Interval;
/**
@ -35,6 +42,13 @@ import org.zkoss.ganttz.util.Interval;
*/
public abstract class TimeTrackerState {
public static Date year(int year) {
Calendar calendar = Calendar.getInstance();
calendar.clear();
calendar.set(Calendar.YEAR, year);
return calendar.getTime();
}
protected static final long MILLSECONDS_IN_DAY = 1000 * 60 * 60 * 24;
// Pending to calculate interval dinamically
@ -65,11 +79,13 @@ public abstract class TimeTrackerState {
return result;
}
protected abstract Collection<DetailItem> createDetailsForFirstLevel(
Interval interval);
protected static LocalDate asLocalDate(Date date) {
return new LocalDate(date);
}
protected abstract Collection<DetailItem> createDetailsForSecondLevel(
Interval interval);
public interface IDetailItemCreator {
DetailItem create(DateTime dateTime);
}
public Collection<DetailItem> getSecondLevelDetails(Interval interval) {
if (getZoomLevel() == ZoomLevel.DETAIL_FIVE) {
@ -99,49 +115,147 @@ public abstract class TimeTrackerState {
return result;
}
protected static int[] calculateInitialEndYear(Date initialDate,
Date endDate) {
public Collection<DetailItem> createDetails(Interval interval,
ReadablePeriod period, IDetailItemCreator detailItemCreator) {
DateTime current = asLocalDate(interval.getStart())
.toDateTimeAtStartOfDay();
DateTime end = asLocalDate(interval.getFinish())
.toDateTimeAtStartOfDay();
List<DetailItem> result = new ArrayList<DetailItem>();
while (current.isBefore(end)) {
result.add(detailItemCreator.create(current));
current = current.plus(period);
}
return result;
}
int[] pairYears = new int[2];
private final Collection<DetailItem> createDetailsForFirstLevel(
Interval interval) {
return createDetails(getRealIntervalFor(interval),
getPeriodFirstLevel(), getDetailItemCreatorFirstLevel());
}
long yearsInBetween = calculateYearsBetween(initialDate, endDate);
Calendar cal = new GregorianCalendar();
cal.setTime(initialDate);
int initialYear = cal.get(Calendar.YEAR);
int endYear;
private final Collection<DetailItem> createDetailsForSecondLevel(
Interval interval) {
return createDetails(getRealIntervalFor(interval),
getPeriodSecondLevel(), getDetailItemCreatorSecondLevel());
}
if (yearsInBetween >= NUMBER_OF_ITEMS_MINIMUM) {
cal.setTime(endDate);
endYear = cal.get(Calendar.YEAR);
} else {
endYear = initialYear + NUMBER_OF_ITEMS_MINIMUM;
protected abstract IDetailItemCreator getDetailItemCreatorFirstLevel();
protected abstract ReadablePeriod getPeriodFirstLevel();
protected abstract IDetailItemCreator getDetailItemCreatorSecondLevel();
protected abstract ReadablePeriod getPeriodSecondLevel();
protected abstract LocalDate round(LocalDate date, boolean down);
public enum PeriodType {
YEARS {
@Override
public ReadablePeriod toPeriod(int amount) {
return Years.years(amount);
}
@Override
public Years differenceBetween(LocalDate start, LocalDate end) {
return Years.yearsBetween(start, end);
}
},
MONTHS {
@Override
public ReadablePeriod toPeriod(int amount) {
return Months.months(amount);
}
@Override
public Months differenceBetween(LocalDate start, LocalDate end) {
return Months.monthsBetween(start, end);
}
},
WEEKS {
@Override
public ReadablePeriod toPeriod(int amount) {
return Weeks.weeks(amount);
}
@Override
public Weeks differenceBetween(LocalDate start, LocalDate end) {
return Weeks.weeksBetween(start, end);
}
},
DAYS {
@Override
public ReadablePeriod toPeriod(int amount) {
return Days.days(amount);
}
@Override
public Days differenceBetween(LocalDate start, LocalDate end) {
return Days.daysBetween(start, end);
}
};
public abstract ReadablePeriod toPeriod(int amount);
public abstract BaseSingleFieldPeriod differenceBetween(
LocalDate start, LocalDate end);
public Period amount(int amount) {
return new Period(this, amount);
}
pairYears[0] = initialYear;
pairYears[1] = endYear;
return pairYears;
}
protected static long calculateYearsBetween(Date initialDate, Date endDate) {
static class Period {
long milsecondsDiff = endDate.getTime() - initialDate.getTime();
private final PeriodType type;
// To chech later: If you put MILLSECONDS_IN_YEAR the
// division is made wrongly.
private final int amount;
long days = milsecondsDiff / MILLSECONDS_IN_DAY;
return (days / 365);
private Period(PeriodType type, int amount) {
this.type = type;
this.amount = amount;
}
ReadablePeriod toPeriod() {
return this.type.toPeriod(amount);
}
BaseSingleFieldPeriod asPeriod(Interval interval) {
LocalDate start = LocalDate.fromDateFields(interval.getStart());
LocalDate end = LocalDate.fromDateFields(interval.getFinish());
return type.differenceBetween(start, end);
}
}
public static Date year(int year) {
Calendar calendar = Calendar.getInstance();
calendar.clear();
calendar.set(Calendar.YEAR, year);
return calendar.getTime();
protected abstract Period getMinimumPeriod();
private Interval calculateIntervalWithMinimum(Interval interval) {
Period minimumPeriod = getMinimumPeriod();
BaseSingleFieldPeriod intervalAsPeriod = minimumPeriod
.asPeriod(interval);
if (intervalAsPeriod.compareTo(minimumPeriod.toPeriod()) >= 0) {
return interval;
}
LocalDate newEnd = new LocalDate(interval.getStart())
.plus(minimumPeriod.toPeriod());
return new Interval(interval.getStart(), newEnd
.toDateTimeAtStartOfDay().toDate());
}
public abstract Interval getRealIntervalFor(Interval testInterval);
public Interval getRealIntervalFor(Interval testInterval) {
return calculateForAtLeastMinimum(calculateIntervalWithMinimum(testInterval));
}
private Interval calculateForAtLeastMinimum(Interval atLeastMinimum) {
LocalDate start = round(asLocalDate(atLeastMinimum.getStart()), true);
LocalDate finish = round(asLocalDate(atLeastMinimum.getFinish()), false);
Interval result = new Interval(start.toDateTimeAtStartOfDay().toDate(),
finish.toDateTimeAtStartOfDay().toDate());
return result;
}
public abstract double daysPerPixel();

View file

@ -1,213 +0,0 @@
/*
* This file is part of NavalPlan
*
* Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e
* Desenvolvemento Tecnolóxico de Galicia
*
* 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.timetracker.zoom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import org.joda.time.DateTime;
import org.joda.time.Days;
import org.joda.time.LocalDate;
import org.joda.time.Months;
import org.joda.time.ReadablePeriod;
import org.joda.time.Weeks;
import org.joda.time.Years;
import org.joda.time.base.BaseSingleFieldPeriod;
import org.zkoss.ganttz.util.Interval;
/**
* @author Óscar González Fernández <ogonzalez@igalia.com>
*/
public abstract class TimeTrackerStateUsingJodaTime extends TimeTrackerState {
TimeTrackerStateUsingJodaTime(
IDetailItemModificator firstLevelModificator,
IDetailItemModificator secondLevelModificator) {
super(firstLevelModificator, secondLevelModificator);
}
protected static LocalDate asLocalDate(Date date) {
return new LocalDate(date);
}
public interface IDetailItemCreator {
DetailItem create(DateTime dateTime);
}
public Collection<DetailItem> createDetails(Interval interval,
ReadablePeriod period, IDetailItemCreator detailItemCreator) {
DateTime current = asLocalDate(interval.getStart())
.toDateTimeAtStartOfDay();
DateTime end = asLocalDate(interval.getFinish())
.toDateTimeAtStartOfDay();
List<DetailItem> result = new ArrayList<DetailItem>();
while (current.isBefore(end)) {
result.add(detailItemCreator.create(current));
current = current.plus(period);
}
return result;
}
@Override
protected Collection<DetailItem> createDetailsForFirstLevel(
Interval interval) {
return createDetails(getRealIntervalFor(interval),
getPeriodFirstLevel(), getDetailItemCreatorFirstLevel());
}
@Override
protected Collection<DetailItem> createDetailsForSecondLevel(
Interval interval) {
return createDetails(getRealIntervalFor(interval),
getPeriodSecondLevel(), getDetailItemCreatorSecondLevel());
}
protected abstract IDetailItemCreator getDetailItemCreatorFirstLevel();
protected abstract ReadablePeriod getPeriodFirstLevel();
protected abstract IDetailItemCreator getDetailItemCreatorSecondLevel();
protected abstract ReadablePeriod getPeriodSecondLevel();
protected abstract LocalDate round(LocalDate date, boolean down);
public enum PeriodType {
YEARS {
@Override
public ReadablePeriod toPeriod(int amount) {
return Years.years(amount);
}
@Override
public Years differenceBetween(LocalDate start,
LocalDate end) {
return Years.yearsBetween(start, end);
}
},
MONTHS {
@Override
public ReadablePeriod toPeriod(int amount) {
return Months.months(amount);
}
@Override
public Months differenceBetween(LocalDate start,
LocalDate end) {
return Months.monthsBetween(start, end);
}
},
WEEKS {
@Override
public ReadablePeriod toPeriod(int amount) {
return Weeks.weeks(amount);
}
@Override
public Weeks differenceBetween(LocalDate start,
LocalDate end) {
return Weeks.weeksBetween(start, end);
}
},
DAYS {
@Override
public ReadablePeriod toPeriod(int amount) {
return Days.days(amount);
}
@Override
public Days differenceBetween(LocalDate start,
LocalDate end) {
return Days.daysBetween(start, end);
}
};
public abstract ReadablePeriod toPeriod(int amount);
public abstract BaseSingleFieldPeriod differenceBetween(LocalDate start,
LocalDate end);
public Period amount(int amount) {
return new Period(this, amount);
}
}
static class Period {
private final PeriodType type;
private final int amount;
private Period(PeriodType type, int amount) {
this.type = type;
this.amount = amount;
}
ReadablePeriod toPeriod() {
return this.type.toPeriod(amount);
}
BaseSingleFieldPeriod asPeriod(Interval interval) {
LocalDate start = LocalDate.fromDateFields(interval.getStart());
LocalDate end = LocalDate.fromDateFields(interval.getFinish());
return type.differenceBetween(start, end);
}
}
protected abstract Period getMinimumPeriod();
private Interval calculateIntervalWithMinimum(Interval interval) {
Period minimumPeriod = getMinimumPeriod();
BaseSingleFieldPeriod intervalAsPeriod = minimumPeriod
.asPeriod(interval);
if (intervalAsPeriod
.compareTo(minimumPeriod.toPeriod()) >= 0) {
return interval;
}
LocalDate newEnd = new LocalDate(interval.getStart())
.plus(minimumPeriod.toPeriod());
return new Interval(interval.getStart(), newEnd
.toDateTimeAtStartOfDay().toDate());
}
private Days asPeriod(Interval interval) {
DateTime start = new DateTime(interval.getStart());
DateTime finish = new DateTime(interval.getFinish());
return Days.daysBetween(start, finish);
}
@Override
public Interval getRealIntervalFor(Interval testInterval) {
return calculateForAtLeastMinimum(calculateIntervalWithMinimum(testInterval));
}
private Interval calculateForAtLeastMinimum(Interval atLeastMinimum) {
LocalDate start = round(asLocalDate(atLeastMinimum.getStart()), true);
LocalDate finish = round(asLocalDate(atLeastMinimum.getFinish()), false);
Interval result = new Interval(start.toDateTimeAtStartOfDay().toDate(),
finish
.toDateTimeAtStartOfDay().toDate());
return result;
}
}