de.austinpadernale.holidays.Holiday.java Source code

Java tutorial

Introduction

Here is the source code for de.austinpadernale.holidays.Holiday.java

Source

/*
 * 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);
    }
}