Skip to content

Commit

Permalink
Merge pull request #48 from Clarenceeey/multiDelete
Browse files Browse the repository at this point in the history
Multi delete for Delete Student By Index
  • Loading branch information
notnotmax authored Oct 10, 2024
2 parents 42a9f7a + a19c32d commit 54786ee
Show file tree
Hide file tree
Showing 13 changed files with 261 additions and 47 deletions.
10 changes: 6 additions & 4 deletions docs/UserGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized fo

* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` : Adds a contact named `John Doe` to the Address Book.

* `delete 3` : Deletes the 3rd contact shown in the current list.
* `delete i/3` : Deletes the 3rd contact shown in the current list.

* `clear` : Deletes all contacts.

Expand Down Expand Up @@ -131,15 +131,17 @@ Examples:

Deletes the specified person from the address book.

Format: `delete INDEX`
Format: `delete i/INDEX...`

* Deletes the person at the specified `INDEX`.
* The index refers to the index number shown in the displayed person list.
* The index **must be a positive integer** 1, 2, 3, …​
* Can delete multiple items at once

Examples:
* `list` followed by `delete 2` deletes the 2nd person in the address book.
* `find Betsy` followed by `delete 1` deletes the 1st person in the results of the `find` command.
* `list` followed by `delete i/2` deletes the 2nd person in the address book.
* `list` followed by `delete i/2 i/3` deletes the 2nd and 3rd person in the address book.
* `find Betsy` followed by `delete i/1` deletes the 1st person in the results of the `find` command.

### Clearing all entries : `clear`

Expand Down
5 changes: 5 additions & 0 deletions src/main/java/seedu/address/commons/core/index/Index.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,9 @@ public boolean equals(Object other) {
public String toString() {
return new ToStringBuilder(this).add("zeroBasedIndex", zeroBasedIndex).toString();
}

@Override
public int hashCode() {
return this.zeroBasedIndex;
}
}
4 changes: 3 additions & 1 deletion src/main/java/seedu/address/logic/Messages.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ public class Messages {

public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command";
public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s";
public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid";
public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person provided at index %1$d is invalid";

public static final String MESSAGE_INVALID_INDEX_SHOWN = "The person provided at index/indices %1$s is invalid";
public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!";
public static final String MESSAGE_DUPLICATE_FIELDS =
"Multiple values specified for the following single-valued field(s): ";
Expand Down
53 changes: 40 additions & 13 deletions src/main/java/seedu/address/logic/commands/DeleteCommand.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package seedu.address.logic.commands;

import static java.util.Objects.requireNonNull;
import static seedu.address.logic.parser.CliSyntax.PREFIX_DELETE_INDEX;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import seedu.address.commons.core.index.Index;
import seedu.address.commons.util.ToStringBuilder;
Expand All @@ -20,29 +24,52 @@ public class DeleteCommand extends Command {

public static final String MESSAGE_USAGE = COMMAND_WORD
+ ": Deletes the person identified by the index number used in the displayed person list.\n"
+ "Parameters: INDEX (must be a positive integer)\n"
+ "Example: " + COMMAND_WORD + " 1";
+ "Parameters: " + PREFIX_DELETE_INDEX + "INDEX (must be a positive integer)...\n"
+ "Example: " + COMMAND_WORD + " " + PREFIX_DELETE_INDEX + "1";

public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s";
public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person(s):\n%1$s";

private final Index targetIndex;
private final Set<Index> targetIndices;

public DeleteCommand(Index targetIndex) {
this.targetIndex = targetIndex;
public DeleteCommand(Set<Index> targetIndices) {
this.targetIndices = targetIndices;
}

@Override
public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);
List<Person> lastShownList = model.getFilteredPersonList();

if (targetIndex.getZeroBased() >= lastShownList.size()) {
throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
boolean throwException = false;
ArrayList<Index> outOfBounds = new ArrayList<>();

for (Index item: targetIndices) {
if (item.getZeroBased() >= lastShownList.size()) {
throwException = true;
outOfBounds.add(item);
}
}

if (throwException) {
String formattedOutOfBoundIndices = outOfBounds.stream()
.map(index -> String.valueOf(index.getOneBased()))
.collect(Collectors.joining(", "));
throw new CommandException(String.format(Messages.MESSAGE_INVALID_INDEX_SHOWN, formattedOutOfBoundIndices));
}

Person personToDelete = lastShownList.get(targetIndex.getZeroBased());
model.deletePerson(personToDelete);
return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, Messages.format(personToDelete)));
List<Person> deletedPeople = targetIndices.stream()
.map(targetIndex -> lastShownList.get(targetIndex.getZeroBased()))
.toList();


deletedPeople.forEach(model::deletePerson);

String formattedDeletedPeople = deletedPeople.stream()
.map(Messages::format)
.collect(Collectors.joining("\n"));


return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, formattedDeletedPeople));
}

@Override
Expand All @@ -57,13 +84,13 @@ public boolean equals(Object other) {
}

DeleteCommand otherDeleteCommand = (DeleteCommand) other;
return targetIndex.equals(otherDeleteCommand.targetIndex);
return targetIndices.equals(otherDeleteCommand.targetIndices);
}

@Override
public String toString() {
return new ToStringBuilder(this)
.add("targetIndex", targetIndex)
.add("targetIndices", targetIndices)
.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public AddCommand parse(String args) throws ParseException {
ArgumentMultimap argMultimap =
ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);

if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL)
if (!AddCommandParser.arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL)
|| !argMultimap.getPreamble().isEmpty()) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
}
Expand All @@ -57,8 +57,7 @@ public AddCommand parse(String args) throws ParseException {
* Returns true if none of the prefixes contains empty {@code Optional} values in the given
* {@code ArgumentMultimap}.
*/
private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
private static boolean arePrefixesPresent(ArgumentMultimap argMultimap, Prefix... prefixes) {
return Stream.of(prefixes).allMatch(prefix -> argMultimap.getValue(prefix).isPresent());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public class ArgumentMultimap {
/** Prefixes mapped to their respective arguments**/
private final Map<Prefix, List<String>> argMultimap = new HashMap<>();



/**
* Associates the specified argument value with {@code prefix} key in this map.
* If the map previously contained a mapping for the key, the new value is appended to the list of existing values.
Expand Down
1 change: 1 addition & 0 deletions src/main/java/seedu/address/logic/parser/CliSyntax.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public class CliSyntax {
public static final Prefix PREFIX_EMAIL = new Prefix("e/");
public static final Prefix PREFIX_ADDRESS = new Prefix("a/");
public static final Prefix PREFIX_TAG = new Prefix("t/");
public static final Prefix PREFIX_DELETE_INDEX = new Prefix("i/");
public static final Prefix PREFIX_COURSE = new Prefix("c/");

}
58 changes: 54 additions & 4 deletions src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
package seedu.address.logic.parser;

import static java.util.Objects.requireNonNull;
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_DELETE_INDEX;

import java.util.Collection;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;

import seedu.address.commons.core.index.Index;
import seedu.address.logic.commands.DeleteCommand;
Expand All @@ -11,19 +19,61 @@
*/
public class DeleteCommandParser implements Parser<DeleteCommand> {

public static final String MESSAGE_EMPTY_INDEX = "Index is not provided.";

/**
* Parses the given {@code String} of arguments in the context of the DeleteCommand
* and returns a DeleteCommand object for execution.
* @throws ParseException if the user input does not conform the expected format
*/
public DeleteCommand parse(String args) throws ParseException {
requireNonNull(args);
ArgumentMultimap argMultimap =
ArgumentTokenizer.tokenize(args, PREFIX_DELETE_INDEX);

if (!DeleteCommandParser.arePrefixesPresent(argMultimap, PREFIX_DELETE_INDEX)
|| !argMultimap.getPreamble().isEmpty()) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE));
}

try {
Index index = ParserUtil.parseIndex(args);
return new DeleteCommand(index);

Set<Index> indices;
Optional<Set<Index>> optionalIndices = parseIndicesForDelete(argMultimap.getAllValues(PREFIX_DELETE_INDEX));
if (optionalIndices.isPresent() && !optionalIndices.get().isEmpty()) {
indices = optionalIndices.get();
return new DeleteCommand(indices);
} else {
throw new ParseException(MESSAGE_EMPTY_INDEX);
}

} catch (ParseException pe) {
throw new ParseException(
String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE), pe);
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE), pe);
}
}

/**
* Parses the given {@code String} of arguments and returns an optional set of Index objects.
* @param indices A collection of Strings representing indices
* @return An optional set which contains Index objects
* @throws ParseException if the user input does not conform the expected format
*/
private Optional<Set<Index>> parseIndicesForDelete(Collection<String> indices) throws ParseException {
assert indices != null;

if (indices.isEmpty()) {
return Optional.empty();
}
Collection<String> indicesSet = indices.size() == 1 && indices.contains("") ? Collections.emptySet() : indices;
return Optional.of(ParserUtil.parseIndices(indicesSet));
}

/**
* Returns true if none of the prefixes contains empty {@code Optional} values in the given
* {@code ArgumentMultimap}.
*/
private static boolean arePrefixesPresent(ArgumentMultimap argMultimap, Prefix... prefixes) {
return Stream.of(prefixes).allMatch(prefix -> argMultimap.getValue(prefix).isPresent());
}

}
12 changes: 12 additions & 0 deletions src/main/java/seedu/address/logic/parser/ParserUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ public static Index parseIndex(String oneBasedIndex) throws ParseException {
return Index.fromOneBased(Integer.parseInt(trimmedIndex));
}

/**
* Parses {@code Collection<String> indices} into a {@code Set<Index>}.
*/
public static Set<Index> parseIndices(Collection<String> indices) throws ParseException {
requireNonNull(indices);
final Set<Index> indicesSet = new HashSet<>();
for (String index : indices) {
indicesSet.add(parseIndex(index));
}
return indicesSet;
}

/**
* Parses a {@code String name} into a {@code Name}.
* Leading and trailing whitespaces will be trimmed.
Expand Down
9 changes: 6 additions & 3 deletions src/test/java/seedu/address/logic/LogicManagerTest.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package seedu.address.logic;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static seedu.address.logic.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX;
import static seedu.address.logic.Messages.MESSAGE_INVALID_INDEX_SHOWN;
import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND;
import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY;
import static seedu.address.logic.parser.CliSyntax.PREFIX_DELETE_INDEX;
import static seedu.address.testutil.Assert.assertThrows;
import static seedu.address.testutil.TypicalPersons.AMY;

Expand All @@ -33,6 +34,8 @@
import seedu.address.storage.StorageManager;
import seedu.address.testutil.PersonBuilder;



public class LogicManagerTest {
private static final IOException DUMMY_IO_EXCEPTION = new IOException("dummy IO exception");
private static final IOException DUMMY_AD_EXCEPTION = new AccessDeniedException("dummy access denied exception");
Expand Down Expand Up @@ -60,8 +63,8 @@ public void execute_invalidCommandFormat_throwsParseException() {

@Test
public void execute_commandExecutionError_throwsCommandException() {
String deleteCommand = "delete 9";
assertCommandException(deleteCommand, MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
String deleteCommand = "delete " + PREFIX_DELETE_INDEX + "9";
assertCommandException(deleteCommand, String.format(MESSAGE_INVALID_INDEX_SHOWN, "9"));
}

@Test
Expand Down
Loading

0 comments on commit 54786ee

Please sign in to comment.