/**
* Copyright 2010 Sven Diedrichsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package de.jollyday.parser;
import static java.time.temporal.TemporalAdjusters.nextOrSame;
import static java.time.temporal.TemporalAdjusters.previousOrSame;
import de.jollyday.config.*;
import de.jollyday.util.CalendarUtil;
import de.jollyday.util.XMLUtil;
import java.time.DayOfWeek;
import java.time.LocalDate;
/**
* The abstract base class for all HolidayParser implementations.
*
* @author Sven Diedrichsen
*/
public abstract class AbstractHolidayParser implements HolidayParser {
private static final String EVERY_YEAR = "EVERY_YEAR";
private static final String ODD_YEARS = "ODD_YEARS";
private static final String EVEN_YEARS = "EVEN_YEARS";
private static final String TWO_YEARS = "2_YEARS";
private static final String THREE_YEARS = "3_YEARS";
private static final String FOUR_YEARS = "4_YEARS";
private static final String FIVE_YEARS = "5_YEARS";
private static final String SIX_YEARS = "6_YEARS";
/**
* Calendar utility class.
*/
protected CalendarUtil calendarUtil = new CalendarUtil();
/**
* XML utility class.
*/
protected XMLUtil xmlUtil = new XMLUtil();
/**
* Evaluates if the provided <code>Holiday</code> instance is valid for the
* provided year.
*
* @param <T> a {@link Holiday} subclass
* @param h
* The holiday configuration entry to validate
* @param year
* The year to validate against.
* @return is valid for the year.
*/
protected <T extends Holiday> boolean isValid(T h, int year) {
return isValidInYear(h, year) && isValidForCycle(h, year);
}
/**
* Checks cyclic holidays and checks if the requested year is hit within the
* cycles.
*
* @param h Holiday to be valid in cycle
* @param year the year for the holiday to be valid in
* @return is valid
*/
private <T extends Holiday> boolean isValidForCycle(T h, int year) {
if (h.getEvery() != null) {
if (!EVERY_YEAR.equals(h.getEvery())) {
if (ODD_YEARS.equals(h.getEvery())) {
return year % 2 != 0;
} else if (EVEN_YEARS.equals(h.getEvery())) {
return year % 2 == 0;
} else {
if (h.getValidFrom() != null) {
int cycleYears;
if (TWO_YEARS.equalsIgnoreCase(h.getEvery())) {
cycleYears = 2;
} else if (THREE_YEARS.equalsIgnoreCase(h.getEvery())) {
cycleYears = 3;
} else if (FOUR_YEARS.equalsIgnoreCase(h.getEvery())) {
cycleYears = 4;
} else if (FIVE_YEARS.equalsIgnoreCase(h.getEvery())) {
cycleYears = 5;
} else if (SIX_YEARS.equalsIgnoreCase(h.getEvery())) {
cycleYears = 6;
} else {
throw new IllegalArgumentException("Cannot handle unknown cycle type '" + h.getEvery()
+ "'.");
}
return (year - h.getValidFrom()) % cycleYears == 0;
}
}
}
}
return true;
}
/**
* Checks whether the holiday is within the valid date range.
*
* @param h the holiday to check for validity
* @param year the year to check the holiday to be valid in
* @return the holiday is valid
*/
private <T extends Holiday> boolean isValidInYear(T h, int year) {
return (h.getValidFrom() == null || h.getValidFrom() <= year)
&& (h.getValidTo() == null || h.getValidTo() >= year);
}
/**
* Moves a date if there are any moving conditions for this holiday and any
* of them fit.
*
* @param fm
* a {@link de.jollyday.config.MoveableHoliday} object.
* @param fixed
* a {@link LocalDate} object.
* @return the moved date
*/
protected LocalDate moveDate(MoveableHoliday fm, LocalDate fixed) {
for (MovingCondition mc : fm.getMovingCondition()) {
if (shallBeMoved(fixed, mc)) {
fixed = moveDate(mc, fixed);
break;
}
}
return fixed;
}
/**
* Determines if the provided date shall be substituted.
*
* @param fixed
* a {@link LocalDate} object.
* @param mc
* a {@link de.jollyday.config.MovingCondition} object.
* @return a boolean.
*/
protected boolean shallBeMoved(LocalDate fixed, MovingCondition mc) {
return fixed.getDayOfWeek() == xmlUtil.getWeekday(mc.getSubstitute());
}
/**
* Moves the date using the FixedMoving information
*
* @param mc the moving condition
* @param fixed the date to move
* @return the eventually moved date
*/
private LocalDate moveDate(MovingCondition mc, LocalDate fixed) {
DayOfWeek weekday = xmlUtil.getWeekday(mc.getWeekday());
return fixed.with(mc.getWith() == With.NEXT ? nextOrSame(weekday) :
previousOrSame(weekday));
}
/**
* <p>
* getEasterSunday.
* </p>
*
* @param year
* a int.
* @param ct
* a {@link de.jollyday.config.ChronologyType} object.
* @return a {@link LocalDate} object.
*/
protected LocalDate getEasterSunday(int year, ChronologyType ct) {
LocalDate easterSunday;
if (ct == ChronologyType.JULIAN) {
easterSunday = calendarUtil.getJulianEasterSunday(year);
} else if (ct == ChronologyType.GREGORIAN) {
easterSunday = calendarUtil.getGregorianEasterSunday(year);
} else {
easterSunday = calendarUtil.getEasterSunday(year);
}
return easterSunday;
}
}