source: branches/eraser6/Eraser.Manager/Schedule.cs @ 1059

Revision 957, 13.9 KB checked in by lowjoel, 6 years ago (diff)

No publicly accessible nested types

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