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
allis 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
ReturnsjohnandJohn 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