Skip to content

Commit

Permalink
Feature undo for regime (#102)
Browse files Browse the repository at this point in the history
* Refactor CommandToEvent method in EventFactory to check for command word string

* Implement undo for add regime command

* Implement undo for delete regime and also add new EventPayload class to manage payload

* Refactor undo-redo code to make use of EventPayload when sharing important data

* Resolve checkstyle issues

* Clean up code for events and commands classes

* Include javadoc comments for ResourceTypeDependentCommand

Closes #65
  • Loading branch information
garylyp authored and t-cheepeng committed Oct 23, 2019
1 parent 87b5595 commit 686891c
Show file tree
Hide file tree
Showing 27 changed files with 706 additions and 177 deletions.
7 changes: 6 additions & 1 deletion src/main/java/seedu/exercise/logic/commands/AddCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@
/**
* Represents an AddCommand with hidden internal logic and the ability to be executed.
*/
public abstract class AddCommand extends Command implements UndoableCommand {
public abstract class AddCommand extends Command implements UndoableCommand, ResourceTypeDependentCommand {

public static final String COMMAND_WORD = "add";

public static final String MESSAGE_USAGE = COMMAND_WORD
+ ": Adds exercise to exercise list or adds regime to regime list.\n"
+ "EXERCISE: " + MESSAGE_USAGE_EXERCISE + "\n"
+ "REGIME: " + MESSAGE_USAGE_REGIME;

@Override
public String getUndoableCommandWord() {
return COMMAND_WORD;
}
}
35 changes: 21 additions & 14 deletions src/main/java/seedu/exercise/logic/commands/AddExerciseCommand.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package seedu.exercise.logic.commands;

import static java.util.Objects.requireNonNull;
import static seedu.exercise.logic.commands.events.AddExerciseEvent.KEY_EXERCISE_TO_ADD;
import static seedu.exercise.logic.parser.CliSyntax.PREFIX_CALORIES;
import static seedu.exercise.logic.parser.CliSyntax.PREFIX_CATEGORY;
import static seedu.exercise.logic.parser.CliSyntax.PREFIX_DATE;
Expand All @@ -10,14 +11,15 @@
import static seedu.exercise.logic.parser.CliSyntax.PREFIX_UNIT;

import seedu.exercise.logic.commands.events.EventHistory;
import seedu.exercise.logic.commands.events.EventPayload;
import seedu.exercise.logic.commands.exceptions.CommandException;
import seedu.exercise.model.Model;
import seedu.exercise.model.resource.Exercise;

/**
* Adds an exercise to the exercise book.
*/
public class AddExerciseCommand extends AddCommand {
public class AddExerciseCommand extends AddCommand implements PayloadCarrierCommand {

public static final String MESSAGE_USAGE_EXERCISE = "Parameters: "
+ PREFIX_CATEGORY + "CATEGORY "
Expand All @@ -38,42 +40,47 @@ public class AddExerciseCommand extends AddCommand {

public static final String MESSAGE_SUCCESS = "New exercise added: %1$s";
public static final String MESSAGE_DUPLICATE_EXERCISE = "This exercise already exists in the exercise book";
public static final String RESOURCE_TYPE = "exercise";

private Exercise toAdd;
private Exercise exerciseToAdd;
private EventPayload<Exercise> eventPayload;

/**
* Creates an AddExerciseCommand to add the specified {@code Exercise}
*/
public AddExerciseCommand(Exercise exercise) {
requireNonNull(exercise);
toAdd = exercise;
exerciseToAdd = exercise;
eventPayload = new EventPayload<>();
}

@Override
public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);
if (model.hasExercise(toAdd)) {
if (model.hasExercise(exerciseToAdd)) {
throw new CommandException(MESSAGE_DUPLICATE_EXERCISE);
}

model.addExercise(toAdd);
model.addExercise(exerciseToAdd);
eventPayload.put(KEY_EXERCISE_TO_ADD, exerciseToAdd);
EventHistory.getInstance().addCommandToUndoStack(this);
return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd));
return new CommandResult(String.format(MESSAGE_SUCCESS, exerciseToAdd));
}

/**
* Returns the exercise to be added to the exercise book.
*
* @return exercise to be added
*/
public Exercise getExercise() {
return toAdd;
@Override
public EventPayload<Exercise> getPayload() {
return eventPayload;
}

@Override
public String getResourceType() {
return RESOURCE_TYPE;
}

@Override
public boolean equals(Object other) {
return other == this // short circuit if same object
|| (other instanceof AddExerciseCommand // instanceof handles nulls
&& toAdd.equals(((AddExerciseCommand) other).toAdd));
&& exerciseToAdd.equals(((AddExerciseCommand) other).exerciseToAdd));
}
}
210 changes: 169 additions & 41 deletions src/main/java/seedu/exercise/logic/commands/AddRegimeCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,21 @@

import static java.util.Objects.requireNonNull;
import static seedu.exercise.commons.util.CollectionUtil.requireAllNonNull;
import static seedu.exercise.logic.commands.events.AddRegimeEvent.KEY_REGIME_TO_ADD;
import static seedu.exercise.logic.commands.events.EditRegimeEvent.KEY_EDITED_REGIME;
import static seedu.exercise.logic.commands.events.EditRegimeEvent.KEY_IS_REGIME_EDITED;
import static seedu.exercise.logic.commands.events.EditRegimeEvent.KEY_ORIGINAL_REGIME;
import static seedu.exercise.logic.parser.CliSyntax.PREFIX_CATEGORY;
import static seedu.exercise.logic.parser.CliSyntax.PREFIX_INDEX;
import static seedu.exercise.logic.parser.CliSyntax.PREFIX_NAME;

import java.util.HashSet;
import java.util.List;

import seedu.exercise.commons.core.Messages;
import seedu.exercise.commons.core.index.Index;
import seedu.exercise.logic.commands.events.EventHistory;
import seedu.exercise.logic.commands.events.EventPayload;
import seedu.exercise.logic.commands.exceptions.CommandException;
import seedu.exercise.model.Model;
import seedu.exercise.model.UniqueResourceList;
Expand All @@ -20,7 +27,7 @@
/**
* Adds a regime to the regime book.
*/
public class AddRegimeCommand extends AddCommand {
public class AddRegimeCommand extends AddCommand implements PayloadCarrierCommand {

public static final String MESSAGE_USAGE_REGIME = "Parameters: "
+ PREFIX_CATEGORY + "CATEGORY "
Expand All @@ -34,71 +41,192 @@ public class AddRegimeCommand extends AddCommand {

public static final String MESSAGE_SUCCESS_NEW_REGIME = "Added new regime to regime list.";
public static final String MESSAGE_SUCCESS_ADD_EXERCISE_TO_REGIME = "Added exercises to regime.";
public static final String MESSAGE_DUPLICATE_EXERCISE_IN_REGIME = "Exercise already in regime.";
public static final String MESSAGE_DUPLICATE_EXERCISE_IN_REGIME = "Duplicate exercise found in regime.";
public static final String MESSAGE_NO_EXERCISES_ADDED = "No index provided, nothing changes.";
public static final String MESSAGE_DUPLICATE_INDEX_WHEN_CREATING_REGIME = "There is duplicate index.";
public static final String MESSAGE_DUPLICATE_INDEX = "There is duplicate index.";
public static final String RESOURCE_TYPE = "regime";

private List<Index> toAddIndexes;
private Name name;
private EventPayload<Object> eventPayload;

public AddRegimeCommand(List<Index> indexes, Name name) {
requireAllNonNull(indexes, name);
toAddIndexes = indexes;
this.name = name;
this.toAddIndexes = indexes;
this.eventPayload = new EventPayload<>();
}

@Override
public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);

List<Exercise> lastShownList = model.getFilteredExerciseList();
checkDuplicateIndexes();
checkValidIndexes(toAddIndexes, lastShownList);

CommandResult commandResult;
if (!isRegimeInModel(model)) {
commandResult = addNewRegimeToModel(model);
} else {
commandResult = addExercisesToExistingRegime(model);
}
EventHistory.getInstance().addCommandToUndoStack(this);
return commandResult;
}

//check all index valid
for (Index targetIndex : toAddIndexes) {
if (targetIndex.getZeroBased() >= lastShownList.size()) {
throw new CommandException(Messages.MESSAGE_INVALID_EXERCISE_DISPLAYED_INDEX);
}
/**
* Adds a new regime with exercises added based on the list of {@code Index} passed into the command.
*
* @param model {@code Model} which the command should operate on
* @return feedback message of the operation result for display
*/
private CommandResult addNewRegimeToModel(Model model) throws CommandException {
Regime regime = new Regime(name, new UniqueResourceList<>());
addExercisesToRegime(regime, model);
model.addRegime(regime);
addToEventPayloadForAddRegime(regime);
return new CommandResult(MESSAGE_SUCCESS_NEW_REGIME);
}

/**
* Adds exercises to the specified regime based on the list of {@code Index} passed into the command.
*
* @param model {@code Model} which the command should operate on
* @return feedback message of the operation result for display
*/
private CommandResult addExercisesToExistingRegime(Model model) throws CommandException {
checkIndexesNotEmpty();
Regime originalRegime = getRegimeFromModel(model);
Regime editedRegime = originalRegime.deepCopy();
addExercisesToRegime(editedRegime, model);
addToEventPayloadForEditRegime(originalRegime, editedRegime);

model.setRegime(originalRegime, editedRegime);
model.updateFilteredRegimeList(Model.PREDICATE_SHOW_ALL_REGIMES);
return new CommandResult(MESSAGE_SUCCESS_ADD_EXERCISE_TO_REGIME);
}

/**
* Returns the actual regime object with all the existing exercises.
*
* @param model {@code Model} which the command should operate on
* @return the existing regime from model
*/
private Regime getRegimeFromModel(Model model) {
List<Regime> regimes = model.getFilteredRegimeList();
int regimeIndex = model.getRegimeIndex(new Regime(name, new UniqueResourceList<>()));
return regimes.get(regimeIndex);
}

/**
* Adds all exercises into the specified regime based on the given indexes.
*
* @param regime the regime to add exercises to
* @param model {@code Model} which the command should operate on
* @throws CommandException If duplicate exercises are found
*/
private void addExercisesToRegime(Regime regime, Model model) throws CommandException {
List<Exercise> lastShownList = model.getFilteredExerciseList();
for (Index index : toAddIndexes) {
Exercise exercise = lastShownList.get(index.getZeroBased());
checkDuplicateExerciseInRegime(exercise, regime);
regime.addExercise(exercise);
}
}

//create new regime
Regime regime = new Regime(name, new UniqueResourceList<Exercise>());
if (!model.hasRegime(regime)) {
for (Index index : toAddIndexes) {
if (regime.getRegimeExercises().contains(lastShownList.get(index.getZeroBased()))) {
throw new CommandException(MESSAGE_DUPLICATE_INDEX_WHEN_CREATING_REGIME);
}
regime.addExercise(lastShownList.get(index.getZeroBased()));
}
/**
* Checks whether the {@code Model} contains a regime with the same name.
*
* @param model {@code Model} which the command should operate on
* @return true if a regime of the same name exists, false otherwise
*/
private boolean isRegimeInModel(Model model) {
Regime regime = new Regime(name, new UniqueResourceList<>());
return model.hasRegime(regime);
}

model.addRegime(regime);
return new CommandResult(MESSAGE_SUCCESS_NEW_REGIME);
} else { //add exercise to existing regime
if (toAddIndexes.size() == 0) {
throw new CommandException(MESSAGE_NO_EXERCISES_ADDED);
}
/**
* Checks whether an exercise is already found in the regime.
*
* @param exercise exercise to be checked against the regime's list
* @param regime the regime to be checked with
* @throws CommandException If a duplicate exercise is found in the regime
*/
private void checkDuplicateExerciseInRegime(Exercise exercise, Regime regime) throws CommandException {
if (regime.getRegimeExercises().contains(exercise)) {
throw new CommandException(MESSAGE_DUPLICATE_EXERCISE_IN_REGIME);
}
}

int indexOfRegime = model.getRegimeIndex(regime);
List<Regime> regimes = model.getFilteredRegimeList();
Regime regimeToAddExercises = regimes.get(indexOfRegime);
/**
* Checks whether the given indexes contain duplicates.
*
* @throws CommandException If a duplicate index is found
*/
private void checkDuplicateIndexes() throws CommandException {
HashSet<Index> indexesSet = new HashSet<>(toAddIndexes);
if (indexesSet.size() < toAddIndexes.size()) {
throw new CommandException(MESSAGE_DUPLICATE_INDEX);
}
}

UniqueResourceList<Exercise> currentExerciseList = regimeToAddExercises.getRegimeExercises();
/**
* Checks if the given indexes is empty.
*
* @throws CommandException If no indexes are provided at all
*/
private void checkIndexesNotEmpty() throws CommandException {
if (toAddIndexes.size() == 0) {
throw new CommandException(MESSAGE_NO_EXERCISES_ADDED);
}
}

//check whether exercise is in current exercise list in regime
for (Index index : toAddIndexes) {
if (currentExerciseList.contains(lastShownList.get(index.getZeroBased()))) {
throw new CommandException(MESSAGE_DUPLICATE_EXERCISE_IN_REGIME);
}
/**
* Checks whether the list of indexes provided is valid,
*
* @param indexes the list of {@code Index} passed into the command
* @param exerciseList the current exercise list of the regime
* @throws CommandException If any one of the indexes is greater than the size of the regime's exercise list
*/
private void checkValidIndexes(List<Index> indexes, List<Exercise> exerciseList) throws CommandException {
for (Index targetIndex : indexes) {
if (targetIndex.getZeroBased() >= exerciseList.size()) {
throw new CommandException(Messages.MESSAGE_INVALID_EXERCISE_DISPLAYED_INDEX);
}
}
}

//add exercise to regime
for (Index index : toAddIndexes) {
regimeToAddExercises.addExercise(lastShownList.get(index.getZeroBased()));
}
/**
* Stores the regime to be added in this command.
*
* @param regimeToAdd the regime to be added
*/
private void addToEventPayloadForAddRegime(Regime regimeToAdd) {
eventPayload.put(KEY_IS_REGIME_EDITED, false);
eventPayload.put(KEY_REGIME_TO_ADD, regimeToAdd);
}

model.setRegime(regime, regimeToAddExercises);
model.updateFilteredRegimeList(Model.PREDICATE_SHOW_ALL_REGIMES);
return new CommandResult(MESSAGE_SUCCESS_ADD_EXERCISE_TO_REGIME);
}
/**
* Stores the various states of the exercise to the payload.
*
* @param originalRegime the regime before it is edited
* @param editedRegime the regime after it is edited
*/
private void addToEventPayloadForEditRegime(Regime originalRegime, Regime editedRegime) {
eventPayload.put(KEY_IS_REGIME_EDITED, true);
eventPayload.put(KEY_ORIGINAL_REGIME, originalRegime);
eventPayload.put(KEY_EDITED_REGIME, editedRegime);
}

@Override
public EventPayload<Object> getPayload() {
return eventPayload;
}

@Override
public String getResourceType() {
return RESOURCE_TYPE;
}

@Override
Expand Down
Loading

0 comments on commit 686891c

Please sign in to comment.