source: branches/eraser6/Manager/Schedule.cs @ 605

Revision 605, 12.7 KB checked in by lowjoel, 6 years ago (diff)

Internationalise the Manager code. Fixes #66

  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008 The Eraser Project
4 * Original Author: Joel Low <lowjoel@users.sourceforge.net>
5 * Modified By:
6 *
7 * This file is part of Eraser.
8 *
9 * Eraser is free software: you can redistribute it and/or modify it under the
10 * terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 3 of the License, or (at your option) any later
12 * version.
13 *
14 * Eraser is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 *
18 * A copy of the GNU General Public License can be found at
19 * <http://www.gnu.org/licenses/>.
20 */
21
22using System;
23using System.Collections.Generic;
24using System.Text;
25using System.Diagnostics;
26using System.Runtime.Serialization;
27using Eraser.Util;
28
29namespace Eraser.Manager
30{
31    /// <summary>
32    /// Base class for all schedule types.
33    /// </summary>
34    [Serializable]
35    public abstract class Schedule : ISerializable
36    {
37        #region Default values
38        [Serializable]
39        private class RunNowSchedule : Schedule
40        {
41            #region Object serialization
42            public RunNowSchedule(SerializationInfo info, StreamingContext context)
43            {
44            }
45
46            public override void GetObjectData(SerializationInfo info,
47                StreamingContext context)
48            {
49            }
50            #endregion
51
52            public RunNowSchedule()
53            {
54            }
55
56            public override string UIText
57            {
58                get { return string.Empty; }
59            }
60        }
61
62        [Serializable]
63        private class RunOnRestartSchedule : Schedule
64        {
65            #region Object serialization
66            public RunOnRestartSchedule(SerializationInfo info, StreamingContext context)
67            {
68            }
69
70            public override void GetObjectData(SerializationInfo info,
71                StreamingContext context)
72            {
73            }
74            #endregion
75
76            public RunOnRestartSchedule()
77            {
78            }
79
80            public override string UIText
81            {
82                get { return S._("Running on restart"); }
83            }
84        }
85        #endregion
86
87        /// <summary>
88        /// Retrieves the text that should be displayed detailing the nature of
89        /// the schedule for use in user interface elements.
90        /// </summary>
91        public abstract string UIText
92        {
93            get;
94        }
95
96        /// <summary>
97        /// Populates a SerializationInfo with the data needed to serialize the
98        /// target object.
99        /// </summary>
100        /// <param name="info">The SerializationInfo to populate with data.</param>
101        /// <param name="context">The destination for this serialization.</param>
102        public abstract void GetObjectData(SerializationInfo info, StreamingContext context);
103
104        /// <summary>
105        /// The global value for tasks which should be run immediately.
106        /// </summary>
107        public static readonly Schedule RunNow = new RunNowSchedule();
108
109        /// <summary>
110        /// The global value for tasks which should be run when the computer is
111        /// restarted
112        /// </summary>
113        public static readonly Schedule RunOnRestart = new RunOnRestartSchedule();
114    }
115
116    /// <summary>
117    /// Recurring runs schedule type.
118    /// </summary>
119    [Serializable]
120    public class RecurringSchedule : Schedule
121    {
122        #region Overridden members
123        public override string UIText
124        {
125            get
126            {
127                string result = string.Empty;
128                switch (type)
129                {
130                    case ScheduleUnit.DAILY:
131                        if (frequency != 1)
132                            result = S._("Once every {0} days", frequency);
133                        else
134                            result = S._("Once every day");
135                        break;
136                    case ScheduleUnit.WEEKDAYS:
137                        result = S._("Every weekday");
138                        break;
139                    case ScheduleUnit.WEEKLY:
140                        if ((weeklySchedule & DaysOfWeek.MONDAY) != 0)
141                            result = S._("Every Monday, {0}");
142                        if ((weeklySchedule & DaysOfWeek.TUESDAY) != 0)
143                            result += S._("Every Tuesday, {0}");
144                        if ((weeklySchedule & DaysOfWeek.WEDNESDAY) != 0)
145                            result += S._("Every Wednesday, {0}");
146                        if ((weeklySchedule & DaysOfWeek.THURSDAY) != 0)
147                            result += S._("Every Thursday, {0}");
148                        if ((weeklySchedule & DaysOfWeek.FRIDAY) != 0)
149                            result += S._("Every Friday, {0}");
150                        if ((weeklySchedule & DaysOfWeek.SATURDAY) != 0)
151                            result += S._("Every Saturday, {0}");
152                        if ((weeklySchedule & DaysOfWeek.SUNDAY) != 0)
153                            result += S._("Every Sunday, {0}");
154
155                        result = string.Format(result, frequency == 1 ?
156                            S._("once every {0} week.", frequency) :
157                            S._("once every {0} weeks.", frequency));
158                        break;
159                    case ScheduleUnit.MONTHLY:
160                        if (frequency == 1)
161                            result = S._("On day {0} of every month", monthlySchedule);
162                        else
163                            result = S._("On day {0} of every {1} months", monthlySchedule,
164                                frequency);
165                        break;
166                }
167
168                return result + S._(", at {0}", executionTime.TimeOfDay.ToString());
169            }
170        }
171        #endregion
172
173        #region Object serialization
174        public RecurringSchedule(SerializationInfo info, StreamingContext context)
175        {
176            type = (ScheduleUnit)info.GetValue("Type", typeof(ScheduleUnit));
177            frequency = (int)info.GetValue("Frequency", typeof(int));
178            executionTime = (DateTime)info.GetValue("ExecutionTime", typeof(DateTime));
179            weeklySchedule = (DaysOfWeek)info.GetValue("WeeklySchedule", typeof(DaysOfWeek));
180            monthlySchedule = (int)info.GetValue("MonthlySchedule", typeof(int));
181
182            lastRun = (DateTime)info.GetDateTime("LastRun");
183            nextRun = (DateTime)info.GetDateTime("NextRun");
184        }
185
186        public override void GetObjectData(SerializationInfo info,
187            StreamingContext context)
188        {
189            info.AddValue("Type", type);
190            info.AddValue("Frequency", frequency);
191            info.AddValue("ExecutionTime", executionTime);
192            info.AddValue("WeeklySchedule", weeklySchedule);
193            info.AddValue("MonthlySchedule", monthlySchedule);
194            info.AddValue("LastRun", lastRun);
195            info.AddValue("NextRun", nextRun);
196        }
197        #endregion
198
199        /// <summary>
200        /// Constructor.
201        /// </summary>
202        public RecurringSchedule()
203        {
204        }
205
206        /// <summary>
207        /// The types of schedule
208        /// </summary>
209        public enum ScheduleUnit
210        {
211            /// <summary>
212            /// Daily schedule type
213            /// </summary>
214            DAILY,
215
216            /// <summary>
217            /// Weekdays-only schedule type
218            /// </summary>
219            WEEKDAYS,
220
221            /// <summary>
222            /// Weekly schedule type
223            /// </summary>
224            WEEKLY,
225
226            /// <summary>
227            /// Monthly schedule type
228            /// </summary>
229            MONTHLY
230        }
231
232        /// <summary>
233        /// The days of the week, with values usable in a bitfield.
234        /// </summary>
235        public enum DaysOfWeek
236        {
237            SUNDAY = 1 << DayOfWeek.Sunday,
238            MONDAY = 1 << DayOfWeek.Monday,
239            TUESDAY = 1 << DayOfWeek.Tuesday,
240            WEDNESDAY = 1 << DayOfWeek.Wednesday,
241            THURSDAY = 1 << DayOfWeek.Thursday,
242            FRIDAY = 1 << DayOfWeek.Friday,
243            SATURDAY = 1 << DayOfWeek.Saturday
244        }
245
246        /// <summary>
247        /// The type of schedule.
248        /// </summary>
249        public ScheduleUnit Type
250        {
251            get { return type; }
252            set { type = value; }
253        }
254
255        /// <summary>
256        /// The frequency of the event. This value is valid only with Daily,
257        /// Weekly and Monthly schedules.
258        /// </summary>
259        public int Frequency
260        {
261            get
262            {
263                if (Type != ScheduleUnit.DAILY && Type != ScheduleUnit.WEEKLY &&
264                    Type != ScheduleUnit.MONTHLY)
265                    throw new ArgumentException(S._("The ScheduleUnit of the schedule does " +
266                        "not require a frequency value, this field would contain garbage."));
267
268                return frequency;
269            }
270            set
271            {
272                if (value == 0)
273                    throw new ArgumentException(S._("The frequency of the recurrance should " +
274                        "be greater than one"));
275
276                frequency = value;
277            }
278        }
279
280        /// <summary>
281        /// The time of day where the task will be executed.
282        /// </summary>
283        public DateTime ExecutionTime
284        {
285            get { return executionTime; }
286            set { executionTime = value; }
287        }
288
289        /// <summary>
290        /// The days of the week which this task should be run. This is valid only
291        /// with Weekly schedules. This field is the DaysOfWeek enumerations
292        /// ORed together.
293        /// </summary>
294        public DaysOfWeek WeeklySchedule
295        {
296            get
297            {
298                if (Type != ScheduleUnit.WEEKLY)
299                    throw new ArgumentException(S._("The ScheduleUnit of the schedule does " +
300                        "not require the WeeklySchedule value, this field would contain garbage"));
301
302                return weeklySchedule;
303            }
304            set
305            {
306                if (value == 0)
307                    throw new ArgumentException(S._("The WeeklySchedule should have at " +
308                        "least one day where the task should be run."));
309
310                weeklySchedule = value;
311            }
312        }
313
314        /// <summary>
315        /// The nth day of the month on which this task will run. This is valid
316        /// only with Monthly schedules
317        /// </summary>
318        public int MonthlySchedule
319        {
320            get
321            {
322                if (Type != ScheduleUnit.MONTHLY)
323                    throw new ArgumentException(S._("The ScheduleUnit of the schedule does " +
324                        "not require the MonthlySchedule value, this field would contain garbage"));
325
326                return monthlySchedule;
327            }
328            set { monthlySchedule = value; }
329        }
330
331        /// <summary>
332        /// The last time this task was executed. This value is used for computing
333        /// the next time the task should be run.
334        /// </summary>
335        public DateTime LastRun
336        {
337            get { return lastRun; }
338        }
339
340        /// <summary>
341        /// Computes the next run time based on the last run time, the current
342        /// schedule, and the current time. The timestamp returned will be the next
343        /// time from now which fulfils the schedule.
344        /// </summary>
345        public DateTime NextRun
346        {
347            get
348            {
349                //Get a good starting point, either now, or the last time the task
350                //was run.
351                DateTime nextRun = LastRun;
352                if (nextRun == DateTime.MinValue)
353                    nextRun = DateTime.Now;
354                nextRun = nextRun.AddHours(executionTime.Hour - nextRun.Hour);
355                nextRun = nextRun.AddMinutes(executionTime.Minute - nextRun.Minute);
356                nextRun = nextRun.AddSeconds(executionTime.Second - nextRun.Second);
357
358                switch (Type)
359                {
360                    case ScheduleUnit.DAILY:
361                    {
362                        //First assume that it is today that we are running the schedule
363                        long daysToAdd = (DateTime.Now - nextRun).Days;
364                        nextRun = nextRun.AddDays(daysToAdd);
365
366                        //If we have passed today's run time, schedule it after the next
367                        //frequency
368                        if (nextRun < DateTime.Now)
369                            nextRun = nextRun.AddDays(frequency);
370                        break;
371                    }
372                    case ScheduleUnit.WEEKDAYS:
373                    {
374                        while (nextRun < DateTime.Now ||
375                            lastRun.DayOfWeek == DayOfWeek.Saturday ||
376                            lastRun.DayOfWeek == DayOfWeek.Sunday)
377                            nextRun = nextRun.AddDays(1);
378                        break;
379                    }
380                    case ScheduleUnit.WEEKLY:
381                    {
382                        if (weeklySchedule == 0)
383                            break;
384
385                        //Find the next eligible day to run the task within this week.
386                        do
387                        {
388                            if (CanRunOnDay(nextRun) && nextRun >= DateTime.Now)
389                                break;
390                            nextRun = nextRun.AddDays(1);
391                        }
392                        while (nextRun.DayOfWeek < DayOfWeek.Saturday);
393
394                        while (nextRun < DateTime.Now || !CanRunOnDay(nextRun))
395                        {
396                            //Find the next eligible day to run the task
397                            nextRun = nextRun.AddDays(7 * (frequency - 1));
398                            for (int daysInWeek = 7; daysInWeek-- != 0; nextRun = nextRun.AddDays(1))
399                            {
400                                if (CanRunOnDay(nextRun) && nextRun >= DateTime.Now)
401                                    break;
402                            }
403                        }
404
405                        break;
406                    }
407                    case ScheduleUnit.MONTHLY:
408                        //Set the next run date to be the day on which the task will run.
409                        nextRun = nextRun.AddDays(-((int)monthlySchedule - nextRun.Day));
410                        while (nextRun < DateTime.Now)
411                            nextRun = nextRun.AddMonths((int)frequency);
412                        break;
413                }
414
415                return nextRun;
416            }
417        }
418
419        /// <summary>
420        /// Gets whether the previous run was missed.
421        /// </summary>
422        public bool MissedPreviousSchedule
423        {
424            get
425            {
426                return lastRun != DateTime.MinValue && NextRun != nextRun;
427            }
428        }
429
430        /// <summary>
431        /// Returns true if the task can run on the given date. Applies only for
432        /// weekly tasks.
433        /// </summary>
434        /// <param name="date">The date to run on.</param>
435        /// <returns>True if the task will be run on the date.</returns>
436        private bool CanRunOnDay(DateTime date)
437        {
438            if (Type != ScheduleUnit.WEEKLY)
439                throw new ArgumentException(S._("The ScheduleUnit of the schedule does " +
440                    "not use the WeeklyScheduly value, this field would contain garbage"));
441            return ((int)weeklySchedule & (1 << (int)date.DayOfWeek)) != 0;
442        }
443
444        /// <summary>
445        /// Reschedules the task.
446        /// </summary>
447        /// <param name="lastRun">The last time the task was run.</param>
448        internal void Reschedule(DateTime lastRun)
449        {
450            this.lastRun = lastRun;
451            nextRun = NextRun;
452        }
453
454        private ScheduleUnit type;
455        private int frequency;
456        private DateTime executionTime;
457        private DaysOfWeek weeklySchedule;
458        private int monthlySchedule;
459
460        private DateTime lastRun;
461        private DateTime nextRun;
462    }
463}
Note: See TracBrowser for help on using the repository browser.