Project: iContacts
iContacts is a Command Line Interface (CLI) application created for University Students who requires the functionality of managing contacts, deadlines and reminders in a single application.
The purpose of this portfolio is to summarise my contributions in iContacts and shows the readers how this contributions are implemented. In addition it contains a few proposed enhancements for upcoming versions of the application.
Code contributed: [Functional code] [Test code]
Exporting selected contacts : export
Exports selected contacts in iContacts.
Format: export r/RANGE p/PATH
Types of range inputs:
Examples:
-
list
export r/all p/c:\exports\classmates
Exports all the contacts to the file classmates.xml in path c:\exports. -
export r/1-4 p/c:\exports\classmates
Exports the contacts from index 1 to index 4 to the file classmates.xml in path c:\exports. -
export r/1-4,6,8 p/c:\exports\classmates
Exports the contacts at index 1 to index 4 with index 6 and index 8 to the file classmates.xml in path c:\exports.
End of Extract
Justification
Exporting contacts from the application is a key feature to enable users to share contact information. As this application is targeted at university students sharing of contacts is necessary in the daily life of the student.
Export mechanism
The ExportCommand
uses XmlAddressBookStorage
class to generate a xml file based on a given range and saves it to the path provided. It takes in two String
values range
and path
. Below is the constructor for the class:
public class ExportCommand extends Command {
public ExportCommand(String range, String path) {
requireNonNull(range);
requireNonNull(path);
this.range = range;
this.path = path;
exportBook = new AddressBook();
}
}
The method getRangefromInput()
splits the range using a separator and returns a String
array for the different values in the range.
Below is an extract of the method getRangefromInput()
:
public class ExportCommand extends Command {
private String[] getRangeFromInput() {
private String[] getRangeFromInput() {
String[] splitStringComma = this.range.split(",");
return splitStringComma;
}
}
}
To determine which contacts should be added to the exportBook
we have to check the the user input. There are three cases:
-
All (Priority)
-
if the word
all
is present in the user input, we will just export all the contacts from the last shown list.
-
-
Specific index (e.g. 1, 2, 3)
-
if the user input contains a specific index, we will add that index (one-based) to the
exportBook
.
-
-
Range of indexes (e.g. 1-5,8-10)
-
if the user input contains a range which is identified by the
-
character, we will add that range of index (one-based) to theexportBook
.
-
Below is the code snippet to identify the three cases in the user input:
public class ExportCommand extends Command {
@Override
public CommandResult execute() throws CommandException {
String[] multipleRange = getRangeFromInput();
if (multipleRange[0].equals("all")) {
exportAll();
} else {
for (int i = 0; i < multipleRange.length; i++) {
if (multipleRange[i].contains("-")) {
String[] rangeToExport = multipleRange[i].split("-");
try {
exportRange(Integer.parseInt(rangeToExport[0]), Integer.parseInt(rangeToExport[1]));
} catch (NumberFormatException e) {
throw new CommandException(MESSAGE_EXPORT_FAIL);
}
} else {
try {
exportSpecific(Integer.parseInt(multipleRange[i]));
} catch (NumberFormatException e) {
throw new CommandException(MESSAGE_EXPORT_FAIL);
}
}
}
}
/... storage is resolved here ...
}
}
The final step is to create the xml file from the exportBook
.
Below is the code snippet to export the data into an xml file using AddressBookStorage
.
public class ExportCommand extends Command {
@Override
public CommandResult execute() throws CommandException {
/... the exporting is resolved here ...
try {
AddressBookStorage storage = new XmlAddressBookStorage(path + ".xml");
storage.saveAddressBook(exportBook);
} catch (IOException ioe) {
return new CommandResult(MESSAGE_EXPORT_FAIL);
}
return new CommandResult(MESSAGE_EXPORT_SUCCESS);
}
}
End of Extract
Importing contacts from file : import
Imports contacts into iContacts.
Format: import p/PATH
Example:
-
import p/c:\exports\classmates.xml
Imports all the contacts stored in the file classmates.xml* into iContacts.
End of Extract
Justification
To complete the entire contact sharing experience, students must be able to import contacts shared by their peers with a single command.
Import mechanism
The ImportCommand
uses XmlAddressBookStorage
to generate a temporary AddressBook
object from a given path. It takes in a String
value path
. The command then adds the contacts found in the temporary AddressBook object into the main address book object. Below is the constructor for the class:
public class ImportCommand extends UndoableCommand {
public ImportCommand(String path) {
this.path = path;
this.addressBookStorage = new XmlAddressBookStorage(path);
numSuccess = 0;
numDuplicates = 0;
}
}
The ImportCommand
first checks if an AddressBook object can be initialised from the specified path, and if it does it initialises a new Person
object and adds it into the main AddressBook
object through the model
. Below is the code snippet:
public class ImportCommand extends UndoableCommand {
@Override
public CommandResult executeUndoableCommand() throws CommandException {
try {
if (addressBookStorage.readAddressBook().isPresent()) {
this.addressBook = new AddressBook(addressBookStorage.readAddressBook().get());
for (ReadOnlyPerson person : this.addressBook.getPersonList()) {
Person personToAdd = new Person(person);
personToAdd.setPopularityCounter(new PopularityCounter());
personToAdd.setDisplayPicture(new DisplayPicture(""));
try {
model.clearSelection();
model.showDefaultPanel();
model.addPerson(personToAdd);
numSuccess++;
} catch (DuplicatePersonException e) {
numDuplicates++;
}
}
} else {
throw new CommandException(String.format(MESSAGE_INVALID_FILE, path));
}
} catch (DataConversionException | IOException e) {
throw new CommandException(String.format(MESSAGE_INVALID_FILE, path));
}
return new CommandResult(String.format(MESSAGE_SUCCESS, numSuccess, numDuplicates));
}
}
Design Considerations
Aspect: Should PopularityCounter
and DisplayPicture
be set to the default value
Alternative 1 (current choice): Both PopularityCounter
and DisplayPicture
should be set to the default value.
Pros: Does not affect the contact’s PopularityCounter
and allows the user to set their own preferred DisplayPicture
of a contact.
Cons: Users will not be able to share DisplayPicture.
and
Alternative 2: Import the contacts with both `PopularityCounterDisplayPicture
unchanged.
Pros: DisplayPicture
will be imported into iContacts.
Cons: The PopularityCounter
of the user’s contacts will be inaccurate.
End of Extract
Locating persons by name: find
Finds persons whose names or nicknames contain any of the given keywords.
Format: find KEYWORD [MORE_KEYWORDS]
Examples:
-
find John
Returnsjohn
andJohn Doe
-
find Betsy Tim John
Returns any person having names or nicknames Betsy
, Tim
, or John
End of Extract
Justification
With the implementation of the nickname feature, the find command should allow users to search for contacts using both nicknames and names. ==== Implementation
Start of Extract [from: Developer Guide]
Find mechanism
The FindCommand
uses NameContainsKeywordsPredicate
to find contacts with matching names or nicknames. It accepts List<String> nameKeywords
as the parameter that is parsed by FindCommandParser
. Below is the constructor for the class:
public FindCommand(NameContainsKeywordsPredicate predicate) {
this.predicate = predicate;
}
The method test(ReadOnlyPerson person)
iterates through nameKeywords
to find a match with the name or nickname of every person
from the address book.
Below is an extract of the method test(ReadOnlyPerson person
.
@Override
public boolean test(ReadOnlyPerson person) {
return keywords.stream()
.anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword))
|| keywords.stream()
.anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getNickname().value, keyword));
}
For the FindCommand
to work properly nameKeywords
must be non-empty. The code extract below checks for empty inputs:
public FindCommand parse(String args) throws ParseException {
String trimmedArgs = args.trim();
if (trimmedArgs.isEmpty()) {
throw new ParseException(
String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
}
String[] nameKeywords = trimmedArgs.split("\\s+");
return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords)));
}
End of Extract
Sorting all persons : sort
Sorts and shows a list of all contacts in the iContacts alphabetically.
Format: sort
End of Extract
Justification
When a contact is added to iContacts it is being appended to the end of the list, creating a very unorganised list. With sorting users can choose to sort iContacts in alphabetical order to have more organised contact list.
Sorting mechanism
The SortCommand
uses a SortedList
that is initialised with the current filteredPerson
and is sorted using a comparator. Below is the code snippet:
sortedfilteredPersons = new SortedList<>(filteredPersons);
@Override
public void sortFilteredPersonList() {
Comparator<ReadOnlyPerson> sortByName = (o1, o2) -> o1.getName().fullName.compareTo(o2.getName().fullName);
sortedfilteredPersons.setComparator(sortByName);
indicateAddressBookChanged();
}
End of Extract
Enhancement Proposed: Automatically add required field prefixs when a valid command is entered
It is not possible to remember all the required fields for all the different commands in iContacts. Although the user is able to refer to check the required field from the help command, it makes the entire experience troublesome. With this enhancement as long as the user types in a valid command all required fields prefix will appear. This will allow for a better user experience.
Enhancement Proposed: delete multiple contacts
The current delete feature for iContacts only allows deleting 1 contact at a time. it will be troublesome if the student wants to delete a group of contacts. With this enhancement the user will be able to delete multiple contacts in a single command.
Enhancement Proposed: Enhance sort command to allow sorting by date added
The current sort feature only sorts iContacts alphabetically, more sorting options can be added.
Other contributions
-
Implemented Colored Tags https://github.com/CS2103AUG2017-W14-B1/main/pull/60