Index: lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/LearningDesignService.java
===================================================================
diff -u
--- lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/LearningDesignService.java (revision 0)
+++ lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/LearningDesignService.java (revision 0d6adda33b694f55c8f56774247b82d3b6cc011f)
@@ -0,0 +1,345 @@
+package org.lamsfoundation.lams.learningdesign.service;
+
+import org.apache.log4j.Logger;
+import org.lamsfoundation.lams.learningdesign.dto.ValidationErrorDTO;
+import org.lamsfoundation.lams.learningdesign.Activity;
+import org.lamsfoundation.lams.learningdesign.GateActivity;
+import org.lamsfoundation.lams.learningdesign.Grouping;
+import org.lamsfoundation.lams.learningdesign.LearningDesign;
+import org.lamsfoundation.lams.learningdesign.OptionsActivity;
+import org.lamsfoundation.lams.learningdesign.Transition;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Vector;
+
+import org.lamsfoundation.lams.util.MessageService;
+
+/**
+ * The LearningDesignService class contains methods which applies validation rules
+ * to determine the validity of a learning design. For the validation rules, please
+ * see the AuthoringDesignDoc in lams_documents.
+ *
+ * If no errors are found, a learning design is considered valid, it will set the valid_design_flag to true.
+ * If validation fails, the validation messages will be returned in the response packet. The validation
+ * messages are a list of ValidationErrorDTO objects.
+ *
+ * @author mtruong
+ *
+ */
+public class LearningDesignService implements ILearningDesignService{
+
+ //protected Logger log = Logger.getLogger(LearningDesignService.class);
+ private Vector listOfValidationErrorDTOs = null;
+ protected MessageService messageService;
+
+ /*
+ * Default constructor
+ *
+ */
+ public LearningDesignService()
+ {
+ }
+
+ /**********************************************
+ * Setter/Getter Methods
+ * *******************************************/
+ /**
+ * Set i18n MessageService
+ */
+ public void setMessageService(MessageService messageService) {
+ this.messageService = messageService;
+ }
+
+ /**
+ * Get i18n MessageService
+ */
+ public MessageService getMessageService() {
+ return this.messageService;
+ }
+
+ /**********************************************
+ * Service Methods
+ * *******************************************/
+
+ /**
+ * This method calls other validation methods which apply the validation
+ * rules to determine whether or not the learning design is valid.
+ *
+ * @param learningDesign
+ * @return list of validation errors
+ */
+ public Vector validateLearningDesign(LearningDesign learningDesign)
+ {
+
+ listOfValidationErrorDTOs = new Vector(); // initialises the list of validation messages.
+
+ validateActivityTransitionRules(learningDesign.getParentActivities(), learningDesign.getTransitions(), listOfValidationErrorDTOs);
+ validateGeneral(learningDesign.getActivities(), listOfValidationErrorDTOs);
+
+ return listOfValidationErrorDTOs;
+
+ }
+
+ /**
+ * Perform transition related validations.
+ *
+ * All activities with no input transitions are added to the vector
+ * noInputTransition
. If the size of this list is greater
+ * than 1 (which violates the rule of having exactly one top level activity
+ * with no input transition), then a ValidationErrorDTO will be created
+ * for each activity with no input transition.
+ * Similarly, the same concept applies for activities with no output transition.
+ *
+ * @param topLevelActivities
+ * @param transitions
+ */
+ private void validateActivityTransitionRules(Set topLevelActivities, Set transitions, Vector listOfValidationErrorDTOs)
+ {
+ validateTransitions(transitions, listOfValidationErrorDTOs);
+ Vector noInputTransition = new Vector(); //a list to hold the activities which have no input transition
+ Vector noOuputTransition = new Vector(); //a list to hold the activities which have no output transition
+ int numOfTopLevelActivities = topLevelActivities.size();
+ Iterator activityIterator = topLevelActivities.iterator();
+
+ while (activityIterator.hasNext())
+ {
+ Activity activity = (Activity)activityIterator.next();
+ checkActivityForTransition(activity, numOfTopLevelActivities, listOfValidationErrorDTOs);
+ if (activity.getTransitionFrom() == null)
+ noOuputTransition.add(activity);
+ if (activity.getTransitionTo() == null)
+ noInputTransition.add(activity);
+ }
+
+ if (numOfTopLevelActivities > 0)
+ {
+ if (noInputTransition.size() == 0)
+ listOfValidationErrorDTOs.add(new ValidationErrorDTO(messageService.getMessage(ValidationErrorDTO.INPUT_TRANSITION_ERROR_TYPE2_KEY)));
+
+ if (noInputTransition.size() > 1)
+ {
+ //there is more than one activity with no input transitions
+ Iterator noInputTransitionIterator = noInputTransition.iterator();
+ while (noInputTransitionIterator.hasNext())
+ {
+ Activity a = (Activity)noInputTransitionIterator.next();
+ listOfValidationErrorDTOs.add(new ValidationErrorDTO(messageService.getMessage(ValidationErrorDTO.INPUT_TRANSITION_ERROR_TYPE1_KEY), a.getActivityUIID()));
+ }
+ }
+
+ if (noOuputTransition.size() == 0)
+ listOfValidationErrorDTOs.add(new ValidationErrorDTO(messageService.getMessage(ValidationErrorDTO.OUTPUT_TRANSITION_ERROR_TYPE2_KEY)));
+ if (noOuputTransition.size() > 1)
+ {
+ //there is more than one activity with no output transitions
+ Iterator noOutputTransitionIterator = noOuputTransition.iterator();
+ while (noOutputTransitionIterator.hasNext())
+ {
+ Activity a = (Activity)noOutputTransitionIterator.next();
+ listOfValidationErrorDTOs.add(new ValidationErrorDTO(messageService.getMessage(ValidationErrorDTO.OUTPUT_TRANSITION_ERROR_TYPE1_KEY), a.getActivityUIID()));
+ }
+ }
+ }
+
+ }
+
+ /**
+ * This method checks if each transition in the learning design has an activity
+ * before and after the transition.
+ *
+ * If there exists a transition which does not have an activity before or after it,
+ * the ValidationErrorDTO is added to the list of validation messages.
+ * @param transitions the set of transitions to iterate through and validate
+ */
+ private void validateTransitions(Set transitions, Vector listOfValidationErrorDTOs)
+ {
+ Iterator i = transitions.iterator();
+ while (i.hasNext())
+ {
+ Transition transition = (Transition)i.next();
+ Activity fromActivity = transition.getFromActivity();
+ Activity toActivity = transition.getToActivity();
+ if (fromActivity == null)
+ listOfValidationErrorDTOs.add(new ValidationErrorDTO(messageService.getMessage(ValidationErrorDTO.TRANSITION_ERROR_KEY), transition.getTransitionUIID()));
+ else if (toActivity == null)
+ listOfValidationErrorDTOs.add(new ValidationErrorDTO(messageService.getMessage(ValidationErrorDTO.TRANSITION_ERROR_KEY), transition.getTransitionUIID()));
+
+ }
+
+ }
+
+ /**
+ * For any learning design that has more than one activity then each activity should have at least an input
+ * or output transition. If there is only one activity in the learning design, then that activity should
+ * not have any transitions.
+ * This method will check if there is an activity that exists that has no transitions at all (if there is
+ * more than one activity in the learning design)
+ * @param activity The Activity to validate
+ * @param numOfActivities The number of activities in the learning design.
+ */
+ private void checkActivityForTransition(Activity activity, int numOfActivities, Vector listOfValidationErrorDTOs)
+ {
+ //if one activity, then shouldnt have any transitions
+ Transition inputTransition = activity.getTransitionTo();
+ Transition outputTransition = activity.getTransitionFrom();
+
+ if(numOfActivities > 1)
+ {
+ if (inputTransition == null && outputTransition == null)
+ listOfValidationErrorDTOs.add(new ValidationErrorDTO(messageService.getMessage(ValidationErrorDTO.ACTIVITY_TRANSITION_ERROR_KEY), activity.getActivityUIID()));
+
+ }
+ if (numOfActivities == 1)
+ {
+ if (inputTransition != null || outputTransition != null)
+ listOfValidationErrorDTOs.add(new ValidationErrorDTO(messageService.getMessage(ValidationErrorDTO.ACTIVITY_TRANSITION_ERROR_KEY), activity.getActivityUIID()));
+
+ }
+
+ }
+
+ /**
+ * This method will call all other validation methods.
+ *
+ * @param activities
+ */
+ private void validateGeneral(Set activities, Vector listOfValidationErrorDTOs)
+ {
+ Iterator activityIterator = activities.iterator();
+ while (activityIterator.hasNext())
+ {
+ Activity activity = (Activity)activityIterator.next();
+ checkIfGroupingRequired(activity, listOfValidationErrorDTOs);
+ validateGroupingIfGroupingIsApplied(activity, listOfValidationErrorDTOs);
+ validateOptionalActivity(activity, listOfValidationErrorDTOs);
+ validateOptionsActivityOrderId(activity, listOfValidationErrorDTOs);
+ Vector activityErrors = (Vector)activity.validateActivity(messageService);
+ if(activityErrors != null && !activityErrors.isEmpty())
+ listOfValidationErrorDTOs.addAll(activityErrors);
+ }
+ }
+
+ /**
+ * If grouping support type is set to GROUPING_SUPPORT_REQUIRED
,
+ * then the activity is validated to ensure that the grouping exists.
+ * If grouping support type is set to GROUPING_SUPPORT_NONE
+ * then the activity is validated to ensure that the grouping does not exist.
+ *
+ * If any validation fails, the message will be added to the list of validation
+ * messages.
+ *
+ * @param activity
+ */
+ private void checkIfGroupingRequired(Activity activity, Vector listOfValidationErrorDTOs)
+ {
+
+ Integer groupingSupportType = activity.getGroupingSupportType();
+ if (groupingSupportType.intValue() == Grouping.GROUPING_SUPPORT_REQUIRED)
+ {
+ //make sure activity has been assigned a grouping
+ Grouping grouping = activity.getGrouping();
+ if (grouping == null)
+ {
+ listOfValidationErrorDTOs.add(new ValidationErrorDTO(messageService.getMessage(ValidationErrorDTO.GROUPING_REQUIRED_ERROR_KEY), activity.getActivityUIID()));
+ }
+ }
+ else if(groupingSupportType.intValue() == Grouping.GROUPING_SUPPORT_NONE)
+ {
+ Grouping grouping = activity.getGrouping();
+ if (grouping != null)
+ {
+ listOfValidationErrorDTOs.add(new ValidationErrorDTO(messageService.getMessage(ValidationErrorDTO.GROUPING_NOT_REQUIRED_ERROR_KEY), activity.getActivityUIID()));
+ }
+ }
+
+ }
+
+ /**
+ * If this activity is an OptionalActivity, then it must contain one or more
+ * activities.
+ *
+ * @param parentActivity
+ */
+ private void validateOptionalActivity(Activity parentActivity, Vector listOfValidationErrorDTOs)
+ {
+
+ if (parentActivity.isOptionsActivity())
+ {
+ //get the child activities and check how many there are.
+ OptionsActivity optionsActivity = (OptionsActivity)parentActivity;
+ Set childActivities = optionsActivity.getActivities();
+ int numOfChildActivities = childActivities.size();
+ if(numOfChildActivities == 0)
+ {
+ listOfValidationErrorDTOs.add(new ValidationErrorDTO(messageService.getMessage(ValidationErrorDTO.OPTIONAL_ACTIVITY_ERROR_KEY), optionsActivity.getActivityUIID()));
+ }
+
+
+ }
+
+ }
+
+ /**
+ * This method ensures that the order id of the optional activities
+ * start from 1, are sequential and do not contain any duplicates.
+ * It will iterate through the child activities of the OptionalActivity,
+ * and compare the current activity id with the previous activity id.
+ * The currentActivityId should be 1 greater than the previous activity id.
+ * @param parentActivity
+ */
+ private void validateOptionsActivityOrderId(Activity parentActivity, Vector listOfValidationErrorDTOs)
+ {
+ Long thisActivityId = null;
+ Long previousActivityId = null;
+ boolean validOrderId = true;
+ if(parentActivity.isOptionsActivity())
+ {
+ OptionsActivity optionsActivity = (OptionsActivity)parentActivity;
+ Set childActivities = optionsActivity.getActivities(); //childActivities should be sorted according to order id (using the activityOrderComparator)
+ Iterator i = childActivities.iterator();
+ while (i.hasNext())
+ {
+ Activity childActivity = (Activity)i.next();
+ thisActivityId= childActivity.getActivityId();
+ if (previousActivityId != null)
+ {
+ //compare the two numbers
+ if (thisActivityId.longValue() != (previousActivityId.longValue() + 1))
+ validOrderId = validOrderId && false;
+
+ }
+ else
+ {
+ //this is the first activity, since the previousActivityId is null
+ if(thisActivityId.longValue()!= 1)
+ validOrderId = validOrderId && false;
+ }
+ previousActivityId = thisActivityId;
+ }
+
+ if (!validOrderId)
+ listOfValidationErrorDTOs.add(new ValidationErrorDTO(messageService.getMessage(ValidationErrorDTO.OPTIONAL_ACTIVITY_ORDER_ID_INVALID_ERROR_KEY), optionsActivity.getActivityUIID()));
+
+ }
+ }
+
+
+ /**
+ * If applyGrouping is set, then the grouping must exist
+ * @param activity
+ */
+ private void validateGroupingIfGroupingIsApplied(Activity activity, Vector listOfValidationErrorDTOs)
+ {
+ if(activity.getApplyGrouping().booleanValue()) //if grouping is applied, ensure grouping exists
+ {
+ if (activity.getGrouping() == null)
+ {
+ listOfValidationErrorDTOs.add(new ValidationErrorDTO(messageService.getMessage(ValidationErrorDTO.GROUPING_SELECTED_ERROR), activity.getActivityUIID()));
+ }
+ }
+
+ }
+
+
+
+}