/// <summary> /// /// </summary> /// <param name="seqRequest"></param> /// <param name="destination"></param> /// <param name="deliveryRequest"></param> /// <param name="exitSession"></param> /// <remarks>Corresponds to SB.2.12 in the SCORM 2004 Sequencing/Navigation manual, appendix C.</remarks> private void ProcessSequencingRequest(SequencingRequest seqRequest, Activity destination, out Activity deliveryRequest, out bool exitSession) { switch(seqRequest) { case SequencingRequest.Choice: deliveryRequest = ChoiceSequencingRequest(destination); exitSession = false; return; case SequencingRequest.Continue: deliveryRequest = ContinueSequencingRequest(); exitSession = false; return; case SequencingRequest.Exit: deliveryRequest = null; exitSession = ExitSequencingRequest(); return; case SequencingRequest.Previous: deliveryRequest = PreviousSequencingRequest(); exitSession = false; return; case SequencingRequest.Retry: deliveryRequest = RetrySequencingRequest(); exitSession = false; return; case SequencingRequest.ResumeAll: deliveryRequest = ResumeAllSequencingRequest(); exitSession = false; return; case SequencingRequest.Start: deliveryRequest = StartSequencingRequest(); exitSession = false; return; } Utilities.Assert(false); deliveryRequest = null; exitSession = false; return; }
/// <summary> /// Processes post condition rules on an activity, and returns a sequencing request and/or a termination request. /// </summary> /// <param name="activity">Activity to perform post condition rules on.</param> /// <param name="seqRequest">Sequencing request to perform based on the post condition rules, or null if none.</param> /// <param name="termRequest">Termination request to perform based on the post condition rules, or null if none.</param> /// <remarks>Corresponds to TB.2.2 in the SCORM 2004 Sequencing/Navigation manual, appendix C.</remarks> private void SequencingPostConditionRules(Activity activity, out SequencingRequest? seqRequest, out TerminationRequest? termRequest) { seqRequest = null; termRequest = null; Resources.Culture = LocalizationManager.GetCurrentCulture(); if(activity.DataModel.ActivityIsSuspended) { return; } SequencingRuleAction? action = SequencingRulesCheck(activity, activity.Sequencing.PostConditionRules); switch(action) { case SequencingRuleAction.Retry: // Attempt to override any pending sequencing request with this one m_navigator.LogSequencing(SequencingEventType.IntermediateNavigation, m_command, Resources.SequencingPostConditionRuleResult, activity.Key, Resources.SequencingPostConditionRuleRequestRetry); seqRequest = SequencingRequest.Retry; return; case SequencingRuleAction.Continue: // Attempt to override any pending sequencing request with this one m_navigator.LogSequencing(SequencingEventType.IntermediateNavigation, m_command, Resources.SequencingPostConditionRuleResult, activity.Key, Resources.SequencingPostConditionRuleRequestContinue); seqRequest = SequencingRequest.Continue; return; case SequencingRuleAction.Previous: // Attempt to override any pending sequencing request with this one m_navigator.LogSequencing(SequencingEventType.IntermediateNavigation, m_command, Resources.SequencingPostConditionRuleResult, activity.Key, Resources.SequencingPostConditionRuleRequestPrevious); seqRequest = SequencingRequest.Previous; return; case SequencingRuleAction.ExitParent: // Terminate the appropriate activity(s) m_navigator.LogSequencing(SequencingEventType.IntermediateNavigation, m_command, Resources.SequencingPostConditionRuleResult, activity.Key, Resources.SequencingPostConditionRuleRequestExitParent); termRequest = TerminationRequest.ExitParent; return; case SequencingRuleAction.ExitAll: // Terminate the appropriate activity(s) m_navigator.LogSequencing(SequencingEventType.IntermediateNavigation, m_command, Resources.SequencingPostConditionRuleResult, activity.Key, Resources.SequencingPostConditionRuleRequestExitAll); termRequest = TerminationRequest.ExitAll; return; case SequencingRuleAction.RetryAll: // Terminate all active activities and move the current activity to the root of the activity tree; then perform an 'in-process' start m_navigator.LogSequencing(SequencingEventType.IntermediateNavigation, m_command, Resources.SequencingPostConditionRuleResult, activity.Key, Resources.SequencingPostConditionRuleRequestRetryAll); seqRequest = SequencingRequest.Retry; termRequest = TerminationRequest.ExitAll; return; } }
private void ProcessNavigationRequest(Activity destination, out SequencingRequest? seqRequest, out TerminationRequest? termRequest) { seqRequest = null; termRequest = null; switch(m_command) { case NavigationCommand.Abandon: if(m_navigator.CurrentActivity != null) { if(m_navigator.CurrentActivity.DataModel.ActivityIsActive) { termRequest = TerminationRequest.Abandon; seqRequest = SequencingRequest.Exit; } else { throw new SequencingException(SequencingExceptionCode.NB_2_1__12); } } else { throw new SequencingException(SequencingExceptionCode.NB_2_1__2); } break; case NavigationCommand.AbandonAll: // the check for CurrentActivity != null was removed // so this function should always succeed termRequest = TerminationRequest.AbandonAll; seqRequest = SequencingRequest.Exit; break; case NavigationCommand.Choose: if(destination == m_navigator.RootActivity || destination.Parent.Sequencing.Choice) { if(m_navigator.CurrentActivity == null) { seqRequest = SequencingRequest.Choice; return; } // We are always allowed to choose a sibling of the current activity - INCORRECT DESPITE BEING IN THE SCORM PSEUDOCODE // if(destination.Parent != m_navigator.CurrentActivity.Parent) { // The common ancestor will not terminate as a result of processing the choice sequencing request, // unless the common ancestor is the CurrentActivity - the current activity should always // be included in the activity path Activity ancestor = FindCommonAncestor(m_navigator.CurrentActivity, destination); Activity final; if(ancestor == m_navigator.CurrentActivity) { final = ancestor.Parent; } else { final = ancestor; } // Make sure that 'choosing' the target will not force an active activity to terminate, // if that activity does not allow choice to terminate it for(Activity a = m_navigator.CurrentActivity ; a != final ; a = a.Parent) { if(a.DataModel.ActivityIsActive && !a.Sequencing.ChoiceExit) { throw new SequencingException(SequencingExceptionCode.NB_2_1__8); } } } if(m_navigator.CurrentActivity.DataModel.ActivityIsActive) { termRequest = TerminationRequest.Exit; } seqRequest = SequencingRequest.Choice; } else { throw new SequencingException(SequencingExceptionCode.NB_2_1__10); } break; case NavigationCommand.Continue: if(m_navigator.CurrentActivity == null) { throw new SequencingException(SequencingExceptionCode.NB_2_1__2); } // Validate that a 'flow' sequencing request can be processed from the current activity if(m_navigator.CurrentActivity != m_navigator.RootActivity && m_navigator.CurrentActivity.Parent.Sequencing.Flow) { seqRequest = SequencingRequest.Continue; // If the current activity has not been terminated, terminate the current the activity if(m_navigator.CurrentActivity.DataModel.ActivityIsActive) { termRequest = TerminationRequest.Exit; } } else { // Flow is not enabled or the current activity is the root of the activity tree throw new SequencingException(SequencingExceptionCode.NB_2_1__4); } break; case NavigationCommand.ExitAll: if(m_navigator.CurrentActivity != null) { // If the sequencing session has already begun, unconditionally terminate all active activities termRequest = TerminationRequest.ExitAll; seqRequest = SequencingRequest.Exit; } else { throw new SequencingException(SequencingExceptionCode.NB_2_1__2); } break; case NavigationCommand.Previous: if(m_navigator.CurrentActivity == null) { throw new SequencingException(SequencingExceptionCode.NB_2_1__2); } // Validate that a 'flow' sequencing request can be processed from the current activity if(m_navigator.CurrentActivity != m_navigator.RootActivity) { if(m_navigator.CurrentActivity.Parent.Sequencing.Flow && !m_navigator.CurrentActivity.Parent.Sequencing.ForwardOnly) { seqRequest = SequencingRequest.Previous; // If the current activity has not been terminated, terminate the current the activity if(m_navigator.CurrentActivity.DataModel.ActivityIsActive) { termRequest = TerminationRequest.Exit; } } else { // Violates control mode throw new SequencingException(SequencingExceptionCode.NB_2_1__5); } } else { // // Cannot move backward from the root of the activity tree throw new SequencingException(SequencingExceptionCode.NB_2_1__6); } break; case NavigationCommand.ResumeAll: if(m_navigator.CurrentActivity == null) { if(m_navigator.SuspendedActivity != null) { seqRequest = SequencingRequest.ResumeAll; } else { throw new SequencingException(SequencingExceptionCode.NB_2_1__3); } } else { throw new SequencingException(SequencingExceptionCode.NB_2_1__1); } break; case NavigationCommand.Start: if(m_navigator.CurrentActivity != null) { throw new SequencingException(SequencingExceptionCode.NB_2_1__1); } seqRequest = SequencingRequest.Start; break; case NavigationCommand.SuspendAll: if(m_navigator.CurrentActivity != null) { termRequest = TerminationRequest.SuspendAll; seqRequest = SequencingRequest.Exit; } else { throw new SequencingException(SequencingExceptionCode.NB_2_1__2); } break; case NavigationCommand.UnqualifiedExit: if(m_navigator.CurrentActivity == null) { throw new SequencingException(SequencingExceptionCode.NB_2_1__2); } if(m_navigator.CurrentActivity.DataModel.ActivityIsActive) { termRequest = TerminationRequest.Exit; seqRequest = SequencingRequest.Exit; } else { throw new SequencingException(SequencingExceptionCode.NB_2_1__12); } break; default: throw new LearningComponentsInternalException("SSN0001"); } }