Java tutorial
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package de.austinpadernale.holidays; import de.austinpadernale.core.exceptions.InvalidArgumentException; import org.apache.commons.lang3.StringUtils; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlType; import java.io.Serializable; import java.util.ArrayList; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.List; import java.util.Locale; import java.util.Objects; @XmlType(propOrder = { "holidayType", "day", "month", "offset", "week", "dayOfWeek", "locations", "names", "shifts", "validities" }) public final class Holiday implements Serializable { private List<HolidayLocation> locations; private List<HolidayName> names; private List<HolidayShift> shifts; private List<HolidayValidity> validities; private HolidayTypes holidayType; private Integer day; private Integer month; private Integer offset; private Integer week; private Integer dayOfWeek; @XmlElementWrapper(name = "locations") @XmlElement(name = "location") public List<HolidayLocation> getLocations() { return locations; } public void setLocations(List<HolidayLocation> locations) { this.locations = locations; } public void addLocation(String country, String... regions) { if (this.locations == null) { this.locations = new ArrayList<>(); } HolidayLocation hl = new HolidayLocation(); hl.setCountry(country); hl.addRegion(regions); if (!this.locations.contains(hl)) { this.locations.add(hl); } } @XmlElementWrapper(name = "names") @XmlElement(name = "name") public List<HolidayName> getNames() { return names; } public void setNames(List<HolidayName> names) { this.names = names; } public void addName(String name, Locale locale) { if (names == null) { names = new ArrayList<>(); } HolidayName hn = new HolidayName(name, locale); if (!names.contains(hn)) { names.add(hn); } } @XmlElementWrapper(name = "schifts") @XmlElement(name = "shift") public List<HolidayShift> getShifts() { return shifts; } public void setShifts(List<HolidayShift> shifts) { this.shifts = shifts; } public void addShift(ShiftType shiftType, String country, String... regions) { if (shifts == null) { shifts = new ArrayList<>(); } HolidayShift hs = new HolidayShift(shiftType); if (StringUtils.isNotEmpty(country)) { hs.addLocation(country, regions); } if (!shifts.contains(hs)) { shifts.add(hs); } } @XmlElementWrapper(name = "validities") @XmlElement(name = "validity") public List<HolidayValidity> getValidities() { return validities; } public void setValidities(List<HolidayValidity> validities) { this.validities = validities; } public void addValidity(Integer from, Integer to, String country, String... regions) { if (this.validities == null) { validities = new ArrayList<>(); } HolidayValidity hv = new HolidayValidity(from, to); if (country != null) { if (country.equals("")) { hv.addLocation(null, (String) null); } else { hv.addLocation(country, regions); } } if (!validities.contains(hv)) { validities.add(hv); } } @XmlAttribute public HolidayTypes getHolidayType() { return holidayType; } public void setHolidayType(HolidayTypes holidayType) { this.holidayType = holidayType; } @XmlAttribute public Integer getDay() { return day; } public void setDay(Integer day) { if (day != null && (day < 1 || day > 31)) { throw new InvalidArgumentException("day", "must be between 1 and 31"); } this.day = day; } @XmlAttribute public Integer getMonth() { return month; } public void setMonth(Integer month) { if (month != null && (month < 1 || month > 12)) { throw new InvalidArgumentException("month", "must be between 1 and 12"); } this.month = month; } @XmlAttribute public Integer getOffset() { return offset; } public void setOffset(Integer offset) { this.offset = offset; } @XmlAttribute public Integer getWeek() { return week; } public void setWeek(Integer week) { this.week = week; } public Integer getDayOfWeek() { return dayOfWeek; } public void setDayOfWeek(Integer dayOfWeek) { if (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY) { throw new InvalidArgumentException("dayOfWeek", "must be between 1 and 7"); } this.dayOfWeek = dayOfWeek; } public boolean isFor(String country, String region) { if (this.locations == null || this.locations.isEmpty()) { return true; } for (HolidayLocation hl : this.locations) { if (hl.isFor(country, region)) { return true; } } return false; } private HolidayValidity getValidity(String country, String region) { if (this.validities == null || validities.isEmpty()) { return null; } HolidayValidity vc = null; HolidayValidity vr = null; for (HolidayValidity hv : validities) { if (hv.isFor(country, region)) { return hv; } } return null; } private HolidayShift getShift(String country, String region) { if (this.shifts == null || shifts.isEmpty()) { return null; } HolidayShift vc = null; HolidayShift vr = null; for (HolidayShift hs : shifts) { if (hs.isFor(country, region)) { return hs; } } return null; } private String createName() { List<String> n = new ArrayList<>(); n.add(String.format("Holiday-Type(%s)", holidayType)); if (day != null) { n.add(String.format("Day(%s)", day)); } if (month != null) { n.add(String.format("Month(%s)", month)); } if (offset != null) { n.add(String.format("Offset(%s)", offset)); } if (week != null) { n.add(String.format("Week(%s)", week)); } return String.join("-", n); } private String holidayNameToName(HolidayName hn) { if (hn == null || StringUtils.isEmpty(hn.getName())) { return createName(); } else { return hn.getName(); } } public String getName(Locale locale) { String result; if (names == null || names.isEmpty()) { result = createName(); } else if (locale == null || locale.equals(Locale.ROOT)) { HolidayName hn = null; for (HolidayName n : names) { if (n.getLanguage() == null || n.getLanguage().equals(Locale.ROOT)) { hn = n; break; } } result = holidayNameToName(hn); } else { HolidayName rn = null; HolidayName ln2 = null; HolidayName ln = null; HolidayName cn = null; for (HolidayName n : names) { Locale l = n.getLanguage(); if (l == null || l.equals(Locale.ROOT)) { if (rn == null) { rn = n; } continue; } if (equals(l.getLanguage(), locale.getLanguage()) && StringUtils.isEmpty(l.getCountry())) { if (ln == null) { ln = n; } continue; } if (equals(l.getLanguage(), locale.getLanguage()) && !StringUtils.isEmpty(l.getCountry())) { if (ln2 == null) { ln2 = n; } continue; } if (equals(l.getLanguage(), locale.getLanguage()) && equals(l.getCountry(), locale.getCountry())) { if (cn == null) { cn = n; } } } if (cn != null) { result = holidayNameToName(cn); } else if (ln != null) { result = holidayNameToName(ln); } else if (ln2 != null) { result = holidayNameToName(ln2); } else if (rn != null) { result = holidayNameToName(rn); } else { result = createName(); } } return result; } public static final Calendar calcEasterSunday(int year) { if (year <= 1582) { throw new IllegalArgumentException("Algorithm invalid before 1583"); } int golden, century, x, z, d, epact, n; golden = (year % 19) + 1; /* E1: metonic cycle */ century = (year / 100) + 1; /* E2: e.g. 1984 was in 20th C */ x = (3 * century / 4) - 12; /* E3: leap year correction */ z = ((8 * century + 5) / 25) - 5; /* E3: sync with moon's orbit */ d = (5 * year / 4) - x - 10; epact = (11 * golden + 20 + z - x) % 30; /* E5: epact */ if ((epact == 25 && golden > 11) || epact == 24) { epact++; } n = 44 - epact; n += 30 * (n < 21 ? 1 : 0); /* E6: */ n += 7 - ((d + n) % 7); if (n > 31) /* E7: */ { return new GregorianCalendar(year, 4 - 1, n - 31); /* April */ } else { return new GregorianCalendar(year, 3 - 1, n); /* March */ } } public HolidayResult calculate(int year, Locale locale) { HolidayResult result = new HolidayResult(); result.setName(getName(locale)); Calendar cal = Calendar.getInstance(); cal.set(Calendar.HOUR, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); switch (holidayType) { case FixedDate: cal.set(year, this.getMonth() - 1, this.getDay()); result.setDate(cal); break; case EasterBased: Calendar easter = calcEasterSunday(year); cal.set(year, easter.get(Calendar.MONTH), easter.get(Calendar.DATE)); cal.add(Calendar.DATE, this.getOffset()); result.setDate(cal); break; case FixedDayOfWeek: cal.set(year, this.getMonth() - 1, 1); while (cal.get(Calendar.DAY_OF_WEEK) != this.getDayOfWeek()) { cal.add(Calendar.DATE, 1); } switch (this.getWeek()) { case 1: //do nothing break; case 2: case 3: case 5: cal.add(Calendar.DATE, (this.getWeek() - 1) * 7); break; } if ((cal.get(Calendar.MONTH) + 1) != this.getMonth()) { cal.add(Calendar.DATE, -7); } result.setDate(cal); break; case November23rdBased: cal.set(year, 10, 23); int dow = cal.get(Calendar.DAY_OF_WEEK); while (dow != this.getDayOfWeek()) { cal.add(Calendar.DATE, 1); dow = cal.get(Calendar.DAY_OF_WEEK); } cal.add(Calendar.DATE, this.getWeek() * 7); result.setDate(cal); break; } return result; } public Calendar shift(Calendar cal, String country, String region) { int dow = cal.get(Calendar.DAY_OF_WEEK); if (dow != Calendar.SATURDAY && dow != Calendar.SUNDAY) { return cal; } HolidayShift hs = getShift(country, region); if (hs == null || hs.getShiftType() == ShiftType.None) { return cal; } int step; switch (hs.getShiftType()) { case Back: step = -1; break; case Forth: step = 1; break; case BackAndForth: step = dow == Calendar.SATURDAY ? -1 : 1; break; default: return cal; } while (dow == Calendar.SATURDAY || dow == Calendar.SUNDAY) { cal.add(Calendar.DATE, step); dow = cal.get(Calendar.DAY_OF_WEEK); } return cal; } public boolean isValid(Calendar date, String country, String region) { HolidayValidity hv = getValidity(country, region); if (hv == null) { return true; } return hv.isValid(date); } @Override public boolean equals(Object other) { if (other == null) { return false; } if (!(other instanceof Holiday)) { return false; } Holiday h = (Holiday) other; return super.equals(other) && equals(this.getHolidayType(), h.getHolidayType()) && equals(this.getDay(), h.getDay()) && equals(this.getMonth(), h.getMonth()) && equals(this.getOffset(), h.getOffset()); } @Override public int hashCode() { int hash = 7; hash = 29 * hash + Objects.hashCode(this.names); hash = 29 * hash + Objects.hashCode(this.shifts); hash = 29 * hash + Objects.hashCode(this.validities); hash = 29 * hash + Objects.hashCode(this.holidayType); hash = 29 * hash + Objects.hashCode(this.day); hash = 29 * hash + Objects.hashCode(this.month); hash = 29 * hash + Objects.hashCode(this.offset); return hash; } protected static <T> boolean equals(T o1, Object o2) { return HolidayInfoBase.equals(o1, o2); } }