source: trunk/eraser/Eraser.Manager/Schedule.cs @ 2516

Revision 2516, 14.9 KB checked in by lowjoel, 3 years ago (diff)

Update copyrights to this year.

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