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

Revision 905, 13.0 KB checked in by lowjoel, 5 years ago (diff)

Ran static code analysis on Manager.

-Do not initialise class members to default values (runtime will handle that)
-Deserialisation constructors should be marked protected for unsealed classes
-Classes implementing ICollection should end with Collection (Mainly fixed here is ErasureTargetsCollection?)
-ISerializable.GetObjectData? should be marked virtual
-Use proper camel casing (Prng, Guid, FileName? etc)
-Enumeration values should be Camel casing
-Bitfields should be marked with the [Flags] attribute
-Implement the proper IDisposable pattern - one Dispose() and one Dispose(bool) - the latter doing the clean ups for both Dispose and finalisers
-public variables should instead be properties
-Abstract classes should have protected constructors
-Renamed Schedule.Type to Schedule.ScheduleType? to prevent ambiguity with GetType?()
-There shouldn't be a need to pass reference variables by reference

  • 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        [Flags]
236        public enum DaysOfWeek
237        {
238            None = 0,
239            Sunday = 1 << DayOfWeek.Sunday,
240            Monday = 1 << DayOfWeek.Monday,
241            Tuesday = 1 << DayOfWeek.Tuesday,
242            Wednesday = 1 << DayOfWeek.Wednesday,
243            Thursday = 1 << DayOfWeek.Thursday,
244            Friday = 1 << DayOfWeek.Friday,
245            Saturday = 1 << DayOfWeek.Saturday
246        }
247
248        /// <summary>
249        /// The type of schedule.
250        /// </summary>
251        public ScheduleUnit ScheduleType
252        {
253            get { return type; }
254            set { type = value; }
255        }
256
257        /// <summary>
258        /// The frequency of the event. This value is valid only with Daily,
259        /// Weekly and Monthly schedules.
260        /// </summary>
261        public int Frequency
262        {
263            get
264            {
265                if (ScheduleType != ScheduleUnit.Daily && ScheduleType != ScheduleUnit.Weekly &&
266                    ScheduleType != ScheduleUnit.Monthly)
267                    throw new ArgumentException(S._("The ScheduleUnit of the schedule does " +
268                        "not require a frequency value, this field would contain garbage."));
269
270                return frequency;
271            }
272            set
273            {
274                if (value == 0)
275                    throw new ArgumentException(S._("The frequency of the recurrance should " +
276                        "be greater than one"));
277
278                frequency = value;
279            }
280        }
281
282        /// <summary>
283        /// The time of day where the task will be executed.
284        /// </summary>
285        public DateTime ExecutionTime
286        {
287            get { return executionTime; }
288            set { executionTime = value; }
289        }
290
291        /// <summary>
292        /// The days of the week which this task should be run. This is valid only
293        /// with Weekly schedules. This field is the DaysOfWeek enumerations
294        /// ORed together.
295        /// </summary>
296        public DaysOfWeek WeeklySchedule
297        {
298            get
299            {
300                if (ScheduleType != ScheduleUnit.Weekly)
301                    throw new ArgumentException(S._("The ScheduleUnit of the schedule does " +
302                        "not require the WeeklySchedule value, this field would contain garbage"));
303
304                return weeklySchedule;
305            }
306            set
307            {
308                if (value == 0)
309                    throw new ArgumentException(S._("The WeeklySchedule should have at " +
310                        "least one day where the task should be run."));
311
312                weeklySchedule = value;
313            }
314        }
315
316        /// <summary>
317        /// The nth day of the month on which this task will run. This is valid
318        /// only with Monthly schedules
319        /// </summary>
320        public int MonthlySchedule
321        {
322            get
323            {
324                if (ScheduleType != ScheduleUnit.Monthly)
325                    throw new ArgumentException(S._("The ScheduleUnit of the schedule does " +
326                        "not require the MonthlySchedule value, this field would contain garbage"));
327
328                return monthlySchedule;
329            }
330            set { monthlySchedule = value; }
331        }
332
333        /// <summary>
334        /// The last time this task was executed. This value is used for computing
335        /// the next time the task should be run.
336        /// </summary>
337        public DateTime LastRun
338        {
339            get { return lastRun; }
340        }
341
342        /// <summary>
343        /// Computes the next run time based on the last run time, the current
344        /// schedule, and the current time. The timestamp returned will be the next
345        /// time from now which fulfils the schedule.
346        /// </summary>
347        public DateTime NextRun
348        {
349            get
350            {
351                //Get a good starting point, either now, or the last time the task
352                //was run.
353                DateTime nextRun = LastRun;
354                if (nextRun == DateTime.MinValue)
355                    nextRun = DateTime.Now;
356                nextRun = nextRun.AddHours(executionTime.Hour - nextRun.Hour);
357                nextRun = nextRun.AddMinutes(executionTime.Minute - nextRun.Minute);
358                nextRun = nextRun.AddSeconds(executionTime.Second - nextRun.Second);
359
360                switch (ScheduleType)
361                {
362                    case ScheduleUnit.Daily:
363                    {
364                        //First assume that it is today that we are running the schedule
365                        long daysToAdd = (DateTime.Now - nextRun).Days;
366                        nextRun = nextRun.AddDays(daysToAdd);
367
368                        //If we have passed today's run time, schedule it after the next
369                        //frequency
370                        if (nextRun < DateTime.Now)
371                            nextRun = nextRun.AddDays(frequency);
372                        break;
373                    }
374                    case ScheduleUnit.Weekdays:
375                    {
376                        while (nextRun < DateTime.Now ||
377                            lastRun.DayOfWeek == DayOfWeek.Saturday ||
378                            lastRun.DayOfWeek == DayOfWeek.Sunday)
379                            nextRun = nextRun.AddDays(1);
380                        break;
381                    }
382                    case ScheduleUnit.Weekly:
383                    {
384                        if (weeklySchedule == 0)
385                            break;
386
387                        //Find the next eligible day to run the task within this week.
388                        do
389                        {
390                            if (CanRunOnDay(nextRun) && nextRun >= DateTime.Now)
391                                break;
392                            nextRun = nextRun.AddDays(1);
393                        }
394                        while (nextRun.DayOfWeek < DayOfWeek.Saturday);
395
396                        while (nextRun < DateTime.Now || !CanRunOnDay(nextRun))
397                        {
398                            //Find the next eligible day to run the task
399                            nextRun = nextRun.AddDays(7 * (frequency - 1));
400                            for (int daysInWeek = 7; daysInWeek-- != 0; nextRun = nextRun.AddDays(1))
401                            {
402                                if (CanRunOnDay(nextRun) && nextRun >= DateTime.Now)
403                                    break;
404                            }
405                        }
406
407                        break;
408                    }
409                    case ScheduleUnit.Monthly:
410                        //Step the number of months since the last run
411                        if (LastRun != DateTime.MinValue)
412                            nextRun = nextRun.AddMonths(frequency);
413
414                        //Ensure that the next run day is before the scheduled day of month
415                        while (monthlySchedule < nextRun.Day)
416                            nextRun = nextRun.AddDays(1);
417                       
418                        //Set the next run date to be the day on which the task will run.
419                        nextRun = nextRun.AddDays(-((int)monthlySchedule - nextRun.Day));
420                        while (nextRun < DateTime.Now)
421                            nextRun = nextRun.AddMonths(frequency);
422                        break;
423                }
424
425                return nextRun;
426            }
427        }
428
429        /// <summary>
430        /// Gets whether the previous run was missed.
431        /// </summary>
432        public bool MissedPreviousSchedule
433        {
434            get
435            {
436                return lastRun != DateTime.MinValue && NextRun != nextRun;
437            }
438        }
439
440        /// <summary>
441        /// Returns true if the task can run on the given date. Applies only for
442        /// weekly tasks.
443        /// </summary>
444        /// <param name="date">The date to run on.</param>
445        /// <returns>True if the task will be run on the date.</returns>
446        private bool CanRunOnDay(DateTime date)
447        {
448            if (ScheduleType != ScheduleUnit.Weekly)
449                throw new ArgumentException(S._("The ScheduleUnit of the schedule does " +
450                    "not use the WeeklyScheduly value, this field would contain garbage"));
451            return ((int)weeklySchedule & (1 << (int)date.DayOfWeek)) != 0;
452        }
453
454        /// <summary>
455        /// Reschedules the task.
456        /// </summary>
457        /// <param name="lastRun">The last time the task was run.</param>
458        internal void Reschedule(DateTime lastRun)
459        {
460            this.lastRun = lastRun;
461            nextRun = NextRun;
462        }
463
464        private ScheduleUnit type;
465        private int frequency;
466        private DateTime executionTime;
467        private DaysOfWeek weeklySchedule;
468        private int monthlySchedule;
469
470        private DateTime lastRun;
471        private DateTime nextRun;
472    }
473}
Note: See TracBrowser for help on using the repository browser.