001/*
002 * #%L
003 * Netarchivesuite - harvester
004 * %%
005 * Copyright (C) 2005 - 2014 The Royal Danish Library, the Danish State and University Library,
006 *             the National Library of France and the Austrian National Library.
007 * %%
008 * This program is free software: you can redistribute it and/or modify
009 * it under the terms of the GNU Lesser General Public License as
010 * published by the Free Software Foundation, either version 2.1 of the
011 * License, or (at your option) any later version.
012 * 
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Lesser Public License for more details.
017 * 
018 * You should have received a copy of the GNU General Lesser Public
019 * License along with this program.  If not, see
020 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
021 * #L%
022 */
023
024package dk.netarkivet.harvester.datamodel;
025
026import java.util.Calendar;
027import java.util.Date;
028import java.util.GregorianCalendar;
029
030import dk.netarkivet.common.exceptions.ArgumentNotValid;
031
032/**
033 * This class implements a frequency of a number of weeks.
034 */
035
036public class WeeklyFrequency extends Frequency {
037
038    /** The minute of the hour the event should happen at. */
039    private int minute;
040    /** The hour of the day the event should happen at. */
041    private int hour;
042    /** The day of the week the event should happen at. */
043    private int dayOfWeek;
044
045    /**
046     * Create a new weekly frequency that happens every numUnits weeks, anytime.
047     *
048     * @param numUnits Number of weeks from event to event.
049     * @throws ArgumentNotValid if numUnits if 0 or negative
050     */
051    public WeeklyFrequency(int numUnits) {
052        super(numUnits, true);
053    }
054
055    /**
056     * Create a new weekly frequency that happens every numUnits days, on the given day of week, hour, and minute.
057     *
058     * @param numUnits Number of days from event to event.
059     * @param dayOfWeek Which day of the week this event should happen. Sunday is day 1 of the week.
060     * @param hour The hour on which the event should happen.
061     * @param minute The minute of hour on which the event should happen.
062     * @throws ArgumentNotValid if numUnits if 0 or negative or dayOfWeek <1=SUNDAY >7=SATURDAY or hour is <0 or >23 or
063     * minutes is <0 or >59
064     */
065    public WeeklyFrequency(int numUnits, int dayOfWeek, int hour, int minute) {
066        super(numUnits, false);
067        Calendar cal = GregorianCalendar.getInstance();
068        if (dayOfWeek < cal.getMinimum(Calendar.DAY_OF_WEEK) || dayOfWeek > cal.getMaximum(Calendar.DAY_OF_WEEK)) {
069            throw new ArgumentNotValid("Day in week must be in legal range '" + cal.getMinimum(Calendar.DAY_OF_WEEK)
070                    + "' to '" + cal.getMaximum(Calendar.DAY_OF_WEEK) + "'");
071        }
072        if (hour < cal.getMinimum(Calendar.HOUR_OF_DAY) || hour > cal.getMaximum(Calendar.HOUR_OF_DAY)) {
073            throw new ArgumentNotValid("Hour of day must be in legal range '" + cal.getMinimum(Calendar.HOUR_OF_DAY)
074                    + "' to '" + cal.getMaximum(Calendar.HOUR_OF_DAY) + "'");
075        }
076        if (minute < cal.getMinimum(Calendar.MINUTE) || minute > cal.getMaximum(Calendar.MINUTE)) {
077            throw new ArgumentNotValid("Minute must be in legal range '" + cal.getMinimum(Calendar.MINUTE) + "' to '"
078                    + cal.getMaximum(Calendar.MINUTE) + "'");
079        }
080
081        this.dayOfWeek = dayOfWeek;
082        this.hour = hour;
083        this.minute = minute;
084    }
085
086    /**
087     * Given when the last event happened, tell us when the next event should happen (even if the new event is in the
088     * past).
089     * <p>
090     * The time of the next event is guaranteed to be later that lastEvent. For certain frequencies (e.g. once a day,
091     * any time of day), the time of the next event is derived from lastEvent, for others (e.g. once a day at 13:00) the
092     * time of the next event is the first matching time after lastEvent.
093     *
094     * @param lastEvent A time from which the next event should be calculated.
095     * @return At what point the event should happen next.
096     */
097    public Date getNextEvent(Date lastEvent) {
098        ArgumentNotValid.checkNotNull(lastEvent, "lastEvent");
099
100        Calendar last = new GregorianCalendar();
101        last.setTime(getFirstEvent(lastEvent));
102        last.add(Calendar.WEEK_OF_YEAR, getNumUnits());
103        return getFirstEvent(last.getTime());
104    }
105
106    /**
107     * Given a starting time, tell us when the first event should happen.
108     *
109     * @param startTime The earliest time the event can happen.
110     * @return At what point the event should happen the first time.
111     */
112    public Date getFirstEvent(Date startTime) {
113        ArgumentNotValid.checkNotNull(startTime, "startTime");
114
115        if (isAnytime()) {
116            return startTime;
117        }
118        Calendar start = new GregorianCalendar();
119        start.setTime(startTime);
120        start.set(Calendar.MINUTE, minute);
121        start.set(Calendar.HOUR_OF_DAY, hour);
122        start.set(Calendar.DAY_OF_WEEK, dayOfWeek);
123        if (start.getTime().before(startTime)) {
124            start.add(Calendar.WEEK_OF_YEAR, 1);
125        }
126        return start.getTime();
127
128    }
129
130    /**
131     * If not anytime, the minute at which events should start.
132     *
133     * @return the minute
134     */
135    public int getMinute() {
136        return minute;
137    }
138
139    /**
140     * If not anytime, the hour at which events should start.
141     *
142     * @return the hour
143     */
144    public int getHour() {
145        return hour;
146    }
147
148    /**
149     * If not anytime, the day in the week at which events should start.
150     *
151     * @return the day. Sunday=1
152     */
153    public int getDayOfWeek() {
154        return dayOfWeek;
155    }
156
157    /**
158     * Autogenerated equals.
159     *
160     * @param o The object to compare with
161     * @return Whether objects are equal
162     */
163    public boolean equals(Object o) {
164        if (this == o) {
165            return true;
166        }
167        if (!(o instanceof WeeklyFrequency)) {
168            return false;
169        }
170        if (!super.equals(o)) {
171            return false;
172        }
173
174        final WeeklyFrequency weeklyFrequency = (WeeklyFrequency) o;
175
176        if (isAnytime()) {
177            return true;
178        }
179
180        if (dayOfWeek != weeklyFrequency.dayOfWeek) {
181            return false;
182        }
183        if (hour != weeklyFrequency.hour) {
184            return false;
185        }
186        if (minute != weeklyFrequency.minute) {
187            return false;
188        }
189
190        return true;
191    }
192
193    /**
194     * Autogenerated hashcode method.
195     *
196     * @return the hashcode
197     */
198    public int hashCode() {
199        int result = super.hashCode();
200        result = 29 * result + minute;
201        result = 29 * result + hour;
202        result = 29 * result + dayOfWeek;
203        return result;
204    }
205
206    /**
207     * Return the exact minute this event should happen on, or null if this is an anyTime event or doesn't define what
208     * minute it should happen on.
209     *
210     * @return the exact minute this event should happen on
211     */
212    public Integer getOnMinute() {
213        if (!isAnytime()) {
214            return minute;
215        }
216        return null;
217    }
218
219    /**
220     * Return the exact hour event should happen on, or null if this is an anyTime event or doesn't define what hour it
221     * should happen on.
222     *
223     * @return the exact hour event should happen on
224     */
225    public Integer getOnHour() {
226        if (!isAnytime()) {
227            return hour;
228        }
229        return null;
230    }
231
232    /**
233     * Return the exact day of week event should happen on, or null if this is an anyTime event or doesn't define what
234     * day of week it should happen on.
235     *
236     * @return the exact day of week event should happen on
237     */
238    public Integer getOnDayOfWeek() {
239        if (!isAnytime()) {
240            return dayOfWeek;
241        }
242        return null;
243    }
244
245    /**
246     * Return the exact day of month event should happen on, or null if this is an anyTime event or doesn't define what
247     * day of month it should happen on.
248     *
249     * @return null (always)
250     */
251    public Integer getOnDayOfMonth() {
252        return null;
253    }
254
255    /**
256     * Return an integer that can be used to identify the kind of frequency. No two subclasses should use the same
257     * integer
258     *
259     * @return an integer that can be used to identify the kind of frequency
260     */
261    public int ordinal() {
262        return TimeUnit.WEEKLY.ordinal();
263    }
264
265    /**
266     * Human readable representation of this object.
267     *
268     * @return Human readable representation
269     */
270    public String toString() {
271        if (isAnytime()) {
272            return "every " + getNumUnits() + " weeks";
273        }
274        return "every " + getNumUnits() + " weeks, on weekday " + dayOfWeek + " at " + hour + ":" + minute;
275    }
276
277}