Index: lams_central/src/java/org/lamsfoundation/lams/authoring/LearningDesignValidator.java =================================================================== diff -u -r3089c12021557041b3259a1f00d3e79cbec2cda2 -rb742536e1befb2354b4e4a7c8aee64b22c24d5e7 --- lams_central/src/java/org/lamsfoundation/lams/authoring/LearningDesignValidator.java (.../LearningDesignValidator.java) (revision 3089c12021557041b3259a1f00d3e79cbec2cda2) +++ lams_central/src/java/org/lamsfoundation/lams/authoring/LearningDesignValidator.java (.../LearningDesignValidator.java) (revision b742536e1befb2354b4e4a7c8aee64b22c24d5e7) @@ -14,84 +14,136 @@ import java.util.Vector; import org.lamsfoundation.lams.util.wddx.FlashMessage; +/** + * The LearningDesignValidator 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 LearningDesignValidator { - protected Logger log = Logger.getLogger(LearningDesignValidator.class); + //protected Logger log = Logger.getLogger(LearningDesignValidator.class); protected LearningDesignDAO learningDesignDAO = null; private Vector listOfValidationErrorDTOs = null; private FlashMessage flashMessage; + /* + * Default constructor + * initialises the list of validation messages. + */ public LearningDesignValidator(LearningDesignDAO learningDesignDAO) { this.learningDesignDAO = learningDesignDAO; listOfValidationErrorDTOs = new Vector(); } - + + /** + * This method calls other validation methods which apply the validation + * rules to determine whether or not the learning design is valid. + * + * @param learningDesign + * @return + */ public FlashMessage validateLearningDesign(LearningDesign learningDesign) { + boolean valid = true; - boolean valid = false; - - checkTransitionForActivities(learningDesign.getTransitions()); + validateActivityTransitionRules(learningDesign.getParentActivities(), learningDesign.getTransitions()); + validateGeneral(learningDesign.getActivities()); - Set topLevelActivities = learningDesign.getParentActivities(); - - checkActivitiesForTransition(topLevelActivities); - //check for input/output transition - checkTopLevelActivitiesForInputTransition(topLevelActivities); - checkTopLevelActivitiesForOutputTransition(topLevelActivities); - checkIfGroupingRequired(learningDesign.getActivities()); - validateOptionalActivity(topLevelActivities); //havent tested - validateGroupingIfGroupingIsApplied(topLevelActivities); //havent tested - - //if the size of the vector is > 0, then design is invalid, otherwise flag the design valid. if (listOfValidationErrorDTOs.size() > 0) { + valid = false; flashMessage = new FlashMessage("storeLearningDesign", new StoreLearningDesignResultsDTO(valid,listOfValidationErrorDTOs), FlashMessage.ERROR); } else { - valid = true; - learningDesign.setValidDesign(new Boolean(true)); + learningDesign.setValidDesign(new Boolean(valid)); learningDesignDAO.insert(learningDesign); flashMessage = new FlashMessage("storeLearningDesign", new StoreLearningDesignResultsDTO(valid, learningDesign.getLearningDesignId())); } return flashMessage; } - private void checkActivitiesForTransition(Set topLevelActivities) + /** + * 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) { - //if one activity, then shouldnt have any transitions - int numOfActivities = topLevelActivities.size(); + validateTransitions(transitions); + 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(); - Iterator i = topLevelActivities.iterator(); - - while (i.hasNext()) + while (activityIterator.hasNext()) { - Activity activity = (Activity)i.next(); - Transition inputTransition = activity.getTransitionTo(); - Transition outputTransition = activity.getTransitionFrom(); + Activity activity = (Activity)activityIterator.next(); + checkActivityForTransition(activity, numOfTopLevelActivities); + 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(ValidationErrorDTO.INPUT_TRANSITION_ERROR_TYPE2)); - if(numOfActivities > 1) + if (noInputTransition.size() > 1) { - if (inputTransition == null && outputTransition == null) + //there is more than one activity with no input transitions + Iterator noInputTransitionIterator = noInputTransition.iterator(); + while (noInputTransitionIterator.hasNext()) { - listOfValidationErrorDTOs.add(new ValidationErrorDTO(ValidationErrorDTO.LD_ACTIVITY_TRANSITION_ERROR, activity.getActivityUIID())); + Activity a = (Activity)noInputTransitionIterator.next(); + listOfValidationErrorDTOs.add(new ValidationErrorDTO(ValidationErrorDTO.INPUT_TRANSITION_ERROR_TYPE1, a.getActivityUIID())); } } - else if (numOfActivities == 1) + + if (noOuputTransition.size() == 0) + listOfValidationErrorDTOs.add(new ValidationErrorDTO(ValidationErrorDTO.OUTPUT_TRANSITION_ERROR_TYPE2)); + if (noOuputTransition.size() > 1) { - if (inputTransition != null || outputTransition != null) + //there is more than one activity with no output transitions + Iterator noOutputTransitionIterator = noOuputTransition.iterator(); + while (noOutputTransitionIterator.hasNext()) { - listOfValidationErrorDTOs.add(new ValidationErrorDTO(ValidationErrorDTO.LD_ACTIVITY_TRANSITION_ERROR, activity.getActivityUIID())); + Activity a = (Activity)noOutputTransitionIterator.next(); + listOfValidationErrorDTOs.add(new ValidationErrorDTO(ValidationErrorDTO.OUTPUT_TRANSITION_ERROR_TYPE1, a.getActivityUIID())); } } } - + } - private void checkTransitionForActivities(Set transitions) + /** + * 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) { Iterator i = transitions.iterator(); while (i.hasNext()) @@ -106,104 +158,71 @@ } - } - private int countNumOfActivitiesWithNoInputTransition(Set topLevelActivities) + /** + * 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) { - int numOfActivitiesWithNoInputTransition = 0; - Iterator i = topLevelActivities.iterator(); - while (i.hasNext()) - { - Activity activity = (Activity)i.next(); - Transition inputTransition = activity.getTransitionTo(); - if (inputTransition == null) - { - numOfActivitiesWithNoInputTransition = numOfActivitiesWithNoInputTransition + 1; - } - } + //if one activity, then shouldnt have any transitions + Transition inputTransition = activity.getTransitionTo(); + Transition outputTransition = activity.getTransitionFrom(); - return numOfActivitiesWithNoInputTransition; - - } - - //only one should have an input transition - private void checkTopLevelActivitiesForInputTransition(Set topLevelActivities) - { - int numOfActivitiesWithNoInputTransition = countNumOfActivitiesWithNoInputTransition(topLevelActivities); - if (numOfActivitiesWithNoInputTransition > 1) + if(numOfActivities > 1) { - Iterator i = topLevelActivities.iterator(); - while (i.hasNext()) - { - Activity activity = (Activity)i.next(); - Transition inputTransition = activity.getTransitionTo(); - if (inputTransition == null) - { - listOfValidationErrorDTOs.add(new ValidationErrorDTO(ValidationErrorDTO.INPUT_TRANSITION_ERROR_TYPE1, activity.getActivityUIID())); - } - } + if (inputTransition == null && outputTransition == null) + listOfValidationErrorDTOs.add(new ValidationErrorDTO(ValidationErrorDTO.ACTIVITY_TRANSITION_ERROR, activity.getActivityUIID())); + } - - //if exactly one top level activity has no input transition then just return an empty list. - if (numOfActivitiesWithNoInputTransition == 0) - listOfValidationErrorDTOs.add(new ValidationErrorDTO(ValidationErrorDTO.INPUT_TRANSITION_ERROR_TYPE2)); - - - } - - private int countNumOfActivitiesWithNoOutputTransition(Set topLevelActivities) - { - int numOfActivitiesWithNoOutputTransition = 0; - Iterator i = topLevelActivities.iterator(); - while (i.hasNext()) - { - Activity activity = (Activity)i.next(); - Transition outputTransition = activity.getTransitionFrom(); - if (outputTransition == null) - { - numOfActivitiesWithNoOutputTransition = numOfActivitiesWithNoOutputTransition+ 1; - } + if (numOfActivities == 1) + { + if (inputTransition != null || outputTransition != null) + listOfValidationErrorDTOs.add(new ValidationErrorDTO(ValidationErrorDTO.ACTIVITY_TRANSITION_ERROR, activity.getActivityUIID())); + } - - return numOfActivitiesWithNoOutputTransition; - + } - //only one should have an output transition - private void checkTopLevelActivitiesForOutputTransition(Set topLevelActivities) + /** + * This method will call all other validation methods. + * + * @param activities + */ + private void validateGeneral(Set activities) { - int numOfActivitiesWithNoOutputTransition = countNumOfActivitiesWithNoOutputTransition(topLevelActivities); - - if (numOfActivitiesWithNoOutputTransition > 1) + Iterator activityIterator = activities.iterator(); + while (activityIterator.hasNext()) { - Iterator i = topLevelActivities.iterator(); - while (i.hasNext()) - { - Activity activity = (Activity)i.next(); - Transition outputTransition = activity.getTransitionFrom(); - if (outputTransition == null) - { - numOfActivitiesWithNoOutputTransition =+ 1; - listOfValidationErrorDTOs.add(new ValidationErrorDTO(ValidationErrorDTO.OUTPUT_TRANSITION_ERROR_TYPE1, activity.getActivityUIID())); - } - } + Activity activity = (Activity)activityIterator.next(); + checkIfGroupingRequired(activity); + validateGroupingIfGroupingIsApplied(activity); + validateOptionalActivity(activity); + validateOptionsActivityOrderId(activity); } - //if more than one activity is missing an input transition, return the list; - if (numOfActivitiesWithNoOutputTransition == 0) - listOfValidationErrorDTOs.add(new ValidationErrorDTO(ValidationErrorDTO.OUTPUT_TRANSITION_ERROR_TYPE2)); - - } - private void checkIfGroupingRequired(Set activities) + /** + * 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) { - Integer groupingSupportType; - Iterator i = activities.iterator(); - while (i.hasNext()) - { - Activity activity = (Activity)i.next(); - groupingSupportType = activity.getGroupingSupportType(); + + Integer groupingSupportType = activity.getGroupingSupportType(); if (groupingSupportType.intValue() == Grouping.GROUPING_SUPPORT_REQUIRED) { //make sure activity has been assigned a grouping @@ -221,17 +240,18 @@ listOfValidationErrorDTOs.add(new ValidationErrorDTO(ValidationErrorDTO.GROUPING_NOT_REQUIRED_ERROR, activity.getActivityUIID())); } } - } + } - - private void validateOptionalActivity(Set parentActivities) + /** + * If this activity is an OptionalActivity, then it must contain one or more + * activities. + * + * @param parentActivity + */ + private void validateOptionalActivity(Activity parentActivity) { - Iterator parentActivityIterator = parentActivities.iterator(); - - while (parentActivityIterator.hasNext()) - { - Activity parentActivity = (Activity)parentActivityIterator.next(); + if (parentActivity.isOptionsActivity()) { //get the child activities and check how many there are. @@ -243,27 +263,72 @@ listOfValidationErrorDTOs.add(new ValidationErrorDTO(ValidationErrorDTO.OPTIONAL_ACTIVITY_ERROR, optionsActivity.getActivityUIID())); } + } - } + } - //if grouping is selected then grouping exists - private void validateGroupingIfGroupingIsApplied(Set parentActivities) + /** + * 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) { - Iterator i = parentActivities.iterator(); - while (i.hasNext()) + Long thisActivityId = null; + Long previousActivityId = null; + boolean validOrderId = true; + if(parentActivity.isOptionsActivity()) { - Activity activity = (Activity)i.next(); + 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(ValidationErrorDTO.OPTIONAL_ACTIVITY_ORDER_ID_INVALID_ERROR, optionsActivity.getActivityUIID())); + + } + } + + + /** + * If applyGrouping is set, then the grouping must exist + * @param activity + */ + private void validateGroupingIfGroupingIsApplied(Activity activity) + { if(activity.getApplyGrouping().booleanValue()) //if grouping is applied, ensure grouping exists { if (activity.getGrouping() == null) { listOfValidationErrorDTOs.add(new ValidationErrorDTO(ValidationErrorDTO.GROUPING_SELECTED_ERROR, activity.getActivityUIID())); } } - - } + } - + + }