/// Check an XML schedule with exceptions.
        public virtual ScheduleStatus CheckSchedule(Stream schedule, Stream exceptions)
        {
            ScheduleStatus sched = new ScheduleStatus();

            sched.Precision = String.Empty;
            ScheduleStatus xSched = new ScheduleStatus();

            xSched.Precision = String.Empty;
            XPathNavigator nav  = null;
            XPathNavigator xNav = null;

            if ((schedule == null) || schedule.Equals(Stream.Null))
            {
                sched.Success = true;
            }
            else
            {
                XPathDocument doc = new XPathDocument(schedule);
                nav = doc.CreateNavigator();
                if (!(nav.MoveToChild(XPathNodeType.Element)) || nav.Name != "schedule")
                {
                    sched.Success = true;
                }
                else
                {
                    CheckNode(nav, DateTime.Now, ref sched);
                }
            }
            if ((exceptions != null) && !(exceptions.Equals(Stream.Null)))
            {
                XPathDocument xDoc = new XPathDocument(exceptions);
                xNav = xDoc.CreateNavigator();
                if (!(xNav.MoveToChild(XPathNodeType.Element)) || xNav.Name != "schedule")
                {
                    xSched.Success = true;
                }
                else
                {
                    CheckNode(xNav, DateTime.Now, ref xSched);
                }
            }
            if (xSched.Success)
            {
                sched.Success = false;
            }
            if (UsePreciseProbe)
            {
                sched = ProbeScheduleExact(nav, xNav, sched, xSched);
            }
            else
            {
                sched = ProbeSchedule(nav, xNav, sched, xSched);
            }
            return(sched);
        }
        /// Check if a free form text schedule with exceptions is valid on the testDate.
        public static ScheduleStatus Test(string schedule, string exceptions, DateTime testDate)
        {
            Scheduler s = new Scheduler();

            s.TestDate = testDate;
            ScheduleStatus status = s.CheckSchedule(schedule, exceptions);

            status.Schedule   = schedule;
            status.Exceptions = exceptions;
            return(status);
        }
        private ScheduleStatus ProbeScheduleExact(XPathNavigator document, XPathNavigator exceptionsDocument, ScheduleStatus schedule, ScheduleStatus exceptionsSchedule)
        {
            ScheduleStatus testSched          = new ScheduleStatus();
            ScheduleStatus testExceptionSched = new ScheduleStatus();
            int            sign         = 1;
            DateTime       nextDate     = DateTime.Now;
            bool           initialState = schedule.Success;
            int            jump         = 0;

            if (schedule.Precision.Equals("daily") || exceptionsSchedule.Precision.Equals("daily"))
            {
                jump = (6 * 60);
            }
            else
            if (schedule.Precision.Equals("weekly") || exceptionsSchedule.Precision.Equals("weekly"))
            {
                jump = (72 * 60);
            }
            else
            if (schedule.Precision.Equals("monthly") || exceptionsSchedule.Precision.Equals("monthly"))
            {
                jump = (360 * 60);
            }
            else
            if (schedule.Precision.Equals("yearly") || exceptionsSchedule.Precision.Equals("yearly"))
            {
                jump = ((720 * 6)
                        * 60);
            }
            else
            {
                jump = (6 * 60);
            }
            for (int probeCount = 1; (probeCount <= 20); probeCount++)
            {
                // reset variables
                testSched.Success = false;
                testSched.Expired = false;
                document.MoveToRoot();
                document.MoveToFirstChild();
                if (exceptionsDocument != null)
                {
                    exceptionsDocument.MoveToRoot();
                    exceptionsDocument.MoveToFirstChild();
                    testExceptionSched.Success = false;
                    testExceptionSched.Expired = false;
                }
                // set next date to check
                nextDate = nextDate.AddMinutes((jump * sign));
                bool valid = (CheckNode(document, nextDate, ref testSched) && ((exceptionsDocument == null) || !(CheckNode(exceptionsDocument, nextDate, ref testExceptionSched))));
                if (valid == initialState)
                {
                    sign = 1;
                }
                else
                {
                    sign = -1;
                }
                // keep moving forward and expand jump if no border found, otherwise narrow jump
                if (sign == -1)
                {
                    jump = (jump / 2);
                }
                else
                {
                    jump = (jump * 2);
                    probeCount--;
                }
                if (jump < 5)
                {
                    jump++;
                }
                // no border found
                if (nextDate > DateTime.Now.AddYears(5))
                {
                    break;
                }
            }
            schedule.NextTestDate = nextDate.AddMinutes((jump * -1));
            return(schedule);
        }
        private ScheduleStatus ProbeSchedule(XPathNavigator document, XPathNavigator exceptionsDocument, ScheduleStatus schedule, ScheduleStatus exceptionsSchedule)
        {
            ScheduleStatus testSched          = new ScheduleStatus();
            ScheduleStatus testExceptionSched = new ScheduleStatus();
            DateTime       nextDate           = DateTime.Now;
            bool           initialState       = schedule.Success;

            for (int probeCount = 0; (probeCount <= 30); probeCount++)
            {
                nextDate = nextDate.AddSeconds(1);
                // reset variables
                testSched.Success = false;
                testSched.Expired = false;
                document.MoveToRoot();
                document.MoveToFirstChild();
                if (exceptionsDocument != null)
                {
                    exceptionsDocument.MoveToRoot();
                    exceptionsDocument.MoveToFirstChild();
                    testExceptionSched.Success = false;
                    testExceptionSched.Expired = false;
                }
                bool valid = (CheckNode(document, nextDate, ref testSched) && ((exceptionsDocument == null) || !(CheckNode(exceptionsDocument, nextDate, ref testExceptionSched))));
                if (valid != initialState)
                {
                    return(schedule);
                }
                schedule.NextTestDate = nextDate;
            }
            return(schedule);
        }
 /// Checks the current navigator if the current nodes define an active schedule. An empty schedule will set Match to true.
 private bool CheckNode(XPathNavigator nav, DateTime checkDate, ref ScheduleStatus sched)
 {
     if (nav == null)
     {
         return(false);
     }
     sched.Precision = nav.Name;
     if (!(nav.MoveToFirstChild()))
     {
         // no schedule limitation
         sched.Success = true;
         return(true);
     }
     while (true)
     {
         // ignore comments
         if (!(nav.NodeType.Equals(XPathNodeType.Comment)))
         {
             string name = nav.Name;
             if (name == "once")
             {
                 if (CheckInterval(nav, checkDate))
                 {
                     sched.Success = true;
                 }
             }
             else
             if (CheckInterval(nav, checkDate))
             {
                 string value = nav.GetAttribute("value", String.Empty);
                 string every = nav.GetAttribute("every", String.Empty);
                 int    check = 0;
                 if (name == "yearly")
                 {
                     check = checkDate.Year;
                 }
                 else
                 if (name == "monthly")
                 {
                     check = checkDate.Month;
                 }
                 else
                 if (name == "weekly")
                 {
                     check = GetWeekOfMonth(checkDate);
                 }
                 else
                 if (name == "daily")
                 {
                     check = ((int)(checkDate.DayOfWeek));
                 }
                 if (CheckNumberInterval(value, check, every))
                 {
                     CheckNode(nav, checkDate, ref sched);
                 }
             }
             // found a match
             if (sched.Expired || sched.Success)
             {
                 break;
             }
         }
         // no more nodes
         if (!(nav.MoveToNext()))
         {
             break;
         }
     }
     return(sched.Success);
 }