diff --git a/.gitignore b/.gitignore index 823d175eb670..720da77b3905 100644 --- a/.gitignore +++ b/.gitignore @@ -16,5 +16,6 @@ preferences.json classes/ /data/ /bin/ -src/main/resources/docs/ out/ +/versions/ +/src/main/resources/docs/ diff --git a/Collate-TUI.jar b/Collate-TUI.jar new file mode 100644 index 000000000000..2b9e032106dd Binary files /dev/null and b/Collate-TUI.jar differ diff --git a/LICENSE b/LICENSE index 39b3478982c3..b2ce44ca8766 100644 --- a/LICENSE +++ b/LICENSE @@ -2,11 +2,11 @@ MIT License Copyright (c) 2016 Software Engineering Education - FOSS Resources -Permission is hereby granted, free of charge, to any person obtaining a copy +Permission is hereby granted, free of charge, to any item obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is +copies of the Software, and to permit items to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all diff --git a/README.adoc b/README.adoc index 142ae1b17fbd..b1f7edcfa3db 100644 --- a/README.adoc +++ b/README.adoc @@ -1,41 +1,32 @@ -= Address Book (Level 4) += JalilEnterprises ifdef::env-github,env-browser[:relfileprefix: docs/] -https://travis-ci.org/se-edu/addressbook-level4[image:https://travis-ci.org/se-edu/addressbook-level4.svg?branch=master[Build Status]] -https://ci.appveyor.com/project/damithc/addressbook-level4[image:https://ci.appveyor.com/api/projects/status/3boko2x2vr5cc3w2?svg=true[Build status]] -https://coveralls.io/github/se-edu/addressbook-level4?branch=master[image:https://coveralls.io/repos/github/se-edu/addressbook-level4/badge.svg?branch=master[Coverage Status]] -https://www.codacy.com/app/damith/addressbook-level4?utm_source=github.com&utm_medium=referral&utm_content=se-edu/addressbook-level4&utm_campaign=Badge_Grade[image:https://api.codacy.com/project/badge/Grade/fc0b7775cf7f4fdeaf08776f3d8e364a[Codacy Badge]] -https://gitter.im/se-edu/Lobby[image:https://badges.gitter.im/se-edu/Lobby.svg[Gitter chat]] +https://travis-ci.org/CS2113-AY1819S1-T12-3/main[image:https://travis-ci.org/CS2113-AY1819S1-T12-3/main.svg?branch=master[Build Status]] +https://ci.appveyor.com/project/gaoqikai/main[image:https://ci.appveyor.com/api/projects/status/436ch41il0wo8259/branch/master?svg=true[Build status]] +https://app.codacy.com/project/CS2113-AY1819S1-T12-3/main/dashboard[image:https://api.codacy.com/project/badge/Grade/d58395f1d4e14eab986c887e16859476[Codacy Badge]] -ifdef::env-github[] -image::docs/images/Ui.png[width="600"] -endif::[] + +//ifndef::env-github[] +//image::docs/images/Ui.png[width="600"] +//endif::[] ifndef::env-github[] image::images/Ui.png[width="600"] endif::[] -* This is a desktop Address Book application. It has a GUI but most of the user interactions happen using a CLI (Command Line Interface). -* It is a Java sample application intended for students learning Software Engineering while using Java as the main programming language. -* It is *written in OOP fashion*. It provides a *reasonably well-written* code example that is *significantly bigger* (around 6 KLoC)than what students usually write in beginner-level SE modules. -* What's different from https://github.com/se-edu/addressbook-level3[level 3]: -** A more sophisticated GUI that includes a list panel and an in-built Browser. -** More test cases, including automated GUI testing. -** Support for _Build Automation_ using Gradle and for _Continuous Integration_ using Travis CI. +* This is a desktop Jalil Enterprises Stock List application. It has a GUI but most of the user interactions happen using a CLI (Command Line Interface). +* This application is inspired by our own observation of the work done by the lab technicians, and ideas on how it can be improved. +* It is a Java application intended for CG (Computer Engineering) lab technicians to help them manage the lab equipments. == Site Map * <> * <> -* <> * <> * <> == Acknowledgements - -* Some parts of this sample application were inspired by the excellent http://code.makery.ch/library/javafx-8-tutorial/[Java FX tutorial] by -_Marco Jakob_. +* This application was morphed from the original AddressBook application. * Libraries used: https://github.com/TestFX/TestFX[TextFX], https://bitbucket.org/controlsfx/controlsfx/[ControlsFX], https://github.com/FasterXML/jackson[Jackson], https://github.com/google/guava[Guava], https://github.com/junit-team/junit5[JUnit5] == Licence : link:LICENSE[MIT] - diff --git a/build.gradle b/build.gradle index f8e614f8b49b..9b7421b94ff5 100644 --- a/build.gradle +++ b/build.gradle @@ -82,7 +82,7 @@ dependencies { } shadowJar { - archiveName = 'addressbook.jar' + archiveName = 'jalilenterprises.jar' destinationDir = file("${buildDir}/jar/") } @@ -126,7 +126,7 @@ nonGuiTests.dependsOn test task(allTests) // `allTests` implies both `guiTests` and `nonGuiTests` -allTests.dependsOn guiTests +//allTests.dependsOn guiTests allTests.dependsOn nonGuiTests test { @@ -207,8 +207,8 @@ asciidoctor { idprefix: '', // for compatibility with GitHub preview idseparator: '-', 'site-root': "${sourceDir}", // must be the same as sourceDir, do not modify - 'site-name': 'AddressBook-Level4', - 'site-githuburl': 'https://github.com/se-edu/addressbook-level4', + 'site-name': 'JalilEnterprises', + 'site-githuburl': 'https://github.com/CS2113-AY1819S1-T12-3/main', 'site-seedu': true, // delete this line if your project is not a fork (not a SE-EDU project) ] @@ -233,6 +233,7 @@ task deployOfflineDocs(type: Copy) { include 'stylesheets/*' include 'images/*' include 'HelpWindow.html' + include 'template.xsl' } } diff --git a/collated/functional/ChewKinWhye.md b/collated/functional/ChewKinWhye.md new file mode 100644 index 000000000000..72bf0080b128 --- /dev/null +++ b/collated/functional/ChewKinWhye.md @@ -0,0 +1,824 @@ +# ChewKinWhye +###### \java\seedu\address\logic\commands\ChangeStatusCommand.java +``` java + +/** + * Updates the status of an existing item in the stock list. + */ + +public class ChangeStatusCommand extends Command { + public static final String COMMAND_WORD = "changeStatus"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Changes the status of the item identified " + + "by the name of the item.\n" + + "The status field can only take the values Ready or Faulty\n" + + "Changing the status to On_Loan is not allowed\n" + + "Parameters: " + + PREFIX_NAME + "NAME " + + PREFIX_QUANTITY + "QUANTITY " + + PREFIX_ORIGINAL_STATUS + "ORIGINAL STATUS " + + PREFIX_NEW_STATUS + "NEW STATUS \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "Arduino " + + PREFIX_QUANTITY + "5 " + + PREFIX_ORIGINAL_STATUS + "Ready " + + PREFIX_NEW_STATUS + "Faulty"; + public static final String MESSAGE_CHANGE_STATUS_SUCCESS = "Changed Status: %1$s"; + public static final String MESSAGE_INVALID_STATUS_FIELD = "The status description is invalid"; + public static final String MESSAGE_INVALID_NAME_FIELD = "The item does not exist"; + public static final String MESSAGE_STATUS_CONSTRAINTS = + "The updated value of each status field has to be positive"; + + private Index index; + private final ChangeStatusDescriptor changeStatusDescriptor; + public ChangeStatusCommand(ChangeStatusDescriptor changeStatusDescriptor) { + requireNonNull(changeStatusDescriptor); + this.changeStatusDescriptor = new ChangeStatusDescriptor(changeStatusDescriptor); + } + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + List lastShownList = model.getFilteredItemList(); + + index = getIndex(lastShownList, changeStatusDescriptor); + + Item itemToUpdate = lastShownList.get(index.getZeroBased()); + + Item updatedItem = createUpdatedItem(itemToUpdate, changeStatusDescriptor); + + model.updateItem(itemToUpdate, updatedItem); + model.updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS); + model.commitStockList(); + return new CommandResult(String.format(MESSAGE_CHANGE_STATUS_SUCCESS, updatedItem)); + + + } + + public Index getIndex(List lastShownList, ChangeStatusDescriptor changeStatusDescriptor) + throws CommandException { + Index index; + int counter = 0; + for (Item item:lastShownList) { + if (item.getName().equals(changeStatusDescriptor.getName())) { + index = Index.fromZeroBased(counter); + return index; + } + counter++; + } + throw new CommandException(MESSAGE_INVALID_NAME_FIELD); + } + /** + * Creates and returns a {@code Item} with the details of {@code itemToUpdate} + * edited with {@code changeStatusDescriptor}. + */ + public Item createUpdatedItem(Item itemToUpdate, + ChangeStatusDescriptor changeStatusDescriptor) throws CommandException { + assert itemToUpdate != null; + Status currentStatus = itemToUpdate.getStatus(); + Status updatedStatus; + Integer updatedReady = currentStatus.getStatusReady(); + Integer updatedOnLoan = currentStatus.getStatusOnLoan(); + Integer updatedFaulty = currentStatus.getStatusFaulty(); + + Integer changeStatusValue = changeStatusDescriptor.getQuantity(); + switch (changeStatusDescriptor.getInitialStatus()) { + case "Ready": + updatedReady -= changeStatusValue; + break; + case "On_Loan": + updatedOnLoan -= changeStatusValue; + break; + case "Faulty": + updatedFaulty -= changeStatusValue; + break; + default: + throw new CommandException(MESSAGE_INVALID_STATUS_FIELD); + } + + switch (changeStatusDescriptor.getUpdatedStatus()) { + case "Ready": + updatedReady += changeStatusValue; + break; + case "On_Loan": + updatedOnLoan += changeStatusValue; + break; + case "Faulty": + updatedFaulty += changeStatusValue; + break; + default: + throw new CommandException(MESSAGE_INVALID_STATUS_FIELD); + } + if (updatedReady < 0 || updatedOnLoan < 0 || updatedFaulty < 0) { + throw new CommandException(MESSAGE_STATUS_CONSTRAINTS); + } + updatedStatus = new Status(updatedReady, updatedOnLoan, updatedFaulty); + + return new Item(itemToUpdate.getName(), itemToUpdate.getQuantity(), itemToUpdate.getMinQuantity(), + itemToUpdate.getLoststatus(), updatedStatus, itemToUpdate.getTags()); + } + /** + * Stores the details to update the item with. + */ + public static class ChangeStatusDescriptor { + private Name name; + private Integer changeStatusQuantity; + private String initialStatus; + private String updatedStatus; + + public ChangeStatusDescriptor() { + } + + public ChangeStatusDescriptor(Name name, Integer changeStatusQuantity, + String initialStatus, String updatedStatus) { + this.name = name; + this.changeStatusQuantity = changeStatusQuantity; + this.initialStatus = initialStatus; + this.updatedStatus = updatedStatus; + } + /** + * Copy constructor. + * A defensive copy of {@code tags} is used internally. + */ + public ChangeStatusDescriptor (ChangeStatusDescriptor toCopy) { + setName(toCopy.name); + setQuantity(toCopy.changeStatusQuantity); + setInitialStatus(toCopy.initialStatus); + setUpdatedStatus(toCopy.updatedStatus); + } + public void setName(Name name) { + this.name = name; + } + public Name getName() { + return name; + } + public void setQuantity(Integer changeStatusQuantity) { + this.changeStatusQuantity = changeStatusQuantity; + } + public Integer getQuantity() { + return changeStatusQuantity; + } + public void setInitialStatus(String initialStatus) { + this.initialStatus = initialStatus; + } + public String getInitialStatus() { + return initialStatus; + } + + public void setUpdatedStatus(String updatedStatus) { + this.updatedStatus = updatedStatus; + } + public String getUpdatedStatus() { + return updatedStatus; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof ChangeStatusDescriptor)) { + return false; + } + + ChangeStatusDescriptor otherItem = (ChangeStatusDescriptor) other; + return (otherItem.getName().equals(this.getName()) + && otherItem.getQuantity().equals(this.getQuantity()) + && otherItem.getInitialStatus().equals(this.getInitialStatus()) + && otherItem.getUpdatedStatus().equals(this.getUpdatedStatus())); + } + + } +} +``` +###### \java\seedu\address\logic\commands\DeleteLoanListCommand.java +``` java + +/** + * Deletes an entry in the loan list base on the Index + */ + +public class DeleteLoanListCommand extends Command { + public static final String COMMAND_WORD = "deleteLoanList"; + public static final String MESSAGE_SUCCESS = "Loan list entry has been deleted"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Deletes the loan list entry identified " + + "by the index number used in the display of the loan list.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + public static final String MESSAGE_EMPTY = "Loan list is currently empty"; + public static final String MESSAGE_INVALID_INDEX = "The input index is invalid"; + + private final Index index; + + public DeleteLoanListCommand(Index index) { + this.index = index; + } + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + if (!MainApp.getLoanListFile().exists()) { + throw new CommandException(MESSAGE_EMPTY); + } + File loanListFile = MainApp.getLoanListFile(); + try { + deleteLoanList(model, history, loanListFile); + } catch (Exception e) { + System.out.println(e.toString()); + } + return new CommandResult(String.format(MESSAGE_SUCCESS)); + } + /** + * Deletes an entry in the loan list base on the Index + */ + public void deleteLoanList(Model model, CommandHistory history, File loanListFile) throws Exception { + + JAXBContext context = JAXBContext.newInstance(XmlAdaptedLoanList.class); + Unmarshaller unmarshaller = context.createUnmarshaller(); + XmlAdaptedLoanList xmlAdaptedLoanList = (XmlAdaptedLoanList) unmarshaller + .unmarshal(loanListFile); + ArrayList loanList = xmlAdaptedLoanList.getLoanList(); + + if (index.getOneBased() > loanList.size()) { + throw new CommandException(MESSAGE_INVALID_INDEX); + } + + updateStatus(model, history, loanList.get(index.getZeroBased())); + + loanList.remove(index.getZeroBased()); + + LoanListCommand.updateXmlLoanListFile(new XmlAdaptedLoanList(loanList), loanListFile); + } + /** + * Changes the status from Ready to On_Loan + */ + private void updateStatus(Model model, CommandHistory history, XmlAdaptedLoanerDescription loanerDescription) + throws CommandException { + ChangeStatusCommand.ChangeStatusDescriptor changeStatusDescriptor = + new ChangeStatusCommand.ChangeStatusDescriptor(new Name(loanerDescription.getItemName()), + loanerDescription.getQuantity(), "On_Loan", "Ready"); + ChangeStatusCommand changeStatusCommand = new ChangeStatusCommand(changeStatusDescriptor); + changeStatusCommand.execute(model, history); + } +} +``` +###### \java\seedu\address\logic\commands\LoanListCommand.java +``` java + +/** + * Creates a loan list, and updates the status from Ready to On_Loan + */ + +public class LoanListCommand extends Command { + public static final String COMMAND_WORD = "loanList"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Creates a loan list. \n" + + "Parameters: " + + PREFIX_NAME + "NAME " + + PREFIX_QUANTITY + "QUANTITY " + + PREFIX_LOANER + "LOANER\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "Arduino " + + PREFIX_QUANTITY + "20 " + + PREFIX_LOANER + "KinWhye"; + + public static final String MESSAGE_SUCCESS = "Loan list created"; + + private final LoanerDescription loaner; + + public LoanListCommand(LoanerDescription loaner) { + this.loaner = loaner; + } + /** + * Updates the XmlLoanListFile + */ + + public static void updateXmlLoanListFile(XmlAdaptedLoanList xmlAdaptedLoanList, File loanListFile) + throws JAXBException { + JAXBContext jaxbContext = JAXBContext.newInstance(XmlAdaptedLoanList.class); + Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); + jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + jaxbMarshaller.marshal(xmlAdaptedLoanList, System.out); + jaxbMarshaller.marshal(xmlAdaptedLoanList, loanListFile); + } + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + updateStatus(model, history); + try { + updateLoanList(MainApp.getLoanListFile(), loaner); + } catch (JAXBException e) { + System.out.println(e.toString()); + } + + return new CommandResult(MESSAGE_SUCCESS); + } + /** + * Updates the XmlAdaptedLoanList, then updates the XmlLoanListFile + */ + public void updateLoanList(File loanListFile, LoanerDescription loaner) throws JAXBException { + XmlAdaptedLoanerDescription toAdd = new XmlAdaptedLoanerDescription(loaner); + JAXBContext context = JAXBContext.newInstance(XmlAdaptedLoanList.class); + XmlAdaptedLoanList xmlAdaptedLoanList = new XmlAdaptedLoanList(); + if (loanListFile.exists()) { + Unmarshaller unmarshaller = context.createUnmarshaller(); + xmlAdaptedLoanList = (XmlAdaptedLoanList) unmarshaller + .unmarshal(loanListFile); + } + xmlAdaptedLoanList.addLoaner(toAdd); + updateXmlLoanListFile(xmlAdaptedLoanList, loanListFile); + } + /** + * Changes the status from On_Loan to Ready + */ + public void updateStatus(Model model, CommandHistory history) throws CommandException { + ChangeStatusCommand.ChangeStatusDescriptor changeStatusDescriptor = + new ChangeStatusCommand.ChangeStatusDescriptor(loaner.getItemName(), + loaner.getQuantity().toInteger(), "Ready", "On_Loan"); + ChangeStatusCommand changeStatusCommand = new ChangeStatusCommand(changeStatusDescriptor); + changeStatusCommand.execute(model, history); + } +} +``` +###### \java\seedu\address\logic\commands\StatusCommand.java +``` java + +/** + * Lists all items in the stock list to the user grouped by the status. + */ +public class StatusCommand extends Command { + + public static final String COMMAND_WORD = "status"; + public static final String MESSAGE_SUCCESS = "Items listed according to status"; + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + ArrayList readyItems = new ArrayList<>(); + ArrayList onLoanItems = new ArrayList<>(); + ArrayList faultyItems = new ArrayList<>(); + + List lastShownList = model.getFilteredItemList(); + + sortSimpleItems(lastShownList, readyItems, onLoanItems, faultyItems); + + String messageOutput = getMessageOutput(readyItems, onLoanItems, faultyItems); + + model.updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS); + + return new CommandResult(messageOutput); + } + + /** + * Sorts the item in the item list into Ready, On_Loan, and Faulty. + */ + void sortSimpleItems (List lastShownList, ArrayList readyItems, + ArrayList onLoanItems, ArrayList faultyItems) { + for (Item item : lastShownList) { + if (item.getStatus().getStatusReady() > 0) { + readyItems.add(new SimpleItem(item.getName(), + new Quantity(Integer.toString(item.getStatus().getStatusReady())))); + } + if (item.getStatus().getStatusOnLoan() > 0) { + onLoanItems.add(new SimpleItem(item.getName(), + new Quantity(Integer.toString(item.getStatus().getStatusOnLoan())))); + } + if (item.getStatus().getStatusFaulty() > 0) { + faultyItems.add(new SimpleItem(item.getName(), + new Quantity(Integer.toString(item.getStatus().getStatusFaulty())))); + } + } + } + + String getMessageOutput (ArrayList readyItems, + ArrayList onLoanItems, ArrayList faultyItems) { + String messageOutput = ""; + messageOutput += MESSAGE_SUCCESS + "\n"; + + messageOutput += "Ready: "; + int counter = 0; + for (SimpleItem simpleItem : readyItems) { + counter++; + messageOutput += simpleItem.getName() + ": " + simpleItem.getQuantity().toString(); + if (counter != readyItems.size()) { + messageOutput += ", "; + } + } + + messageOutput += "\nOn Loan: "; + counter = 0; + for (SimpleItem simpleItem : onLoanItems) { + counter++; + messageOutput += simpleItem.getName() + ": " + simpleItem.getQuantity().toString(); + if (counter != onLoanItems.size()) { + messageOutput += ", "; + } + } + + + messageOutput += "\nFaulty: "; + counter = 0; + for (SimpleItem simpleItem : faultyItems) { + counter++; + messageOutput += simpleItem.getName() + ": " + simpleItem.getQuantity().toString(); + if (counter != onLoanItems.size()) { + messageOutput += ", "; + } + } + + return messageOutput; + } + +} +``` +###### \java\seedu\address\logic\commands\ViewLoanListCommand.java +``` java + +/** + * Lists all the entries in the loan list to the user. + */ + +public class ViewLoanListCommand extends Command { + public static final String COMMAND_WORD = "viewLoanList"; + public static final String MESSAGE_SUCCESS = "Loan list displayed"; + public static final String MESSAGE_EMPTY = "Loan list is currently empty"; + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + if (!MainApp.getLoanListFile().exists()) { + throw new CommandException(MESSAGE_EMPTY); + } + String messageOutput = getMessageOutput(MainApp.getLoanListFile()); + return new CommandResult(messageOutput); + } + + public String getMessageOutput(File loanListFile) throws CommandException { + try { + Integer counter = 1; + String messageOutput = new String(); + messageOutput += MESSAGE_SUCCESS + "\n"; + JAXBContext context = JAXBContext.newInstance(XmlAdaptedLoanList.class); + Unmarshaller unmarshaller = context.createUnmarshaller(); + XmlAdaptedLoanList xmlAdaptedLoanList = (XmlAdaptedLoanList) unmarshaller + .unmarshal(loanListFile); + if (xmlAdaptedLoanList.getLoanList().size() == 0) { + throw new CommandException(MESSAGE_EMPTY); + } + for (XmlAdaptedLoanerDescription loanerDescription : xmlAdaptedLoanList.getLoanList()) { + messageOutput += counter + ". "; + messageOutput += loanerDescription.getLoanerName() + ": "; + messageOutput += loanerDescription.getItemName() + " "; + messageOutput += loanerDescription.getQuantity() + "\n"; + counter++; + } + return messageOutput; + } catch (JAXBException e) { + System.out.println(e.toString()); + } + return null; + } +} +``` +###### \java\seedu\address\logic\parser\ChangeStatusCommandParser.java +``` java + +/** + * Parses input arguments and creates a new ChangeStatusCommand object + */ +public class ChangeStatusCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the ChangeStatusCommand + * and returns an ChangeStatusCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + + public ChangeStatusCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, + PREFIX_QUANTITY, PREFIX_ORIGINAL_STATUS, PREFIX_NEW_STATUS); + + if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_QUANTITY, PREFIX_ORIGINAL_STATUS, PREFIX_NEW_STATUS) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ChangeStatusCommand.MESSAGE_USAGE)); + } + String initialStatus = ParserUtil + .parseStatus(argMultimap.getValue(PREFIX_ORIGINAL_STATUS).get()); + String updatedStatus = ParserUtil + .parseStatus(argMultimap.getValue(PREFIX_NEW_STATUS).get()); + if (initialStatus.equals("On_Loan") || updatedStatus.equals("On_Loan")) { + throw new ParseException(String.format(ChangeStatusCommand.MESSAGE_INVALID_STATUS_FIELD)); + } + ChangeStatusDescriptor changeStatusDescriptor = new ChangeStatusDescriptor(); + changeStatusDescriptor.setName(ParserUtil + .parseName(argMultimap.getValue(PREFIX_NAME).get())); + changeStatusDescriptor.setQuantity(ParserUtil + .parseQuantity(argMultimap.getValue(PREFIX_QUANTITY).get()).toInteger()); + changeStatusDescriptor.setInitialStatus(initialStatus); + changeStatusDescriptor.setUpdatedStatus(updatedStatus); + + return new ChangeStatusCommand(changeStatusDescriptor); + } + + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } +} +``` +###### \java\seedu\address\logic\parser\DeleteLoanListCommandParser.java +``` java + +/** + * Parses input arguments and creates a new DeleteLoanListCommand object + */ +public class DeleteLoanListCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the DeleteLoanListCommand + * and returns an DeleteLoanListCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + + public DeleteLoanListCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new DeleteLoanListCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteLoanListCommand.MESSAGE_USAGE), pe); + } + } +} +``` +###### \java\seedu\address\logic\parser\LoanListCommandParser.java +``` java + +/** + * Parses input arguments and creates a new LoanListCommand object + */ +public class LoanListCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the LoanListCommand + * and returns an LoanListCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + + public LoanListCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_QUANTITY, PREFIX_LOANER); + + if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_QUANTITY, PREFIX_LOANER) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, LoanListCommand.MESSAGE_USAGE)); + } + Name itemName = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); + Name loanerName = ParserUtil.parseName(argMultimap.getValue(PREFIX_LOANER).get()); + Quantity quantity = ParserUtil.parseQuantity(argMultimap.getValue(PREFIX_QUANTITY).get()); + return new LoanListCommand(new LoanerDescription(itemName, loanerName, quantity)); + } + + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } +} +``` +###### \java\seedu\address\model\item\LoanerDescription.java +``` java + +/** + * Represents a loaner in the loan list. + */ + +public class LoanerDescription { + private final Name itemName; + private final Name loanerName; + private final Quantity quantity; + + public LoanerDescription(Name itemName, Name loanerName, Quantity quantity) { + requireAllNonNull(itemName, loanerName, quantity); + this.itemName = itemName; + this.loanerName = loanerName; + this.quantity = quantity; + } + public LoanerDescription(LoanerDescription loaner) { + itemName = loaner.getItemName(); + loanerName = loaner.getLoanerName(); + quantity = loaner.getQuantity(); + } + public Name getItemName() { + return itemName; + } + + public Quantity getQuantity() { + return quantity; + } + public Name getLoanerName() { + return loanerName; + } + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getLoanerName()) + .append(" loanerName: ") + .append(getLoanerName()) + .append(" itemName: ") + .append(getItemName()) + .append(" Quantity: ") + .append(getQuantity()); + return builder.toString(); + } +} +``` +###### \java\seedu\address\model\item\SimpleItem.java +``` java + +/** + * Simplified item with fields removed to be used in the status command + */ + +public class SimpleItem { + private final Name name; + private final Quantity quantity; + + public SimpleItem(Name name, Quantity quantity) { + requireAllNonNull(name, quantity); + this.name = name; + this.quantity = quantity; + } + public Name getName() { + return name; + } + + public Quantity getQuantity() { + return quantity; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof SimpleItem)) { + return false; + } + + SimpleItem otherItem = (SimpleItem) other; + return (otherItem.getName().toString().equals(getName().toString()) + && otherItem.getQuantity().toString().equals(getQuantity().toString())); + } +} +``` +###### \java\seedu\address\model\item\Status.java +``` java + +/** + * Represents an Item's status in the stock list. + */ +public class Status { + public static final int STATUS_READY = 0; + public static final int STATUS_ON_LOAN = 1; + public static final int STATUS_FAULTY = 2; + private final List status = new ArrayList<>(3); + + public Status() { + this(0, 0, 0); + } + public Status(int ready, int onLoan, int faulty) { + status.add(ready); + status.add(onLoan); + status.add(faulty); + } + public Integer getStatusReady() { + return status.get(STATUS_READY); + } + public Integer getStatusOnLoan() { + return status.get(STATUS_ON_LOAN); + } + public Integer getStatusFaulty() { + return status.get(STATUS_FAULTY); + } + + public void setStatusReady(Integer ready) { + status.set(STATUS_READY, ready); + } + public void setStatusOnLoan(Integer onLoan) { + status.set(STATUS_ON_LOAN, onLoan); + } + public void setStatusFaulty(Integer faulty) { + status.set(STATUS_FAULTY, faulty); + } + public void setDefaultValues(int quantity) { + status.set(STATUS_READY, quantity); + status.set(STATUS_ON_LOAN, 0); + status.set(STATUS_FAULTY, 0); + } +} + +``` +###### \java\seedu\address\storage\XmlAdaptedLoanerDescription.java +``` java + +/** + * JAXB-friendly version of the LoanerDescription. + */ +@XmlRootElement(name = "Loaner") +@XmlAccessorType(XmlAccessType.FIELD) + +public class XmlAdaptedLoanerDescription { + private String itemName; + private String loanerName; + private Integer quantity; + + public XmlAdaptedLoanerDescription() { + + } + public XmlAdaptedLoanerDescription(String itemName, String loanerName, Integer quantity) { + this.itemName = itemName; + this.loanerName = loanerName; + this.quantity = quantity; + } + public XmlAdaptedLoanerDescription(LoanerDescription loaner) { + itemName = loaner.getItemName().toString(); + loanerName = loaner.getLoanerName().toString(); + quantity = loaner.getQuantity().toInteger(); + } + + public String getItemName() { + return itemName; + } + + public void setItemName(String itemName) { + this.itemName = itemName; + } + + public String getLoanerName() { + return loanerName; + } + + public void setLoanerName(String loanerName) { + this.loanerName = loanerName; + } + + public Integer getQuantity() { + return quantity; + } + + public void setQuantity(Integer quantity) { + this.quantity = quantity; + } +} +``` +###### \java\seedu\address\storage\XmlAdaptedLoanList.java +``` java + +/** + * JAXB-friendly loan list. + */ + +@XmlRootElement(name = "LoanList") +@XmlAccessorType(XmlAccessType.FIELD) + +public class XmlAdaptedLoanList { + @XmlElement(name = "Loaner") + private ArrayList xmlAdaptedLoanList = null; + + public XmlAdaptedLoanList() { + xmlAdaptedLoanList = new ArrayList<>(); + } + public XmlAdaptedLoanList(ArrayList xmlAdaptedLoanList) { + this.xmlAdaptedLoanList = xmlAdaptedLoanList; + } + public ArrayList getLoanList() { + return xmlAdaptedLoanList; + } + + public void addLoaner(XmlAdaptedLoanerDescription toAdd) { + xmlAdaptedLoanList.add(toAdd); + } + +} +``` diff --git a/collated/functional/HeHaowei.md b/collated/functional/HeHaowei.md new file mode 100644 index 000000000000..83d197116553 --- /dev/null +++ b/collated/functional/HeHaowei.md @@ -0,0 +1,405 @@ +# HeHaowei +###### \java\seedu\address\logic\commands\FoundCommand.java +``` java + +/** + * Found an existing item in the stock list. + */ + +public class FoundCommand extends Command { + public static final String COMMAND_WORD = "found"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Found a item from the stock list identified " + + "by the index number used in the displayed item list" + + "Parameters: INDEX (must be a positive integer) " + + PREFIX_QUANTITY + "QUANTITY\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_QUANTITY + "25"; + + + public static final String MESSAGE_FOUND_ITEM_SUCCESS = "Found Item: %1$s"; + public static final String MESSAGE_INVALID_QUANTITY = "The found quantity input is invalid"; + public static final String MESSAGE_FOUND_LARGER_THAN_LOST = "The quantity of found items " + + "must be less than or equal to quantity of lost items"; + + private final Index targetIndex; + private final FoundDescriptor foundDescriptor; + + public FoundCommand(Index targetIndex, FoundDescriptor foundDescriptor) { + requireNonNull(targetIndex); + requireNonNull(foundDescriptor); + this.targetIndex = targetIndex; + this.foundDescriptor = new FoundDescriptor(foundDescriptor); + + } + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + List lastShownList = model.getFilteredItemList(); + + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_ITEM_DISPLAYED_INDEX); + } + + Item itemToFound = lastShownList.get(targetIndex.getZeroBased()); + Item foundItem = createFoundItem(itemToFound, foundDescriptor); + + if (!itemToFound.isSameItem(foundItem) && model.hasItem(foundItem)) { + throw new CommandException(MESSAGE_INVALID_QUANTITY); + } + model.updateItem(itemToFound, foundItem); + model.updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS); + model.commitStockList(); + return new CommandResult(String.format(MESSAGE_FOUND_ITEM_SUCCESS, foundItem)); + } + + /** + * Creates and returns a {@code Item} with the details of {@code itemToFound} + * edited with {@code FoundDescriptor}. + */ + private static Item createFoundItem(Item itemToFound, FoundDescriptor foundDescriptor) + throws CommandException { + assert itemToFound != null; + Loststatus currentLoststatus = itemToFound.getLoststatus(); + Loststatus updatedLoststatus; + Integer updatedLost = currentLoststatus.getLoststatusLost(); + Integer updatedFound = currentLoststatus.getLoststatusFound(); + + Integer initialReadyValue = itemToFound.getStatus().getStatusReady(); + + Integer updatedValue = foundDescriptor.getFoundQuantity(); + Integer initialValue = itemToFound.getQuantity().toInteger(); + + updatedLost -= updatedValue; + updatedFound += updatedValue; + if (updatedLost < 0) { + throw new CommandException(MESSAGE_FOUND_LARGER_THAN_LOST); + } + updatedLoststatus = new Loststatus(updatedLost, updatedFound); + + Quantity updatedQuantity = new Quantity(Integer.toString(initialValue + updatedValue)); + Status updatedStatus = new Status(initialReadyValue + updatedValue, + itemToFound.getStatus().getStatusOnLoan(), itemToFound.getStatus().getStatusFaulty()); + + + + + return new Item(itemToFound.getName(), updatedQuantity, + itemToFound.getMinQuantity(), updatedLoststatus, updatedStatus, itemToFound.getTags()); + } + + /** + * Stores the details to lost the item with. + */ + + public static class FoundDescriptor { + private Integer foundQuantity; + + public FoundDescriptor() {} + + public FoundDescriptor(FoundDescriptor toCopy) { + setFoundQuantity(toCopy.foundQuantity); + + } + public void setFoundQuantity(Integer foundQuantity) { + this.foundQuantity = foundQuantity; } + + public Integer getFoundQuantity() { + return foundQuantity; } + + } + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FoundCommand // instanceof handles nulls + && targetIndex.equals(((FoundCommand) other).targetIndex)); // state check + } +} +``` +###### \java\seedu\address\logic\commands\LostandFoundCommand.java +``` java + +/** + * Lists all lost items with the lost number in the stock list to the user. + */ +public class LostandFoundCommand extends Command { + + public static final String COMMAND_WORD = "lost&found"; + public static final String MESSAGE_SUCCESS = "Lost items listed"; + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + ArrayList lostItems = new ArrayList<>(); + List lastShownList = model.getFilteredItemList(); + sortSimpleItems(lastShownList, lostItems); + String messageOutput = getMessageOutput(lostItems); + model.updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS); + return new CommandResult(messageOutput); + } + + /** + * Sorts the lost item from the Stock List. + */ + void sortSimpleItems (List lastShownList, List lostItems + ) { + for (Item item : lastShownList) { + if (item.getLoststatus().getLoststatusLost() > 0) { + lostItems.add(new SimpleItem(item.getName(), + new Quantity(Integer.toString(item.getLoststatus().getLoststatusLost())))); + } + + } + } + String getMessageOutput (List lostItems) { + String messageOutput = ""; + messageOutput += MESSAGE_SUCCESS + "\n"; + + messageOutput += "Lost: "; + int counter = 0; + for (SimpleItem simpleItem : lostItems) { + counter++; + messageOutput += counter + ". " + + + simpleItem.getName() + + + ": " + + simpleItem.getQuantity().toString() + + "\n" + + " "; + + } + + return messageOutput; + } + +} +``` +###### \java\seedu\address\logic\commands\LostCommand.java +``` java + +/** + * Lost an existing item in the stock list. + */ + +public class LostCommand extends Command { + public static final String COMMAND_WORD = "lost"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Lost a item from the stock list identified " + + "by the index number used in the displayed item list" + + "Parameters: INDEX (must be a positive integer) " + + PREFIX_QUANTITY + "QUANTITY\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_QUANTITY + "25"; + + + public static final String MESSAGE_LOST_ITEM_SUCCESS = "Lost Item: %1$s"; + public static final String MESSAGE_INVALID_QUANTITY = "The lost quantity input is invalid"; + public static final String MESSAGE_LOST_LARGER_THAN_READY = "The lost quantity much be larger than " + + "or equal to the quantity of Ready items"; + + private final Index targetIndex; + private final LostDescriptor lostDescriptor; + + public LostCommand(Index targetIndex, LostDescriptor lostDescriptor) { + requireNonNull(targetIndex); + requireNonNull(lostDescriptor); + this.targetIndex = targetIndex; + this.lostDescriptor = new LostDescriptor(lostDescriptor); + + } + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + List lastShownList = model.getFilteredItemList(); + + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_ITEM_DISPLAYED_INDEX); + } + + Item itemToLost = lastShownList.get(targetIndex.getZeroBased()); + Item lostItem = createLostItem(itemToLost, lostDescriptor); + + if (!itemToLost.isSameItem(lostItem) && model.hasItem(lostItem)) { + throw new CommandException(MESSAGE_INVALID_QUANTITY); + } + model.updateItem(itemToLost, lostItem); + model.updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS); + model.commitStockList(); + return new CommandResult(String.format(MESSAGE_LOST_ITEM_SUCCESS, lostItem)); + } + + /** + * Creates and returns a {@code Item} with the details of {@code itemToLost} + * edited with {@code LostDescriptor}. + */ + private static Item createLostItem(Item itemToLost, LostDescriptor lostDescriptor) + throws CommandException { + assert itemToLost != null; + Loststatus currentLoststatus = itemToLost.getLoststatus(); + Loststatus updatedLoststatus; + Integer updatedLost = currentLoststatus.getLoststatusLost(); + Integer updatedFound = currentLoststatus.getLoststatusFound(); + + Integer updatedValue = lostDescriptor.getLostQuantity(); + Integer initialValue = itemToLost.getQuantity().toInteger(); + Integer initialReadyValue = itemToLost.getStatus().getStatusReady(); + if (initialReadyValue - updatedValue < 0) { + throw new CommandException(MESSAGE_LOST_LARGER_THAN_READY); + } + updatedLost += updatedValue; + updatedFound -= updatedValue; + updatedLoststatus = new Loststatus(updatedLost, updatedFound); + + Quantity updatedQuantity = new Quantity(Integer.toString(initialValue - updatedValue)); + Status updatedStatus = new Status(initialReadyValue - updatedValue, + itemToLost.getStatus().getStatusOnLoan(), itemToLost.getStatus().getStatusFaulty()); + + + return new Item(itemToLost.getName(), updatedQuantity, + itemToLost.getMinQuantity(), updatedLoststatus, updatedStatus, itemToLost.getTags()); + } + + /** + * Stores the details to lost the item with. + */ + + public static class LostDescriptor { + private Integer lostQuantity; + + public LostDescriptor(){} + + public LostDescriptor(LostDescriptor toCopy) { + setLostQuantity(toCopy.lostQuantity); + } + public void setLostQuantity(Integer lostQuantity) { + this.lostQuantity = lostQuantity; } + + public Integer getLostQuantity() { + return lostQuantity; } + + } + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof LostCommand // instanceof handles nulls + && targetIndex.equals(((LostCommand) other).targetIndex)); // state check + } +} +``` +###### \java\seedu\address\logic\parser\FoundCommandParser.java +``` java +/** + * Parses input arguments and creates a new FoundCommand object + */ +public class FoundCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FoundCommand + * and returns an FoundCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public FoundCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_QUANTITY); + + Index targetIndex; + try { + targetIndex = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FoundCommand.MESSAGE_USAGE), pe); + } + FoundDescriptor foundDescriptor = new FoundDescriptor(); + + foundDescriptor.setFoundQuantity(ParserUtil + .parseQuantity(argMultimap.getValue(PREFIX_QUANTITY).get()).toInteger()); + + return new FoundCommand(targetIndex, foundDescriptor); + } + +} + +``` +###### \java\seedu\address\logic\parser\LostCommandParser.java +``` java +/** + * Parses input arguments and creates a new LostCommand object + */ +public class LostCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the LostCommand + * and returns an LostCommand object for execution. + * + * @throws ParseException if the user input does not conform the expected format + */ + public LostCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_QUANTITY); + + Index targetIndex; + try { + targetIndex = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, LostCommand.MESSAGE_USAGE), pe); + } + LostDescriptor lostDescriptor = new LostDescriptor(); + lostDescriptor.setLostQuantity(ParserUtil + .parseQuantity(argMultimap.getValue(PREFIX_QUANTITY).get()).toInteger()); + return new LostCommand(targetIndex, lostDescriptor); + } +} + +``` +###### \java\seedu\address\model\item\Loststatus.java +``` java +public class Loststatus { + public static final int LOSTSTATUS_LOST = 0; + public static final int LOSTSTATUS_FOUND = 1; + private final List loststatus = new ArrayList<>(2); + + public Loststatus() { + this(0, 0); } + public Loststatus(int lost, int found) { + loststatus.add(lost); + loststatus.add(found); + } + public Integer getLoststatusLost() { + return loststatus.get(LOSTSTATUS_LOST); } + public Integer getLoststatusFound() { + return loststatus.get(LOSTSTATUS_FOUND); + } + + public void setLoststatusLost(Integer lost) { + loststatus.set(LOSTSTATUS_LOST, lost); + } + public void setLoststatusFound(Integer found) { + loststatus.set(LOSTSTATUS_FOUND, found); + } + public void setDefaultValues(int quantity) { + loststatus.set(LOSTSTATUS_FOUND, quantity); + loststatus.set(LOSTSTATUS_LOST, 0); + + } + +} + +``` diff --git a/collated/functional/gaoqikai.md b/collated/functional/gaoqikai.md new file mode 100644 index 000000000000..263385c76acd --- /dev/null +++ b/collated/functional/gaoqikai.md @@ -0,0 +1,577 @@ +# gaoqikai +###### \java\seedu\address\logic\commands\AddTagCommand.java +``` java +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_ITEMS; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.commons.util.CollectionUtil; +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.item.Item; +import seedu.address.model.item.Name; +import seedu.address.model.item.Quantity; +import seedu.address.model.tag.Tag; + + + + +/** + * Add the given tags to selected item by index. + */ + +public class AddTagCommand extends Command { + public static final String COMMAND_WORD = "addTag"; + + public static final String MESSAGE_SUCCESS = "Added tags to the selected item."; + + public static final String MESSAGE_DUPLICATE = "This item already has the inputted tags. Please try again."; + + public static final String MESSAGE_NO_TAG = "Please include the tags you want to add."; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Add the inputted tags to the selected item " + + "by the index number used in the displayed item list.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_TAG + "TAG [MORE_TAGS]" + + " Example: " + COMMAND_WORD + " 1 " + PREFIX_TAG + " Lab1"; + + private final AddTagDescriptor addTagDescriptor; + private final Index index; + + public AddTagCommand(Index index, AddTagDescriptor addTagDescriptor) { + requireNonNull(index); + requireNonNull(addTagDescriptor); + + this.index = index; + this.addTagDescriptor = new AddTagDescriptor(addTagDescriptor); + } + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + List lastShownList = model.getFilteredItemList(); + + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_ITEM_DISPLAYED_INDEX); + } + + Item itemToEdit = lastShownList.get(index.getZeroBased()); + Item editedItem = createEditedItem(itemToEdit, addTagDescriptor); + + model.updateItem(itemToEdit, editedItem); + model.updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS); + model.commitStockList(); + if(itemToEdit.getTags().size() == lastShownList.get(index.getZeroBased()).getTags().size()) { + return new CommandResult(MESSAGE_DUPLICATE); + } + return new CommandResult(MESSAGE_SUCCESS); + + } + + /** + * Creates and returns a {@code Item} with the details of {@code itemToEdit} + * edited with {@code addTagDescriptor}. + */ + private static Item createEditedItem(Item itemToEdit, AddTagCommand.AddTagDescriptor addTagDescriptor) { + assert itemToEdit != null; + + Name updatedName = itemToEdit.getName(); + Quantity updatedQuantity = itemToEdit.getQuantity(); + Quantity updatedMinQuantity = itemToEdit.getMinQuantity(); + Set updatedTags = addTagDescriptor.getTags(); + updatedTags.addAll(itemToEdit.getTags()); //A set will automatically sort its elements + return new Item(updatedName, updatedQuantity, updatedMinQuantity, updatedTags); + } + + /** + * Temporarily stores the tags to be added. + */ + public static class AddTagDescriptor { + private Set tags; + + public AddTagDescriptor() {} + + /** + * Copy constructor. + * A defensive copy of {@code tags} is used internally. + */ + public AddTagDescriptor(AddTagDescriptor toCopy) { + setTags(toCopy.tags); + } + + /** + * Returns true if there is tag to add. + */ + public boolean haveTag() { + return CollectionUtil.isAnyNonNull(tags); + } + + /** + * Sets {@code tags} to this object's {@code tags}. + * A defensive copy of {@code tags} is used internally. + */ + public void setTags(Set tags) { + this.tags = (tags != null) ? new HashSet<>(tags) : null; + } + + public Set getTags() { + return tags; + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof AddTagDescriptor)) { + return false; + } + + // state check + AddTagDescriptor e = (AddTagDescriptor) other; + + return getTags().equals(e.getTags()); + } + } +} +``` +###### \java\seedu\address\logic\commands\DeleteTagCommand.java +``` java +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_ITEMS; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.commons.util.CollectionUtil; +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.item.Item; +import seedu.address.model.item.Name; +import seedu.address.model.item.Quantity; +import seedu.address.model.tag.Tag; + + + + +/** + * Delete the given tags to selected item by index. + */ + +public class DeleteTagCommand extends Command { + public static final String COMMAND_WORD = "deleteTag"; + + public static final String MESSAGE_SUCCESS = "Deleted tags from the selected item."; + + public static final String MESSAGE_NO_TAG = "Please include the tags you want to delete."; + + public static final String MESSAGE_DOES_NOT_EXIST = "The selected item does not contain the inputted tags."; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Delete the inputted tags from the selected item " + + "by the index number used in the displayed item list.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_TAG + "TAG [MORE_TAGS]" + + " Example: " + COMMAND_WORD + " 1 " + PREFIX_TAG + " Lab1"; + + private static boolean flag = false; //to indicate whether a deletion is executed. + private final DeleteTagDescriptor deleteTagDescriptor; + private final Index index; + + public DeleteTagCommand(Index index, DeleteTagDescriptor deleteTagDescriptor) { + requireNonNull(index); + requireNonNull(deleteTagDescriptor); + + this.index = index; + this.deleteTagDescriptor = new DeleteTagDescriptor(deleteTagDescriptor); + } + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + List lastShownList = model.getFilteredItemList(); + + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_ITEM_DISPLAYED_INDEX); + } + + Item itemToEdit = lastShownList.get(index.getZeroBased()); + Item editedItem = createEditedItem(itemToEdit, deleteTagDescriptor); + + model.updateItem(itemToEdit, editedItem); + model.updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS); + model.commitStockList(); + return new CommandResult(flag ? (MESSAGE_SUCCESS) : (MESSAGE_DOES_NOT_EXIST)); + + } + + /** + * Creates and returns a {@code Item} with the details of {@code itemToEdit} + * edited with {@code deleteTagDescriptor}. + */ + private static Item createEditedItem(Item itemToEdit, DeleteTagCommand.DeleteTagDescriptor deleteTagDescriptor) { + assert itemToEdit != null; + + Name updatedName = itemToEdit.getName(); + Quantity updatedQuantity = itemToEdit.getQuantity(); + Quantity updatedMinQuantity = itemToEdit.getMinQuantity(); + Set updatedTags = new HashSet<>(itemToEdit.getTags()); + flag = updatedTags.removeIf((Tag current) -> deleteTagDescriptor.getTags().toString().toLowerCase() + .contains(current.toString().toLowerCase())); + return new Item(updatedName, updatedQuantity, updatedMinQuantity, updatedTags); + } + + /** + * Temporarily stores the tags to be added. + */ + public static class DeleteTagDescriptor { + private Set tags; + + public DeleteTagDescriptor() {} + + /** + * Copy constructor. + * A defensive copy of {@code tags} is used internally. + */ + public DeleteTagDescriptor(DeleteTagDescriptor toCopy) { + setTags(toCopy.tags); + } + + /** + * Returns true if there is tag to delete. + */ + public boolean haveTag() { + return CollectionUtil.isAnyNonNull(tags); + } + + /** + * Sets {@code tags} to this object's {@code tags}. + * A defensive copy of {@code tags} is used internally. + */ + public void setTags(Set tags) { + this.tags = (tags != null) ? new HashSet<>(tags) : null; + } + + public Set getTags() { + return tags; + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof DeleteTagDescriptor)) { + return false; + } + + // state check + DeleteTagDescriptor e = (DeleteTagDescriptor) other; + + return getTags().equals(e.getTags()); + } + } +} +``` +###### \java\seedu\address\logic\commands\TagCommand.java +``` java +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.item.TagContainsKeywordsPredicate; + +/** + * Find and show items under specific tags + * + */ +public class TagCommand extends Command { + public static final String COMMAND_WORD = "tag"; + + public static final String MESSAGE_SUCCESS = "Listed all items for your tag(s)."; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": List all items whose tag matches " + + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" + + "Parameters: KEYWORD [MORE_KEYWORDS]..." + + " Example: " + COMMAND_WORD + " Lab1"; + + private final TagContainsKeywordsPredicate predicate; + + public TagCommand(TagContainsKeywordsPredicate predicate) { + this.predicate = predicate; + } + + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + model.updateFilteredItemListByTag(predicate); + return new CommandResult( + String.format(Messages.MESSAGE_ITEMS_LISTED_OVERVIEW, model.getFilteredItemList().size())); + } +} +``` +###### \java\seedu\address\logic\parser\AddTagCommandParser.java +``` java +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.AddTagCommand; +import seedu.address.logic.commands.AddTagCommand.AddTagDescriptor; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.tag.Tag; + +/** + * Parses input arguments and creates a new AddTagCommand object + */ +public class AddTagCommandParser implements Parser { + + /** + * Parsers the given (@code String) of arguments in the context of the AddTagCommand and + * returns an AddTagCommand object for execution. + * @Throws ParseException if the user input does not conform the expected format + */ + public AddTagCommand parse(String args) throws ParseException { + requireNonNull(args); + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_TAG); + + Index index; + + AddTagDescriptor addTagDescriptor = new AddTagCommand.AddTagDescriptor(); + + parseTagsToAdd(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(addTagDescriptor::setTags); + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTagCommand.MESSAGE_USAGE), pe); + } + + if (!addTagDescriptor.haveTag()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTagCommand.MESSAGE_NO_TAG)); + } + + return new AddTagCommand(index, addTagDescriptor); + } + + /** + * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty. + * If {@code tags} contain only one element which is an empty string, it will be parsed into a + * {@code Set} containing zero tags. + */ + private Optional> parseTagsToAdd(Collection tags) throws ParseException { + assert tags != null; + + if (tags.isEmpty()) { + return Optional.empty(); + } + Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; + return Optional.of(ParserUtil.parseTags(tagSet)); + } +} +``` +###### \java\seedu\address\logic\parser\DeleteTagCommandParser.java +``` java +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.DeleteTagCommand; +import seedu.address.logic.commands.DeleteTagCommand.DeleteTagDescriptor; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.tag.Tag; + +/** + * Parses input arguments and creates a new DeleteTagCommand object + */ +public class DeleteTagCommandParser implements Parser { + + /** + * Parsers the given (@code String) of arguments in the context of the DeleteTagCommand and + * returns an DeleteTagCommand object for execution. + * @Throws ParseException if the user input does not conform the expected format + */ + public DeleteTagCommand parse(String args) throws ParseException { + requireNonNull(args); + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_TAG); + + Index index; + + DeleteTagDescriptor deleteTagDescriptor = new DeleteTagCommand.DeleteTagDescriptor(); + + parseTagsToAdd(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(deleteTagDescriptor::setTags); + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteTagCommand.MESSAGE_USAGE), pe); + } + + if (!deleteTagDescriptor.haveTag()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteTagCommand.MESSAGE_NO_TAG)); + } + + return new DeleteTagCommand(index, deleteTagDescriptor); + } + /** + * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty. + * If {@code tags} contain only one element which is an empty string, it will be parsed into a + * {@code Set} containing zero tags. + */ + private Optional> parseTagsToAdd(Collection tags) throws ParseException { + assert tags != null; + + if (tags.isEmpty()) { + return Optional.empty(); + } + Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; + return Optional.of(ParserUtil.parseTags(tagSet)); + } +} +``` +###### \java\seedu\address\logic\parser\TagCommandParser.java +``` java +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.Arrays; + +import seedu.address.logic.commands.TagCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.item.TagContainsKeywordsPredicate; + +/** + * Parses input arguments and creates a new TagCommand object + */ +public class TagCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the TagCommand + * and returns an TagCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public TagCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, TagCommand.MESSAGE_USAGE)); + } + + String[] tagKeywords = trimmedArgs.split("\\s+"); + + return new TagCommand(new TagContainsKeywordsPredicate (Arrays.asList(tagKeywords))); + } +} +``` +###### \java\seedu\address\model\item\TagContainsKeywordsPredicate.java +``` java +package seedu.address.model.item; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Tests that a {@code Item}'s {@code Tags} matches any of the keywords given. + */ +public class TagContainsKeywordsPredicate implements Predicate { + private final List keywords; + + public TagContainsKeywordsPredicate (List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Item item) { + return keywords.stream() + .anyMatch(keyword -> item.getTags().toString().toLowerCase().contains(keyword.toLowerCase())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TagContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((TagContainsKeywordsPredicate) other).keywords)); // state check + } + +} +``` +###### \java\seedu\address\model\Model.java +``` java + /** + * Updates the tag filter of the filtered item list to filter by the given tag: {@code predicate}. + * @throws NullPointerException if {@code predicate} is null. + */ + void updateFilteredItemListByTag(Predicate predicate); +``` +###### \java\seedu\address\model\ModelManager.java +``` java + @Override + //update the stock list that is filtered by tag + public void updateFilteredItemListByTag(Predicate predicate) { + requireNonNull(predicate); + filteredItems.setPredicate(predicate); + } +``` diff --git a/collated/functional/kelvintankaiboon.md b/collated/functional/kelvintankaiboon.md new file mode 100644 index 000000000000..606d8f3cb9b9 --- /dev/null +++ b/collated/functional/kelvintankaiboon.md @@ -0,0 +1,80 @@ +# kelvintankaiboon +###### \java\seedu\address\logic\parser\ParserUtil.java +``` java + /** + * + * Parses a {@code String fileName}. + * @throws ParseException if the given string is invalid. + */ + public static String parseFileName(String fileName) throws ParseException { + requireNonNull(fileName); + String invalidFileNameRegex = "[^\\w\\-. ]"; + String trimmedName = fileName.trim(); + if (trimmedName.matches(invalidFileNameRegex)) { + throw new ParseException(Name.MESSAGE_NAME_CONSTRAINTS); + } + return trimmedName; + } +} +``` +###### \java\seedu\address\model\Model.java +``` java + /** Saves the current version of the StockList */ + void saveStockList(String fileName); + + /** Opens the .xml file indicated by user in the browser pane*/ + void openStockList(String fileName); +``` +###### \java\seedu\address\model\ModelManager.java +``` java + /** Raises an event to indicate that saveCommand has been called */ + private void indicateSaveStockListVersion(String fileName) { + raise(new SaveStockListVersionEvent(versionedStockList, fileName)); + } + + @Override + public void saveStockList(String fileName) { + indicateSaveStockListVersion(fileName); + } + + /** Raises an event to indicate that OpenCommand has been called */ + private void indicateOpenStockListVersion(String fileName) { + raise(new OpenStockListVersionEvent(versionedStockList, fileName)); + } + + @Override + public void openStockList(String fileName) { + indicateOpenStockListVersion(fileName); + } +``` +###### \java\seedu\address\storage\StorageManager.java +``` java + @Override + public void saveStockListVersion(ReadOnlyStockList stockList, Path filePath) throws IOException { + logger.fine("Attempting to write to data file: " + filePath); + stockListStorage.saveStockListVersion(stockList, filePath); + } + + @Override + @Subscribe + public void handleSaveStockListVersionEvent(SaveStockListVersionEvent event) { + logger.info(LogsCenter.getEventHandlingLogMessage(event, "Saving current version to file")); + try { + saveStockListVersion(event.data, Paths.get("versions", event.fileName)); + } catch (IOException e) { + raise(new DataSavingExceptionEvent(e)); + } + } +``` +###### \java\seedu\address\storage\XmlStockListStorage.java +``` java + @Override + public void saveStockListVersion(ReadOnlyStockList stockList, Path filePath) throws IOException { + requireNonNull(stockList); + requireNonNull(filePath); + + FileUtil.createIfMissing(filePath); + XmlFileStorage.saveStockListDataToFile(filePath, new XmlSerializableStockList(stockList)); + } +} +``` diff --git a/collated/test/ChewKinWhye.md b/collated/test/ChewKinWhye.md new file mode 100644 index 000000000000..af1efb3c8120 --- /dev/null +++ b/collated/test/ChewKinWhye.md @@ -0,0 +1,272 @@ +# ChewKinWhye +\java\seedu\address\logic\commands\ChangeStatusCommandTest.java +``` java + +public class ChangeStatusCommandTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private CommandHistory commandHistory = new CommandHistory(); + private Model model = new ModelManager(getStockList(), new UserPrefs(), getTypicalAccountList()); + private List lastShownList = model.getFilteredItemList(); + private ChangeStatusCommand changeStatusCommand = new ChangeStatusCommand(getChangeStatusDescriptor()); + + @Test + public void constructorNullChangeStatusDescriptorThrowsNullPointerException() { + thrown.expect(NullPointerException.class); + new ChangeStatusCommand(null); + } + @Test + public void changeStatusDescriptorConstructor() { + ChangeStatusCommand.ChangeStatusDescriptor changeStatusDescriptor = + new ChangeStatusCommand.ChangeStatusDescriptor(getChangeStatusDescriptor()); + ChangeStatusCommand.ChangeStatusDescriptor changeStatusDescriptorDuplicate = + new ChangeStatusCommand.ChangeStatusDescriptor(changeStatusDescriptor); + assertEquals(changeStatusDescriptor, changeStatusDescriptorDuplicate); + } + @Test + public void changeStatusCommandGetIndexTest() { + try { + Index actualIndex = changeStatusCommand.getIndex(lastShownList, getChangeStatusDescriptor()); + assertEquals(actualIndex, getExpectedIndex()); + } catch (CommandException e) { + throw new AssertionError("The item is present in the stock list."); + } + } + @Test + public void createUpdatedItemTest() { + try { + Item actualUpdatedItem = changeStatusCommand + .createUpdatedItem(getItemToUpdate(), getChangeStatusDescriptor()); + assertEquals(actualUpdatedItem, getExpectedUpdatedItem()); + } catch (Exception e) { + throw new AssertionError("The updated item does not have invalid status"); + } + } + @Test + public void changeStatusCommandExecuteTestWithoutLogin() { + assertCommandFailure(changeStatusCommand, model, commandHistory, MESSAGE_LOGIN); + } + @Test + public void changeStatusCommandExecuteTestWithLogin() { + Username admin = new Username("admin"); + model.setLoggedInUser(admin); + try { + changeStatusCommand.execute(model, commandHistory); + } catch (CommandException e) { + throw new AssertionError("The execute function is valid"); + } + assertCommandFailure(changeStatusCommand, model, commandHistory, MESSAGE_STATUS_CONSTRAINTS); + } + +} +``` +\java\seedu\address\logic\commands\LoanListCommandsTest.java +``` java + +public class LoanListCommandsTest { + private Model model = new ModelManager(getStockList(), new UserPrefs(), getTypicalAccountList()); + private CommandHistory commandHistory = new CommandHistory(); + private LoanListCommand loanListCommand = new LoanListCommand(getLoanerDescription()); + private ViewLoanListCommand viewLoanListCommand = new ViewLoanListCommand(); + private DeleteLoanListCommand deleteLoanListCommand = new DeleteLoanListCommand(getTestIndex()); + @Test + public void executeViewLoanListCommandWithoutLogin() { + assertCommandFailure(viewLoanListCommand, model, commandHistory, MESSAGE_LOGIN); + } + @Test + public void executeDeleteLoanListCommandWithoutLogin() { + assertCommandFailure(deleteLoanListCommand, model, commandHistory, MESSAGE_LOGIN); + } + @Test + public void executeLoanListCommandWithoutLogin() { + assertCommandFailure(loanListCommand, model, commandHistory, MESSAGE_LOGIN); + } + /*@Test + public void executeLoanListAndViewLoanListAndDeleteLoanList() { + Username admin = new Username("admin"); + model.setLoggedInUser(admin); + try { + loanListCommand.updateLoanList(getLoanListTestFile(), getLoanerDescription()); + } catch (JAXBException e) { + throw new AssertionError("The loan list test file is vaild"); + } + + try { + loanListCommand.updateStatus(model, commandHistory); + } catch (CommandException e) { + throw new AssertionError("The updated status is valid"); + } + Item actualUpdatedArduinoItem = model.getFilteredItemList().get(0); + assertEquals(actualUpdatedArduinoItem, getExpectedUpdatedArduinoItem()); + + try { + String actualViewLoanListMessageOutput = viewLoanListCommand.getMessageOutput(getLoanListTestFile()); + assertEquals(actualViewLoanListMessageOutput, getExpectedViewLoanListMessageOutput()); + } catch (CommandException e) { + throw new AssertionError("Loan list file is not empty"); + } + + try { + deleteLoanListCommand.deleteLoanList(model, commandHistory, getLoanListTestFile()); + } catch (Exception e) { + throw new AssertionError("The loan list entry should be deleted"); + } + + try { + viewLoanListCommand.getMessageOutput(getLoanListTestFile()); + } catch (CommandException e) { + assertEquals(e.getMessage(), MESSAGE_EMPTY); + } + }*/ +} +``` +\java\seedu\address\logic\commands\StatusCommandTest.java +``` java + +public class StatusCommandTest { + private Model model = new ModelManager(getStockList(), new UserPrefs(), getTypicalAccountList()); + private CommandHistory commandHistory = new CommandHistory(); + private List lastShownList = model.getFilteredItemList(); + private StatusCommand statusCommand = new StatusCommand(); + private ArrayList actualReadyItems = new ArrayList<>(); + private ArrayList actualOnLoanItems = new ArrayList<>(); + private ArrayList actualFaultyItems = new ArrayList<>(); + + @Test + public void checkSortSimpleItems() { + statusCommand.sortSimpleItems(lastShownList, actualReadyItems, actualOnLoanItems, actualFaultyItems); + assertEquals(actualReadyItems, getExpectedReadyItems()); + assertEquals(actualOnLoanItems, getExpectedOnLoanItems()); + assertEquals(actualFaultyItems, getExpectedFaultyItems()); + } + @Test + public void checkGetMessageOutput() { + statusCommand.sortSimpleItems(lastShownList, actualReadyItems, actualOnLoanItems, actualFaultyItems); + String actualMessageOutput = statusCommand.getMessageOutput(actualReadyItems, + actualOnLoanItems, actualFaultyItems); + assertEquals(actualMessageOutput, getExpectedMessageOutput()); + } + @Test + public void executeStatusCommandWithoutLogin() { + assertCommandFailure(new StatusCommand(), model, commandHistory, MESSAGE_LOGIN); + } + + @Test + public void executeStatusCommandWithLogin() { + Username admin = new Username("admin"); + model.setLoggedInUser(admin); + try { + CommandResult actualCommandResult = statusCommand.execute(model, commandHistory); + assertEquals(actualCommandResult, getExpectedCommandResult()); + } catch (CommandException e) { + throw new AssertionError("The account should be logged in."); + } + } +} +``` +\java\seedu\address\testutil\StatusFeatureTestObjects.java +``` java + +/** + * A utility class containing a list of {@code Item} objects to be used in tests, specifically + * designed for the status feature test cases. + */ +public class StatusFeatureTestObjects { + private static final Item ARDUINO = new Item(new Name("Arduino"), new Quantity("100"), + new Quantity("20"), new Status(70, 20, 10), new HashSet<>()); + private static final Item Lidar = new Item(new Name("Lidar"), new Quantity("200"), + new Quantity("80"), new Status(150, 20, 30), new HashSet<>()); + private static final Item Motor = new Item(new Name("Motor"), new Quantity("500"), + new Quantity("100"), new Status(300, 120, 80), new HashSet<>()); + + private static File userDirectory = new File(System.getProperty("user.dir")); + private static String loanListDirectory = userDirectory.getAbsolutePath().replace("\\", "/"); + private static File loanListTestFile = new File(loanListDirectory + "/data/LoanListTest.xml"); + + private StatusFeatureTestObjects() { + } // prevents instantiation + + public static File getLoanListTestFile() { + return loanListTestFile; + } + + public static StockList getStockList() { + StockList ab = new StockList(); + for (Item item : getItems()) { + ab.addItem(item); + } + return ab; + } + public static List getItems() { + return new ArrayList<>(Arrays.asList(ARDUINO, Lidar, Motor)); + } + public static ArrayList getExpectedReadyItems() { + ArrayList expectedReadyItems = new ArrayList<>(); + for (Item item : getItems()) { + if (item.getStatus().getStatusReady() > 0) { + expectedReadyItems.add(new SimpleItem(item.getName(), + new Quantity(item.getStatus().getStatusReady().toString()))); + } + } + return expectedReadyItems; + } + public static ArrayList getExpectedOnLoanItems() { + ArrayList expectedOnLoanItems = new ArrayList<>(); + for (Item item : getItems()) { + if (item.getStatus().getStatusOnLoan() > 0) { + expectedOnLoanItems.add(new SimpleItem(item.getName(), + new Quantity(item.getStatus().getStatusOnLoan().toString()))); + } + } + return expectedOnLoanItems; + } + public static ArrayList getExpectedFaultyItems() { + ArrayList expectedFaultyItems = new ArrayList<>(); + for (Item item : getItems()) { + if (item.getStatus().getStatusFaulty() > 0) { + expectedFaultyItems.add(new SimpleItem(item.getName(), + new Quantity(item.getStatus().getStatusFaulty().toString()))); + } + } + return expectedFaultyItems; + } + public static String getExpectedMessageOutput() { + return "Items listed according to status\n" + + "Ready: Arduino: 70, Lidar: 150, Motor: 300\n" + + "On Loan: Arduino: 20, Lidar: 20, Motor: 120\n" + + "Faulty: Arduino: 10, Lidar: 30, Motor: 80"; + } + public static Index getTestIndex() { + return Index.fromZeroBased(0); + } + public static CommandResult getExpectedCommandResult() { + return new CommandResult(getExpectedMessageOutput()); + } + public static LoanerDescription getLoanerDescription() { + return new LoanerDescription(new Name("Arduino"), new Name("KinWhye"), new Quantity("10")); + } + public static Item getExpectedUpdatedArduinoItem() { + return new Item(new Name("Arduino"), new Quantity("100"), + new Quantity("20"), new Status(60, 30, 10), new HashSet<>()); + } + public static String getExpectedViewLoanListMessageOutput() { + return ("Loan list displayed\n" + + "1. KinWhye: Arduino 10\n"); + } + public static ChangeStatusCommand.ChangeStatusDescriptor getChangeStatusDescriptor() { + return new ChangeStatusCommand.ChangeStatusDescriptor(new Name("Lidar"), 80, + "Ready", "Faulty"); + } + public static Index getExpectedIndex() { + return Index.fromZeroBased(1); + } + public static Item getItemToUpdate() { + return getItems().get(1); + } + public static Item getExpectedUpdatedItem() { + return new Item(new Name("Lidar"), new Quantity("200"), + new Quantity("80"), new Status(70, 20, 110), new HashSet<>()); + } +} +``` diff --git a/collated/test/Davindran.md b/collated/test/Davindran.md new file mode 100644 index 000000000000..54f8d753e7b5 --- /dev/null +++ b/collated/test/Davindran.md @@ -0,0 +1,62 @@ +# Davindran +\java\seedu\address\logic\commands\AddCommandTest.java +``` java + @Override + public void resetAccountData(ReadOnlyAccountList newData) { + throw new AssertionError("This method should not be called."); + } + + public ReadOnlyAccountList getAccountList() { + throw new AssertionError("This method should not be called."); + } + + @Override + public boolean hasAccount(Account account) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void deleteAccount(Account target) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void addAccount(Account account) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateAccount(Account target, Account editedAccount) { + throw new AssertionError("This method should not be called."); + } + + @Override + public boolean getLoginStatus() { + throw new AssertionError("This method should not be called."); + } + + @Override + public String getLoggedInUser() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setLoggedInUser(Username username) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setLoggedOutStatus() { + throw new AssertionError("This method should not be called."); + } + + @Override + public ObservableList getFilteredAccountList() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateFilteredAccountList(Predicate predicate) { + throw new AssertionError("This method should not be called."); + } +``` diff --git a/collated/test/gaoqikai.md b/collated/test/gaoqikai.md new file mode 100644 index 000000000000..df2e4f4f9d9d --- /dev/null +++ b/collated/test/gaoqikai.md @@ -0,0 +1,88 @@ +# gaoqikai +\java\seedu\address\logic\commands\TagCommandTest.java +``` java +package seedu.address.logic.commands; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static seedu.address.commons.core.Messages.MESSAGE_ITEMS_LISTED_OVERVIEW; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalAccounts.getTypicalAccountList; +import static seedu.address.testutil.TypicalItems.ARDUINO; +import static seedu.address.testutil.TypicalItems.RPLIDAR; +import static seedu.address.testutil.TypicalItems.getTypicalStockList; + +import java.util.Arrays; +import java.util.Collections; + +import org.junit.Before; +import org.junit.Test; + +import seedu.address.logic.CommandHistory; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.account.Username; +import seedu.address.model.item.TagContainsKeywordsPredicate; + +/** + * Contains integration tests (interaction with the Model) for {@code TagCommand}. + */ + +public class TagCommandTest { + private Model model = new ModelManager(getTypicalStockList(), new UserPrefs(), getTypicalAccountList()); + private Model expectedModel = new ModelManager(getTypicalStockList(), new UserPrefs(), getTypicalAccountList()); + private CommandHistory commandHistory = new CommandHistory(); + @Before + public void setUp() { + Username admin = new Username("admin"); + model = new ModelManager(getTypicalStockList(), new UserPrefs(), getTypicalAccountList()); + model.setLoggedInUser(admin); + } + @Test + public void equals() { + TagContainsKeywordsPredicate firstPredicate = + new TagContainsKeywordsPredicate(Collections.singletonList("first")); + TagContainsKeywordsPredicate secondPredicate = + new TagContainsKeywordsPredicate(Collections.singletonList("second")); + TagCommand tagFirstCommand = new TagCommand(firstPredicate); + TagCommand tagSecondCommand = new TagCommand(secondPredicate); + // same object -> returns true + assertTrue(tagFirstCommand.equals(tagFirstCommand)); + // different types -> returns false + assertFalse(tagFirstCommand.equals(1)); + // null -> returns false + assertFalse(tagFirstCommand == (null)); + // different item -> returns false + assertFalse(tagFirstCommand.equals(tagSecondCommand)); + } + @Test + public void execute_zeroKeywords_noItemFound() { + String expectedMessage = String.format(MESSAGE_ITEMS_LISTED_OVERVIEW, 0); + TagContainsKeywordsPredicate predicate = preparePredicate(" "); + TagCommand command = new TagCommand(predicate); + expectedModel.updateFilteredItemListByTag(predicate); + assertCommandSuccess(command, model, commandHistory, expectedMessage, expectedModel); + assertEquals(Collections.emptyList(), model.getFilteredItemList()); + } + @Test + public void execute_multipleKeywords_multipleItemsFound() { + String expectedMessage = String.format(MESSAGE_ITEMS_LISTED_OVERVIEW, 2); + TagContainsKeywordsPredicate predicate = preparePredicate("Lab1 Lab2"); + TagCommand command = new TagCommand(predicate); + expectedModel.updateFilteredItemListByTag(predicate); + assertCommandSuccess(command, model, commandHistory, expectedMessage, expectedModel); + assertEquals(Arrays.asList(ARDUINO, RPLIDAR), model.getFilteredItemList()); + } + + /** + * Parses {@code userInput} into a {@code NameContainsKeywordsPredicate}. + */ + + private TagContainsKeywordsPredicate preparePredicate(String userInput) { + return new TagContainsKeywordsPredicate(Arrays.asList(userInput.split("\\s+"))); + } + +} +``` diff --git a/collated/test/kelvintankaiboon.md b/collated/test/kelvintankaiboon.md new file mode 100644 index 000000000000..e9ddd804e6e4 --- /dev/null +++ b/collated/test/kelvintankaiboon.md @@ -0,0 +1,13 @@ +# kelvintankaiboon +\java\seedu\address\logic\commands\AddCommandTest.java +``` java + @Override + public void saveStockList(String fileName) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void openStockList(String fileName) { + throw new AssertionError("This method should not be called."); + } +``` diff --git a/copyright.txt b/copyright.txt index 93aa2a39ce25..492e6aad4cb2 100644 --- a/copyright.txt +++ b/copyright.txt @@ -1,9 +1,11 @@ Some code adapted from http://code.makery.ch/library/javafx-8-tutorial/ by Marco Jakob Copyright by Susumu Yoshida - http://www.mcdodesign.com/ -- address_book_32.png - AddressApp.ico Copyright by Jan Jan Kovařík - http://glyphicons.com/ - calendar.png - edit.png + +Copyright by Arduino - https://www.arduino.cc/ +- stock_list_32.png diff --git a/docs/AboutUs.adoc b/docs/AboutUs.adoc index e647ed1e715a..c80629c3854d 100644 --- a/docs/AboutUs.adoc +++ b/docs/AboutUs.adoc @@ -4,53 +4,52 @@ :imagesDir: images :stylesDir: stylesheets -AddressBook - Level 4 was developed by the https://se-edu.github.io/docs/Team.html[se-edu] team. + -_{The dummy content given below serves as a placeholder to be used by future forks of the project.}_ + -{empty} + +Jalil Enterprise was developed by the https://github.com/CS2113-AY1819S1-T12-3[CS2113-AY1819S1-T12-3] team. + We are a team based in the http://www.comp.nus.edu.sg[School of Computing, National University of Singapore]. == Project Team -=== John Doe -image::damithc.jpg[width="150", align="left"] -{empty}[http://www.comp.nus.edu.sg/~damithch[homepage]] [https://github.com/damithc[github]] [<>] +=== Chew Kin Whye +image::chewkinwhye.png[width="150", align="left"] +{empty} [https://github.com/ChewKinWhye[github]] -Role: Project Advisor +Role: Team Lead, Scheduling and tracking + +Responsibilities: Logic, Status ''' -=== John Roe -image::lejolly.jpg[width="150", align="left"] -{empty}[http://github.com/lejolly[github]] [<>] +=== Gao Qikai +image::gaoqikai.png[width="150", align="left"] +{empty}[https://github.com/gaoqikai[github]] -Role: Team Lead + -Responsibilities: UI +Role: Developer, Testing + +Responsibilities: UI, Tag ''' -=== Johnny Doe -image::yijinl.jpg[width="150", align="left"] -{empty}[http://github.com/yijinl[github]] [<>] +=== Kelvin Tan +image::kelvintankaiboon.png[width="150", align="left"] +{empty}[https://github.com/kelvintankaiboon[github]] -Role: Developer + -Responsibilities: Data +Role: Developer, Documentation + +Responsibilities: Data, Archive ''' -=== Johnny Roe -image::m133225.jpg[width="150", align="left"] -{empty}[http://github.com/m133225[github]] [<>] +=== He Haowei +image::hehaowei.png[width="150", align="left"] +{empty}[https://github.com/HeHaowei[github]] -Role: Developer + -Responsibilities: Dev Ops + Threading +Role: Developer, Code quality + +Responsibilities: Model, Lost & Found ''' -=== Benson Meier -image::yl_coder.jpg[width="150", align="left"] -{empty}[http://github.com/yl-coder[github]] [<>] +=== Davindran +image::davindran.png[width="150", align="left"] +{empty}[https://github.com/Davindran[github]] -Role: Developer + -Responsibilities: UI +Role: Developer, Deliverables and deadlines + +Responsibilities: UI, Security ''' diff --git a/docs/ContactUs.adoc b/docs/ContactUs.adoc index 5de5363abffd..3be37fc4811a 100644 --- a/docs/ContactUs.adoc +++ b/docs/ContactUs.adoc @@ -2,6 +2,10 @@ :site-section: ContactUs :stylesDir: stylesheets -* *Bug reports, Suggestions* : Post in our https://github.com/se-edu/addressbook-level4/issues[issue tracker] if you noticed bugs or have suggestions on how to improve. -* *Contributing* : We welcome pull requests. Follow the process described https://github.com/oss-generic/process[here] -* *Email us* : You can also reach us at `damith [at] comp.nus.edu.sg` +* *Bug reports, Suggestions* : Post in our https://github.com/CS2113-AY1819S1-T12-3/main/issues[issue tracker] if you noticed bugs or have suggestions on how to improve. +* *Email us* : You can also reach us at: + + Davin: `e0175442@u.nus.edu` + + Haowei: `e0253738@u.nus.edu` + + Kelvin: `kelvintan@u.nus.edu` + + Kin Whye: `e0200920@u.nus.edu` + + Qikai: `gao@u.nus.edu` diff --git a/docs/DeveloperGuide.adoc b/docs/DeveloperGuide.adoc index ea58481e4740..969e90d6fda7 100644 --- a/docs/DeveloperGuide.adoc +++ b/docs/DeveloperGuide.adoc @@ -1,4 +1,4 @@ -= AddressBook Level 4 - Developer Guide += JalilEnterprises - Developer Guide :site-section: DeveloperGuide :toc: :toc-title: @@ -12,9 +12,11 @@ ifdef::env-github[] :note-caption: :information_source: :warning-caption: :warning: endif::[] -:repoURL: https://github.com/se-edu/addressbook-level4/tree/master -By: `Team SE-EDU`      Since: `Jun 2016`      Licence: `MIT` +:repoURL: https://github.com/CS2113-AY1819S1-T12-3/main/tree/master + + +By: `Team T12-3`      Since: `Sep 2018`      Licence: `MIT` == Setting up @@ -84,7 +86,7 @@ Set up Travis to perform Continuous Integration (CI) for your fork. See <>). [NOTE] -Coverage reporting could be useful for a team repository that hosts the final version but it is not that useful for your personal fork. +Coverage reporting could be useful for a team repository that hosts the final version but it is not that useful for your itemal fork. Optionally, you can set up AppVeyor as a second CI (see <>). @@ -100,6 +102,7 @@ When you are ready to start coding, == Design +//tag::2.1[] [[Design-Architecture]] === Architecture @@ -144,15 +147,15 @@ image::LogicClassDiagram.png[width="800"] The _Sequence Diagram_ below shows how the components interact for the scenario where the user issues the command `delete 1`. .Component interactions for `delete 1` command (part 1) -image::SDforDeletePerson.png[width="800"] +image::SDforDeleteItem.png[width="800"] [NOTE] -Note how the `Model` simply raises a `AddressBookChangedEvent` when the Address Book data are changed, instead of asking the `Storage` to save the updates to the hard disk. +Note how the `Model` simply raises a `StockListChangedEvent` when the Stock List data are changed, instead of asking the `Storage` to save the updates to the hard disk. The diagram below shows how the `EventsCenter` reacts to that event, which eventually results in the updates being saved to the hard disk and the status bar of the UI being updated to reflect the 'Last Updated' time. .Component interactions for `delete 1` command (part 2) -image::SDforDeletePersonEventHandling.png[width="800"] +image::SDforDeleteItemEventHandling.png[width="800"] [NOTE] Note how the event is propagated through the `EventsCenter` to the `Storage` and `UI` without `Model` having to be coupled to either of them. This is an example of how this Event Driven approach helps us reduce direct coupling between components. @@ -160,6 +163,8 @@ Note how the event is propagated through the `EventsCenter` to the `Storage` and The sections below give more details of each component. [[Design-Ui]] +//end::2.1[] +// tag::2.2[] === UI component .Structure of the UI Component @@ -167,16 +172,17 @@ image::UiClassDiagram.png[width="800"] *API* : link:{repoURL}/src/main/java/seedu/address/ui/Ui.java[`Ui.java`] -The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter`, `BrowserPanel` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class. +The UI consists of a `MainWindow` that is made up of parts including `CommandBox`, `ResultDisplay`, `ItemListPanel`, `StatusBarFooter`, `BrowserPanel` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class. -The `UI` component uses JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the link:{repoURL}/src/main/java/seedu/address/ui/MainWindow.java[`MainWindow`] is specified in link:{repoURL}/src/main/resources/view/MainWindow.fxml[`MainWindow.fxml`] +The `UI` component uses JavaFx UI framework. The layout of these UI parts is defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the link:{repoURL}/src/main/java/seedu/address/ui/MainWindow.java[`MainWindow`] is specified in link:{repoURL}/src/main/resources/view/MainWindow.fxml[`MainWindow.fxml`] The `UI` component, * Executes user commands using the `Logic` component. -* Binds itself to some data in the `Model` so that the UI can auto-update when data in the `Model` change. +* Binds itself to some data in the `Model` component so that the UI can auto-update when data in the `Model` change. * Responds to events raised from various parts of the App and updates the UI accordingly. +// end::2.2[] [[Design-Logic]] === Logic component @@ -187,15 +193,15 @@ image::LogicClassDiagram.png[width="800"] *API* : link:{repoURL}/src/main/java/seedu/address/logic/Logic.java[`Logic.java`] -. `Logic` uses the `AddressBookParser` class to parse the user command. +. `Logic` uses the `StockListParser` class to parse the user command. . This results in a `Command` object which is executed by the `LogicManager`. -. The command execution can affect the `Model` (e.g. adding a person) and/or raise events. +. The command execution can affect the `Model` (e.g. adding a item) and/or raise events. . The result of the command execution is encapsulated as a `CommandResult` object which is passed back to the `Ui`. Given below is the Sequence Diagram for interactions within the `Logic` component for the `execute("delete 1")` API call. .Interactions Inside the Logic Component for the `delete 1` Command -image::DeletePersonSdForLogic.png[width="800"] +image::DeleteItemSdForLogic.png[width="800"] [[Design-Model]] === Model component @@ -208,12 +214,12 @@ image::ModelClassDiagram.png[width="800"] The `Model`, * stores a `UserPref` object that represents the user's preferences. -* stores the Address Book data. -* exposes an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. +* stores the Stock List data. +* exposes an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. * does not depend on any of the other three components. [NOTE] -As a more OOP model, we can store a `Tag` list in `Address Book`, which `Person` can reference. This would allow `Address Book` to only require one `Tag` object per unique `Tag`, instead of each `Person` needing their own `Tag` object. An example of how such a model may look like is given below. + +As a more OOP model, we can store a `Tag` list in `Stock List`, which `Item` can reference. This would allow `Stock List` to only require one `Tag` object per unique `Tag`, instead of each `Item` needing their own `Tag` object. An example of how such a model may look like is given below. + + image:ModelClassBetterOopDiagram.png[width="800"] @@ -228,7 +234,7 @@ image::StorageClassDiagram.png[width="800"] The `Storage` component, * can save `UserPref` objects in json format and read it back. -* can save the Address Book data in xml format and read it back. +* can save the Stock List data in xml format and read it back. [[Design-Commons]] === Common classes @@ -243,54 +249,54 @@ This section describes some noteworthy details on how certain features are imple === Undo/Redo feature ==== Current Implementation -The undo/redo mechanism is facilitated by `VersionedAddressBook`. -It extends `AddressBook` with an undo/redo history, stored internally as an `addressBookStateList` and `currentStatePointer`. +The undo/redo mechanism is facilitated by `VersionedStockList`. +It extends `StockList` with an undo/redo history, stored internally as an `stockListStateList` and `currentStatePointer`. Additionally, it implements the following operations: -* `VersionedAddressBook#commit()` -- Saves the current address book state in its history. -* `VersionedAddressBook#undo()` -- Restores the previous address book state from its history. -* `VersionedAddressBook#redo()` -- Restores a previously undone address book state from its history. +* `VersionedStockList#commit()` -- Saves the current stock list state in its history. +* `VersionedStockList#undo()` -- Restores the previous stock list state from its history. +* `VersionedStockList#redo()` -- Restores a previously undone stock list state from its history. -These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively. +These operations are exposed in the `Model` interface as `Model#commitStockList()`, `Model#undoStockList()` and `Model#redoStockList()` respectively. Given below is an example usage scenario and how the undo/redo mechanism behaves at each step. -Step 1. The user launches the application for the first time. The `VersionedAddressBook` will be initialized with the initial address book state, and the `currentStatePointer` pointing to that single address book state. +Step 1. The user launches the application for the first time. The `VersionedStockList` will be initialized with the initial stock list state, and the `currentStatePointer` pointing to that single stock list state. image::UndoRedoStartingStateListDiagram.png[width="800"] -Step 2. The user executes `delete 5` command to delete the 5th person in the address book. The `delete` command calls `Model#commitAddressBook()`, causing the modified state of the address book after the `delete 5` command executes to be saved in the `addressBookStateList`, and the `currentStatePointer` is shifted to the newly inserted address book state. +Step 2. The user executes `delete 5` command to delete the 5th item in the stock list. The `delete` command calls `Model#commitStockList()`, causing the modified state of the stock list after the `delete 5` command executes to be saved in the `stockListStateList`, and the `currentStatePointer` is shifted to the newly inserted stock list state. image::UndoRedoNewCommand1StateListDiagram.png[width="800"] -Step 3. The user executes `add n/David ...` to add a new person. The `add` command also calls `Model#commitAddressBook()`, causing another modified address book state to be saved into the `addressBookStateList`. +Step 3. The user executes `add n/David ...` to add a new item. The `add` command also calls `Model#commitStockList()`, causing another modified stock list state to be saved into the `stockListStateList`. image::UndoRedoNewCommand2StateListDiagram.png[width="800"] [NOTE] -If a command fails its execution, it will not call `Model#commitAddressBook()`, so the address book state will not be saved into the `addressBookStateList`. +If a command fails its execution, it will not call `Model#commitStockList()`, so the stock list state will not be saved into the `stockListStateList`. -Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoAddressBook()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous address book state, and restores the address book to that state. +Step 4. The user now decides that adding the item was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoStockList()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous stock list state, and restores the stock list to that state. image::UndoRedoExecuteUndoStateListDiagram.png[width="800"] [NOTE] -If the `currentStatePointer` is at index 0, pointing to the initial address book state, then there are no previous address book states to restore. The `undo` command uses `Model#canUndoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the undo. +If the `currentStatePointer` is at index 0, pointing to the initial stock list state, then there are no previous stock list states to restore. The `undo` command uses `Model#canUndoStockList()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the undo. The following sequence diagram shows how the undo operation works: image::UndoRedoSequenceDiagram.png[width="800"] -The `redo` command does the opposite -- it calls `Model#redoAddressBook()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the address book to that state. +The `redo` command does the opposite -- it calls `Model#redoStockList()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the stock list to that state. [NOTE] -If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest address book state, then there are no undone address book states to restore. The `redo` command uses `Model#canRedoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo. +If the `currentStatePointer` is at index `stockListStateList.size() - 1`, pointing to the latest stock list state, then there are no undone stock list states to restore. The `redo` command uses `Model#canRedoStockList()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo. -Step 5. The user then decides to execute the command `list`. Commands that do not modify the address book, such as `list`, will usually not call `Model#commitAddressBook()`, `Model#undoAddressBook()` or `Model#redoAddressBook()`. Thus, the `addressBookStateList` remains unchanged. +Step 5. The user then decides to execute the command `list`. Commands that do not modify the stock list, such as `list`, will usually not call `Model#commitStockList()`, `Model#undoStockList()` or `Model#redoStockList()`. Thus, the `stockListStateList` remains unchanged. image::UndoRedoNewCommand3StateListDiagram.png[width="800"] -Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. We designed it this way because it no longer makes sense to redo the `add n/David ...` command. This is the behavior that most modern desktop applications follow. +Step 6. The user executes `clear`, which calls `Model#commitStockList()`. Since the `currentStatePointer` is not pointing at the end of the `stockListStateList`, all stock list states after the `currentStatePointer` will be purged. We designed it this way because it no longer makes sense to redo the `add n/David ...` command. This is the behavior that most modern desktop applications follow. image::UndoRedoNewCommand4StateListDiagram.png[width="800"] @@ -302,27 +308,344 @@ image::UndoRedoActivityDiagram.png[width="650"] ===== Aspect: How undo & redo executes -* **Alternative 1 (current choice):** Saves the entire address book. +* **Alternative 1 (current choice):** Saves the entire stock list. ** Pros: Easy to implement. ** Cons: May have performance issues in terms of memory usage. * **Alternative 2:** Individual command knows how to undo/redo by itself. -** Pros: Will use less memory (e.g. for `delete`, just save the person being deleted). +** Pros: Will use less memory (e.g. for `delete`, just save the item being deleted). ** Cons: We must ensure that the implementation of each individual command are correct. ===== Aspect: Data structure to support the undo/redo commands -* **Alternative 1 (current choice):** Use a list to store the history of address book states. +* **Alternative 1 (current choice):** Use a list to store the history of stock list states. ** Pros: Easy for new Computer Science student undergraduates to understand, who are likely to be the new incoming developers of our project. -** Cons: Logic is duplicated twice. For example, when a new command is executed, we must remember to update both `HistoryManager` and `VersionedAddressBook`. +** Cons: Logic is duplicated twice. For example, when a new command is executed, we must remember to update both `HistoryManager` and `VersionedStockList`. * **Alternative 2:** Use `HistoryManager` for undo/redo ** Pros: We do not need to maintain a separate list, and just reuse what is already in the codebase. ** Cons: Requires dealing with commands that have already been undone: We must remember to skip these commands. Violates Single Responsibility Principle and Separation of Concerns as `HistoryManager` now needs to do two different things. // end::undoredo[] -// tag::dataencryption[] -=== [Proposed] Data Encryption +// tag::saveopen[] +=== Save/Open feature +==== Current Implementation + +The save/open mechanism is facilitated by `SaveCommand` and `OpenCommand`. It extends `Command` and implements the following operation: + +* `Command#SaveCommand()` -- Saves the current version of the stock list as an XML file in a /versions/ folder. +* `Command#OpenCommand()` -- Opens the saved XML file as a table in the browser panel. + +The operation is exposed in the `Model` interface as `Model#saveStockList()`. + +Given below is an example usage scenario and how the saveCommand mechanism behaves at each step. + +Step 1. The user executes `save april_18` command to save the current version of the stock list as an xml file named `april_18.xml`. + +Step 2. The `save` command calls `Model#saveStockList()`, which initiates a `saveStockListVersionEvent`. The `fileName` and `ReadOnlyStockList` are saved as public final variables in the event. + +Step 3. The `handleSaveStockListVersionEvent` handler calls the `saveStockListVersion()` method from the `StorageManager` class. + +The following sequence diagram shows how the save operation works: + +image::SaveCommandSequenceDiagram.png[width="800"] +.Figure 10. How save/open operation works + +Step 4. The `ReadOnlyStockList` is saved as an xml file. + +Step 5. The user executes `open april_18` command to open the saved april_18.xml file. + +Step 6. The `open` command calls `Model#openStockList()`, which initiates a `openStockListVersionEvent`. The `fileName` is saved as a public final variable in the event. + +Step 7. The `handleOpenStockListVersionEvent` handler calls the `loadFileAsPage()` method from the `BrowserPanel` class. + +Step 8. The `loadFileAsPage()` method takes in the fileName and passes the directories of the `april_18.xml` and `template.xsl` files into `transformXml()` method. + +Step 9. The `april_18.xml` file is transformed with the `template.xsl` stylesheet into a table format and is displayed by the browser engine on the browser panel. + +==== Design Considerations + +===== Aspect: How save executes + +* **Alternative 1** (current choice) Save as .xml file. +** Pros: Able to display the inventory as a table by using XML transformation with XSLT. The .xml file can also be opened in Excel. +** Cons: - + +* **Alternative 2** Save as .csv file. +** Pros: File can be imported or exported. +** Cons: Unable to style the displayed table in the application. + +===== Aspect: How open executes + +* **Alternative 1** (current choice) Transform .xml file into a table with a .xsl stylesheet. +** Pros: Able to display the inventory in a table format for ease of view. +** Cons: A template.xsl file must be included in the application. + +* **Alternative 2** Open the .xml file as it is +** Pros: Does not require transforming the .xml file with XSLT. +** Cons: The displayed inventory is in XML format and hard to decipher. +// end::saveopen[] + +// tag::statusfeature[] +=== Status feature +==== Current Implementation + +The status feature is facilitated by the `Item` class, which contains the `Status` class. + +* The status class keeps track of the quantities `Ready`, `OnLoan`, and `Faulty`. + +The status feature is further facilitated by the 5 commands, `StatusCommand`, `ChangeStatusCommand`, `LoanListCommand`, `DeleteLoanListCommand` and `ViewLoansListCommand`. The commands extend `Command` and implements the following operations: + +* `Command#StatusCommand()` - Lists out the items according to their status. +* `Command#ChangeStatusCommand()` - Changes the status from `Ready` to `Faulty`, or vice versa. +* `Command#LoanListCommand()` - Creates and stores a loan list and changes the item status from `Ready` to `OnLoan`. +* `Command#DeleteLoanListCommand()` - Deletes the loan list and changes the item status from `OnLoan` to `Ready`. +* `Command#ViewLoanListCommand()` - Lists out the loan lists. + +Given below is an example usage scenario and how the command mechanisms behaves at each step. + +Step 1. The user executes the `AddCommand` to add 50 Arduinos into the stock list. + +* The `AddCommand` calls on the `Item Constructor` which creates the item object, and sets the status of all 50 Arduinos to `Ready` by default. + +Step 2. The user executes the `ChangeStatusCommand` to change the status of 10 Arduinos from `Ready` to `Faulty`. + +* The `ChangeStatusCommandParser#parseCommand()` is called and takes in the input string from the user, parses it into the `ChangeStatusDescriptor`, and returns a new `ChangeStatusCommand`. +* The `ChangeStatusDescriptor` class consists of the `itemName`, `quantity`, `originalStatus`, and `updatedStatus`. +* The `ChangeStatusCommand#execute()` is called. This calls the `Model#getFilteredItemList()` to obtain the item to update, and calls `Model#updateItem()` to update the item inside of the stock list with the new status. + +The following sequence diagram shows how the changeStatus operation works: + +image::ChangeStatus.png[width="800"] + +Step 3. The user executes the `StatusCommand` to list the items by status. + +* The `StatusCommand#execute` is called. This calls the `Model#getFilteredItemList()` to obtain the item list. +* The `StatusCommand#sortSimpleItems()` iterates through the item list and sorts them according to the status. +* The sorted items are stored as `SimpleItem` objects, which stores the just `itemName` the and `quantity`, as these are the only 2 relevant fields. +* The results are shown to the user. + +Step 4. The user executes the `LoanListCommand` to loan out 20 Arduinos to Davind. + +* The `LoanListParser#parseCommand` is called and takes in the input string from the user, parses it into the `LoanerDescription`, and returns a new `LoanListCommand`. +* The `LoanerDescription` class consists of the `itemName`, `loanerName`, and `quantity`. +* The `LoanListCommand#execute()` is called. This creates a new `ChangeStatusCommand` and calls the `ChangeStatusCommand#execute()` to change the status from Ready to On_Loan. +* The `LoanListCommand#updateLoanList()` is called. This creates an `XmlAdaptedLoanerDescription` object and is added into the `XmlAdaptedLoanList`. +* The `LoanListCommand#updateXmlLoanListFile()` is called to save the changes in storage. + +Step 5. The user executes the `ViewLoanListCommand` to view the loan list. + +* The `XmlAdaptedLoanList#getLoanList()` is used to obtain the loan list +* The `ViewLoanListCommand#getMessageOutput()` iterates through the loan list and returns the result to the user. + +Step 6. The user executes the `DeleteLoanListCommand` to delete the loan list when Davind returns the Arduinos. + +* The `DeleteLoanListCommand#execute()` is called. This calls the `ChangeStatusCommand#execute()` to change the status from On_Loan to Ready. +* The `xmlAdaptedLoanList#getLoanList()` is called to obtain the loan list. +* The `ArrayList#remove()` is called to remove the loan list entry +* The `LoanListCommand#updateXmlLoanListFile()` is called to save the changes in storage. + +==== Design Considerations + +===== Aspect: How StatusCommand executes + +* **Alternative 1:** Storing the items as an Item instead of SimpleItem. +** Pros: Easier to implement as I would not have to create a new class. +** Cons: This slows down the code, and it contains redundant information that is not required. +* **Alternative 2:** Iterate through the item list 3 times, one for each Status field. +** Pros: We would not have to create 3 ArrayList to store the items as they can be printed out immediately. +** Cons: This slows down the code significantly, and makes the code longer. + +===== Aspect: How ChangeStatusCommand executes + +* **Alternative 1:** Not creating a ChangeStatusDescriptor class to store the user inputs +** Pros: Easier to implement as I would not have to create a new class. +** Cons: This would make the code extremely messy as there would be multiple parameters to handle. + +// end::statusfeature[] + +// tag::login[] +=== Login feature +==== Current Implementation + +The login mechanism is facilitated by `LoginCommand`. It extends `Command` and implements the following operations: + + +* `LoginCommand#modifyLoginStatus()` -- checks if the password matches the account in the database, if it exists. If true, updates the logged in account status in `Model` accordingly. + +* `LoginCommand#execute()` -- calls `LoginCommand#modifyLoginStatus()`. Then, checks login status in `Model` and displays a login success message if true and displays a failure message otherwise. + +These operations are exposed in the `Model` interface as `Model#setLoggedInUser()` and `Model#getLoginStatus()` respectively. + + +Given below is an example usage scenario and how the LoginCommand mechanism behaves at each step. + +Step 1. The user executes `login u/admin p/admin` command to log into StockList with *admin* and *admin* being the username and password credentials respectively. + +Step 2. The `execute` command calls `Model#getLoginStatus()` and checks if the user is already logged in. If true, `execute` throws a `CommandException` notifying the user that he is already logged in. + +Step 3. The `execute` command then calls `LoginCommand#modifyLoginStatus()`, which checks if the username *admin* exists in the account list, and if it does, checks if the given password *admin* matches the password associated with the username *admin*. + +Step 4. If the *admin* password matches, `LoginCommand#modifyLoginStatus()` calls `Model#setLoggedInUser()` which updates the logged in account status in `model` with the logged in account set to *admin* and logged in status set to true. + +Step 5. The `execute` command then checks the log in status via `Model#getLoginStatus()`. A success message is printed if true; otherwise a failure message is printed. + +The following sequence diagram shows how the login operation works: + +image::LoginSequenceDiagram.png[width="800"] + +==== Design Considerations + +===== Aspect: How login executes + +* **Alternative 1** (current choice) Check against various accounts stored in a file and allow access if match. +** Pros: Allows for multiple accounts with access to StockList. +** Cons: More memory usage. + +* **Alternative 2** Checks against a single account that can be modified. +** Pros: Simple to implement, minimal memory usage, allows for only one access account. +** Cons: Does not allow access for multiple accounts, locked out of app if credentials lost. +// end::login[] + +// tag::tag[] +=== Tag feature +==== Current Implementation + +The Tag feature is facilitated by `TagCommand`. +It extends `Command` and implements the following operation: + +* `TagCommand()` -- Finds and shows all items under specific tags. + +Given below is an example usage scenario and how the tag mechanism behaves at each step. + +Step 1. The user executes `tag Lab1` command to list all items with the tag `Lab1` + +Step 2. The `tag` command calls `updateFilteredItemListByTag()`, which shows the search result to the user. + +The following sequence diagram shows how the tag operation works: + +image::TagComponentSequenceDiagram.png[width="800"] + +==== Design Considerations + +===== Aspect: How tagCommand executes + +* **Alternative 1 (current choice):** When multiple tags are used for search, the search result will be `all the items` contains at least one tag. +** Pros: Easy to implement. +** Cons: May be difficult for the user to tell which item contains one tag and which items contain the other tag. +* **Alternative 2:** Group the search result by different tags inputted. +** Pros: Will be useful in real-life scenario +** Cons: It is harder to implement and takes up more screen space to show the results. + + +// end::tag[] + +// tag::addTag[] +=== AddTag/DeleteTag feature +==== Current Implementation + +The AddTag and DeleteTag features are facilitated by `AddTagCommand` and `DeleteTagCommand`. +They extend `Command` and implement the following operations: + +* `AddTagCommand()` -- Adds new tags to a selected item in the Stock List. +* `DeleteTagCommand()` -- Deletes some tags while keeping the rest of a selected item in the Stock List. + +Given below are example usage scenarios and how the addTag/deleteTag mechanism behave at each step. + +Scenario 1: `AddTag` + +Step 1. The user executes `addTag 1 t/ Lab2` command to add a tag `Lab2` to the item with index 1 in the Stock List. + +Step 2. The `addTag` command calls `updateFilteredItemList();` in `model` to show the stock list after the tag is added. + +Scenario 2: `DeleteTag` + +Step 1. The user executes `deleteTag 1 t/ Lab1` command to delete a tag `Lab1` from the item with index 1 in the Stock List. -_{Explain here how the data encryption feature will be implemented}_ +Step 2. The `deleteTag` command calls `updateFilteredItemList();` in `model` to show the stock list after the tag is deleted. + + +==== Design Considerations + +* **Alternative 1 (current choice):** Adds tags to or deletes tags from one item in the stock list. +** Pros: Easy to implement. +** Cons: May be complicated when the same tags need to be added to or deleted from multiple items. +* **Alternative 2:** Adds tags to or deletes tag from multiple items in the stock list. +** Pros: Will be more user friendly when same tags need to be added to or deleted from multiple items. +** Cons: It is harder to implement. + + +// end::addTag[] + +//tag::lost&foundfeature[] +=== Lost and Found feature +==== Current Implementation +The Lost and Found mechanism is facilitated by `LostCommand`, `FoundCommand` and `LostandFoundCommand`. These three commands extend `Command`. And two class: LostDescriptor and Found Descriptor are created as well. The 3 commands implement the following operations: +* `LostCommand#lost()` -- Lost an item with its number from the Stock List. +* `FoundCommand#found()` -- Found a number of lost items from the Stock List. +* `LostandFoundCommand#lost&found()` -- List the lost items and the lost number. + +Given below are example usage scenarios and how the Lost and Found mechanism behaves at each step. + +Scenario 1: + +Step 1. The user executes `lost 1 q/20` command to indicate 20 Arduinos are lost from the Stock List. + +Step 2. The `LostDescriptor` consists of the lost quantity of the item. + +Step 3. The `lost` command firstly calls `getFilteredItemList()` to get the item of the given index and its original quantity. + +Step 4. Then `LostDescriptor` will be called and the lost quantity of the item will be returned. + +Step 5. By using the original quantity of the item minus the lost quantity, the updated quantity of the item will be got. + +Step 6. A copy is created and the change of the quantity is made to the copy. The copy then replaces the original item. + +Step 7. The updated item list and success message is shown to the user. Updates are committed to the storage. + +Scenario 2: + +Step 1. The user executes `found 1 q/20` command to indicate 20 lost Arduinos from the Stock List are found. + +Step 2. The `FoundDescriptor` consists of the found quantity of the item. + +Step 3. The `found` command firstly calls `getFilteredItemList()` to get the item of the given index and its original quantity. + +Step 4. Then `FoundDescriptor` will be called and the found quantity of the item will be returned. + +Step 5. By using the original quantity of the item adds the found quantity, the updated quantity of the item will be got. + +Step 6. A copy is created and the change of the quantity is made to the copy. The copy then replaces the original item. + +Step 7. The updated item list and success message is shown to the user. Updates are committed to the storage. + +Scenario 3: + +Step 1. The user executes `lost 1 q/5` command to indicate 5 Arduinos are lost. + +Step 2. The user executes `lost 2 q/3` command to indicate 3 Rasperry Pis are lost. + +Step 3. The user executes `found 1 q/2` command to indicate 2 lost Arduinos are found. + +Step 4. The user executes `lost&found`. + +Step 5. The lost list will be shown to the user. + +==== Design Considerations + +===== Aspect: How Lost and Found executes + + +* **Alternative 1** (current choice) Create a lost&found list to record the lost&found history. +** Pros: Able to list all the lost&found records and history. +** Cons: May have performance issues in terms of usage and require more memory. + +* **Alternative 2** When executing lost or found command, update the quantity of the item in the StockList. +** Pros: Easy to handle while only increasing and decreasing the quantity will be used and no need to record all the lost history. +** Cons: Unable to list all the lost and found history. + +//end::lost&foundfeature[] +// end::lostcommand[],foundcommand[],lostandfoundcommand[] +// tag::dataencryption[] +=== Data Encryption (Coming in V2.0) // end::dataencryption[] @@ -341,11 +664,18 @@ We are using `java.util.logging` package for logging. The `LogsCenter` class is * `INFO` : Information showing the noteworthy actions by the App * `FINE` : Details that is not usually noteworthy but may be useful in debugging e.g. print the actual list instead of just its size + [[Implementation-Configuration]] === Configuration Certain properties of the application can be controlled (e.g App name, logging level) through the configuration file (default: `config.json`). +// tag::dataencryption[] +=== Data Encryption (Coming in V2.0) +In v2.0, app data will be encrypted using AES encryption for added security. + +// end::dataencryption[] + == Documentation We use asciidoc for writing documentation. @@ -528,391 +858,451 @@ Here are the steps to create a new release. === Managing Dependencies -A project often depends on third-party libraries. For example, Address Book depends on the http://wiki.fasterxml.com/JacksonHome[Jackson library] for XML parsing. Managing these _dependencies_ can be automated using Gradle. For example, Gradle can download the dependencies automatically, which is better than these alternatives. + +A project often depends on third-party libraries. For example, Stock List depends on the http://wiki.fasterxml.com/JacksonHome[Jackson library] for XML parsing. Managing these _dependencies_ can be automated using Gradle. For example, Gradle can download the dependencies automatically, which is better than these alternatives. + a. Include those libraries in the repo (this bloats the repo size) + b. Require developers to download those libraries manually (this creates extra work for developers) -[[GetStartedProgramming]] [appendix] -== Suggested Programming Tasks to Get Started +== Product Scope -Suggested path for new programmers: +*Target user profile*: -1. First, add small local-impact (i.e. the impact of the change does not go beyond the component) enhancements to one component at a time. Some suggestions are given in <>. +* has a need to manage a significant number of stocks +* prefer desktop apps over other types +* can type fast +* prefers typing over mouse input +* is reasonably comfortable using CLI apps -2. Next, add a feature that touches multiple components to learn how to implement an end-to-end feature across all components. <> explains how to go about adding such a feature. +*Value proposition*: manage stocks faster than a the current method, which is by physical stock taking, or using a spreadsheet. -[[GetStartedProgramming-EachComponent]] -=== Improving each component +[appendix] +== User Stories -Each individual exercise in this section is component-based (i.e. you would not need to modify the other components to get it to work). +Priorities: High (must have) - `* * \*`, Medium (nice to have) - `* \*`, Low (unlikely to have) - `*` -[discrete] -==== `Logic` component +[width="59%",cols="22%,<23%,<25%,<30%",options="header",] +|======================================================================= +|Priority |As a ... |I want to ... |So that I can... -*Scenario:* You are in charge of `logic`. During dog-fooding, your team realize that it is troublesome for the user to type the whole command in order to execute a command. Your team devise some strategies to help cut down the amount of typing necessary, and one of the suggestions was to implement aliases for the command words. Your job is to implement such aliases. +|`* * *` |Lab Technician |Control who has access to my inventory |Secure my sensitive data -[TIP] -Do take a look at <> before attempting to modify the `Logic` component. +|`* * *` |Lab Technician |Add new items to the list |Keep track of the new item -. Add a shorthand equivalent alias for each of the individual commands. For example, besides typing `clear`, the user can also type `c` to remove all persons in the list. -+ -**** -* Hints -** Just like we store each individual command word constant `COMMAND_WORD` inside `*Command.java` (e.g. link:{repoURL}/src/main/java/seedu/address/logic/commands/FindCommand.java[`FindCommand#COMMAND_WORD`], link:{repoURL}/src/main/java/seedu/address/logic/commands/DeleteCommand.java[`DeleteCommand#COMMAND_WORD`]), you need a new constant for aliases as well (e.g. `FindCommand#COMMAND_ALIAS`). -** link:{repoURL}/src/main/java/seedu/address/logic/parser/AddressBookParser.java[`AddressBookParser`] is responsible for analyzing command words. -* Solution -** Modify the switch statement in link:{repoURL}/src/main/java/seedu/address/logic/parser/AddressBookParser.java[`AddressBookParser#parseCommand(String)`] such that both the proper command word and alias can be used to execute the same intended command. -** Add new tests for each of the aliases that you have added. -** Update the user guide to document the new aliases. -** See this https://github.com/se-edu/addressbook-level4/pull/785[PR] for the full solution. -**** +|`* * *` |Lab Technician |Update the quantity of an item in the list |Record the quantity change of the item -[discrete] -==== `Model` component +|`* * *` |Lab Technician |Update the status of an item in the list |Record the change in status of the item -*Scenario:* You are in charge of `model`. One day, the `logic`-in-charge approaches you for help. He wants to implement a command such that the user is able to remove a particular tag from everyone in the address book, but the model API does not support such a functionality at the moment. Your job is to implement an API method, so that your teammate can use your API to implement his command. +|`* * *` |Lab Technician |Delete an item from the list |Remove all information regarding to the item -[TIP] -Do take a look at <> before attempting to modify the `Model` component. - -. Add a `removeTag(Tag)` method. The specified tag will be removed from everyone in the address book. -+ -**** -* Hints -** The link:{repoURL}/src/main/java/seedu/address/model/Model.java[`Model`] and the link:{repoURL}/src/main/java/seedu/address/model/AddressBook.java[`AddressBook`] API need to be updated. -** Think about how you can use SLAP to design the method. Where should we place the main logic of deleting tags? -** Find out which of the existing API methods in link:{repoURL}/src/main/java/seedu/address/model/AddressBook.java[`AddressBook`] and link:{repoURL}/src/main/java/seedu/address/model/person/Person.java[`Person`] classes can be used to implement the tag removal logic. link:{repoURL}/src/main/java/seedu/address/model/AddressBook.java[`AddressBook`] allows you to update a person, and link:{repoURL}/src/main/java/seedu/address/model/person/Person.java[`Person`] allows you to update the tags. -* Solution -** Implement a `removeTag(Tag)` method in link:{repoURL}/src/main/java/seedu/address/model/AddressBook.java[`AddressBook`]. Loop through each person, and remove the `tag` from each person. -** Add a new API method `deleteTag(Tag)` in link:{repoURL}/src/main/java/seedu/address/model/ModelManager.java[`ModelManager`]. Your link:{repoURL}/src/main/java/seedu/address/model/ModelManager.java[`ModelManager`] should call `AddressBook#removeTag(Tag)`. -** Add new tests for each of the new public methods that you have added. -** See this https://github.com/se-edu/addressbook-level4/pull/790[PR] for the full solution. -**** +|`* * *` |Lab Technician |Show all items |Check and explore all my items -[discrete] -==== `Ui` component +|`* * *` |Lab Technician |Find a specific item |Easily check the quantity available -*Scenario:* You are in charge of `ui`. During a beta testing session, your team is observing how the users use your address book application. You realize that one of the users occasionally tries to delete non-existent tags from a contact, because the tags all look the same visually, and the user got confused. Another user made a typing mistake in his command, but did not realize he had done so because the error message wasn't prominent enough. A third user keeps scrolling down the list, because he keeps forgetting the index of the last person in the list. Your job is to implement improvements to the UI to solve all these problems. +|`* * *` |Lab Technician |List all materials required for a lab session |Prepare and account for them easily -[TIP] -Do take a look at <> before attempting to modify the `UI` component. +|`* *` |Lab Technician |Search for an item with just a partial keyword |Find items more efficiently -. Use different colors for different tags inside person cards. For example, `friends` tags can be all in brown, and `colleagues` tags can be all in yellow. -+ -**Before** -+ -image::getting-started-ui-tag-before.png[width="300"] -+ -**After** -+ -image::getting-started-ui-tag-after.png[width="300"] -+ -**** -* Hints -** The tag labels are created inside link:{repoURL}/src/main/java/seedu/address/ui/PersonCard.java[the `PersonCard` constructor] (`new Label(tag.tagName)`). https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/Label.html[JavaFX's `Label` class] allows you to modify the style of each Label, such as changing its color. -** Use the .css attribute `-fx-background-color` to add a color. -** You may wish to modify link:{repoURL}/src/main/resources/view/DarkTheme.css[`DarkTheme.css`] to include some pre-defined colors using css, especially if you have experience with web-based css. -* Solution -** You can modify the existing test methods for `PersonCard` 's to include testing the tag's color as well. -** See this https://github.com/se-edu/addressbook-level4/pull/798[PR] for the full solution. -*** The PR uses the hash code of the tag names to generate a color. This is deliberately designed to ensure consistent colors each time the application runs. You may wish to expand on this design to include additional features, such as allowing users to set their own tag colors, and directly saving the colors to storage, so that tags retain their colors even if the hash code algorithm changes. -**** +|`* *` |Lab Technician |Check which items are understocked |Easily restock them -. Modify link:{repoURL}/src/main/java/seedu/address/commons/events/ui/NewResultAvailableEvent.java[`NewResultAvailableEvent`] such that link:{repoURL}/src/main/java/seedu/address/ui/ResultDisplay.java[`ResultDisplay`] can show a different style on error (currently it shows the same regardless of errors). -+ -**Before** -+ -image::getting-started-ui-result-before.png[width="200"] -+ -**After** -+ -image::getting-started-ui-result-after.png[width="200"] -+ -**** -* Hints -** link:{repoURL}/src/main/java/seedu/address/commons/events/ui/NewResultAvailableEvent.java[`NewResultAvailableEvent`] is raised by link:{repoURL}/src/main/java/seedu/address/ui/CommandBox.java[`CommandBox`] which also knows whether the result is a success or failure, and is caught by link:{repoURL}/src/main/java/seedu/address/ui/ResultDisplay.java[`ResultDisplay`] which is where we want to change the style to. -** Refer to link:{repoURL}/src/main/java/seedu/address/ui/CommandBox.java[`CommandBox`] for an example on how to display an error. -* Solution -** Modify link:{repoURL}/src/main/java/seedu/address/commons/events/ui/NewResultAvailableEvent.java[`NewResultAvailableEvent`] 's constructor so that users of the event can indicate whether an error has occurred. -** Modify link:{repoURL}/src/main/java/seedu/address/ui/ResultDisplay.java[`ResultDisplay#handleNewResultAvailableEvent(NewResultAvailableEvent)`] to react to this event appropriately. -** You can write two different kinds of tests to ensure that the functionality works: -*** The unit tests for `ResultDisplay` can be modified to include verification of the color. -*** The system tests link:{repoURL}/src/test/java/systemtests/AddressBookSystemTest.java[`AddressBookSystemTest#assertCommandBoxShowsDefaultStyle() and AddressBookSystemTest#assertCommandBoxShowsErrorStyle()`] to include verification for `ResultDisplay` as well. -** See this https://github.com/se-edu/addressbook-level4/pull/799[PR] for the full solution. -*** Do read the commits one at a time if you feel overwhelmed. -**** +|`* *` |Lab Technician |Add one item to use in some labs |Easily schedule labs -. Modify the link:{repoURL}/src/main/java/seedu/address/ui/StatusBarFooter.java[`StatusBarFooter`] to show the total number of people in the address book. -+ -**Before** -+ -image::getting-started-ui-status-before.png[width="500"] -+ -**After** -+ -image::getting-started-ui-status-after.png[width="500"] -+ -**** -* Hints -** link:{repoURL}/src/main/resources/view/StatusBarFooter.fxml[`StatusBarFooter.fxml`] will need a new `StatusBar`. Be sure to set the `GridPane.columnIndex` properly for each `StatusBar` to avoid misalignment! -** link:{repoURL}/src/main/java/seedu/address/ui/StatusBarFooter.java[`StatusBarFooter`] needs to initialize the status bar on application start, and to update it accordingly whenever the address book is updated. -* Solution -** Modify the constructor of link:{repoURL}/src/main/java/seedu/address/ui/StatusBarFooter.java[`StatusBarFooter`] to take in the number of persons when the application just started. -** Use link:{repoURL}/src/main/java/seedu/address/ui/StatusBarFooter.java[`StatusBarFooter#handleAddressBookChangedEvent(AddressBookChangedEvent)`] to update the number of persons whenever there are new changes to the addressbook. -** For tests, modify link:{repoURL}/src/test/java/guitests/guihandles/StatusBarFooterHandle.java[`StatusBarFooterHandle`] by adding a state-saving functionality for the total number of people status, just like what we did for save location and sync status. -** For system tests, modify link:{repoURL}/src/test/java/systemtests/AddressBookSystemTest.java[`AddressBookSystemTest`] to also verify the new total number of persons status bar. -** See this https://github.com/se-edu/addressbook-level4/pull/803[PR] for the full solution. -**** +|`* *` |Lab Technician |Delete one item to use in some labs |Easily schedule labs and correct my mistakes -[discrete] -==== `Storage` component +|`* *` |Lab Technician |Record statuses of individual materials (Free, Loan, Faulty) |Account for the state of items in the lab -*Scenario:* You are in charge of `storage`. For your next project milestone, your team plans to implement a new feature of saving the address book to the cloud. However, the current implementation of the application constantly saves the address book after the execution of each command, which is not ideal if the user is working on limited internet connection. Your team decided that the application should instead save the changes to a temporary local backup file first, and only upload to the cloud after the user closes the application. Your job is to implement a backup API for the address book storage. +|`* *` |Lab Technician |Report all the lost items |Record the lost status for all the items and check the lost list whenever I want -[TIP] -Do take a look at <> before attempting to modify the `Storage` component. +|`*` |Lab Technician |Withdraw my operation sometime |Go back to my previous edition anytime -. Add a new method `backupAddressBook(ReadOnlyAddressBook)`, so that the address book can be saved in a fixed temporary location. -+ -**** -* Hint -** Add the API method in link:{repoURL}/src/main/java/seedu/address/storage/AddressBookStorage.java[`AddressBookStorage`] interface. -** Implement the logic in link:{repoURL}/src/main/java/seedu/address/storage/StorageManager.java[`StorageManager`] and link:{repoURL}/src/main/java/seedu/address/storage/XmlAddressBookStorage.java[`XmlAddressBookStorage`] class. -* Solution -** See this https://github.com/se-edu/addressbook-level4/pull/594[PR] for the full solution. -**** +|`*` |Lab Technician |Create loan lists for each project group |Account for items loaned when they are returned -[[GetStartedProgramming-RemarkCommand]] -=== Creating a new command: `remark` +|`*` |Lab Technician |Store the quantities of all my items at a given time |Review them anytime +|======================================================================= -By creating this command, you will get a chance to learn how to implement a feature end-to-end, touching all major components of the app. +_{More to be added}_ -*Scenario:* You are a software maintainer for `addressbook`, as the former developer team has moved on to new projects. The current users of your application have a list of new feature requests that they hope the software will eventually have. The most popular request is to allow adding additional comments/notes about a particular contact, by providing a flexible `remark` field for each contact, rather than relying on tags alone. After designing the specification for the `remark` command, you are convinced that this feature is worth implementing. Your job is to implement the `remark` command. +[appendix] +== Use Cases: -==== Description -Edits the remark for a person specified in the `INDEX`. + -Format: `remark INDEX r/[REMARK]` +(For all use cases below, the *System* is `StockList` and the *Actor* is the `user`, unless specified otherwise) -Examples: +[discrete] +=== Use case: Delete item -* `remark 1 r/Likes to drink coffee.` + -Edits the remark for the first person to `Likes to drink coffee.` -* `remark 1 r/` + -Removes the remark for the first person. +*MSS* -==== Step-by-step Instructions +1. User requests to list items +2. StockList shows a list of items +3. User requests to delete a specific item in the list +4. StockList deletes the item ++ +Use case ends. -===== [Step 1] Logic: Teach the app to accept 'remark' which does nothing -Let's start by teaching the application how to parse a `remark` command. We will add the logic of `remark` later. +*Extensions* -**Main:** +[none] +* 2a. The list is empty. ++ +Use case ends. -. Add a `RemarkCommand` that extends link:{repoURL}/src/main/java/seedu/address/logic/commands/Command.java[`Command`]. Upon execution, it should just throw an `Exception`. -. Modify link:{repoURL}/src/main/java/seedu/address/logic/parser/AddressBookParser.java[`AddressBookParser`] to accept a `RemarkCommand`. +* 3a. The given index is invalid. ++ +[none] +** 3a1. StockList shows an error message. ++ +Use case resumes at step 2. -**Tests:** +[discrete] +=== Use case: List items by tag -. Add `RemarkCommandTest` that tests that `execute()` throws an Exception. -. Add new test method to link:{repoURL}/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java[`AddressBookParserTest`], which tests that typing "remark" returns an instance of `RemarkCommand`. +*MSS* -===== [Step 2] Logic: Teach the app to accept 'remark' arguments -Let's teach the application to parse arguments that our `remark` command will accept. E.g. `1 r/Likes to drink coffee.` +1. User requests to list all items +2. StockList shows a list of items +3. User requests to show items with a specific tag +4. StockList finds and shows the items ++ +Use case ends. -**Main:** +*Extensions* -. Modify `RemarkCommand` to take in an `Index` and `String` and print those two parameters as the error message. -. Add `RemarkCommandParser` that knows how to parse two arguments, one index and one with prefix 'r/'. -. Modify link:{repoURL}/src/main/java/seedu/address/logic/parser/AddressBookParser.java[`AddressBookParser`] to use the newly implemented `RemarkCommandParser`. +[none] +* 2a. The item list is empty ++ +[none] +** 2a1. StockList shows an error message. ++ +Use case ends. -**Tests:** +* 3a. The given tag is invalid ++ +[none] +** 3a1. StockList shows an error message. ++ +Use case resumes at step 2. -. Modify `RemarkCommandTest` to test the `RemarkCommand#equals()` method. -. Add `RemarkCommandParserTest` that tests different boundary values -for `RemarkCommandParser`. -. Modify link:{repoURL}/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java[`AddressBookParserTest`] to test that the correct command is generated according to the user input. +[discrete] +=== Use case: Add tags to item -===== [Step 3] Ui: Add a placeholder for remark in `PersonCard` -Let's add a placeholder on all our link:{repoURL}/src/main/java/seedu/address/ui/PersonCard.java[`PersonCard`] s to display a remark for each person later. +*MSS* -**Main:** +1. User requests to list all items +2. StockList shows a list of items +3. User requests to add tags to an item +4. Stock list adds the inputted tags to the item ++ +Use case ends. -. Add a `Label` with any random text inside link:{repoURL}/src/main/resources/view/PersonListCard.fxml[`PersonListCard.fxml`]. -. Add FXML annotation in link:{repoURL}/src/main/java/seedu/address/ui/PersonCard.java[`PersonCard`] to tie the variable to the actual label. +*Extensions* -**Tests:** +[none] +* 2a. The index of item is wrong. ++ +[none] +** 2a1. StockList shows an error message. ++ +Use case resumes at step 2. -. Modify link:{repoURL}/src/test/java/guitests/guihandles/PersonCardHandle.java[`PersonCardHandle`] so that future tests can read the contents of the remark label. +* 3a. The given tag is invalid. ++ +[none] +** 3a1. StockList shows an error message. ++ +Use case ends. -===== [Step 4] Model: Add `Remark` class -We have to properly encapsulate the remark in our link:{repoURL}/src/main/java/seedu/address/model/person/Person.java[`Person`] class. Instead of just using a `String`, let's follow the conventional class structure that the codebase already uses by adding a `Remark` class. +[discrete] +=== Use case: Delete some tags of item -**Main:** +*MSS* -. Add `Remark` to model component (you can copy from link:{repoURL}/src/main/java/seedu/address/model/person/Address.java[`Address`], remove the regex and change the names accordingly). -. Modify `RemarkCommand` to now take in a `Remark` instead of a `String`. +1. User requests to list all items +2. StockList shows a list of items +3. User requests to delete some tags of an item +4. Stock list deletes the inputted tags of the item ++ +Use case ends. -**Tests:** +*Extensions* -. Add test for `Remark`, to test the `Remark#equals()` method. +[none] +* 2a. The index of item is wrong. ++ +[none] +** 2a1. StockList shows an error message. ++ +Use case resumes at step 2. -===== [Step 5] Model: Modify `Person` to support a `Remark` field -Now we have the `Remark` class, we need to actually use it inside link:{repoURL}/src/main/java/seedu/address/model/person/Person.java[`Person`]. +* 3a. The given tag does not exist. ++ +[none] +** 3a1. StockList shows an error message. ++ +Use case ends. -**Main:** +[discrete] +=== Use case: Lost item -. Add `getRemark()` in link:{repoURL}/src/main/java/seedu/address/model/person/Person.java[`Person`]. -. You may assume that the user will not be able to use the `add` and `edit` commands to modify the remarks field (i.e. the person will be created without a remark). -. Modify link:{repoURL}/src/main/java/seedu/address/model/util/SampleDataUtil.java/[`SampleDataUtil`] to add remarks for the sample data (delete your `addressBook.xml` so that the application will load the sample data when you launch it.) +*MSS* -===== [Step 6] Storage: Add `Remark` field to `XmlAdaptedPerson` class -We now have `Remark` s for `Person` s, but they will be gone when we exit the application. Let's modify link:{repoURL}/src/main/java/seedu/address/storage/XmlAdaptedPerson.java[`XmlAdaptedPerson`] to include a `Remark` field so that it will be saved. +1. User requests to list items +2. StockList shows a list of items +3. User requests to lose an item with the lost quantity +4. StockList loses the item ++ +Use case ends. -**Main:** +*Extensions* -. Add a new Xml field for `Remark`. +[none] +* 2a. The list is empty. ++ +Use case ends. -**Tests:** +* 3a. The given lost quantity is invalid. ++ +[none] +** 3a1. StockList shows an error message. ++ +Use case resumes at step 2. -. Fix `invalidAndValidPersonAddressBook.xml`, `typicalPersonsAddressBook.xml`, `validAddressBook.xml` etc., such that the XML tests will not fail due to a missing `` element. +[discrete] +=== Use case: Found item -===== [Step 6b] Test: Add withRemark() for `PersonBuilder` -Since `Person` can now have a `Remark`, we should add a helper method to link:{repoURL}/src/test/java/seedu/address/testutil/PersonBuilder.java[`PersonBuilder`], so that users are able to create remarks when building a link:{repoURL}/src/main/java/seedu/address/model/person/Person.java[`Person`]. +*MSS* -**Tests:** +1. User requests to list items +2. StockList shows a list of items +3. User requests to find a lost item with the found quantity +4. StockList found the item ++ +Use case ends. -. Add a new method `withRemark()` for link:{repoURL}/src/test/java/seedu/address/testutil/PersonBuilder.java[`PersonBuilder`]. This method will create a new `Remark` for the person that it is currently building. -. Try and use the method on any sample `Person` in link:{repoURL}/src/test/java/seedu/address/testutil/TypicalPersons.java[`TypicalPersons`]. +*Extensions* -===== [Step 7] Ui: Connect `Remark` field to `PersonCard` -Our remark label in link:{repoURL}/src/main/java/seedu/address/ui/PersonCard.java[`PersonCard`] is still a placeholder. Let's bring it to life by binding it with the actual `remark` field. +[none] +* 2a. The list is empty. ++ +Use case ends. -**Main:** +* 3a. The given found quantity is invalid. ++ +[none] +** 3a1. StockList shows an error message. ++ +Use case resumes at step 2. -. Modify link:{repoURL}/src/main/java/seedu/address/ui/PersonCard.java[`PersonCard`]'s constructor to bind the `Remark` field to the `Person` 's remark. +[discrete] +=== Use case: List lost items -**Tests:** +*MSS* -. Modify link:{repoURL}/src/test/java/seedu/address/ui/testutil/GuiTestAssert.java[`GuiTestAssert#assertCardDisplaysPerson(...)`] so that it will compare the now-functioning remark label. +1. User requests to list lost items +2. StockList shows the list of all lost items ++ +Use case ends. -===== [Step 8] Logic: Implement `RemarkCommand#execute()` logic -We now have everything set up... but we still can't modify the remarks. Let's finish it up by adding in actual logic for our `remark` command. +// tag::accountUseCase[] +[discrete] +=== Use case: Login -**Main:** +*MSS* -. Replace the logic in `RemarkCommand#execute()` (that currently just throws an `Exception`), with the actual logic to modify the remarks of a person. +1. User requests to list accounts +2. StockList shows the list of accounts +3. User requests to login using his account credentials +4. StockList shows that the user has successfully logged in ++ +Use case ends. -**Tests:** +*Extensions* -. Update `RemarkCommandTest` to test that the `execute()` logic works. +[none] +* 2a. The list does not contain user's account. ++ +Use case ends. -==== Full Solution +* 3a. The given account credentials are invalid. ++ +[none] +** 3a1. StockList shows an error message. ++ +Use case resumes at step 2. -See this https://github.com/se-edu/addressbook-level4/pull/599[PR] for the step-by-step solution. +[discrete] +=== Use case: Delete account -[appendix] -== Product Scope +*MSS* -*Target user profile*: +1. User requests to list accounts +2. StockList shows a list of accounts +3. User requests to delete a specific account in the list +4. StockList deletes the account ++ +Use case ends. -* has a need to manage a significant number of contacts -* prefer desktop apps over other types -* can type fast -* prefers typing over mouse input -* is reasonably comfortable using CLI apps +*Extensions* -*Value proposition*: manage contacts faster than a typical mouse/GUI driven app +* 3a. The given index is invalid. ++ +[none] +** 3a1. StockList shows an error message. ++ +Use case resumes at step 2. -[appendix] -== User Stories +[discrete] +=== Use case: Add account -Priorities: High (must have) - `* * \*`, Medium (nice to have) - `* \*`, Low (unlikely to have) - `*` +*MSS* -[width="59%",cols="22%,<23%,<25%,<30%",options="header",] -|======================================================================= -|Priority |As a ... |I want to ... |So that I can... -|`* * *` |new user |see usage instructions |refer to instructions when I forget how to use the App +1. User requests to add an account +2. StockList adds the account to the database. ++ +Use case ends. -|`* * *` |user |add a new person | +*Extensions* -|`* * *` |user |delete a person |remove entries that I no longer need +* 1a. The given account is already in the database. ++ +[none] +** 1a1. StockList shows an error message. ++ +Use case resumes at step 1. -|`* * *` |user |find a person by name |locate details of persons without having to go through the entire list +[discrete] +=== Use case: Edit account -|`* *` |user |hide <> by default |minimize chance of someone else seeing them by accident +*MSS* -|`*` |user with many persons in the address book |sort persons by name |locate a person easily -|======================================================================= +1. User requests to list accounts +2. StockList shows a list of accounts +3. User requests to edit a specific account in the list +4. StockList edits the account ++ +Use case ends. -_{More to be added}_ +*Extensions* -[appendix] -== Use Cases +* 3a. The given index is invalid. ++ +[none] +** 3a1. StockList shows an error message. ++ +Use case resumes at step 2. +// end::accountUseCase[] -(For all use cases below, the *System* is the `AddressBook` and the *Actor* is the `user`, unless specified otherwise) +// tag::saveopen[] [discrete] -=== Use case: Delete person +=== Use case: Save stocklist *MSS* -1. User requests to list persons -2. AddressBook shows a list of persons -3. User requests to delete a specific person in the list -4. AddressBook deletes the person +1. User requests to save stocklist +2. StockList saves current inventory as .xml file with specified file name. + Use case ends. *Extensions* [none] -* 2a. The list is empty. +* 1a. The file name is invalid. +* 1b. Stocklist shows an error message. + Use case ends. -* 3a. The given index is invalid. + +[discrete] +=== Use case: Open stocklist version + +*MSS* + +1. User requests to open stocklist version +2. StockList opens specified .xml file and displays as table. + +Use case ends. + +*Extensions* + [none] -** 3a1. AddressBook shows an error message. +* 1a. The file name is invalid. +* 1b. Stocklist shows an error message. + -Use case resumes at step 2. +Use case ends. +// end::saveopen[] -_{More to be added}_ +[discrete] +=== Use case: List items by status -[appendix] -== Non Functional Requirements +*MSS* -. Should work on any <> as long as it has Java `9` or higher installed. -. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage. -. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse. +1. User requests to list items according to status +2. StockList lists out the item according to the status ++ +Use case ends. -_{More to be added}_ +[discrete] +=== Use case: Change Status -[appendix] -== Glossary +*MSS* -[[mainstream-os]] Mainstream OS:: -Windows, Linux, Unix, OS-X +1. User requests to list all items +2. StockList shows a list the list of all items +3. User requests to change the status of an item. +4. StockList changes the status of the item. ++ +Use case ends. -[[private-contact-detail]] Private contact detail:: -A contact detail that is not meant to be shared with others +*Extensions* -[appendix] -== Product Survey +* 3a. Any of the inputs are invalid. ++ +[none] +** 3a1. StockList shows an error message. -*Product Name* +[discrete] +=== Use case: Creating loan list entry -Author: ... +*MSS* -Pros: +1. User requests create a loan list entry +2. StockList creates the loan list entry +3. User requests to view the loan list +4. StockList shows the updated loan list ++ +Use case ends. + +*Extensions* -* ... -* ... +* 1a. Any of the inputs are invalid. ++ +[none] +** 1a1. StockList shows an error message. + +[discrete] +=== Use case: Deleting loan list entry + +*MSS* + +1. User requests delete a loan list entry +2. StockList deletes the loan list entry +3. User requests to view the loan list +4. StockList shows the updated loan list ++ +Use case ends. + +*Extensions* + +* 1a. Any of the inputs are invalid. ++ +[none] +** 1a1. StockList shows an error message. -Cons: +[appendix] +== Non Functional Requirements -* ... -* ... +. Should work on any mainstream OS as long as it has Java `9` or higher installed. +. Should be able to hold up to 1000 items without a noticeable sluggishness in performance for typical usage. +. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse. [appendix] == Instructions for Manual Testing @@ -936,21 +1326,86 @@ These instructions only provide a starting point for testers to work on; testers .. Re-launch the app by double-clicking the jar file. + Expected: The most recent window size and location is retained. -_{ more test cases ... }_ +=== Deleting a item -=== Deleting a person +. Deleting a item while all items are listed -. Deleting a person while all persons are listed - -.. Prerequisites: List all persons using the `list` command. Multiple persons in the list. +.. Prerequisites: List all items using the `list` command. Multiple items in the list. .. Test case: `delete 1` + Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated. .. Test case: `delete 0` + - Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. + Expected: No item is deleted. Error details shown in the status message. Status bar remains the same. .. Other incorrect delete commands to try: `delete`, `delete x` (where x is larger than the list size) _{give more}_ + Expected: Similar to previous. -_{ more test cases ... }_ +=== Lost an item + +. Lost an item while all items are listed + +.. Prerequisites: List all items using the `list` command. Multiple items in the list. +.. Test case: `lost 1 q/15` + + Expected: First item is decreased by 15 from the list. Details of the lost item shown in the status message. Timestamp in the status bar is updated. +.. Test case: `lost 0 q/15` + + Expected: No item is lost. Error details shown in the status message. Status bar remains the same. +.. Other incorrect lost commands to try: `lost 1 q/x` (where x is larger than the Ready quantity of the first item) + + Expected: Similar to previous. + +=== Found a lost item + +. Found a lost item while all items are listed + +.. Prerequisites: List all lost items using the `lost&found` command. Multiple items in the list. +.. Prerequisites: List all items using the `list` command. Multiple items in the list. +.. Test case: `found 1 q/15` + + Expected: First item is increased by 15 from the list. The corresponding lost quantity of this item will be decreased. Details of the found item shown in the status message. Timestamp in the status bar is updated. +.. Test case: `found 0 q/15` + + Expected: No item is lost. Error details shown in the status message. Status bar remains the same. +.. Other incorrect found commands to try: `found 1 q/x` (where x is larger than the lost quantity of the first item) + + Expected: Similar to previous. + +=== Logging in + +. Logging in while all accounts are listed + +.. Prerequisites: List all accounts using the `listAccounts` command. Only default account 'admin' in the list, with the default credentials. Login status is currently logged out. +.. Test case: `login u/admin p/lol` + + Expected: Account is not logged in. Error details shown in the status message. Status bar remains the same. +.. Test case: `login u/admin p/admin` + + Expected: Account is logged in. Details of the logged in account shown in the status message. Timestamp in the status bar is updated. +.. Other incorrect login commands to try: `login`, `login u/x p/y` (where x and y differ from the registered account data) + + Expected: Similar to the first test case. + +=== Account management + +. Adding an account + +.. Prerequisites: List all accounts using the `listAccounts` command. Only default account 'admin' in the list, with the default credentials. Login status is currently logged in as 'admin'. +.. Test case: `addAccount u/admin p/lol` + + Expected: Account is not added. Error details shown in the status message. Status bar remains the same. +.. Test case: `addAccount u/john p/doe` + + Expected: Account is added. Details of the added account shown in the status message. Timestamp in the status bar is updated. +.. Other incorrect addAccount commands to try: `addAccount`, `addAccount u/x p/y` (where x matches an account already in the database) + + Expected: Similar to first test case. + +. Editing an account while all accounts are listed + +.. Prerequisites: List all accounts using the `listAccounts` command. Accounts 'admin' and 'john' are in the list. Login status is currently logged in as 'admin'. +.. Test case: `editAccount 2 u/john p/password123!` + + Expected: Account is edited. Details of the edited account shown in the status message. Timestamp in the status bar is updated. +.. Test case: `editAccount 0 u/chew p/lol` + + Expected: Account is not edited. Error details shown in the status message. Status bar remains the same. +.. Other incorrect editAccount commands to try: `editAccount`, `editAccount x u/y p/z` (where x is larger than the account list size) + + +. Deleting an account while all accounts are listed + +.. Prerequisites: List all accounts using the `listAccounts` command. Accounts 'admin' and 'john' are in the list. Login status is currently logged in as 'admin'. +.. Test case: `deleteAccount 2` + + Expected: Account is deleted. Details of the deleted account shown in the status message. Timestamp in the status bar is updated. +.. Test case: `deleteAccount 0` + + Expected: No account is deleted. Error details shown in the status message. Status bar remains the same. +.. Other incorrect deleteAccount commands to try: `deleteAccount`, `deleteAccount x` (where x is larger than the account list size) + + Expected: Similar to previous. +// end::accountTesting[] === Saving data @@ -959,3 +1414,4 @@ _{ more test cases ... }_ .. _{explain how to simulate a missing/corrupted file and the expected behavior}_ _{ more test cases ... }_ + diff --git a/docs/LearningOutcomes.adoc b/docs/LearningOutcomes.adoc index 83cda0927226..f16c87dcb2f0 100644 --- a/docs/LearningOutcomes.adoc +++ b/docs/LearningOutcomes.adoc @@ -38,7 +38,7 @@ Note how the <> uses events to com == Use API Design `[LO-ApiDesign]` -Note how components of AddressBook have well-defined APIs. For example, the API of the `Logic` component is given in the link:{repoURL}/src/main/java/seedu/address/logic/Logic.java[`Logic.java`] +Note how components of StockList have well-defined APIs. For example, the API of the `Logic` component is given in the link:{repoURL}/src/main/java/seedu/address/logic/Logic.java[`Logic.java`] image:LogicClassDiagram.png[width="800"] *Resources* @@ -49,7 +49,7 @@ image:LogicClassDiagram.png[width="800"] == Use Assertions `[LO-Assertions]` -Note how the AddressBook app uses Java ``assert``s to verify assumptions. +Note how the StockList app uses Java ``assert``s to verify assumptions. *Resources* @@ -58,14 +58,14 @@ Note how the AddressBook app uses Java ``assert``s to verify assumptions. === Exercise: Add more assertions * Make sure assertions are enabled in your IDE by forcing an assertion failure (e.g. add `assert false;` somewhere in the code and run the code to ensure the runtime reports an assertion failure). -* Add more assertions to AddressBook as you see fit. +* Add more assertions to StockList as you see fit. ''' == Use Logging `[LO-Logging]` -Note <>. +Note <>. *Resources* @@ -73,14 +73,14 @@ Note <>. +Note <>. *Resources* @@ -116,7 +116,7 @@ Note <>. (https://travis-ci.org/se-edu/addressbook-level4[image:https://travis-ci.org/se-edu/addressbook-level4.svg?branch=master[Build Status]]) +Note <>. (https://travis-ci.org/se-edu/addressbook-level4[image:https://travis-ci.org/se-edu/addressbook-level4.svg?branch=master[Build Status]]) *Resources* @@ -162,8 +162,8 @@ class gives some examples of how to use _Equivalence Partitions_, _Boundary Valu Consider the link:{repoURL}/src/test/java/seedu/address/storage/StorageManagerTest.java[`StorageManagerTest.java`] class. -* Test methods `prefsReadSave()` and `addressBookReadSave()` are integration tests. Note how they simply test if The `StorageManager` class is correctly wired to its dependencies. -* Test method `handleAddressBookChangedEvent_exceptionThrown_eventRaised()` is a unit test because it uses _dependency injection_ to isolate the SUT `StorageManager#handleAddressBookChangedEvent(...)` from its dependencies. +* Test methods `prefsReadSave()` and `stockListReadSave()` are integration tests. Note how they simply test if The `StorageManager` class is correctly wired to its dependencies. +* Test method `handleStockListChangedEvent_exceptionThrown_eventRaised()` is a unit test because it uses _dependency injection_ to isolate the SUT `StorageManager#handleStockListChangedEvent(...)` from its dependencies. Compare the above with link:{repoURL}/src/test/java/seedu/address/logic/LogicManagerTest.java[`LogicManagerTest`]. Some of the tests in that class (e.g. `execute_*` methods) are neither integration nor unit tests. They are _integration + unit_ tests because they not only check if the LogicManager is correctly wired to its dependencies, but also checks the working of its dependencies. For example, the following two lines test the `LogicManager` but also the `Parser`. @@ -223,7 +223,7 @@ Here are some example design patterns used in the code base. * *MVC Pattern* : ** The 'View' part of the application is mostly in the `.fxml` files in the `src/main/resources/view` folder. ** `Model` component contains the 'Model'. However, note that it is possible to view the `Logic` as the model because it hides the `Model` behind it and the view has to go through the `Logic` to access the `Model`. -** Sub classes of link:{repoURL}/src/main/java/seedu/address/ui/UiPart.java[`UiPart`] (e.g. `PersonListPanel` ) act as 'Controllers', each controlling some part of the UI and communicating with the 'Model' (via the `Logic` component which sits between the 'Controller' and the 'Model'). +** Sub classes of link:{repoURL}/src/main/java/seedu/address/ui/UiPart.java[`UiPart`] (e.g. `ItemListPanel` ) act as 'Controllers', each controlling some part of the UI and communicating with the 'Model' (via the `Logic` component which sits between the 'Controller' and the 'Model'). * *Abstraction Occurrence Pattern* : Not currently used in the app. *Resources* diff --git a/docs/UserGuide.adoc b/docs/UserGuide.adoc index 7e0070e12f49..520be0139f8a 100644 --- a/docs/UserGuide.adoc +++ b/docs/UserGuide.adoc @@ -1,4 +1,4 @@ -= AddressBook Level 4 - User Guide += JalilEnterprises - User Guide :site-section: UserGuide :toc: :toc-title: @@ -12,31 +12,51 @@ ifdef::env-github[] :tip-caption: :bulb: :note-caption: :information_source: endif::[] -:repoURL: https://github.com/se-edu/addressbook-level4 +:repoURL: https://github.com/CS2113-AY1819S1-T12-3/main -By: `Team SE-EDU` Since: `Jun 2016` Licence: `MIT` +By: `Team T12-3`      Since: `Sep 2018`      Licence: `MIT` == Introduction -AddressBook Level 4 (AB4) is for those who *prefer to use a desktop app for managing contacts*. More importantly, AB4 is *optimized for those who prefer to work with a Command Line Interface* (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, AB4 can get your contact management tasks done faster than traditional GUI apps. Interested? Jump to the <> to get started. Enjoy! +JalilEnterprises is for those who *prefer to use a desktop app for stock taking*. More importantly, JalilEnterprises is *optimized for those who prefer to work with a Command Line Interface* (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, JalilEnterprises can get your stock management tasks done faster than traditional GUI apps. Interested? Jump to the <> to get started. Enjoy! == Quick Start . Ensure you have Java version `9` or later installed in your Computer. -. Download the latest `addressbook.jar` link:{repoURL}/releases[here]. -. Copy the file to the folder you want to use as the home folder for your Address Book. +. Download the latest `JalilEnterprises.jar` link:{repoURL}/releases[here]. +. Copy the file to the folder you want to use as the home folder for your Stock List. . Double-click the file to start the app. The GUI should appear in a few seconds. + image::Ui.png[width="790"] + +. You should log into this app via the *`login`* command before you start to use it. + +Example of how to log in: +* **`login`**`u/admin p/admin` + +. This app comes with a default admin account and it is strongly recommended to: +* not delete this account, +* note the changes made to this account to avoid the case of being locked out of StockList. ++ +Credentials of the default account: +* `u/admin p/admin` + +. When logged out, all commands are locked, except: +* `login` +* `listAccounts` +* `findAccount` +* `loginStatus` +* `history` +* `help` +* `exit` + . Type the command in the command box and press kbd:[Enter] to execute it. + e.g. typing *`help`* and pressing kbd:[Enter] will open the help window. . Some example commands you can try: -* *`list`* : lists all contacts -* **`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 -* *`exit`* : exits the app +* *`list`* : lists all items +* **`add`**`n/Arduino q/20 m/5 t/Lab1 t/Lab2` : adds 20 `Arduino` item to the StockList, with a minimum quantity of 5 and tags `Lab1` and `Lab2` +* **`delete`**`1` : deletes the item with index 1 +* *`status`* : Shows the number of every item in each status category (Ready, On_Loan, Faulty) . Refer to <> for details of each command. @@ -46,125 +66,257 @@ e.g. typing *`help`* and pressing kbd:[Enter] will open the help window. ==== *Command Format* -* Words in `UPPER_CASE` are the parameters to be supplied by the user e.g. in `add n/NAME`, `NAME` is a parameter which can be used as `add n/John Doe`. -* Items in square brackets are optional e.g `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`. -* Items with `…`​ after them can be used multiple times including zero times e.g. `[t/TAG]...` can be used as `{nbsp}` (i.e. 0 times), `t/friend`, `t/friend t/family` etc. -* Parameters can be in any order e.g. if the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable. +* Words in `UPPER_CASE` are the parameters to be supplied by the user e.g. in `add n/NAME`, `NAME` is a parameter which can be used as `add n/Arduino`. +* Items in square brackets are optional e.g `n/NAME [t/TAG]`` can be used as `n/Arduino t/Lab1` or as `n/Arduino`. +* Items with `…​` after them can be used multiple times including zero times e.g. `[t/TAG]…​` can be used as ` ` (i.e. 0 times), `t/Lab1`, `t/Lab2 t/Lab5` etc. +* Parameters can be in any order e.g. if the command specifies `n/NAME q/QUANTITY`, `q/QUANTITY n/NAME` is also acceptable. ==== === Viewing help : `help` Format: `help` -=== Adding a person: `add` +// tag::login[] +=== List registered accounts: `listAccounts` -Adds a person to the address book + -Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]...` +Lists all the accounts currently registered in StockList. + +Format: `listAccounts` -[TIP] -A person can have any number of tags (including 0) +=== Find a registered account: `findAccount` + +Finds a registered account whose names contain any of the given keywords. + +Format: `findAccount KEYWORD [MORE_KEYWORDS]` + +**** +* The search is case insensitive. e.g `admin` will match `Admin` +* The order of the keywords does not matter. e.g. `admin jalil` will match `jalil admin` +* Only the name is searched. +* Partial words will be matched e.g. `ad` will match `admin` +* Items matching at least one keyword will be returned (i.e. `OR` search). e.g. `admin jalil` will return `admin tan`, `admin lee` +**** Examples: -* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` -* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal` +* `findAccount admin` + +Returns `admin` and `Admin Jalil` +* `findAccount admin tom` + +Returns any items having names `admin` or `tom` -=== Listing all persons : `list` +=== Logging in: `login` -Shows a list of all persons in the address book. + -Format: `list` +Logs into Stock List. + +Format: `login u/USERNAME p/PASSWORD` -=== Editing a person : `edit` +Examples: + +* `login u/admin p/admin` + +=== Checking login status: `loginStatus` + +Checks login status of Stock List. + +Format: `loginStatus` +// end::login[] + +=== Adding an item: `add` -Edits an existing person in the address book. + -Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]...` +Adds an item to the stock list + +Format: `add n/NAME q/QUANTITY m/MINIMUM_QUANTITY [t/TAG]` + + + +Examples: + +* `add n/Arduino q/20 m/5 t/Lab1 t/Lab2` + + +=== Editing an item : `edit` + +Edits an existing item in the stock list + +Format: `edit INDEX [n/NAME] [q/QUANTITY] [m/MINIMUM_QUANTITY] [t/TAG]` **** -* Edits 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, ... -* At least one of the optional fields must be provided. +* Edits 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, …​ +* At least one of the fields (name, quantity, minimum quantity or tag) must be provided. * Existing values will be updated to the input values. -* When editing tags, the existing tags of the person will be removed i.e adding of tags is not cumulative. -* You can remove all the person's tags by typing `t/` without specifying any tags after it. +* When editing tags, the existing tags of the item will be removed i.e adding of tags is not cumulative. +* You can remove all the item’s tags by typing t/ without specifying any tags after it. **** +Examples: + +* `edit 1 n/Arduino q/25 t/Lab7 t/Lab8` + +Edits the name, quantity and tags of the 1st item to be `Arduino`, `25` and `Lab7`, `Lab8` respectively. + +=== Deleting an item : `delete` + +Deletes an item from the stock list + +Format: `delete INDEX Examples: -* `edit 1 p/91234567 e/johndoe@example.com` + -Edits the phone number and email address of the 1st person to be `91234567` and `johndoe@example.com` respectively. -* `edit 2 n/Betsy Crower t/` + -Edits the name of the 2nd person to be `Betsy Crower` and clears all existing tags. +* `delete 1` + +Deletes the item with index 1 + +=== Listing all items : `list` + +Shows all items in the stock list + +Format: `list` -=== Locating persons by name: `find` +=== Selecting and highlighting an item : `select` + +Selects and highlights one item using its index number + +Format: `select INDEX` + +Examples: -Finds persons whose names contain any of the given keywords. + +* `select 1` + +Selects and highlights the item with the index number 1 + +=== Clearing all items : `clear` + +Clears all items in the stock list + +Format: `clear` + +=== Locating items by name: `find` + +Finds items whose names contain any of the given keywords. + Format: `find KEYWORD [MORE_KEYWORDS]` **** -* The search is case insensitive. e.g `hans` will match `Hans` -* The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans` +* The search is case insensitive. e.g `arduino` will match `Arduino` +* The order of the keywords does not matter. e.g. `ESP8226 Chip` will match `Chip ESP8266` * Only the name is searched. -* Only full words will be matched e.g. `Han` will not match `Hans` -* Persons matching at least one keyword will be returned (i.e. `OR` search). e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang` +* Partial words will be matched e.g. `Ard` will match `Arduino` +* Items matching at least one keyword will be returned (i.e. `OR` search). e.g. `Arduino Uno` will return `Arduino Mega`, `Arduino Leonardo` **** Examples: -* `find John` + -Returns `john` and `John Doe` -* `find Betsy Tim John` + -Returns any person having names `Betsy`, `Tim`, or `John` +* `find Arduino` + +Returns `Arduino` and `Arduino Uno` +* `find Arduino Pi` + +Returns any items having names `Arduino` or `Pi` -=== Deleting a person : `delete` +// tag::statuscommand[] +=== Listing item according to status: `status` -Deletes the specified person from the address book. + -Format: `delete INDEX` +Shows the quantity of every item in each status category (Ready, On_Loan, Faulty) + +Format: `status` +// end::statuscommand[] -**** -* 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, ... -**** +// tag::changestatuscommand[] +=== Changing status of item: `changeStatus` + +The status of the items can be changed from Ready to Faulty, and vice-versa + +Format: `changeStatus n/NAME q/QUANTITY os/ORIGINALSTATUS ns/NEWSTATUS` Examples: -* `list` + -`delete 2` + -Deletes the 2nd person in the address book. -* `find Betsy` + -`delete 1` + -Deletes the 1st person in the results of the `find` command. +* `changeStatus n/Arduino q/20 os/Ready ns/Faulty` + +The status of 20 Arduinos would be changed from Ready to Faulty +// end::changestatuscommand[] -=== Selecting a person : `select` +// tag::loanlistcommand[] +=== Creating a loan list: `loanList` +A loan list can be created whenever a list of items loaned out needs to be kept tracked on + +Format: `loanList n/NAME q/QUANTITY l/LOANER` -Selects the person identified by the index number used in the displayed person list. + -Format: `select INDEX` +Examples: + +* `loanList n/Arduino q/20 l/KinWhye` + +This would update the status of the 20 arduinos to On_Loan, and add the entry into the loan list +// end::loanlistcommand[] + +// tag::viewloanlistcommand[] +=== Viewing the loan list: `viewLoanList` + +Shows every loan list entry + +Format: `viewLoanList` +// end::viewloanlistcommand[] + +// tag::deleteloanlistcommand[] +=== Deleting a loan list entry: `deleteLoanList` + +An entry in the loan list can be deleted when the loaner returns the items + +Format: `deleteLoanList INDEX` + +Examples: + +* `deleteLoanList 1` + +The first entry as shown on the viewLoanList command will be deleted. The status of the item will be automatically changed back to ready +// end::deleteloanlistcommand[] + +// tag::tag[] +=== Listing items under specific tags: `tag` + +Finds and lists items whose tags contain any of the given keywords. + +Format: `tag KEYWORD [MORE_KEYWORDS] **** -* Selects the person and loads the Google search page 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, ...` +* The search is case insensitive. e.g `lab1` will match `Lab1` +* Only the tag is searched. +* Partial words are recognized. e.g. `Lab` will match `Lab1` **** Examples: -* `list` + -`select 2` + -Selects the 2nd person in the address book. -* `find Betsy` + -`select 1` + -Selects the 1st person in the results of the `find` command. +* `tag Lab1 Lab2` + +Returns the items whose tags includes "Lab1" or "Lab2" -=== Listing entered commands : `history` +=== Add tags to one item: `addTag` -Lists all the commands that you have entered in reverse chronological order. + -Format: `history` +Adds one or multiple tags to one item in the stock list by its index. + +Format: `addTag INDEX t/ TAG [MORE_TAGS]` -[NOTE] -==== -Pressing the kbd:[↑] and kbd:[↓] arrows will display the previous and next input respectively in the command box. -==== +Examples: + +* `addTag 1 t/ Lab3 t/ Lab4` + +Add tags "Lab3" and "Lab4" to the item with index 1 + +=== Delete tags from one item: `deleteTag` + +Deletes some tags and keeps the rest of one item in the stock list by its index. + +Format: `deleteTag INDEX t/ TAG [MORE_TAGS]` + +Examples: -// tag::undoredo[] +* `deleteTag 1 t/ Lab1 t/ Lab2` + +Deletes tags "Lab1" and "Lab2" from the item with index 1 +// end::tag[] + +//tag::lostcommand[] +=== Lost an item: `lost` + +Lost an item From the stock list + +Format: `lost INDEX q/QUANTITY` + + + +Examples: + +* `lost 1 q/25` +//end::lostcommand[] + +//tag::foundcommand[] +=== Found an item: `found` + +Found an item From the Lost&Found List + +Format: `found INDEX q/QUANTITY` + + + +Examples: + +* `found 1 q/25` +//end::foundcommand[] + +//tag::lost&found[] +=== Listing Lost&Found List: `lost&found` + +Shows the number of every lost item that has been lost + +Format: `lost&found` + + +//end::lost&found[] === Undoing previous command : `undo` Restores the address book to the state before the previous _undoable_ command was executed. + @@ -189,7 +341,7 @@ The `undo` command fails as there are no undoable commands executed previously. * `delete 1` + `clear` + `undo` (reverses the `clear` command) + -`undo` (reverses the `delete 1` command) + +`undo` (reverses the `delete 1` command) === Redoing the previously undone command : `redo` @@ -212,49 +364,178 @@ The `redo` command fails as there are no `undo` commands executed previously. `undo` (reverses the `delete 1` command) + `redo` (reapplies the `delete 1` command) + `redo` (reapplies the `clear` command) + -// end::undoredo[] -=== Clearing all entries : `clear` +=== Listing entered commands : `history` -Clears all entries from the address book. + -Format: `clear` +Lists all the commands that you have entered in reverse chronological order. + +Format: `history` + +[NOTE] +==== +Pressing the kbd:[↑] and kbd:[↓] arrows will display the previous and next input respectively in the command box. +==== + +// tag::savecommand[] +=== Save current version of stock list : `save` + +An xml file recording current stocks will be created and named. + +Format: `save NAME` + +Examples: + +* `save ForAugust` + +This would save the stock list at the time when the command is inputted, and the name for this xml file is +`ForAugust.xml`, under a /versions/ folder in main. +// end::savecommand[] + +// tag::opencommand[] +=== Open and view a saved stock list : `open` + +The .xml file specified will be opened and displayed in table format on the Browser Panel. + +Format: `open NAME` + +Examples: + +* `open ForAugust` + +This would open the saved stock list under /versions/ folder in main at the time when the command is inputted +// end::opencommand[] + +// tag::accountManagement[] +=== Adding an account: `addAccount` + +Adds an account to the database. + +Format: `addAccount u/USERNAME p/PASSWORD` + +Examples: + +* `addAccount u/john p/doe` +* `addAccount u/jalil p/boss123` + +=== Deleting an account: `deleteAccount` + +Deletes an account from the database. + +Format: `delete INDEX` + + +Index can be found using `listAccounts` + +Examples: + +* `deleteAccount 2` + +Deletes the second account in the database, according to the index via `listAccounts`. + +NOTE: It is strongly NOT recommended to delete the first account i.e. the admin account. + + +=== Editing an account currently in the database: `editAccount` + +Edits a registered account in the database. + +Format: `edit INDEX u/USERNAME [p/PASSWORD]` + +**** +* Edits the account at the specified INDEX. The index refers to the index number shown in the displayed list when 'listAccounts' is used. The index must be a positive integer 1, 2, 3, …​ +* At least one of the optional fields must be provided. +* Existing credentials will be updated to the input credentials. +**** +Examples: + +* `editAccount 2 p/password123!` + +Changes the password of the second account to `password123!` + +NOTE: Exercise caution when editing the admin account. + +=== Resetting the account database: `resetAccounts` + +Resets the account database. + +All accounts will be deleted and a default admin account will be created. + +Credentials of the default account: `u/admin p/admin` + +Format: `resetAccounts` + +=== Logging out: `logout` + +Logs out of Stock List. + +Format: `logout` +// end::accountManagement[] === Exiting the program : `exit` Exits the program. + +This automatically logs out of StockList if the user is still logged in. + Format: `exit` === Saving the data -Address book data are saved in the hard disk automatically after any command that changes the data. + +JalilEnterprises data are saved in the hard disk automatically after any command that changes the data. + There is no need to save manually. // tag::dataencryption[] === Encrypting data files `[coming in v2.0]` +In v2.0, your account and inventory data can be encrypted for added security. You can choose to enable/disable encryption via the `setEncryption` command. -_{explain how the user can enable/disable data encryption}_ // end::dataencryption[] +=== minQuantity checking `[coming in v2.0]` + +This would constantly check the Quantity with the minQuantity and notify the user whenever the Quantity drops below the minQuantity. + == FAQ *Q*: How do I transfer my data to another Computer? + -*A*: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous Address Book folder. +*A*: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous Stock List folder. == Command Summary -* *Add* `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]...` + -e.g. `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague` -* *Clear* : `clear` -* *Delete* : `delete INDEX` + -e.g. `delete 3` -* *Edit* : `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]...` + -e.g. `edit 2 n/James Lee e/jameslee@example.com` -* *Find* : `find KEYWORD [MORE_KEYWORDS]` + -e.g. `find James Jake` -* *List* : `list` -* *Help* : `help` -* *Select* : `select INDEX` + -e.g.`select 2` -* *History* : `history` +* *Help* `help` +* *List Accounts* `listAccounts` +* *Find Account* `findAccount` + +e.g. `findAccount john doe` +* *Login* `login u/USERNAME p/PASSWORD` + +e.g. `login u/admin p/admin` +* *Check login status* `loginStatus` +* *Add* `add n/NAME q/QUANTITY m/MINIMUM_QUANTITY [t/TAG]...` + +e.g. `add n/Arduino q/20 m/5 t/Lab1 t/Lab2` +* *Edit* `edit INDEX n/NAME [q/QUANTITY] [m/MINIMUM_QUANTITY] [t/TAG]` + +e.g. `edit 1 n/Arduino q/25 t/Lab7 t/Lab8` +* *Delete* `delete INDEX q/QUANTITY` + +e.g. `delete 1 q/20` +* *List* `list` +* *Select* `select INDEX` + +e.g. `select 1` +* *Clear* `clear` +* *Find* `find KEYWORD [MORE_KEYWORDS]` + +e.g. `find Arduino` +* *Status* `status` +* *Change Status* `changeStatus n/NAME q/QUANTITY os/ORIGINALSTATUS ns/NEWSTATUS` + +e.g. `changeStatus n/Arduino q/20 os/Ready ns/Faulty` +* *Loan List* `loanList n/NAME q/QUANTITY l/LOANER` + +e.g. `loanList n/Arduino q/20 l/KinWhye` +* *View Loan List* `viewLoanList` +* *Delete Loan List* `deleteLoanList INDEX` + +e.g. `deleteLoanList 1` +* *Tag* `tag KEYWORD [MORE_KEYWORDS]` + +e.g. `tag Lab1` +* *Add Tag* `addTag INDEX t/TAG [MORE_TAGS]` + +e.g. `addTag 1 t/Lab2` +* *Delete Tag* `deleteTag INDEX t/TAG [MORE_TAGS]` + +e.g. `deleteTag 1 t/Lab2` +* *Lost* : `lost INDEX q/QUANTITY` + +e.g. `lost 1 q/25` +* *Found* : `found INDEX q/QUANTITY` + +e.g. `found 1 q/25` +* *Lost&Found List* : `lost&found` * *Undo* : `undo` * *Redo* : `redo` +* *History* : `history` +* *Save* `save NAME` + +e.g. `save ForAugust` +* *Open* `open NAME` + +e.g. `open forAugust` +* *Add Account* `addAccount u/USERNAME p/PASSWORD` + +e.g. `add u/jalil p/boss` +* *Delete Account* `deleteAccount INDEX` + +e.g. `deleteAccount 2` +* *Edit Account* `editAccount INDEX u/USERNAME [p/PASSWORD]` + +e.g. `edit 2 p/password123!` +* *Reset Accounts* `resetAccounts` +* *Logout* `logout` +* *Exit* `exit` diff --git a/docs/UsingTravis.adoc b/docs/UsingTravis.adoc index 8418759fa5d3..d1d545f04611 100644 --- a/docs/UsingTravis.adoc +++ b/docs/UsingTravis.adoc @@ -59,10 +59,10 @@ image:travis_build.png[Travis build] . Ensure that you have followed the steps above to set up Travis CI. . On GitHub, create a new user account and give this account collaborator and admin access to the repo. + - Using this account, generate a personal access token https://github.com/settings/tokens/new[here]. + Using this account, generate a itemal access token https://github.com/settings/tokens/new[here]. + [NOTE] -Personal access tokens are like passwords so make sure you keep them secret! If the personal access token is leaked, please delete it and generate a new one. +Itemal access tokens are like passwords so make sure you keep them secret! If the itemal access token is leaked, please delete it and generate a new one. + [NOTE] We use a new user account to generate the token for team projects to prevent team members from gaining access to other team members' repos. + @@ -71,7 +71,7 @@ If you are the only one with write access to the repo, you can use your own acco -- * Add a description for the token. (e.g. `Travis CI - deploy docs to gh-pages`) * Check the `public_repo` checkbox. -* Click `Generate Token` and copy your new personal access token. +* Click `Generate Token` and copy your new itemal access token. -- We will use this token to grant Travis access to the repo. + @@ -85,14 +85,14 @@ image:flick_repository_switch.png[Activate the switch] + -- * name: `GITHUB_TOKEN` -* value: personal access token copied in step 1 +* value: itemal access token copied in step 1 * Display value in build log: `OFF` -- image:travis_add_token.png[Travis add token] + [NOTE] *Make sure you set `Display value in build log` to `OFF`.* + -Otherwise, other people will be able to see the personal access token and thus have access this repo. + +Otherwise, other people will be able to see the itemal access token and thus have access this repo. + Similarly, make sure you *do not print `$GITHUB_TOKEN` to the logs* in Travis scripts as the logs are viewable by the public. . Now, whenever there's a new commit to master branch, Travis will push the latest documentation to gh-pages branch. diff --git a/docs/diagrams/LoginSequenceDiagram.pptx b/docs/diagrams/LoginSequenceDiagram.pptx new file mode 100644 index 000000000000..a9784d698b46 Binary files /dev/null and b/docs/diagrams/LoginSequenceDiagram.pptx differ diff --git a/docs/diagrams/TagComponentSequenceDiagram.pptx b/docs/diagrams/TagComponentSequenceDiagram.pptx new file mode 100644 index 000000000000..4f69a464669c Binary files /dev/null and b/docs/diagrams/TagComponentSequenceDiagram.pptx differ diff --git a/docs/diagrams/UndoRedoActivityDiagram.pptx b/docs/diagrams/UndoRedoActivityDiagram.pptx index 16fec930cf3f..e8b25e8fbee6 100644 Binary files a/docs/diagrams/UndoRedoActivityDiagram.pptx and b/docs/diagrams/UndoRedoActivityDiagram.pptx differ diff --git a/docs/diagrams/UndoRedoExecuteUndoStateListDiagram.pptx b/docs/diagrams/UndoRedoExecuteUndoStateListDiagram.pptx index 6fd31b5f3fbd..9e28fc469e01 100644 Binary files a/docs/diagrams/UndoRedoExecuteUndoStateListDiagram.pptx and b/docs/diagrams/UndoRedoExecuteUndoStateListDiagram.pptx differ diff --git a/docs/diagrams/UndoRedoNewCommand1StateListDiagram.pptx b/docs/diagrams/UndoRedoNewCommand1StateListDiagram.pptx index 1f3261976dce..77347cdeff5a 100644 Binary files a/docs/diagrams/UndoRedoNewCommand1StateListDiagram.pptx and b/docs/diagrams/UndoRedoNewCommand1StateListDiagram.pptx differ diff --git a/docs/diagrams/UndoRedoNewCommand2StateListDiagram.pptx b/docs/diagrams/UndoRedoNewCommand2StateListDiagram.pptx index e2907d4a9cae..45876df0599a 100644 Binary files a/docs/diagrams/UndoRedoNewCommand2StateListDiagram.pptx and b/docs/diagrams/UndoRedoNewCommand2StateListDiagram.pptx differ diff --git a/docs/diagrams/UndoRedoNewCommand3StateListDiagram.pptx b/docs/diagrams/UndoRedoNewCommand3StateListDiagram.pptx index 4ecc659bd600..39b3746cb080 100644 Binary files a/docs/diagrams/UndoRedoNewCommand3StateListDiagram.pptx and b/docs/diagrams/UndoRedoNewCommand3StateListDiagram.pptx differ diff --git a/docs/diagrams/UndoRedoNewCommand4StateListDiagram.pptx b/docs/diagrams/UndoRedoNewCommand4StateListDiagram.pptx index 16ebf585ddbd..1cf16e199b49 100644 Binary files a/docs/diagrams/UndoRedoNewCommand4StateListDiagram.pptx and b/docs/diagrams/UndoRedoNewCommand4StateListDiagram.pptx differ diff --git a/docs/diagrams/UndoRedoSequenceDiagram.pptx b/docs/diagrams/UndoRedoSequenceDiagram.pptx index 5ccc1042caac..183fd12e127b 100644 Binary files a/docs/diagrams/UndoRedoSequenceDiagram.pptx and b/docs/diagrams/UndoRedoSequenceDiagram.pptx differ diff --git a/docs/diagrams/UndoRedoStartingStateListDiagram.pptx b/docs/diagrams/UndoRedoStartingStateListDiagram.pptx index 98ce067642ff..9d1ba50065bf 100644 Binary files a/docs/diagrams/UndoRedoStartingStateListDiagram.pptx and b/docs/diagrams/UndoRedoStartingStateListDiagram.pptx differ diff --git a/docs/images/ChangeStatus.png b/docs/images/ChangeStatus.png new file mode 100644 index 000000000000..21bb52d73cb7 Binary files /dev/null and b/docs/images/ChangeStatus.png differ diff --git a/docs/images/DeleteItemSdForLogic.png b/docs/images/DeleteItemSdForLogic.png new file mode 100644 index 000000000000..fe62f1507f0a Binary files /dev/null and b/docs/images/DeleteItemSdForLogic.png differ diff --git a/docs/images/DeletePersonSdForLogic.png b/docs/images/DeletePersonSdForLogic.png deleted file mode 100644 index 0462b9b7be6e..000000000000 Binary files a/docs/images/DeletePersonSdForLogic.png and /dev/null differ diff --git a/docs/images/LoanList.png b/docs/images/LoanList.png new file mode 100644 index 000000000000..6af8942f50fb Binary files /dev/null and b/docs/images/LoanList.png differ diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png index f4ecf65b3193..ce80527464bd 100644 Binary files a/docs/images/LogicClassDiagram.png and b/docs/images/LogicClassDiagram.png differ diff --git a/docs/images/LoginSequenceDiagram.png b/docs/images/LoginSequenceDiagram.png new file mode 100644 index 000000000000..5bd5bc1d6334 Binary files /dev/null and b/docs/images/LoginSequenceDiagram.png differ diff --git a/docs/images/ModelClassBetterOopDiagram.png b/docs/images/ModelClassBetterOopDiagram.png index 9ba8eb5e31d0..2dad0930b616 100644 Binary files a/docs/images/ModelClassBetterOopDiagram.png and b/docs/images/ModelClassBetterOopDiagram.png differ diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png index 9fb19078b859..16b0ecfe97ca 100644 Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ diff --git a/docs/images/SDforDeleteItem.png b/docs/images/SDforDeleteItem.png new file mode 100644 index 000000000000..433b5c0ec9b4 Binary files /dev/null and b/docs/images/SDforDeleteItem.png differ diff --git a/docs/images/SDforDeleteItemEventHandling.png b/docs/images/SDforDeleteItemEventHandling.png new file mode 100644 index 000000000000..b620b162cf5a Binary files /dev/null and b/docs/images/SDforDeleteItemEventHandling.png differ diff --git a/docs/images/SDforDeletePerson.png b/docs/images/SDforDeletePerson.png deleted file mode 100644 index 1e836f10dcd8..000000000000 Binary files a/docs/images/SDforDeletePerson.png and /dev/null differ diff --git a/docs/images/SDforDeletePersonEventHandling.png b/docs/images/SDforDeletePersonEventHandling.png deleted file mode 100644 index ecec0805d32c..000000000000 Binary files a/docs/images/SDforDeletePersonEventHandling.png and /dev/null differ diff --git a/docs/images/SaveCommandSequenceDiagram.png b/docs/images/SaveCommandSequenceDiagram.png new file mode 100644 index 000000000000..829ee4954b7d Binary files /dev/null and b/docs/images/SaveCommandSequenceDiagram.png differ diff --git a/docs/images/Status.png b/docs/images/Status.png new file mode 100644 index 000000000000..e136a5335fb9 Binary files /dev/null and b/docs/images/Status.png differ diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png index 7a4cd2700cbf..5480a5c4eb80 100644 Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ diff --git a/docs/images/TagComponentSequenceDiagram.png b/docs/images/TagComponentSequenceDiagram.png new file mode 100644 index 000000000000..01cdbd41c6d8 Binary files /dev/null and b/docs/images/TagComponentSequenceDiagram.png differ diff --git a/docs/images/Ui.png b/docs/images/Ui.png index 5ec9c527b49c..6e4597f0a89d 100644 Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png index 369469ef176e..9c8eeff3a8e7 100644 Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ diff --git a/docs/images/UndoRedoActivityDiagram.png b/docs/images/UndoRedoActivityDiagram.png index 55e4138cc64f..2205180655e8 100644 Binary files a/docs/images/UndoRedoActivityDiagram.png and b/docs/images/UndoRedoActivityDiagram.png differ diff --git a/docs/images/UndoRedoExecuteUndoStateListDiagram.png b/docs/images/UndoRedoExecuteUndoStateListDiagram.png index 29c365d6b4a1..444cb063b0db 100644 Binary files a/docs/images/UndoRedoExecuteUndoStateListDiagram.png and b/docs/images/UndoRedoExecuteUndoStateListDiagram.png differ diff --git a/docs/images/UndoRedoNewCommand1StateListDiagram.png b/docs/images/UndoRedoNewCommand1StateListDiagram.png index 76e661d62027..a6b3ba3835f9 100644 Binary files a/docs/images/UndoRedoNewCommand1StateListDiagram.png and b/docs/images/UndoRedoNewCommand1StateListDiagram.png differ diff --git a/docs/images/UndoRedoNewCommand2StateListDiagram.png b/docs/images/UndoRedoNewCommand2StateListDiagram.png index adcb9aeadc51..040c79938687 100644 Binary files a/docs/images/UndoRedoNewCommand2StateListDiagram.png and b/docs/images/UndoRedoNewCommand2StateListDiagram.png differ diff --git a/docs/images/UndoRedoNewCommand3StateListDiagram.png b/docs/images/UndoRedoNewCommand3StateListDiagram.png index aac9c5fe05db..a58728688b5b 100644 Binary files a/docs/images/UndoRedoNewCommand3StateListDiagram.png and b/docs/images/UndoRedoNewCommand3StateListDiagram.png differ diff --git a/docs/images/UndoRedoNewCommand4StateListDiagram.png b/docs/images/UndoRedoNewCommand4StateListDiagram.png index 66a0a3b5f323..93f14a585c39 100644 Binary files a/docs/images/UndoRedoNewCommand4StateListDiagram.png and b/docs/images/UndoRedoNewCommand4StateListDiagram.png differ diff --git a/docs/images/UndoRedoSequenceDiagram.png b/docs/images/UndoRedoSequenceDiagram.png index 5c9d5936f098..77efe0dea756 100644 Binary files a/docs/images/UndoRedoSequenceDiagram.png and b/docs/images/UndoRedoSequenceDiagram.png differ diff --git a/docs/images/UndoRedoStartingStateListDiagram.png b/docs/images/UndoRedoStartingStateListDiagram.png index 002f3e2bbf79..b94e4d4c98e2 100644 Binary files a/docs/images/UndoRedoStartingStateListDiagram.png and b/docs/images/UndoRedoStartingStateListDiagram.png differ diff --git a/docs/images/chewkinwhye.png b/docs/images/chewkinwhye.png new file mode 100644 index 000000000000..f9c054debe13 Binary files /dev/null and b/docs/images/chewkinwhye.png differ diff --git a/docs/images/damithc.jpg b/docs/images/damithc.jpg deleted file mode 100644 index 127543883893..000000000000 Binary files a/docs/images/damithc.jpg and /dev/null differ diff --git a/docs/images/davindran.png b/docs/images/davindran.png new file mode 100644 index 000000000000..a386d4f7977e Binary files /dev/null and b/docs/images/davindran.png differ diff --git a/docs/images/gaoqikai.png b/docs/images/gaoqikai.png new file mode 100644 index 000000000000..963b71b5375a Binary files /dev/null and b/docs/images/gaoqikai.png differ diff --git a/docs/images/hehaowei.png b/docs/images/hehaowei.png new file mode 100644 index 000000000000..5e7d5a6ab8d8 Binary files /dev/null and b/docs/images/hehaowei.png differ diff --git a/docs/images/kelvintankaiboon.png b/docs/images/kelvintankaiboon.png new file mode 100644 index 000000000000..250d5b2ddff2 Binary files /dev/null and b/docs/images/kelvintankaiboon.png differ diff --git a/docs/images/lejolly.jpg b/docs/images/lejolly.jpg deleted file mode 100644 index 2d1d94e0cf5d..000000000000 Binary files a/docs/images/lejolly.jpg and /dev/null differ diff --git a/docs/images/m133225.jpg b/docs/images/m133225.jpg deleted file mode 100644 index fd14fb94593a..000000000000 Binary files a/docs/images/m133225.jpg and /dev/null differ diff --git a/docs/images/yijinl.jpg b/docs/images/yijinl.jpg deleted file mode 100644 index adbf62ad9406..000000000000 Binary files a/docs/images/yijinl.jpg and /dev/null differ diff --git a/docs/images/yl_coder.jpg b/docs/images/yl_coder.jpg deleted file mode 100644 index 17b48a732272..000000000000 Binary files a/docs/images/yl_coder.jpg and /dev/null differ diff --git a/docs/stylesheets/template.xsl b/docs/stylesheets/template.xsl new file mode 100644 index 000000000000..e6e1639614e5 --- /dev/null +++ b/docs/stylesheets/template.xsl @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameQuantityMinimum QuantityStatus-ReadyStatus-On LoanStatus-Faulty
+ + +
+
diff --git a/docs/team/chewkinwhye.adoc b/docs/team/chewkinwhye.adoc new file mode 100644 index 000000000000..f579a84c0e89 --- /dev/null +++ b/docs/team/chewkinwhye.adoc @@ -0,0 +1,89 @@ += Chew Kin Whye - Project Portfolio +:site-section: AboutUs +:toc: +:toc-title: +:toc-placement: preamble +:sectnums: +:imagesDir: ../images +:stylesDir: ../stylesheets +:xrefstyle: full +ifdef::env-github[] +:tip-caption: :bulb: +:note-caption: :information_source: +:warning-caption: :warning: +endif::[] + +:repoURL: https://github.com/CS2113-AY1819S1-T12-3/main/tree/master + + +Project: Jalil Enterprises Stock List + +== Overview + +=== Project Overview + +This project was a part of the CS2113T (Software Engineering & Object-Oriented Programming) module, and our team was tasked to enhance the existing AddressBook application for a specific target audience group of our choice. + +We decided to morph it into Jalil Enterprises, a stock list application for Computer Engineering lab technicians. + +This project was inspired by our observations of the work of the lab technician when we were taking our Computer Engineering modules, and ideas on how it could be made more efficient and convenient. + +The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java and has ~10 kLoC. + +=== Project Portfolio Overview + +This Project Portfolio is to illustrate the contribution of me, Chew Kin Whye (GitHub: ChewKinWhye) to the JalilEnterprises project. + +This includes my feature added, my role in the team, and my contributions to the User Guide and Developer Guide. + +== Features added +For the project, I was responsible for the logic component of the application as well as the implementation of the “status” feature. + +The status feature is a vital component of our application. This is be cause as a lab technician, it is not enough to store just the quantity of the items. Additional information regarding the status of the items is required. + +* Code contributed: https://nuscs2113-ay1819s1.github.io/dashboard/#=undefined&search=chewkinwhye&sort=displayName&since=2018-09-12&until=2018-11-05&timeframe=day&reverse=false&repoSort=true[Project Code Dashboard] +* *Major enhancement*: : *status* command. +** What it does: Allows the user to view the inventory according to the status. +** Justification: Very often, the lab technician is only concerned with the items of a certain status. For example, he might only be concerned with the items that are Ready. + +* *Major enhancement*: : *changeStatus* command. +** What it does: Allows the user change the status of the items. +** Justification: The status of the items are constantly changing, like when an item becomes faulty. The lab technician needs this command to be able to update the stock list. + +* *Major enhancement*: : *loanList*, *viewLoanList* and *deleteLoanList* command. +** What it does: Allows the user to manage loan lists when students borrow out items. +** Justification: The lab technicians are constantly loaning out items to students. Thus, this feature is required to replicate what is done with the previous manual system, and help to digitize it. +** Highlights: This enhancement required changes in the logic and storage component of the application. Much self directed learning was required to use JAXB and store the data inside an XML file. + +* *Other contributions*: + +** Role as a team leader: +For this project, I was assigned to be the team leader, helping to set goals and deadlines, facilitate discussions, allocate tasks among group members, listen to feedback, solve conflicts, boost team morale, and ensure team cohesiveness. Besides that, I contributed significantly to the code and the documentation. + +** Project management: +*** Contribute to the morphing of the application from Address Book to Stock List. +*** Ensuring that the milestones are completed before every deadline. +*** Checking other PRs and features to ensure that the commands are all working and the main code is always functional. +*** Wrote tests for all of my commands implemented. + +== Contributions to the User Guide + + +|=== +|_Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users._ +|=== + +include::../UserGuide.adoc[tag=statuscommand] + +include::../UserGuide.adoc[tag=changestatuscommand] + +include::../UserGuide.adoc[tag=loanlistcommand] + +include::../UserGuide.adoc[tag=viewloanlistcommand] + +include::../UserGuide.adoc[tag=deleteloanlistcommand] + +== Contributions to the Developer Guide + +|=== +|_Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project._ +|=== + +include::../DeveloperGuide.adoc[tag=statusfeature] + + diff --git a/docs/team/davindran.adoc b/docs/team/davindran.adoc new file mode 100644 index 000000000000..2f076444fc61 --- /dev/null +++ b/docs/team/davindran.adoc @@ -0,0 +1,335 @@ += Davindran - Project Portfolio +:site-section: AboutUs +:imagesDir: ../images +:stylesDir: ../stylesheets + +== PROJECT: Jalil Enterprises Stock List + +--- + +== Overview + +JalilEnterprises StockList is a stock managing application used for accounting for the equipment in the Computer Engineering labs in NUS. The app was developed by my team, JalilEnterprises. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 11 kLoC. My role in the team was to improve the app’s security, and I accomplished this by creating a login feature with account management. This portfolio serves to document my role and contributions to the project. + +== Summary of contributions + +* *Major enhancement*: added a *login/logout feature* +** What it does: allows the user to log in and log out with an account registered on the app. +** Justification: This feature improves the product significantly with regards to security, because sensitive information will be stored with regards to the equipment in the labs and the authorised user should be able to control who has access to this app. +** Highlights: This enhancement affects the usability of the app as a whole. Users are not allowed to modify the inventory until they have logged in. This required an in-depth analysis of how the app processes the commands and storage files. The implementation was also challenging as it required changes that touched a lot of aspects of the app. +** Credits: _AddressBook-Level4 by se-edu for the base app which we could morph and improve on._ + +* *Minor enhancement*: added an *account management feature*, which allows for: +** Adding accounts +** Editing accounts +** Deleting accounts +** Listing registered accounts +** Finding a registered account +** Resetting the registered accounts to default +** Checking current login status of the account + +* *Code contributed*: https://nuscs2113-ay1819s1.github.io/dashboard/#=undefined&search=davindran[Project Code Dashboard] + +* *Other contributions*: + +** Project management: +*** Managed releases `v1.1` - `v1.4rc` (4 releases) on GitHub +** Enhancements to existing features: +*** Contributed in morphing the AddressBook across the board to StockList +*** Contributed in morphing test cases from that of AddressBook to StockList +** Documentation: +*** Morphed the Developer Guide from AddressBook into one tailored for our StockList: https://github.com[#13] +** Community: +*** PRs reviewed (with non-trivial review comments): https://github.com[#96], https://github.com[#97] +*** Reported bugs and suggestions for other teams in the class +** Tools: +*** Integrated a new Github plugin (TravisCI) to the team repo + + +== Contributions to the User Guide + + +|=== +|_Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users._ +|=== + +// tag::login[] +=== List registered accounts: `listAccounts` + +Lists all the accounts currently registered in StockList. + +Format: `listAccounts` + +=== Find a registered account: `findAccount` + +Finds a registered account whose names contain any of the given keywords. + +Format: `findAccount KEYWORD [MORE_KEYWORDS]` + +**** +* The search is case insensitive. e.g `admin` will match `Admin` +* The order of the keywords does not matter. e.g. `admin jalil` will match `jalil admin` +* Only the name is searched. +* Partial words will be matched e.g. `ad` will match `admin` +* Items matching at least one keyword will be returned (i.e. `OR` search). e.g. `admin jalil` will return `admin tan`, `admin lee` +**** + +Examples: + +* `findAccount admin` + +Returns `admin` and `Admin Jalil` +* `findAccount admin tom` + +Returns any items having names `admin` or `tom` + +=== Logging in: `login` + +Logs into Stock List. + +Format: `login u/USERNAME p/PASSWORD` + +Examples: + +* `login u/admin p/admin` + +=== Checking login status: `loginStatus` + +Checks login status of Stock List. + +Format: `loginStatus` +// end::login[] + +// tag::accountManagement[] +=== Adding an account: `addAccount` + +Adds an account to the database. + +Format: `addAccount u/USERNAME p/PASSWORD` + +Examples: + +* `addAccount u/john p/doe` +* `addAccount u/jalil p/boss123` + +=== Deleting an account: `deleteAccount` + +Deletes an account from the database. + +Format: `delete INDEX` + + +Index can be found using `listAccounts` + +Examples: + +* `deleteAccount 2` + +Deletes the second account in the database, according to the index via `listAccounts`. + +NOTE: It is strongly NOT recommended to delete the first account i.e. the admin account. + + +=== Editing an account currently in the database: `editAccount` + +Edits a registered account in the database. + +Format: `edit INDEX u/USERNAME [p/PASSWORD]` + +**** +* Edits the account at the specified INDEX. The index refers to the index number shown in the displayed list when 'listAccounts' is used. The index must be a positive integer 1, 2, 3, …​ +* At least one of the optional fields must be provided. +* Existing credentials will be updated to the input credentials. +**** +Examples: + +* `editAccount 2 p/password123!` + +Changes the password of the second account to `password123!` + +NOTE: Exercise caution when editing the admin account. + +=== Resetting the account database: `resetAccounts` + +Resets the account database. + +All accounts will be deleted and a default admin account will be created. + +Credentials of the default account: `u/admin p/admin` + +Format: `resetAccounts` + +=== Logging out: `logout` + +Logs out of Stock List. + +Format: `logout` +// end::accountManagement[] + +== Contributions to the Developer Guide + +|=== +|_Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project._ +|=== + +// tag::login[] +=== Login feature +==== Current Implementation + +The login mechanism is facilitated by `LoginCommand`. It extends `Command` and implements the following operations: + + +* `LoginCommand#modifyLoginStatus()` -- checks if the password matches the account in the database, if it exists. If true, updates the logged in account status in `Model` accordingly. + +* `LoginCommand#execute()` -- calls `LoginCommand#modifyLoginStatus()`. Then, checks login status in `Model` and displays a login success message if true and displays a failure message otherwise. + +These operations are exposed in the `Model` interface as `Model#setLoggedInUser()` and `Model#getLoginStatus()` respectively. + + +Given below is an example usage scenario and how the LoginCommand mechanism behaves at each step. + +Step 1. The user executes `login u/admin p/admin` command to log into StockList with *admin* and *admin* being the username and password credentials respectively. + +Step 2. The `execute` command calls `Model#getLoginStatus()` and checks if the user is already logged in. If true, `execute` throws a `CommandException` notifying the user that he is already logged in. + +Step 3. The `execute` command then calls `LoginCommand#modifyLoginStatus()`, which checks if the username *admin* exists in the account list, and if it does, checks if the given password *admin* matches the password associated with the username *admin*. + +Step 4. If the *admin* password matches, `LoginCommand#modifyLoginStatus()` calls `Model#setLoggedInUser()` which updates the logged in account status in `model` with the logged in account set to *admin* and logged in status set to true. + +Step 5. The `execute` command then checks the log in status via `Model#getLoginStatus()`. A success message is printed if true; otherwise a failure message is printed. + +The following sequence diagram shows how the login operation works: + +image::LoginSequenceDiagram.png[width="800"] + +==== Design Considerations + +===== Aspect: How login executes + +* **Alternative 1** (current choice) Check against various accounts stored in a file and allow access if match. +** Pros: Allows for multiple accounts with access to StockList. +** Cons: More memory usage. + +* **Alternative 2** Checks against a single account that can be modified. +** Pros: Simple to implement, minimal memory usage, allows for only one access account. +** Cons: Does not allow access for multiple accounts, locked out of app if credentials lost. +// end::login[] + +// tag::accountUseCase[] +[discrete] +=== Use case: Login + +*MSS* + +1. User requests to list accounts +2. StockList shows the list of accounts +3. User requests to login using his account credentials +4. StockList shows that the user has successfully logged in ++ +Use case ends. + +*Extensions* + +[none] +* 2a. The list does not contain user's account. ++ +Use case ends. + +* 3a. The given account credentials are invalid. ++ +[none] +** 3a1. StockList shows an error message. ++ +Use case resumes at step 2. + +[discrete] +=== Use case: Delete account + +*MSS* + +1. User requests to list accounts +2. StockList shows a list of accounts +3. User requests to delete a specific account in the list +4. StockList deletes the account ++ +Use case ends. + +*Extensions* + +* 3a. The given index is invalid. ++ +[none] +** 3a1. StockList shows an error message. ++ +Use case resumes at step 2. + +[discrete] +=== Use case: Add account + +*MSS* + +1. User requests to add an account +2. StockList adds the account to the database. ++ +Use case ends. + +*Extensions* + +* 1a. The given account is already in the database. ++ +[none] +** 1a1. StockList shows an error message. ++ +Use case resumes at step 1. + +[discrete] +=== Use case: Edit account + +*MSS* + +1. User requests to list accounts +2. StockList shows a list of accounts +3. User requests to edit a specific account in the list +4. StockList edits the account ++ +Use case ends. + +*Extensions* + +* 3a. The given index is invalid. ++ +[none] +** 3a1. StockList shows an error message. ++ +Use case resumes at step 2. +// end::accountUseCase[] + +// tag::accountTesting[] +=== Logging in + +. Logging in while all accounts are listed + +.. Prerequisites: List all accounts using the `listAccounts` command. Only default account 'admin' in the list, with the default credentials. Login status is currently logged out. +.. Test case: `login u/admin p/lol` + + Expected: Account is not logged in. Error details shown in the status message. Status bar remains the same. +.. Test case: `login u/admin p/admin` + + Expected: Account is logged in. Details of the logged in account shown in the status message. Timestamp in the status bar is updated. +.. Other incorrect login commands to try: `login`, `login u/x p/y` (where x and y differ from the registered account data) + + Expected: Similar to the first test case. + +=== Account management + +. Adding an account + +.. Prerequisites: List all accounts using the `listAccounts` command. Only default account 'admin' in the list, with the default credentials. Login status is currently logged in as 'admin'. +.. Test case: `addAccount u/admin p/lol` + + Expected: Account is not added. Error details shown in the status message. Status bar remains the same. +.. Test case: `addAccount u/john p/doe` + + Expected: Account is added. Details of the added account shown in the status message. Timestamp in the status bar is updated. +.. Other incorrect addAccount commands to try: `addAccount`, `addAccount u/x p/y` (where x matches an account already in the database) + + Expected: Similar to first test case. + +. Editing an account while all accounts are listed + +.. Prerequisites: List all accounts using the `listAccounts` command. Accounts 'admin' and 'john' are in the list. Login status is currently logged in as 'admin'. +.. Test case: `editAccount 2 u/john p/password123!` + + Expected: Account is edited. Details of the edited account shown in the status message. Timestamp in the status bar is updated. +.. Test case: `editAccount 0 u/chew p/lol` + + Expected: Account is not edited. Error details shown in the status message. Status bar remains the same. +.. Other incorrect editAccount commands to try: `editAccount`, `editAccount x u/y p/z` (where x is larger than the account list size) + + +. Deleting an account while all accounts are listed + +.. Prerequisites: List all accounts using the `listAccounts` command. Accounts 'admin' and 'john' are in the list. Login status is currently logged in as 'admin'. +.. Test case: `deleteAccount 2` + + Expected: Account is deleted. Details of the deleted account shown in the status message. Timestamp in the status bar is updated. +.. Test case: `deleteAccount 0` + + Expected: No account is deleted. Error details shown in the status message. Status bar remains the same. +.. Other incorrect deleteAccount commands to try: `deleteAccount`, `deleteAccount x` (where x is larger than the account list size) + + Expected: Similar to previous. +// end::accountTesting[] diff --git a/docs/team/gaoqikai.adoc b/docs/team/gaoqikai.adoc new file mode 100644 index 000000000000..7304921dcfad --- /dev/null +++ b/docs/team/gaoqikai.adoc @@ -0,0 +1,73 @@ += Gao Qikai - Project Portfolio +:site-section: AboutUs +:toc: +:toc-title: +:toc-placement: preamble +:sectnums: +:imagesDir: ../images +:stylesDir: ../stylesheets +:xrefstyle: full +ifdef::env-github[] +:tip-caption: :bulb: +:note-caption: :information_source: +:warning-caption: :warning: +endif::[] + +Project: Jalil Enterprises Stock List + +--- + +== Overview + +This Project Portfolio is to illustrate the contribution of me, Gao Qikai (GitHub: gaoqikai) to the JalilEnterprises project. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java. + +This project is completed by team T12-3 of module CS2113T in National University of Singapore, in the first semester of academic year 2018/2019. + + +== Summary of contributions + +* *Major enhancements:* + + +* Added the ability to list items in our stock list by tags. +** What it does: List out all the items the user will need for specifically one or multiple labs (under specific tags). +** Justification: This feature improves the usability of the program, as our program is intended to be used by real life laboratory technicians. + +* Added the ability to add tags to a specific item. +** What it does: Add some tags to a specific item and keep the existing tags as well. +** Justification: Although the user can use the “edit” command to modify an item’s tags, it would be complicated to add tags while keeping the existing tags because the “edit” command was implemented for the user to replace the existing tags with new ones. + +* Added the ability to delete some of the tags of a specific item. +** What it does: Delete accidentally added tags or unwanted tags of an item. +** Justification: The "edit" command does this in a different way. “deleteTag” command allows the user to delete tags by choosing which tags to remove, but “edit” command is doing so by choosing which tags to keep. + +* *Code contributed*: [https://nuscs2113-ay1819s1.github.io/dashboard/#=undefined&search=gaoqikai[Functional Code]] + +* *Other contributions*: + +** Documentation: +*** Did some tweaks to the existing Program parser, User guide and Developer guide: https://github.com/CS2113-AY1819S1-T12-3/main/pull/134[#134], https://github.com/CS2113-AY1819S1-T12-3/main/pull/167[#167]. +** Community: +*** Reported bugs and suggestions for other teams in the class. + +** Tools: + +*** Integrated new Github plugins (https://travis-ci.org/CS2113-AY1819S1-T12-3/main[Travis CI], https://ci.appveyor.com/project/gaoqikai/main[AppVeyor], and https://app.codacy.com/project/CS2113-AY1819S1-T12-3/main/dashboard[Codacy]) to the team repo to monitor code qualities. +*** Implemented auto-documenting and auto-publishing function to our project. https://cs2113-ay1819s1-t12-3.github.io/main/[Project Page]. + +== Contributions to the User Guide + + +|=== +|_Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users._ +|=== + +include::../UserGuide.adoc[tag=tag] + +== Contributions to the Developer Guide + +|=== +|_Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project._ +|=== + +include::../DeveloperGuide.adoc[tag=2.2] +include::../DeveloperGuide.adoc[tag=tag] +include::../DeveloperGuide.adoc[tag=addTag] diff --git a/docs/team/hehaowei.adoc b/docs/team/hehaowei.adoc new file mode 100644 index 000000000000..f67cb8d4b784 --- /dev/null +++ b/docs/team/hehaowei.adoc @@ -0,0 +1,72 @@ += He Haowei - Project Portfolio +:site-section: AboutUs +:toc: +:toc-title: +:toc-placement: preamble +:sectnums: +:imagesDir: ../images +:stylesDir: ../stylesheets +:xrefstyle: full +ifdef::env-github[] +:tip-caption: :bulb: +:note-caption: :information_source: +:warning-caption: :warning: +endif::[] + +:repoURL: https://github.com/CS2113-AY1819S1-T12-3/main/tree/master + + +Project: Jalil Enterprises Stock List + +== Overview + +=== Project Portfolio Overview + +This Project Portfolio is to illustrate the contribution of me, He Haowei (GitHub: HeHaowei) to the JalilEnterprises project. + +This includes my feature added as well as my contributions to the User Guide and Developer Guide. + +== Summary of contributions + +* Code contributed: https://nuscs2113-ay1819s1.github.io/dashboard/#=undefined&search=hehaowei[Project Code Dashboard] +* *Major enhancement*: added the ability to do *lost and found features*. +** What it does: allows the user to save the current inventory as a .xml file and open it anytime in a table format. +** Justification: This feature improves the product significantly because it can help the lab technicians to report the lost items in a quite convenient way and can handle the lost and found problem easily. +** Highlights: This enhancement affects existing commands and commands to be added in the future. It is challenging as it requires to make some user interface changes and be coordinate with some existing commands. + +* *Other contributions*: +** Modify the logic class in the test folder. +** Modify the editCommand and changeStatusCommand thus editCommand, changeStatusCommand, lostCommand and foundCommand can cooperate with each other. + +** Project management: +*** Managed project releases `V1.0` on GitHub +*** Responsible for milestone completion on GitHub +*** Responsible for Readme file on GitHub +** Enhancements to existing features: +*** Modify the logic class in the test folder. + + +*** Wrote tests for LostCommand to increase coverage + + +== Contributions to the User Guide + + +|=== +|_Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users._ +|=== + +include::../UserGuide.adoc[tag=lostcommand] +include::../UserGuide.adoc[tag=foundcommand] +include::../UserGuide.adoc[tag=lost&found] + +== Contributions to the Developer Guide + +|=== +|_Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project._ +|=== + +include::../DeveloperGuide.adoc[tag=2.1] +include::../DeveloperGuide.adoc[tag=lost&foundfeature] + + + diff --git a/docs/team/johndoe.adoc b/docs/team/johndoe.adoc deleted file mode 100644 index 453c2152ab9d..000000000000 --- a/docs/team/johndoe.adoc +++ /dev/null @@ -1,72 +0,0 @@ -= John Doe - Project Portfolio -:site-section: AboutUs -:imagesDir: ../images -:stylesDir: ../stylesheets - -== PROJECT: AddressBook - Level 4 - ---- - -== Overview - -AddressBook - Level 4 is a desktop address book application used for teaching Software Engineering principles. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC. - -== Summary of contributions - -* *Major enhancement*: added *the ability to undo/redo previous commands* -** What it does: allows the user to undo all previous commands one at a time. Preceding undo commands can be reversed by using the redo command. -** Justification: This feature improves the product significantly because a user can make mistakes in commands and the app should provide a convenient way to rectify them. -** Highlights: This enhancement affects existing commands and commands to be added in future. It required an in-depth analysis of design alternatives. The implementation too was challenging as it required changes to existing commands. -** Credits: _{mention here if you reused any code/ideas from elsewhere or if a third-party library is heavily used in the feature so that a reader can make a more accurate judgement of how much effort went into the feature}_ - -* *Minor enhancement*: added a history command that allows the user to navigate to previous commands using up/down keys. - -* *Code contributed*: [https://github.com[Functional code]] [https://github.com[Test code]] _{give links to collated code files}_ - -* *Other contributions*: - -** Project management: -*** Managed releases `v1.3` - `v1.5rc` (3 releases) on GitHub -** Enhancements to existing features: -*** Updated the GUI color scheme (Pull requests https://github.com[#33], https://github.com[#34]) -*** Wrote additional tests for existing features to increase coverage from 88% to 92% (Pull requests https://github.com[#36], https://github.com[#38]) -** Documentation: -*** Did cosmetic tweaks to existing contents of the User Guide: https://github.com[#14] -** Community: -*** PRs reviewed (with non-trivial review comments): https://github.com[#12], https://github.com[#32], https://github.com[#19], https://github.com[#42] -*** Contributed to forum discussions (examples: https://github.com[1], https://github.com[2], https://github.com[3], https://github.com[4]) -*** Reported bugs and suggestions for other teams in the class (examples: https://github.com[1], https://github.com[2], https://github.com[3]) -*** Some parts of the history feature I added was adopted by several other class mates (https://github.com[1], https://github.com[2]) -** Tools: -*** Integrated a third party library (Natty) to the project (https://github.com[#42]) -*** Integrated a new Github plugin (CircleCI) to the team repo - -_{you can add/remove categories in the list above}_ - -== Contributions to the User Guide - - -|=== -|_Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users._ -|=== - -include::../UserGuide.adoc[tag=undoredo] - -include::../UserGuide.adoc[tag=dataencryption] - -== Contributions to the Developer Guide - -|=== -|_Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project._ -|=== - -include::../DeveloperGuide.adoc[tag=undoredo] - -include::../DeveloperGuide.adoc[tag=dataencryption] - - -== PROJECT: PowerPointLabs - ---- - -_{Optionally, you may include other projects in your portfolio.}_ diff --git a/docs/team/kelvintankaiboon.adoc b/docs/team/kelvintankaiboon.adoc new file mode 100644 index 000000000000..8002e0f30c69 --- /dev/null +++ b/docs/team/kelvintankaiboon.adoc @@ -0,0 +1,74 @@ += Kelvin Tan - Project Portfolio +:site-section: AboutUs +:toc: +:toc-title: +:toc-placement: preamble +:sectnums: +:imagesDir: ../images +:stylesDir: ../stylesheets +:xrefstyle: full +ifdef::env-github[] +:tip-caption: :bulb: +:note-caption: :information_source: +:warning-caption: :warning: +endif::[] + +:repoURL: https://github.com/CS2113-AY1819S1-T12-3/main/tree/master + + +Project: Jalil Enterprises Stock List + +== Overview + +=== Project Overview + +This project was a part of the CS2113T (Software Engineering & Object-Oriented Programming) module, and our team was tasked to enhance the existing AddressBook application for a specific target audience group of our choice. + +=== Product Overview + +Morphed from AddressBook – Level 4 [link to AB4], Jalil Enterprises is a desktop inventory list application used for recording stocks in NUS Computer Engineering laboratories. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java and has ~10 kLoC. + +Some key features of the Jalil Enterprises application are storing quantities and statuses of stocks, creating loan lists for loaning of equipment, and saving and viewing archives of the inventory on command. + +== Summary of contributions +For the project, I was responsible for the storage component of the application as well as the implementation of “saving and viewing a version of the stock list” feature. + +* Code contributed: https://nuscs2113-ay1819s1.github.io/dashboard/#=undefined&search=kelvintankaiboon[Project Code Dashboard] +* *Major enhancement*: : Implemented a series of commands which allow the user to *save and view past versions of the inventory*. +** What it does: allows the user to save the current inventory as a .xml file and open it anytime in a table format. +** Justification: This feature improves the product significantly because a lab technician would need to save and review past inventory statuses monthly. +** Highlights: This enhancement affects both storage and events management. It required an in-depth analysis of storage designs and XML transformations with XSLT. The implementation too was challenging as it required the creation of new commands. + +* *Other contributions*: + +** Project management: +*** Managed project releases `V1.0` – `V1.3` on GitHub +*** Responsible for milestone completion on GitHub +** Enhancements to existing features: +*** Enhanced FindCommand to search for items with a partial keyword + + Example: find “ardui” -> returns Arduino item + +*** Wrote tests for SaveCommand to increase coverage (Pull requests https://github.com/CS2113-AY1819S1-T12-3/main/pull/112[#112]) + + +== Contributions to the User Guide + + +|=== +|_Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users._ +|=== + +include::../UserGuide.adoc[tag=savecommand] + +include::../UserGuide.adoc[tag=opencommand] + +== Contributions to the Developer Guide + +|=== +|_Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project._ +|=== + +include::../DeveloperGuide.adoc[tag=saveopen] + + diff --git a/docs/template.xsl b/docs/template.xsl new file mode 100644 index 000000000000..e6e1639614e5 --- /dev/null +++ b/docs/template.xsl @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameQuantityMinimum QuantityStatus-ReadyStatus-On LoanStatus-Faulty
+ + +
+
diff --git a/docs/templates/LICENSE b/docs/templates/LICENSE index 2073b44dee64..9801377ff841 100644 --- a/docs/templates/LICENSE +++ b/docs/templates/LICENSE @@ -5,11 +5,11 @@ MIT License Copyright (C) 2012-2018 Dan Allen, Ryan Waldron and the Asciidoctor Project -Permission is hereby granted, free of charge, to any person obtaining a copy +Permission is hereby granted, free of charge, to any item obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is +copies of the Software, and to permit items to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java index ecdd043a4f81..a88e6246e0e9 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/seedu/address/MainApp.java @@ -1,5 +1,6 @@ package seedu.address; +import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; @@ -20,18 +21,23 @@ import seedu.address.commons.util.StringUtil; import seedu.address.logic.Logic; import seedu.address.logic.LogicManager; -import seedu.address.model.AddressBook; +import seedu.address.model.AccountList; import seedu.address.model.Model; import seedu.address.model.ModelManager; -import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.ReadOnlyAccountList; +import seedu.address.model.ReadOnlyStockList; +import seedu.address.model.StockList; import seedu.address.model.UserPrefs; +import seedu.address.model.util.SampleAccountDataUtil; import seedu.address.model.util.SampleDataUtil; -import seedu.address.storage.AddressBookStorage; +import seedu.address.storage.AccountListStorage; import seedu.address.storage.JsonUserPrefsStorage; +import seedu.address.storage.StockListStorage; import seedu.address.storage.Storage; import seedu.address.storage.StorageManager; import seedu.address.storage.UserPrefsStorage; -import seedu.address.storage.XmlAddressBookStorage; +import seedu.address.storage.XmlAccountListStorage; +import seedu.address.storage.XmlStockListStorage; import seedu.address.ui.Ui; import seedu.address.ui.UiManager; @@ -40,10 +46,12 @@ */ public class MainApp extends Application { - public static final Version VERSION = new Version(0, 6, 0, true); + public static final Version VERSION = new Version(1, 3, 0, true); private static final Logger logger = LogsCenter.getLogger(MainApp.class); + private static File loanListFile; + protected Ui ui; protected Logic logic; protected Storage storage; @@ -51,19 +59,30 @@ public class MainApp extends Application { protected Config config; protected UserPrefs userPrefs; - + public static File getLoanListFile() { + return loanListFile; + } @Override public void init() throws Exception { - logger.info("=============================[ Initializing AddressBook ]==========================="); + logger.info("=============================[ Initializing StockList ]==========================="); super.init(); + File userDirectory = new File(System.getProperty("user.dir")); + String loanListDirectory = userDirectory.getAbsolutePath().replace("\\", "/"); + loanListDirectory += "/data/LoanList.xml"; + System.out.println(loanListDirectory); + loanListFile = new File(loanListDirectory); + + AppParameters appParameters = AppParameters.parse(getParameters()); config = initConfig(appParameters.getConfigPath()); UserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(config.getUserPrefsFilePath()); userPrefs = initPrefs(userPrefsStorage); - AddressBookStorage addressBookStorage = new XmlAddressBookStorage(userPrefs.getAddressBookFilePath()); - storage = new StorageManager(addressBookStorage, userPrefsStorage); + StockListStorage stockListStorage = new XmlStockListStorage(userPrefs.getStockListFilePath()); + AccountListStorage accountListStorage = new XmlAccountListStorage(userPrefs.getAccountListFilePath()); + storage = new StorageManager(stockListStorage, userPrefsStorage, accountListStorage); + initLogging(config); @@ -77,28 +96,45 @@ public void init() throws Exception { } /** - * Returns a {@code ModelManager} with the data from {@code storage}'s address book and {@code userPrefs}.
- * The data from the sample address book will be used instead if {@code storage}'s address book is not found, - * or an empty address book will be used instead if errors occur when reading {@code storage}'s address book. + * Returns a {@code ModelManager} with the data from {@code storage}'s stock list and {@code userPrefs}.
+ * The data from the sample stock list will be used instead if {@code storage}'s stock list is not found, + * or an empty stock list will be used instead if errors occur when reading {@code storage}'s stock list. */ private Model initModelManager(Storage storage, UserPrefs userPrefs) { - Optional addressBookOptional; - ReadOnlyAddressBook initialData; + Optional stockListOptional; + ReadOnlyStockList initialData; + Optional accountListOptional; + ReadOnlyAccountList initialAccountData; + + try { + stockListOptional = storage.readStockList(); + if (!stockListOptional.isPresent()) { + logger.info("Data file not found. Will be starting with a sample StockList"); + } + initialData = stockListOptional.orElseGet(SampleDataUtil::getSampleStockList); + } catch (DataConversionException e) { + logger.warning("Data file not in the correct format. Will be starting with an empty StockList"); + initialData = new StockList(); + } catch (IOException e) { + logger.warning("Problem while reading from the file. Will be starting with an empty StockList"); + initialData = new StockList(); + } + try { - addressBookOptional = storage.readAddressBook(); - if (!addressBookOptional.isPresent()) { - logger.info("Data file not found. Will be starting with a sample AddressBook"); + accountListOptional = storage.readAccountList(); + if (!accountListOptional.isPresent()) { + logger.info("Data file not found. Will be starting with a sample account database"); } - initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook); + initialAccountData = accountListOptional.orElseGet(SampleAccountDataUtil::getSampleAccountList); } catch (DataConversionException e) { - logger.warning("Data file not in the correct format. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + logger.warning("Data file not in the correct format. Will be starting with an empty account database"); + initialAccountData = new AccountList(); } catch (IOException e) { - logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + logger.warning("Problem while reading from the file. Will be starting with an empty account database"); + initialAccountData = new AccountList(); } - return new ModelManager(initialData, userPrefs); + return new ModelManager(initialData, userPrefs, initialAccountData); } private void initLogging(Config config) { @@ -159,7 +195,7 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { + "Using default user prefs"); initializedPrefs = new UserPrefs(); } catch (IOException e) { - logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); + logger.warning("Problem while reading from the file. Will be starting with an empty StockList"); initializedPrefs = new UserPrefs(); } @@ -179,13 +215,13 @@ private void initEventsCenter() { @Override public void start(Stage primaryStage) { - logger.info("Starting AddressBook " + MainApp.VERSION); + logger.info("Starting StockList " + MainApp.VERSION); ui.start(primaryStage); } @Override public void stop() { - logger.info("============================ [ Stopping Address Book ] ============================="); + logger.info("============================ [ Stopping Stock List ] ============================="); ui.stop(); try { storage.saveUserPrefs(userPrefs); diff --git a/src/main/java/seedu/address/commons/core/Config.java b/src/main/java/seedu/address/commons/core/Config.java index e978d621e086..db49f1f74abc 100644 --- a/src/main/java/seedu/address/commons/core/Config.java +++ b/src/main/java/seedu/address/commons/core/Config.java @@ -13,7 +13,7 @@ public class Config { public static final Path DEFAULT_CONFIG_FILE = Paths.get("config.json"); // Config values customizable through config file - private String appTitle = "Address App"; + private String appTitle = "Jalil Enterprise"; private Level logLevel = Level.INFO; private Path userPrefsFilePath = Paths.get("preferences.json"); @@ -70,5 +70,4 @@ public String toString() { sb.append("\nPreference file Location : " + userPrefsFilePath); return sb.toString(); } - } diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java index 1deb3a1e4695..3e4e2db66ac3 100644 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ b/src/main/java/seedu/address/commons/core/Messages.java @@ -7,7 +7,10 @@ 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_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; + public static final String MESSAGE_INVALID_ITEM_DISPLAYED_INDEX = "The item index provided is invalid"; + public static final String MESSAGE_INVALID_ACCOUNT_DISPLAYED_INDEX = "The account index provided is invalid"; + public static final String MESSAGE_INVALID_ACCOUNT_DELETION = "Cannot delete logged in account!"; + public static final String MESSAGE_ITEMS_LISTED_OVERVIEW = "%1$d items listed!"; + public static final String MESSAGE_ACCOUNTS_FOUND_OVERVIEW = "%1$d accounts found!"; } diff --git a/src/main/java/seedu/address/commons/events/model/AccountListChangedEvent.java b/src/main/java/seedu/address/commons/events/model/AccountListChangedEvent.java new file mode 100644 index 000000000000..3004860c3fa5 --- /dev/null +++ b/src/main/java/seedu/address/commons/events/model/AccountListChangedEvent.java @@ -0,0 +1,19 @@ +package seedu.address.commons.events.model; + +import seedu.address.commons.events.BaseEvent; +import seedu.address.model.ReadOnlyAccountList; + +/** Indicates the AccountList in the model has changed*/ +public class AccountListChangedEvent extends BaseEvent { + + public final ReadOnlyAccountList data; + + public AccountListChangedEvent(ReadOnlyAccountList data) { + this.data = data; + } + + @Override + public String toString() { + return "number of accounts " + data.getAccountList().size(); + } +} diff --git a/src/main/java/seedu/address/commons/events/model/AddressBookChangedEvent.java b/src/main/java/seedu/address/commons/events/model/AddressBookChangedEvent.java deleted file mode 100644 index b72ad4740e5a..000000000000 --- a/src/main/java/seedu/address/commons/events/model/AddressBookChangedEvent.java +++ /dev/null @@ -1,19 +0,0 @@ -package seedu.address.commons.events.model; - -import seedu.address.commons.events.BaseEvent; -import seedu.address.model.ReadOnlyAddressBook; - -/** Indicates the AddressBook in the model has changed*/ -public class AddressBookChangedEvent extends BaseEvent { - - public final ReadOnlyAddressBook data; - - public AddressBookChangedEvent(ReadOnlyAddressBook data) { - this.data = data; - } - - @Override - public String toString() { - return "number of persons " + data.getPersonList().size(); - } -} diff --git a/src/main/java/seedu/address/commons/events/model/OpenStockListVersionEvent.java b/src/main/java/seedu/address/commons/events/model/OpenStockListVersionEvent.java new file mode 100644 index 000000000000..3923cd9e9227 --- /dev/null +++ b/src/main/java/seedu/address/commons/events/model/OpenStockListVersionEvent.java @@ -0,0 +1,21 @@ +package seedu.address.commons.events.model; + +import seedu.address.commons.events.BaseEvent; +import seedu.address.model.ReadOnlyStockList; + +/** Indicates the command to save current stock list version*/ +public class OpenStockListVersionEvent extends BaseEvent { + + public final ReadOnlyStockList data; + public final String fileName; + + public OpenStockListVersionEvent(ReadOnlyStockList data, String fileName) { + this.data = data; + this.fileName = fileName; + } + + @Override + public String toString() { + return "number of items " + data.getItemList().size(); + } +} diff --git a/src/main/java/seedu/address/commons/events/model/SaveStockListVersionEvent.java b/src/main/java/seedu/address/commons/events/model/SaveStockListVersionEvent.java new file mode 100644 index 000000000000..9512890a0f2c --- /dev/null +++ b/src/main/java/seedu/address/commons/events/model/SaveStockListVersionEvent.java @@ -0,0 +1,21 @@ +package seedu.address.commons.events.model; + +import seedu.address.commons.events.BaseEvent; +import seedu.address.model.ReadOnlyStockList; + +/** Indicates the command to save current stock list version*/ +public class SaveStockListVersionEvent extends BaseEvent { + + public final ReadOnlyStockList data; + public final String fileName; + + public SaveStockListVersionEvent(ReadOnlyStockList data, String fileName) { + this.data = data; + this.fileName = fileName; + } + + @Override + public String toString() { + return "number of items " + data.getItemList().size(); + } +} diff --git a/src/main/java/seedu/address/commons/events/model/StockListChangedEvent.java b/src/main/java/seedu/address/commons/events/model/StockListChangedEvent.java new file mode 100644 index 000000000000..1a730d93aa99 --- /dev/null +++ b/src/main/java/seedu/address/commons/events/model/StockListChangedEvent.java @@ -0,0 +1,19 @@ +package seedu.address.commons.events.model; + +import seedu.address.commons.events.BaseEvent; +import seedu.address.model.ReadOnlyStockList; + +/** Indicates the StockList in the model has changed*/ +public class StockListChangedEvent extends BaseEvent { + + public final ReadOnlyStockList data; + + public StockListChangedEvent(ReadOnlyStockList data) { + this.data = data; + } + + @Override + public String toString() { + return "number of items " + data.getItemList().size(); + } +} diff --git a/src/main/java/seedu/address/commons/events/ui/ItemPanelSelectionChangedEvent.java b/src/main/java/seedu/address/commons/events/ui/ItemPanelSelectionChangedEvent.java new file mode 100644 index 000000000000..5c8de84ff3c9 --- /dev/null +++ b/src/main/java/seedu/address/commons/events/ui/ItemPanelSelectionChangedEvent.java @@ -0,0 +1,26 @@ +package seedu.address.commons.events.ui; + +import seedu.address.commons.events.BaseEvent; +import seedu.address.model.item.Item; + +/** + * Represents a selection change in the Item List Panel + */ +public class ItemPanelSelectionChangedEvent extends BaseEvent { + + + private final Item newSelection; + + public ItemPanelSelectionChangedEvent(Item newSelection) { + this.newSelection = newSelection; + } + + @Override + public String toString() { + return getClass().getSimpleName(); + } + + public Item getNewSelection() { + return newSelection; + } +} diff --git a/src/main/java/seedu/address/commons/events/ui/JumpToListRequestEvent.java b/src/main/java/seedu/address/commons/events/ui/JumpToListRequestEvent.java index a890f8b47350..682ca4240d17 100644 --- a/src/main/java/seedu/address/commons/events/ui/JumpToListRequestEvent.java +++ b/src/main/java/seedu/address/commons/events/ui/JumpToListRequestEvent.java @@ -4,7 +4,7 @@ import seedu.address.commons.events.BaseEvent; /** - * Indicates a request to jump to the list of persons + * Indicates a request to jump to the list of items */ public class JumpToListRequestEvent extends BaseEvent { diff --git a/src/main/java/seedu/address/commons/events/ui/PersonPanelSelectionChangedEvent.java b/src/main/java/seedu/address/commons/events/ui/PersonPanelSelectionChangedEvent.java deleted file mode 100644 index c5c8b9ce90ed..000000000000 --- a/src/main/java/seedu/address/commons/events/ui/PersonPanelSelectionChangedEvent.java +++ /dev/null @@ -1,26 +0,0 @@ -package seedu.address.commons.events.ui; - -import seedu.address.commons.events.BaseEvent; -import seedu.address.model.person.Person; - -/** - * Represents a selection change in the Person List Panel - */ -public class PersonPanelSelectionChangedEvent extends BaseEvent { - - - private final Person newSelection; - - public PersonPanelSelectionChangedEvent(Person newSelection) { - this.newSelection = newSelection; - } - - @Override - public String toString() { - return getClass().getSimpleName(); - } - - public Person getNewSelection() { - return newSelection; - } -} diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java index 8b34b862039a..440a317d1082 100644 --- a/src/main/java/seedu/address/logic/Logic.java +++ b/src/main/java/seedu/address/logic/Logic.java @@ -4,7 +4,7 @@ import seedu.address.logic.commands.CommandResult; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Person; +import seedu.address.model.item.Item; /** * API of the Logic component @@ -19,8 +19,9 @@ public interface Logic { */ CommandResult execute(String commandText) throws CommandException, ParseException; - /** Returns an unmodifiable view of the filtered list of persons */ - ObservableList getFilteredPersonList(); + /** Returns an unmodifiable view of the filtered list of items */ + ObservableList getFilteredItemList(); + /** Returns the list of input entered by the user, encapsulated in a {@code ListElementPointer} object */ ListElementPointer getHistorySnapshot(); diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java index 9aff86fc33dc..51f65b7720b1 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/address/logic/LogicManager.java @@ -8,10 +8,10 @@ import seedu.address.logic.commands.Command; import seedu.address.logic.commands.CommandResult; import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.AddressBookParser; +import seedu.address.logic.parser.StockListParser; import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.Model; -import seedu.address.model.person.Person; +import seedu.address.model.item.Item; /** * The main LogicManager of the app. @@ -21,19 +21,19 @@ public class LogicManager extends ComponentManager implements Logic { private final Model model; private final CommandHistory history; - private final AddressBookParser addressBookParser; + private final StockListParser stockListParser; public LogicManager(Model model) { this.model = model; history = new CommandHistory(); - addressBookParser = new AddressBookParser(); + stockListParser = new StockListParser(); } @Override public CommandResult execute(String commandText) throws CommandException, ParseException { logger.info("----------------[USER COMMAND][" + commandText + "]"); try { - Command command = addressBookParser.parseCommand(commandText); + Command command = stockListParser.parseCommand(commandText); return command.execute(model, history); } finally { history.add(commandText); @@ -41,8 +41,9 @@ public CommandResult execute(String commandText) throws CommandException, ParseE } @Override - public ObservableList getFilteredPersonList() { - return model.getFilteredPersonList(); + public ObservableList getFilteredItemList() { + + return model.getFilteredItemList(); } @Override diff --git a/src/main/java/seedu/address/logic/commands/AddAccountCommand.java b/src/main/java/seedu/address/logic/commands/AddAccountCommand.java new file mode 100644 index 000000000000..8c7fec36a6c8 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AddAccountCommand.java @@ -0,0 +1,63 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PASSWORD; +import static seedu.address.logic.parser.CliSyntax.PREFIX_USERNAME; + +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.account.Account; + +/** + * Adds an account to the account list. + */ +public class AddAccountCommand extends Command { + + public static final String COMMAND_WORD = "addAccount"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds an account to the account database. " + + "Parameters: " + + PREFIX_USERNAME + "USERNAME " + + PREFIX_PASSWORD + "PASSWORD\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_USERNAME + "admin " + + PREFIX_PASSWORD + "admin"; + + + public static final String MESSAGE_SUCCESS = "New account added: %1$s"; + public static final String MESSAGE_DUPLICATE_ACCOUNT = "This account already exists in the database"; + + private final Account toAdd; + + /** + * Creates an AddCommand to add the specified {@code Item} + */ + public AddAccountCommand(Account account) { + requireNonNull(account); + toAdd = account; + } + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + if (model.hasAccount(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_ACCOUNT); + } + + model.addAccount(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddAccountCommand // instanceof handles nulls + && toAdd.equals(((AddAccountCommand) other).toAdd)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java index d88e831ff1ce..cf0047396384 100644 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ b/src/main/java/seedu/address/logic/commands/AddCommand.java @@ -1,62 +1,64 @@ package seedu.address.logic.commands; import static java.util.Objects.requireNonNull; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MIN_QUANTITY; import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_QUANTITY; import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; import seedu.address.logic.CommandHistory; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import seedu.address.model.person.Person; +import seedu.address.model.item.Item; /** - * Adds a person to the address book. + * Adds a item to the stock list. */ public class AddCommand extends Command { public static final String COMMAND_WORD = "add"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. " + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a item to the stock list.\n" + "Parameters: " + PREFIX_NAME + "NAME " - + PREFIX_PHONE + "PHONE " - + PREFIX_EMAIL + "EMAIL " - + PREFIX_ADDRESS + "ADDRESS " - + "[" + PREFIX_TAG + "TAG]...\n" + + PREFIX_QUANTITY + "QUANTITY " + + PREFIX_MIN_QUANTITY + "MINIMUM QUANTITY " + + "[Optional: " + PREFIX_TAG + "TAG]...\n" + "Example: " + COMMAND_WORD + " " - + PREFIX_NAME + "John Doe " - + PREFIX_PHONE + "98765432 " - + PREFIX_EMAIL + "johnd@example.com " - + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 " - + PREFIX_TAG + "friends " - + PREFIX_TAG + "owesMoney"; + + PREFIX_NAME + "Arduino " + + PREFIX_QUANTITY + "20 " + + PREFIX_MIN_QUANTITY + "5 " + + PREFIX_TAG + "Lab1 " + + PREFIX_TAG + "Lab2"; - public static final String MESSAGE_SUCCESS = "New person added: %1$s"; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book"; - private final Person toAdd; + public static final String MESSAGE_SUCCESS = "New item added: %1$s"; + public static final String MESSAGE_DUPLICATE_ITEM = "This item already exists in the stock list"; + + private final Item toAdd; /** - * Creates an AddCommand to add the specified {@code Person} + * Creates an AddCommand to add the specified {@code Item} */ - public AddCommand(Person person) { - requireNonNull(person); - toAdd = person; + public AddCommand(Item item) { + requireNonNull(item); + toAdd = item; } @Override public CommandResult execute(Model model, CommandHistory history) throws CommandException { requireNonNull(model); - if (model.hasPerson(toAdd)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + if (model.hasItem(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_ITEM); } - model.addPerson(toAdd); - model.commitAddressBook(); + model.addItem(toAdd); + model.commitStockList(); return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); } diff --git a/src/main/java/seedu/address/logic/commands/AddTagCommand.java b/src/main/java/seedu/address/logic/commands/AddTagCommand.java new file mode 100644 index 000000000000..c24a8aacdb3b --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AddTagCommand.java @@ -0,0 +1,152 @@ +//@@author gaoqikai +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_ITEMS; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.commons.util.CollectionUtil; +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.item.Item; +import seedu.address.model.item.Name; +import seedu.address.model.item.Quantity; +import seedu.address.model.tag.Tag; + + + + +/** + * Add the given tags to selected item by index. + */ + +public class AddTagCommand extends Command { + public static final String COMMAND_WORD = "addTag"; + + public static final String MESSAGE_SUCCESS = "Added tags to the selected item."; + + public static final String MESSAGE_DUPLICATE = "This item already has the inputted tags. Please try again."; + + public static final String MESSAGE_NO_TAG = "Please include the tags you want to add."; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Add the inputted tags to the selected item " + + "by the index number used in the displayed item list.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_TAG + "TAG [MORE_TAGS]" + + " Example: " + COMMAND_WORD + " 1 " + PREFIX_TAG + " Lab1"; + + private final AddTagDescriptor addTagDescriptor; + private final Index index; + + public AddTagCommand(Index index, AddTagDescriptor addTagDescriptor) { + requireNonNull(index); + requireNonNull(addTagDescriptor); + + this.index = index; + this.addTagDescriptor = new AddTagDescriptor(addTagDescriptor); + } + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + List lastShownList = model.getFilteredItemList(); + + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_ITEM_DISPLAYED_INDEX); + } + + Item itemToEdit = lastShownList.get(index.getZeroBased()); + Item editedItem = createEditedItem(itemToEdit, addTagDescriptor); + + model.updateItem(itemToEdit, editedItem); + model.updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS); + model.commitStockList(); + if (itemToEdit.getTags().size() == lastShownList.get(index.getZeroBased()).getTags().size()) { + return new CommandResult(MESSAGE_DUPLICATE); + } + return new CommandResult(MESSAGE_SUCCESS); + + } + + /** + * Creates and returns a {@code Item} with the details of {@code itemToEdit} + * edited with {@code addTagDescriptor}. + */ + private static Item createEditedItem(Item itemToEdit, AddTagCommand.AddTagDescriptor addTagDescriptor) { + assert itemToEdit != null; + + Name updatedName = itemToEdit.getName(); + Quantity updatedQuantity = itemToEdit.getQuantity(); + Quantity updatedMinQuantity = itemToEdit.getMinQuantity(); + Set updatedTags = addTagDescriptor.getTags(); + updatedTags.addAll(itemToEdit.getTags()); //A set will automatically sort its elements + return new Item(updatedName, updatedQuantity, updatedMinQuantity, updatedTags); + } + + /** + * Temporarily stores the tags to be added. + */ + public static class AddTagDescriptor { + private Set tags; + + public AddTagDescriptor() {} + + /** + * Copy constructor. + * A defensive copy of {@code tags} is used internally. + */ + public AddTagDescriptor(AddTagDescriptor toCopy) { + setTags(toCopy.tags); + } + + /** + * Returns true if there is tag to add. + */ + public boolean haveTag() { + return CollectionUtil.isAnyNonNull(tags); + } + + /** + * Sets {@code tags} to this object's {@code tags}. + * A defensive copy of {@code tags} is used internally. + */ + public void setTags(Set tags) { + this.tags = (tags != null) ? new HashSet<>(tags) : null; + } + + public Set getTags() { + return tags; + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof AddTagDescriptor)) { + return false; + } + + // state check + AddTagDescriptor e = (AddTagDescriptor) other; + + return getTags().equals(e.getTags()); + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/ChangeStatusCommand.java b/src/main/java/seedu/address/logic/commands/ChangeStatusCommand.java new file mode 100644 index 000000000000..f822fcc08ccc --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ChangeStatusCommand.java @@ -0,0 +1,214 @@ +package seedu.address.logic.commands; +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NEW_STATUS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ORIGINAL_STATUS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_QUANTITY; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_ITEMS; + +import java.util.List; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.item.Item; +import seedu.address.model.item.Name; +import seedu.address.model.item.Status; + + +//@@author ChewKinWhye + +/** + * Updates the status of an existing item in the stock list. + */ + +public class ChangeStatusCommand extends Command { + public static final String COMMAND_WORD = "changeStatus"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Changes the status of the item identified " + + "by the name of the item.\n" + + "The status field can only take the values Ready or Faulty\n" + + "Changing the status to On_Loan is not allowed\n" + + "Parameters: " + + PREFIX_NAME + "NAME " + + PREFIX_QUANTITY + "QUANTITY " + + PREFIX_ORIGINAL_STATUS + "ORIGINAL STATUS " + + PREFIX_NEW_STATUS + "NEW STATUS \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "Arduino " + + PREFIX_QUANTITY + "5 " + + PREFIX_ORIGINAL_STATUS + "Ready " + + PREFIX_NEW_STATUS + "Faulty"; + public static final String MESSAGE_CHANGE_STATUS_SUCCESS = "Changed Status: %1$s"; + public static final String MESSAGE_INVALID_STATUS_FIELD = "The status description is invalid"; + public static final String MESSAGE_INVALID_NAME_FIELD = "The item does not exist"; + public static final String MESSAGE_STATUS_CONSTRAINTS = + "The updated value of each status field has to be positive"; + + private Index index; + private final ChangeStatusDescriptor changeStatusDescriptor; + public ChangeStatusCommand(ChangeStatusDescriptor changeStatusDescriptor) { + requireNonNull(changeStatusDescriptor); + this.changeStatusDescriptor = new ChangeStatusDescriptor(changeStatusDescriptor); + } + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + List lastShownList = model.getFilteredItemList(); + + index = getIndex(lastShownList, changeStatusDescriptor); + + Item itemToUpdate = lastShownList.get(index.getZeroBased()); + + Item updatedItem = createUpdatedItem(itemToUpdate, changeStatusDescriptor); + + model.updateItem(itemToUpdate, updatedItem); + model.updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS); + model.commitStockList(); + return new CommandResult(String.format(MESSAGE_CHANGE_STATUS_SUCCESS, updatedItem)); + + + } + + public Index getIndex(List lastShownList, ChangeStatusDescriptor changeStatusDescriptor) + throws CommandException { + Index index; + int counter = 0; + for (Item item:lastShownList) { + if (item.getName().equals(changeStatusDescriptor.getName())) { + index = Index.fromZeroBased(counter); + return index; + } + counter++; + } + throw new CommandException(MESSAGE_INVALID_NAME_FIELD); + } + /** + * Creates and returns a {@code Item} with the details of {@code itemToUpdate} + * edited with {@code changeStatusDescriptor}. + */ + public Item createUpdatedItem(Item itemToUpdate, + ChangeStatusDescriptor changeStatusDescriptor) throws CommandException { + assert itemToUpdate != null; + Status currentStatus = itemToUpdate.getStatus(); + Status updatedStatus; + Integer updatedReady = currentStatus.getStatusReady(); + Integer updatedOnLoan = currentStatus.getStatusOnLoan(); + Integer updatedFaulty = currentStatus.getStatusFaulty(); + + Integer changeStatusValue = changeStatusDescriptor.getQuantity(); + switch (changeStatusDescriptor.getInitialStatus()) { + case "Ready": + updatedReady -= changeStatusValue; + break; + case "On_Loan": + updatedOnLoan -= changeStatusValue; + break; + case "Faulty": + updatedFaulty -= changeStatusValue; + break; + default: + throw new CommandException(MESSAGE_INVALID_STATUS_FIELD); + } + + switch (changeStatusDescriptor.getUpdatedStatus()) { + case "Ready": + updatedReady += changeStatusValue; + break; + case "On_Loan": + updatedOnLoan += changeStatusValue; + break; + case "Faulty": + updatedFaulty += changeStatusValue; + break; + default: + throw new CommandException(MESSAGE_INVALID_STATUS_FIELD); + } + if (updatedReady < 0 || updatedOnLoan < 0 || updatedFaulty < 0) { + throw new CommandException(MESSAGE_STATUS_CONSTRAINTS); + } + updatedStatus = new Status(updatedReady, updatedOnLoan, updatedFaulty); + + return new Item(itemToUpdate.getName(), itemToUpdate.getQuantity(), itemToUpdate.getMinQuantity(), + itemToUpdate.getLoststatus(), updatedStatus, itemToUpdate.getTags()); + } + /** + * Stores the details to update the item with. + */ + public static class ChangeStatusDescriptor { + private Name name; + private Integer changeStatusQuantity; + private String initialStatus; + private String updatedStatus; + + public ChangeStatusDescriptor() { + } + + public ChangeStatusDescriptor(Name name, Integer changeStatusQuantity, + String initialStatus, String updatedStatus) { + this.name = name; + this.changeStatusQuantity = changeStatusQuantity; + this.initialStatus = initialStatus; + this.updatedStatus = updatedStatus; + } + /** + * Copy constructor. + * A defensive copy of {@code tags} is used internally. + */ + public ChangeStatusDescriptor (ChangeStatusDescriptor toCopy) { + setName(toCopy.name); + setQuantity(toCopy.changeStatusQuantity); + setInitialStatus(toCopy.initialStatus); + setUpdatedStatus(toCopy.updatedStatus); + } + public void setName(Name name) { + this.name = name; + } + public Name getName() { + return name; + } + public void setQuantity(Integer changeStatusQuantity) { + this.changeStatusQuantity = changeStatusQuantity; + } + public Integer getQuantity() { + return changeStatusQuantity; + } + public void setInitialStatus(String initialStatus) { + this.initialStatus = initialStatus; + } + public String getInitialStatus() { + return initialStatus; + } + + public void setUpdatedStatus(String updatedStatus) { + this.updatedStatus = updatedStatus; + } + public String getUpdatedStatus() { + return updatedStatus; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof ChangeStatusDescriptor)) { + return false; + } + + ChangeStatusDescriptor otherItem = (ChangeStatusDescriptor) other; + return (otherItem.getName().equals(this.getName()) + && otherItem.getQuantity().equals(this.getQuantity()) + && otherItem.getInitialStatus().equals(this.getInitialStatus()) + && otherItem.getUpdatedStatus().equals(this.getUpdatedStatus())); + } + + } +} diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java index 1f85bcfe85a8..a2e6c8fdc6cf 100644 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ b/src/main/java/seedu/address/logic/commands/ClearCommand.java @@ -3,23 +3,29 @@ import static java.util.Objects.requireNonNull; import seedu.address.logic.CommandHistory; -import seedu.address.model.AddressBook; +import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; +import seedu.address.model.StockList; /** - * Clears the address book. + * Clears the stock list. */ public class ClearCommand extends Command { public static final String COMMAND_WORD = "clear"; - public static final String MESSAGE_SUCCESS = "Address book has been cleared!"; + public static final String MESSAGE_SUCCESS = "Stock list has been cleared!"; @Override - public CommandResult execute(Model model, CommandHistory history) { + public CommandResult execute(Model model, CommandHistory history) throws CommandException { requireNonNull(model); - model.resetData(new AddressBook()); - model.commitAddressBook(); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + model.resetData(new StockList()); + model.commitStockList(); return new CommandResult(MESSAGE_SUCCESS); } } diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/seedu/address/logic/commands/Command.java index 34e99d786ec6..4a55cbd0dbce 100644 --- a/src/main/java/seedu/address/logic/commands/Command.java +++ b/src/main/java/seedu/address/logic/commands/Command.java @@ -9,6 +9,8 @@ */ public abstract class Command { + public static final String MESSAGE_LOGIN = "User not logged in. Please log in first"; + /** * Executes the command and returns the result message. * diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/address/logic/commands/CommandResult.java index abdc267a2c44..7948c72fc515 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/address/logic/commands/CommandResult.java @@ -13,4 +13,17 @@ public CommandResult(String feedbackToUser) { this.feedbackToUser = requireNonNull(feedbackToUser); } + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof CommandResult)) { + return false; + } + + CommandResult otherCommandResult = (CommandResult) other; + return (otherCommandResult.feedbackToUser.equals(feedbackToUser)); + } } diff --git a/src/main/java/seedu/address/logic/commands/DeleteAccountCommand.java b/src/main/java/seedu/address/logic/commands/DeleteAccountCommand.java new file mode 100644 index 000000000000..5895d8f643ae --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/DeleteAccountCommand.java @@ -0,0 +1,66 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.account.Account; + +/** + * Deletes an account identified using it's displayed index from the account list. + * Raises an exception if last account in the list. + */ +public class DeleteAccountCommand extends Command { + + public static final String COMMAND_WORD = "deleteAccount"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the account identified by the index number used in the displayed account database.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_DELETE_ACCOUNT_SUCCESS = "Deleted Account: %1$s"; + + private final Index targetIndex; + + public DeleteAccountCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + List lastShownList = model.getFilteredAccountList(); + + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_ACCOUNT_DISPLAYED_INDEX); + } + + Account itemToDelete = lastShownList.get(targetIndex.getZeroBased()); + + if (itemToDelete.getUsername().toString().equals(model.getLoggedInUser())) { + throw new CommandException(Messages.MESSAGE_INVALID_ACCOUNT_DELETION); + } + + model.deleteAccount(itemToDelete); + return new CommandResult(String.format(MESSAGE_DELETE_ACCOUNT_SUCCESS, itemToDelete)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteAccountCommand // instanceof handles nulls + && targetIndex.equals(((DeleteAccountCommand) other).targetIndex)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java index a20e9d49eac7..2b3abb17a2f6 100644 --- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java +++ b/src/main/java/seedu/address/logic/commands/DeleteCommand.java @@ -4,26 +4,33 @@ import java.util.List; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; + +import seedu.address.MainApp; import seedu.address.commons.core.Messages; import seedu.address.commons.core.index.Index; import seedu.address.logic.CommandHistory; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import seedu.address.model.person.Person; +import seedu.address.model.item.Item; +import seedu.address.storage.XmlAdaptedLoanList; +import seedu.address.storage.XmlAdaptedLoanerDescription; /** - * Deletes a person identified using it's displayed index from the address book. + * Deletes a item identified using it's displayed index from the stock list. */ public class DeleteCommand extends Command { public static final String COMMAND_WORD = "delete"; public static final String MESSAGE_USAGE = COMMAND_WORD - + ": Deletes the person identified by the index number used in the displayed person list.\n" + + ": Deletes the item identified by the index number used in the displayed item list.\n" + "Parameters: INDEX (must be a positive integer)\n" + "Example: " + COMMAND_WORD + " 1"; - public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s"; + public static final String MESSAGE_DELETE_ITEM_SUCCESS = "Deleted Item: %1$s"; private final Index targetIndex; @@ -34,16 +41,40 @@ public DeleteCommand(Index targetIndex) { @Override public CommandResult execute(Model model, CommandHistory history) throws CommandException { requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + List lastShownList = model.getFilteredItemList(); + if (targetIndex.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + throw new CommandException(Messages.MESSAGE_INVALID_ITEM_DISPLAYED_INDEX); + } + Item itemToDelete = lastShownList.get(targetIndex.getZeroBased()); + if (MainApp.getLoanListFile().exists()) { + try { + int counter = 0; + JAXBContext context = JAXBContext.newInstance(XmlAdaptedLoanList.class); + Unmarshaller unmarshaller = context.createUnmarshaller(); + XmlAdaptedLoanList xmlAdaptedLoanList = (XmlAdaptedLoanList) unmarshaller + .unmarshal(MainApp.getLoanListFile()); + for (XmlAdaptedLoanerDescription loanerDescription : xmlAdaptedLoanList.getLoanList()) { + if (loanerDescription.getItemName().equals(itemToDelete.getName().toString())) { + DeleteLoanListCommand toDelete = new DeleteLoanListCommand(Index.fromZeroBased(counter)); + toDelete.execute(model, history); + } + counter++; + } + } catch (JAXBException e) { + System.out.println(e.toString()); + } } - Person personToDelete = lastShownList.get(targetIndex.getZeroBased()); - model.deletePerson(personToDelete); - model.commitAddressBook(); - return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, personToDelete)); + model.deleteItem(itemToDelete); + model.commitStockList(); + return new CommandResult(String.format(MESSAGE_DELETE_ITEM_SUCCESS, itemToDelete)); } @Override diff --git a/src/main/java/seedu/address/logic/commands/DeleteLoanListCommand.java b/src/main/java/seedu/address/logic/commands/DeleteLoanListCommand.java new file mode 100644 index 000000000000..d569c8240680 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/DeleteLoanListCommand.java @@ -0,0 +1,92 @@ +package seedu.address.logic.commands; + +import java.io.File; +import java.util.ArrayList; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.Unmarshaller; + +import seedu.address.MainApp; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.item.Name; +import seedu.address.storage.XmlAdaptedLoanList; +import seedu.address.storage.XmlAdaptedLoanerDescription; + +//@@author ChewKinWhye + +/** + * Deletes an entry in the loan list base on the Index + */ + +public class DeleteLoanListCommand extends Command { + public static final String COMMAND_WORD = "deleteLoanList"; + public static final String MESSAGE_SUCCESS = "Loan list entry has been deleted"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Deletes the loan list entry identified " + + "by the index number used in the display of the loan list.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + public static final String MESSAGE_EMPTY = "Loan list is currently empty"; + public static final String MESSAGE_INVALID_INDEX = "The input index is invalid"; + + private final Index index; + + public DeleteLoanListCommand(Index index) { + this.index = index; + } + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + if (!MainApp.getLoanListFile().exists()) { + throw new CommandException(MESSAGE_EMPTY); + } + File loanListFile = MainApp.getLoanListFile(); + try { + + deleteLoanList(model, history, loanListFile); + } catch (Exception e) { + if (e.getClass() == CommandException.class) { + throw new CommandException(e.getMessage()); + } + } + return new CommandResult(String.format(MESSAGE_SUCCESS)); + } + /** + * Deletes an entry in the loan list base on the Index + */ + public void deleteLoanList(Model model, CommandHistory history, File loanListFile) throws Exception { + + JAXBContext context = JAXBContext.newInstance(XmlAdaptedLoanList.class); + Unmarshaller unmarshaller = context.createUnmarshaller(); + XmlAdaptedLoanList xmlAdaptedLoanList = (XmlAdaptedLoanList) unmarshaller + .unmarshal(loanListFile); + ArrayList loanList = xmlAdaptedLoanList.getLoanList(); + + if (index.getOneBased() > loanList.size()) { + throw new CommandException(MESSAGE_INVALID_INDEX); + } + + updateStatus(model, history, loanList.get(index.getZeroBased())); + + loanList.remove(index.getZeroBased()); + + LoanListCommand.updateXmlLoanListFile(new XmlAdaptedLoanList(loanList), loanListFile); + } + /** + * Changes the status from Ready to On_Loan + */ + private void updateStatus(Model model, CommandHistory history, XmlAdaptedLoanerDescription loanerDescription) + throws CommandException { + ChangeStatusCommand.ChangeStatusDescriptor changeStatusDescriptor = + new ChangeStatusCommand.ChangeStatusDescriptor(new Name(loanerDescription.getItemName()), + loanerDescription.getQuantity(), "On_Loan", "Ready"); + ChangeStatusCommand changeStatusCommand = new ChangeStatusCommand(changeStatusDescriptor); + changeStatusCommand.execute(model, history); + } +} diff --git a/src/main/java/seedu/address/logic/commands/DeleteTagCommand.java b/src/main/java/seedu/address/logic/commands/DeleteTagCommand.java new file mode 100644 index 000000000000..f5fe6ad5f696 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/DeleteTagCommand.java @@ -0,0 +1,151 @@ +//@@author gaoqikai +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_ITEMS; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.commons.util.CollectionUtil; +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.item.Item; +import seedu.address.model.item.Name; +import seedu.address.model.item.Quantity; +import seedu.address.model.tag.Tag; + + + + +/** + * Delete the given tags to selected item by index. + */ + +public class DeleteTagCommand extends Command { + public static final String COMMAND_WORD = "deleteTag"; + + public static final String MESSAGE_SUCCESS = "Deleted tags from the selected item."; + + public static final String MESSAGE_NO_TAG = "Please include the tags you want to delete."; + + public static final String MESSAGE_DOES_NOT_EXIST = "The selected item does not contain the inputted tags."; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Delete the inputted tags from the selected item " + + "by the index number used in the displayed item list.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_TAG + "TAG [MORE_TAGS]" + + " Example: " + COMMAND_WORD + " 1 " + PREFIX_TAG + " Lab1"; + + private static boolean flag = false; //to indicate whether a deletion is executed. + private final DeleteTagDescriptor deleteTagDescriptor; + private final Index index; + + public DeleteTagCommand(Index index, DeleteTagDescriptor deleteTagDescriptor) { + requireNonNull(index); + requireNonNull(deleteTagDescriptor); + + this.index = index; + this.deleteTagDescriptor = new DeleteTagDescriptor(deleteTagDescriptor); + } + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + List lastShownList = model.getFilteredItemList(); + + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_ITEM_DISPLAYED_INDEX); + } + + Item itemToEdit = lastShownList.get(index.getZeroBased()); + Item editedItem = createEditedItem(itemToEdit, deleteTagDescriptor); + + model.updateItem(itemToEdit, editedItem); + model.updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS); + model.commitStockList(); + return new CommandResult(flag ? (MESSAGE_SUCCESS) : (MESSAGE_DOES_NOT_EXIST)); + + } + + /** + * Creates and returns a {@code Item} with the details of {@code itemToEdit} + * edited with {@code deleteTagDescriptor}. + */ + private static Item createEditedItem(Item itemToEdit, DeleteTagCommand.DeleteTagDescriptor deleteTagDescriptor) { + assert itemToEdit != null; + + Name updatedName = itemToEdit.getName(); + Quantity updatedQuantity = itemToEdit.getQuantity(); + Quantity updatedMinQuantity = itemToEdit.getMinQuantity(); + Set updatedTags = new HashSet<>(itemToEdit.getTags()); + flag = updatedTags.removeIf((Tag current) -> deleteTagDescriptor.getTags().toString().toLowerCase() + .contains(current.toString().toLowerCase())); + return new Item(updatedName, updatedQuantity, updatedMinQuantity, updatedTags); + } + + /** + * Temporarily stores the tags to be added. + */ + public static class DeleteTagDescriptor { + private Set tags; + + public DeleteTagDescriptor() {} + + /** + * Copy constructor. + * A defensive copy of {@code tags} is used internally. + */ + public DeleteTagDescriptor(DeleteTagDescriptor toCopy) { + setTags(toCopy.tags); + } + + /** + * Returns true if there is tag to delete. + */ + public boolean haveTag() { + return CollectionUtil.isAnyNonNull(tags); + } + + /** + * Sets {@code tags} to this object's {@code tags}. + * A defensive copy of {@code tags} is used internally. + */ + public void setTags(Set tags) { + this.tags = (tags != null) ? new HashSet<>(tags) : null; + } + + public Set getTags() { + return tags; + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof DeleteTagDescriptor)) { + return false; + } + + // state check + DeleteTagDescriptor e = (DeleteTagDescriptor) other; + + return getTags().equals(e.getTags()); + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/EditAccountCommand.java b/src/main/java/seedu/address/logic/commands/EditAccountCommand.java new file mode 100644 index 000000000000..cedd695a1b86 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/EditAccountCommand.java @@ -0,0 +1,181 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PASSWORD; +import static seedu.address.logic.parser.CliSyntax.PREFIX_USERNAME; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_ACCOUNTS; + +import java.util.List; +import java.util.Optional; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.commons.util.CollectionUtil; +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.account.Account; +import seedu.address.model.account.Password; +import seedu.address.model.account.Username; + +/** + * Edits the details of an existing account in the account list. + */ +public class EditAccountCommand extends Command { + + public static final String COMMAND_WORD = "editAccount"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the account identified " + + "by the index number used in the displayed account database. " + + "Existing values will be overwritten by the input values.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_USERNAME + "USERNAME] " + + "[" + PREFIX_PASSWORD + "PASSWORD]\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_USERNAME + "jalil"; + + public static final String MESSAGE_EDIT_ACCOUNT_SUCCESS = "Edited Account: %1$s"; + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; + public static final String MESSAGE_DUPLICATE_ACCOUNT = "This account already exists in the database."; + + private final Index index; + private final EditAccountDescriptor editAccountDescriptor; + + /** + * @param index of the account in the filtered account list to edit + * @param editAccountDescriptor details to edit the account with + */ + public EditAccountCommand(Index index, EditAccountDescriptor editAccountDescriptor) { + requireNonNull(index); + requireNonNull(editAccountDescriptor); + + this.index = index; + this.editAccountDescriptor = new EditAccountDescriptor(editAccountDescriptor); + } + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + List lastShownList = model.getFilteredAccountList(); + + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_ACCOUNT_DISPLAYED_INDEX); + } + + Account accountToEdit = lastShownList.get(index.getZeroBased()); + Account editedAccount = createEditedAccount(accountToEdit, editAccountDescriptor); + + if (!accountToEdit.isSameAccount(editedAccount) && model.hasAccount(editedAccount)) { + throw new CommandException(MESSAGE_DUPLICATE_ACCOUNT); + } + + model.updateAccount(accountToEdit, editedAccount); + model.updateFilteredAccountList(PREDICATE_SHOW_ALL_ACCOUNTS); + + if (accountToEdit.getUsername().toString().equals(model.getLoggedInUser())) { + model.setLoggedInUser(editedAccount.getUsername()); + } + + return new CommandResult(String.format(MESSAGE_EDIT_ACCOUNT_SUCCESS, editedAccount)); + + } + + /** + * Creates and returns a {@code Account} with the details of {@code accountToEdit} + * edited with {@code editAccountDescriptor}. + */ + private static Account createEditedAccount(Account accountToEdit, EditAccountDescriptor editAccountDescriptor) { + assert accountToEdit != null; + + Username updatedUsername = editAccountDescriptor.getUsername().orElse(accountToEdit.getUsername()); + Password updatedPassword = editAccountDescriptor.getPassword().orElse(accountToEdit.getPassword()); + + return new Account(updatedUsername, updatedPassword); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditAccountCommand)) { + return false; + } + + // state check + EditAccountCommand e = (EditAccountCommand) other; + return index.equals(e.index) + && editAccountDescriptor.equals(e.editAccountDescriptor); + } + + /** + * Stores the details to edit the account with. Each non-empty field value will replace the + * corresponding field value of the account. + */ + public static class EditAccountDescriptor { + private Username username; + private Password password; + + public EditAccountDescriptor() {} + + /** + * Copy constructor. + */ + public EditAccountDescriptor(EditAccountDescriptor toCopy) { + setUsername(toCopy.username); + setPassword(toCopy.password); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(username, password); + } + + public void setUsername(Username username) { + this.username = username; + } + + public Optional getUsername() { + return Optional.ofNullable(username); + } + + public void setPassword(Password password) { + this.password = password; + } + + public Optional getPassword() { + return Optional.ofNullable(password); + } + + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditAccountDescriptor)) { + return false; + } + + // state check + EditAccountDescriptor e = (EditAccountDescriptor) other; + + return getUsername().equals(e.getUsername()) + && getPassword().equals(e.getPassword()); + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java index dc782d8e230f..6d52ada5d197 100644 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ b/src/main/java/seedu/address/logic/commands/EditCommand.java @@ -1,13 +1,13 @@ package seedu.address.logic.commands; import static java.util.Objects.requireNonNull; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MIN_QUANTITY; import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_QUANTITY; import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_ITEMS; +import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -20,88 +20,101 @@ import seedu.address.logic.CommandHistory; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; +import seedu.address.model.item.Item; +import seedu.address.model.item.Loststatus; +import seedu.address.model.item.Name; +import seedu.address.model.item.Quantity; +import seedu.address.model.item.Status; import seedu.address.model.tag.Tag; /** - * Edits the details of an existing person in the address book. + * Edits the details of an existing item in the stock list. */ public class EditCommand extends Command { public static final String COMMAND_WORD = "edit"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the person identified " - + "by the index number used in the displayed person list. " + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the item identified " + + "by the index number used in the displayed item list. " + "Existing values will be overwritten by the input values.\n" + "Parameters: INDEX (must be a positive integer) " + "[" + PREFIX_NAME + "NAME] " - + "[" + PREFIX_PHONE + "PHONE] " - + "[" + PREFIX_EMAIL + "EMAIL] " - + "[" + PREFIX_ADDRESS + "ADDRESS] " + + "[" + PREFIX_QUANTITY + "QUANTITY] " + + "[" + PREFIX_MIN_QUANTITY + "MIN QUANTITY] " + "[" + PREFIX_TAG + "TAG]...\n" + "Example: " + COMMAND_WORD + " 1 " - + PREFIX_PHONE + "91234567 " - + PREFIX_EMAIL + "johndoe@example.com"; + + PREFIX_QUANTITY + "25 " + + PREFIX_TAG + "Lab1 " + + PREFIX_TAG + "Lab2"; - public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s"; + public static final String MESSAGE_EDIT_ITEM_SUCCESS = "Edited Item: %1$s"; public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book."; + public static final String MESSAGE_DUPLICATE_ITEM = "This item already exists in the stock list."; private final Index index; - private final EditPersonDescriptor editPersonDescriptor; + private final EditItemDescriptor editItemDescriptor; /** - * @param index of the person in the filtered person list to edit - * @param editPersonDescriptor details to edit the person with + * @param index of the item in the filtered item list to edit + * @param editItemDescriptor details to edit the item with */ - public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) { + public EditCommand(Index index, EditItemDescriptor editItemDescriptor) { requireNonNull(index); - requireNonNull(editPersonDescriptor); + requireNonNull(editItemDescriptor); this.index = index; - this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor); + this.editItemDescriptor = new EditItemDescriptor(editItemDescriptor); } @Override public CommandResult execute(Model model, CommandHistory history) throws CommandException { requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + List lastShownList = model.getFilteredItemList(); + if (index.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + throw new CommandException(Messages.MESSAGE_INVALID_ITEM_DISPLAYED_INDEX); } - Person personToEdit = lastShownList.get(index.getZeroBased()); - Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor); + Item itemToEdit = lastShownList.get(index.getZeroBased()); + Item editedItem = createEditedItem(itemToEdit, editItemDescriptor); - if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); + if (!itemToEdit.isSameItem(editedItem) && model.hasItem(editedItem)) { + throw new CommandException(MESSAGE_DUPLICATE_ITEM); } - model.updatePerson(personToEdit, editedPerson); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - model.commitAddressBook(); - return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson)); + model.updateItem(itemToEdit, editedItem); + model.updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS); + model.commitStockList(); + return new CommandResult(String.format(MESSAGE_EDIT_ITEM_SUCCESS, editedItem)); + } /** - * Creates and returns a {@code Person} with the details of {@code personToEdit} - * edited with {@code editPersonDescriptor}. + * Creates and returns a {@code Item} with the details of {@code itemToEdit} + * edited with {@code editItemDescriptor}. */ - private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) { - assert personToEdit != null; - - Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName()); - Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone()); - Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail()); - Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress()); - Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags()); - - return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags); + private static Item createEditedItem(Item itemToEdit, EditItemDescriptor editItemDescriptor) { + assert itemToEdit != null; + + Name updatedName = editItemDescriptor.getName().orElse(itemToEdit.getName()); + Quantity updatedQuantity = editItemDescriptor.getQuantity().orElse(itemToEdit.getQuantity()); + Loststatus updatedLoststatus = new Loststatus(itemToEdit.getLoststatus().getLoststatusLost(), + updatedQuantity.toInteger() - itemToEdit.getLoststatus().getLoststatusLost()); + Quantity updatedMinQuantity = editItemDescriptor.getMinQuantity().orElse(itemToEdit.getMinQuantity()); + Set updatedTags = editItemDescriptor.getTags().orElse(itemToEdit.getTags()); + Status updatedStatus = new Status(updatedQuantity.toInteger() + - itemToEdit.getStatus().getStatusFaulty() + - itemToEdit.getStatus().getStatusOnLoan(), + itemToEdit.getStatus().getStatusOnLoan(), + itemToEdit.getStatus().getStatusFaulty()); + return new Item(updatedName, updatedQuantity, updatedMinQuantity, updatedLoststatus, + updatedStatus, updatedTags); } @Override @@ -119,31 +132,32 @@ public boolean equals(Object other) { // state check EditCommand e = (EditCommand) other; return index.equals(e.index) - && editPersonDescriptor.equals(e.editPersonDescriptor); + && editItemDescriptor.equals(e.editItemDescriptor); } /** - * Stores the details to edit the person with. Each non-empty field value will replace the - * corresponding field value of the person. + * Stores the details to edit the item with. Each non-empty field value will replace the + * corresponding field value of the item. */ - public static class EditPersonDescriptor { + public static class EditItemDescriptor { private Name name; - private Phone phone; - private Email email; - private Address address; + private Quantity quantity; + private Quantity minQuantity; + //private Loststatus loststatus; + private List status; private Set tags; - public EditPersonDescriptor() {} + public EditItemDescriptor() {} /** * Copy constructor. * A defensive copy of {@code tags} is used internally. */ - public EditPersonDescriptor(EditPersonDescriptor toCopy) { + public EditItemDescriptor(EditItemDescriptor toCopy) { setName(toCopy.name); - setPhone(toCopy.phone); - setEmail(toCopy.email); - setAddress(toCopy.address); + setQuantity(toCopy.quantity); + setMinQuantity(toCopy.minQuantity); + setStatus(toCopy.status); setTags(toCopy.tags); } @@ -151,7 +165,7 @@ public EditPersonDescriptor(EditPersonDescriptor toCopy) { * Returns true if at least one field is edited. */ public boolean isAnyFieldEdited() { - return CollectionUtil.isAnyNonNull(name, phone, email, address, tags); + return CollectionUtil.isAnyNonNull(name, quantity, minQuantity, tags); } public void setName(Name name) { @@ -162,28 +176,28 @@ public Optional getName() { return Optional.ofNullable(name); } - public void setPhone(Phone phone) { - this.phone = phone; + public void setQuantity(Quantity quantity) { + this.quantity = quantity; } - public Optional getPhone() { - return Optional.ofNullable(phone); + public Optional getQuantity() { + return Optional.ofNullable(quantity); } - public void setEmail(Email email) { - this.email = email; + public void setMinQuantity(Quantity minQuantity) { + this.minQuantity = minQuantity; } - public Optional getEmail() { - return Optional.ofNullable(email); + public Optional getMinQuantity() { + return Optional.ofNullable(minQuantity); } - public void setAddress(Address address) { - this.address = address; + public void setStatus(List status) { + this.status = (status != null) ? new ArrayList<>(status) : null; } - public Optional
getAddress() { - return Optional.ofNullable(address); + public Optional> getStatus() { + return (status != null) ? Optional.of(Collections.unmodifiableList(status)) : Optional.empty(); } /** @@ -211,17 +225,17 @@ public boolean equals(Object other) { } // instanceof handles nulls - if (!(other instanceof EditPersonDescriptor)) { + if (!(other instanceof EditItemDescriptor)) { return false; } // state check - EditPersonDescriptor e = (EditPersonDescriptor) other; + EditItemDescriptor e = (EditItemDescriptor) other; return getName().equals(e.getName()) - && getPhone().equals(e.getPhone()) - && getEmail().equals(e.getEmail()) - && getAddress().equals(e.getAddress()) + && getQuantity().equals(e.getQuantity()) + && getMinQuantity().equals(e.getMinQuantity()) + && getStatus().equals(e.getStatus()) && getTags().equals(e.getTags()); } } diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java index e848fa918964..bee5dfec80df 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java @@ -12,7 +12,7 @@ public class ExitCommand extends Command { public static final String COMMAND_WORD = "exit"; - public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ..."; + public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Stock List as requested ..."; @Override public CommandResult execute(Model model, CommandHistory history) { diff --git a/src/main/java/seedu/address/logic/commands/FindAccountCommand.java b/src/main/java/seedu/address/logic/commands/FindAccountCommand.java new file mode 100644 index 000000000000..397521c1e3dc --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/FindAccountCommand.java @@ -0,0 +1,66 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.CommandHistory; +import seedu.address.model.Model; +import seedu.address.model.account.Account; +import seedu.address.model.account.UsernameContainsKeywordsPredicate; + +/** + * Finds and lists all accounts in account list whose username contains any of the argument keywords. + * Keyword matching is case insensitive. + */ +public class FindAccountCommand extends Command { + + public static final String COMMAND_WORD = "findAccount"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all accounts whose usernames contain any of " + + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" + + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" + + "Example: " + COMMAND_WORD + " jalil kelvin"; + + private final UsernameContainsKeywordsPredicate predicate; + + public FindAccountCommand(UsernameContainsKeywordsPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model, CommandHistory history) { + requireNonNull(model); + + model.updateFilteredAccountList(predicate); + List foundAccounts = model.getFilteredAccountList(); + String messageOutput = getMessageOutput(model.getFilteredAccountList().size(), foundAccounts); + return new CommandResult(messageOutput); + + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FindAccountCommand // instanceof handles nulls + && predicate.equals(((FindAccountCommand) other).predicate)); // state check + } + + private String getMessageOutput (int numAccounts, List accounts) { + String messageOutput = ""; + messageOutput += String.format(Messages.MESSAGE_ACCOUNTS_FOUND_OVERVIEW, numAccounts) + "\n"; + + messageOutput += "Accounts: \n"; + int counter = 0; + for (Account account : accounts) { + counter++; + messageOutput += counter + ". " + + + account.getUsername() + + "\n"; + } + + return messageOutput; + } +} diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java index beb178e3a3f5..25d91c39b641 100644 --- a/src/main/java/seedu/address/logic/commands/FindCommand.java +++ b/src/main/java/seedu/address/logic/commands/FindCommand.java @@ -4,18 +4,19 @@ import seedu.address.commons.core.Messages; import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.model.item.NameContainsKeywordsPredicate; /** - * Finds and lists all persons in address book whose name contains any of the argument keywords. + * Finds and lists all items in stock list whose name contains any of the argument keywords. * Keyword matching is case insensitive. */ public class FindCommand extends Command { public static final String COMMAND_WORD = "find"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose names contain any of " + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all items whose names contain any of " + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" + "Example: " + COMMAND_WORD + " alice bob charlie"; @@ -27,11 +28,17 @@ public FindCommand(NameContainsKeywordsPredicate predicate) { } @Override - public CommandResult execute(Model model, CommandHistory history) { + public CommandResult execute(Model model, CommandHistory history) throws CommandException { requireNonNull(model); - model.updateFilteredPersonList(predicate); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + model.updateFilteredItemList(predicate); return new CommandResult( - String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size())); + String.format(Messages.MESSAGE_ITEMS_LISTED_OVERVIEW, model.getFilteredItemList().size())); + } @Override diff --git a/src/main/java/seedu/address/logic/commands/FoundCommand.java b/src/main/java/seedu/address/logic/commands/FoundCommand.java new file mode 100644 index 000000000000..2262313e1660 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/FoundCommand.java @@ -0,0 +1,146 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import static seedu.address.logic.parser.CliSyntax.PREFIX_QUANTITY; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_ITEMS; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.item.Item; +import seedu.address.model.item.Loststatus; +import seedu.address.model.item.Quantity; +import seedu.address.model.item.Status; + + +//@@author HeHaowei + +/** + * Found an existing item in the stock list. + */ + +public class FoundCommand extends Command { + public static final String COMMAND_WORD = "found"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Found a item from the stock list identified " + + "by the index number used in the displayed item list" + + "Parameters: INDEX (must be a positive integer) " + + PREFIX_QUANTITY + "QUANTITY\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_QUANTITY + "25"; + + + public static final String MESSAGE_FOUND_ITEM_SUCCESS = "Found Item: %1$s"; + public static final String MESSAGE_INVALID_QUANTITY = "The found quantity input is invalid"; + public static final String MESSAGE_FOUND_LARGER_THAN_LOST = "The quantity of found items " + + "must be less than or equal to quantity of lost items"; + + private final Index targetIndex; + private final FoundDescriptor foundDescriptor; + + public FoundCommand(Index targetIndex, FoundDescriptor foundDescriptor) { + requireNonNull(targetIndex); + requireNonNull(foundDescriptor); + this.targetIndex = targetIndex; + this.foundDescriptor = new FoundDescriptor(foundDescriptor); + + } + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + List lastShownList = model.getFilteredItemList(); + + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_ITEM_DISPLAYED_INDEX); + } + + Item itemToFound = lastShownList.get(targetIndex.getZeroBased()); + Item foundItem = createFoundItem(itemToFound, foundDescriptor); + + if (!itemToFound.isSameItem(foundItem) && model.hasItem(foundItem)) { + throw new CommandException(MESSAGE_INVALID_QUANTITY); + } + model.updateItem(itemToFound, foundItem); + model.updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS); + model.commitStockList(); + return new CommandResult(String.format(MESSAGE_FOUND_ITEM_SUCCESS, foundItem)); + } + + /** + * Creates and returns a {@code Item} with the details of {@code itemToFound} + * edited with {@code FoundDescriptor}. + */ + public Item createFoundItem(Item itemToFound, FoundDescriptor foundDescriptor) + throws CommandException { + assert itemToFound != null; + Loststatus currentLoststatus = itemToFound.getLoststatus(); + Loststatus updatedLoststatus; + Integer updatedLost = currentLoststatus.getLoststatusLost(); + Integer updatedFound = currentLoststatus.getLoststatusFound(); + + Integer initialReadyValue = itemToFound.getStatus().getStatusReady(); + + Integer updatedValue = foundDescriptor.getFoundQuantity(); + Integer initialValue = itemToFound.getQuantity().toInteger(); + + updatedLost -= updatedValue; + updatedFound += updatedValue; + if (updatedLost < 0) { + throw new CommandException(MESSAGE_FOUND_LARGER_THAN_LOST); + } + updatedLoststatus = new Loststatus(updatedLost, updatedFound); + + Quantity updatedQuantity = new Quantity(Integer.toString(initialValue + updatedValue)); + Status updatedStatus = new Status(initialReadyValue + updatedValue, + itemToFound.getStatus().getStatusOnLoan(), itemToFound.getStatus().getStatusFaulty()); + + + + + return new Item(itemToFound.getName(), updatedQuantity, + itemToFound.getMinQuantity(), updatedLoststatus, updatedStatus, itemToFound.getTags()); + } + + /** + * Stores the details to lost the item with. + */ + + public static class FoundDescriptor { + private Integer foundQuantity; + + public FoundDescriptor() {} + + public FoundDescriptor(Integer foundQuantity) { + this.foundQuantity = foundQuantity; + } + + + public FoundDescriptor(FoundDescriptor toCopy) { + setFoundQuantity(toCopy.foundQuantity); + + } + public void setFoundQuantity(Integer foundQuantity) { + this.foundQuantity = foundQuantity; } + + public Integer getFoundQuantity() { + return foundQuantity; } + + } + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FoundCommand // instanceof handles nulls + && targetIndex.equals(((FoundCommand) other).targetIndex)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/ListAccountsCommand.java b/src/main/java/seedu/address/logic/commands/ListAccountsCommand.java new file mode 100644 index 000000000000..8fb1918c3a21 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ListAccountsCommand.java @@ -0,0 +1,47 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_ACCOUNTS; + +import java.util.List; + +import seedu.address.logic.CommandHistory; +import seedu.address.model.Model; +import seedu.address.model.account.Account; + +/** + * Lists all accounts in the account list to the user. + */ +public class ListAccountsCommand extends Command { + + public static final String COMMAND_WORD = "listAccounts"; + + public static final String MESSAGE_SUCCESS = "Listed all accounts"; + + + @Override + public CommandResult execute(Model model, CommandHistory history) { + requireNonNull(model); + model.updateFilteredAccountList(PREDICATE_SHOW_ALL_ACCOUNTS); + List listOfAccounts = model.getFilteredAccountList(); + String messageOutput = getMessageOutput(listOfAccounts); + return new CommandResult(messageOutput); + } + + private String getMessageOutput (List accounts) { + String messageOutput = ""; + messageOutput += MESSAGE_SUCCESS + "\n"; + + messageOutput += "Accounts: \n"; + int counter = 0; + for (Account account : accounts) { + counter++; + messageOutput += counter + ". " + + + account.getUsername() + + "\n"; + } + + return messageOutput; + } +} diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java index 6d44824c7d1b..dca94e6abafd 100644 --- a/src/main/java/seedu/address/logic/commands/ListCommand.java +++ b/src/main/java/seedu/address/logic/commands/ListCommand.java @@ -1,25 +1,31 @@ package seedu.address.logic.commands; import static java.util.Objects.requireNonNull; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_ITEMS; import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; /** - * Lists all persons in the address book to the user. + * Lists all items in the stock list to the user. */ public class ListCommand extends Command { public static final String COMMAND_WORD = "list"; - public static final String MESSAGE_SUCCESS = "Listed all persons"; + public static final String MESSAGE_SUCCESS = "Listed all items"; @Override - public CommandResult execute(Model model, CommandHistory history) { + public CommandResult execute(Model model, CommandHistory history) throws CommandException { requireNonNull(model); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + model.updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS); return new CommandResult(MESSAGE_SUCCESS); } } diff --git a/src/main/java/seedu/address/logic/commands/LoanListCommand.java b/src/main/java/seedu/address/logic/commands/LoanListCommand.java new file mode 100644 index 000000000000..82410eee6bc2 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/LoanListCommand.java @@ -0,0 +1,102 @@ +package seedu.address.logic.commands; + +import static seedu.address.logic.parser.CliSyntax.PREFIX_LOANER; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_QUANTITY; + +import java.io.File; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; + +import seedu.address.MainApp; +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.item.LoanerDescription; +import seedu.address.storage.XmlAdaptedLoanList; +import seedu.address.storage.XmlAdaptedLoanerDescription; + +//@@author ChewKinWhye + +/** + * Creates a loan list, and updates the status from Ready to On_Loan + */ + +public class LoanListCommand extends Command { + public static final String COMMAND_WORD = "loanList"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Creates a loan list. \n" + + "Parameters: " + + PREFIX_NAME + "NAME " + + PREFIX_QUANTITY + "QUANTITY " + + PREFIX_LOANER + "LOANER\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "Arduino " + + PREFIX_QUANTITY + "20 " + + PREFIX_LOANER + "KinWhye"; + + public static final String MESSAGE_SUCCESS = "Loan list created"; + + private final LoanerDescription loaner; + + public LoanListCommand(LoanerDescription loaner) { + this.loaner = loaner; + } + /** + * Updates the XmlLoanListFile + */ + + public static void updateXmlLoanListFile(XmlAdaptedLoanList xmlAdaptedLoanList, File loanListFile) + throws JAXBException { + JAXBContext jaxbContext = JAXBContext.newInstance(XmlAdaptedLoanList.class); + Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); + jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + jaxbMarshaller.marshal(xmlAdaptedLoanList, System.out); + jaxbMarshaller.marshal(xmlAdaptedLoanList, loanListFile); + } + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + updateStatus(model, history); + try { + updateLoanList(MainApp.getLoanListFile(), loaner); + } catch (JAXBException e) { + System.out.println(e.toString()); + } + + return new CommandResult(MESSAGE_SUCCESS); + } + /** + * Updates the XmlAdaptedLoanList, then updates the XmlLoanListFile + */ + public void updateLoanList(File loanListFile, LoanerDescription loaner) throws JAXBException { + XmlAdaptedLoanerDescription toAdd = new XmlAdaptedLoanerDescription(loaner); + JAXBContext context = JAXBContext.newInstance(XmlAdaptedLoanList.class); + XmlAdaptedLoanList xmlAdaptedLoanList = new XmlAdaptedLoanList(); + if (loanListFile.exists()) { + Unmarshaller unmarshaller = context.createUnmarshaller(); + xmlAdaptedLoanList = (XmlAdaptedLoanList) unmarshaller + .unmarshal(loanListFile); + } + xmlAdaptedLoanList.addLoaner(toAdd); + updateXmlLoanListFile(xmlAdaptedLoanList, loanListFile); + } + /** + * Changes the status from On_Loan to Ready + */ + public void updateStatus(Model model, CommandHistory history) throws CommandException { + ChangeStatusCommand.ChangeStatusDescriptor changeStatusDescriptor = + new ChangeStatusCommand.ChangeStatusDescriptor(loaner.getItemName(), + loaner.getQuantity().toInteger(), "Ready", "On_Loan"); + ChangeStatusCommand changeStatusCommand = new ChangeStatusCommand(changeStatusDescriptor); + changeStatusCommand.execute(model, history); + } +} diff --git a/src/main/java/seedu/address/logic/commands/LoginCommand.java b/src/main/java/seedu/address/logic/commands/LoginCommand.java new file mode 100644 index 000000000000..b47a87fee029 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/LoginCommand.java @@ -0,0 +1,82 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PASSWORD; +import static seedu.address.logic.parser.CliSyntax.PREFIX_USERNAME; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_ACCOUNTS; + +import java.util.List; + +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.account.Account; +import seedu.address.model.account.UsernameMatchPredicate; + +//@@author Davindran +/** + * Logs in account into Stock List. + */ + +public class LoginCommand extends Command { + + public static final String COMMAND_WORD = "login"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Logs in account into Stock List. " + + "Parameters: " + + PREFIX_USERNAME + "USERNAME " + + PREFIX_PASSWORD + "PASSWORD\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_USERNAME + "admin " + + PREFIX_PASSWORD + "admin"; + + public static final String MESSAGE_SUCCESS = "Logged in: %1$s"; + public static final String MESSAGE_FAIL = "Wrong username/password. Try again"; + public static final String MESSAGE_ALREADY_LOGGED_IN = "Already logged in as: %1$s"; + + private final UsernameMatchPredicate predicate; + private final String password; + + public LoginCommand(UsernameMatchPredicate predicate, Account account) { + this.predicate = predicate; + this.password = account.getPassword().toString(); + } + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + if (model.getLoginStatus()) { + throw new CommandException(String.format(MESSAGE_ALREADY_LOGGED_IN, model.getLoggedInUser())); + } + requireNonNull(model); + model.updateFilteredAccountList(predicate); + List matchedAccounts = model.getFilteredAccountList(); + modifyLoginStatus(matchedAccounts, password, model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_FAIL); + } + + model.updateFilteredAccountList(PREDICATE_SHOW_ALL_ACCOUNTS); + + return new CommandResult(String.format(MESSAGE_SUCCESS, model.getLoggedInUser())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof LoginCommand // instanceof handles nulls + && predicate.equals(((LoginCommand) other).predicate)); // state check + } + + /** + * sets logged in user in the model if passwords match + */ + private void modifyLoginStatus (List matchedAccounts, String givenPassword, Model model) { + for (Account account : matchedAccounts) { + if (account.getPassword().toString().equals(givenPassword)) { + model.setLoggedInUser(account.getUsername()); + } + } + } + +} diff --git a/src/main/java/seedu/address/logic/commands/LoginStatusCommand.java b/src/main/java/seedu/address/logic/commands/LoginStatusCommand.java new file mode 100644 index 000000000000..c0cc20e1d9d0 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/LoginStatusCommand.java @@ -0,0 +1,29 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; + +/** + * Logs out account from Stock List. + */ + +public class LoginStatusCommand extends Command { + + public static final String COMMAND_WORD = "loginStatus"; + public static final String MESSAGE_SUCCESS = "Logged in as: %1$s"; + public static final String MESSAGE_NOT_LOGGED_IN = "Not logged in"; + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_NOT_LOGGED_IN); + } + + return new CommandResult(String.format(MESSAGE_SUCCESS, model.getLoggedInUser())); + } +} diff --git a/src/main/java/seedu/address/logic/commands/LogoutCommand.java b/src/main/java/seedu/address/logic/commands/LogoutCommand.java new file mode 100644 index 000000000000..85cdc4295485 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/LogoutCommand.java @@ -0,0 +1,32 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; + +/** + * Logs out account from Stock List. + */ + +public class LogoutCommand extends Command { + + public static final String COMMAND_WORD = "logout"; + public static final String MESSAGE_SUCCESS = "Logged out: %1$s"; + public static final String MESSAGE_ALREADY_LOGGED_OUT = "Already logged out"; + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_ALREADY_LOGGED_OUT); + } + + String loggedOutUser = model.getLoggedInUser(); + model.setLoggedOutStatus(); + return new CommandResult(String.format(MESSAGE_SUCCESS, loggedOutUser)); + } + +} diff --git a/src/main/java/seedu/address/logic/commands/LostCommand.java b/src/main/java/seedu/address/logic/commands/LostCommand.java new file mode 100644 index 000000000000..e09586e3cdd3 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/LostCommand.java @@ -0,0 +1,140 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import static seedu.address.logic.parser.CliSyntax.PREFIX_QUANTITY; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_ITEMS; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.item.Item; +import seedu.address.model.item.Loststatus; +import seedu.address.model.item.Quantity; +import seedu.address.model.item.Status; + + +//@@author HeHaowei + +/** + * Lost an existing item in the stock list. + */ + +public class LostCommand extends Command { + public static final String COMMAND_WORD = "lost"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Lost a item from the stock list identified " + + "by the index number used in the displayed item list" + + "Parameters: INDEX (must be a positive integer) " + + PREFIX_QUANTITY + "QUANTITY\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_QUANTITY + "25"; + + + public static final String MESSAGE_LOST_ITEM_SUCCESS = "Lost Item: %1$s"; + public static final String MESSAGE_INVALID_QUANTITY = "The lost quantity input is invalid"; + public static final String MESSAGE_LOST_LARGER_THAN_READY = "The lost quantity much be larger than " + + "or equal to the quantity of Ready items"; + + private final Index targetIndex; + private final LostDescriptor lostDescriptor; + + public LostCommand(Index targetIndex, LostDescriptor lostDescriptor) { + requireNonNull(targetIndex); + requireNonNull(lostDescriptor); + this.targetIndex = targetIndex; + this.lostDescriptor = new LostDescriptor(lostDescriptor); + + } + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + List lastShownList = model.getFilteredItemList(); + + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_ITEM_DISPLAYED_INDEX); + } + + Item itemToLost = lastShownList.get(targetIndex.getZeroBased()); + Item lostItem = createLostItem(itemToLost, lostDescriptor); + + if (!itemToLost.isSameItem(lostItem) && model.hasItem(lostItem)) { + throw new CommandException(MESSAGE_INVALID_QUANTITY); + } + model.updateItem(itemToLost, lostItem); + model.updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS); + model.commitStockList(); + return new CommandResult(String.format(MESSAGE_LOST_ITEM_SUCCESS, lostItem)); + } + + /** + * Creates and returns a {@code Item} with the details of {@code itemToLost} + * edited with {@code LostDescriptor}. + */ + public Item createLostItem(Item itemToLost, LostDescriptor lostDescriptor) + throws CommandException { + assert itemToLost != null; + Loststatus currentLoststatus = itemToLost.getLoststatus(); + Loststatus updatedLoststatus; + Integer updatedLost = currentLoststatus.getLoststatusLost(); + Integer updatedFound = currentLoststatus.getLoststatusFound(); + + Integer updatedValue = lostDescriptor.getLostQuantity(); + Integer initialValue = itemToLost.getQuantity().toInteger(); + Integer initialReadyValue = itemToLost.getStatus().getStatusReady(); + if (initialReadyValue - updatedValue < 0) { + throw new CommandException(MESSAGE_LOST_LARGER_THAN_READY); + } + updatedLost += updatedValue; + updatedFound -= updatedValue; + updatedLoststatus = new Loststatus(updatedLost, updatedFound); + + Quantity updatedQuantity = new Quantity(Integer.toString(initialValue - updatedValue)); + Status updatedStatus = new Status(initialReadyValue - updatedValue, + itemToLost.getStatus().getStatusOnLoan(), itemToLost.getStatus().getStatusFaulty()); + + + return new Item(itemToLost.getName(), updatedQuantity, + itemToLost.getMinQuantity(), updatedLoststatus, updatedStatus, itemToLost.getTags()); + } + + /** + * Stores the details to lost the item with. + */ + + public static class LostDescriptor { + private Integer lostQuantity; + + public LostDescriptor(){} + + public LostDescriptor(Integer lostQuantity) { + this.lostQuantity = lostQuantity; + } + + public LostDescriptor(LostDescriptor toCopy) { + setLostQuantity(toCopy.lostQuantity); + } + public void setLostQuantity(Integer lostQuantity) { + this.lostQuantity = lostQuantity; } + + public Integer getLostQuantity() { + return lostQuantity; } + + } + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof LostCommand // instanceof handles nulls + && targetIndex.equals(((LostCommand) other).targetIndex)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/LostandFoundCommand.java b/src/main/java/seedu/address/logic/commands/LostandFoundCommand.java new file mode 100644 index 000000000000..17ba5f4f4b35 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/LostandFoundCommand.java @@ -0,0 +1,75 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_ITEMS; + +import java.util.ArrayList; +import java.util.List; + +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.item.Item; +import seedu.address.model.item.Quantity; +import seedu.address.model.item.SimpleItem; + +//@@author HeHaowei + +/** + * Lists all lost items with the lost number in the stock list to the user. + */ +public class LostandFoundCommand extends Command { + + public static final String COMMAND_WORD = "lost&found"; + public static final String MESSAGE_SUCCESS = "Lost items listed"; + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + ArrayList lostItems = new ArrayList<>(); + List lastShownList = model.getFilteredItemList(); + sortSimpleItems(lastShownList, lostItems); + String messageOutput = getMessageOutput(lostItems); + model.updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS); + return new CommandResult(messageOutput); + } + + /** + * Sorts the lost item from the Stock List. + */ + public void sortSimpleItems (List lastShownList, List lostItems + ) { + for (Item item : lastShownList) { + if (item.getLoststatus().getLoststatusLost() > 0) { + lostItems.add(new SimpleItem(item.getName(), + new Quantity(Integer.toString(item.getLoststatus().getLoststatusLost())))); + } + + } + } + String getMessageOutput (List lostItems) { + String messageOutput = ""; + messageOutput += MESSAGE_SUCCESS + "\n"; + + messageOutput += "Lost: "; + int counter = 0; + for (SimpleItem simpleItem : lostItems) { + counter++; + messageOutput += counter + ". " + + + simpleItem.getName() + + + ": " + + simpleItem.getQuantity().toString() + + "\n" + + " "; + + } + + return messageOutput; + } + +} diff --git a/src/main/java/seedu/address/logic/commands/OpenCommand.java b/src/main/java/seedu/address/logic/commands/OpenCommand.java new file mode 100644 index 000000000000..cda3eff118fa --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/OpenCommand.java @@ -0,0 +1,53 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; + +/** + * Lists all items in the stock list to the user. + */ +public class OpenCommand extends Command { + + public static final String COMMAND_WORD = "open"; + + public static final String MESSAGE_SUCCESS = "Opened File"; + + public static final String MESSAGE_INVALID_FILE_NAME = "Invalid file name! \n%1$s"; + + public static final String MESSAGE_EMPTY_FILE_NAME = "No file name inputted! \n%1$s"; + + public static final String MESSAGE_FILE_NOT_EXIST = "No such file in directory! \n%1$s"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Opens specified .xml file version of stock list " + + "\n" + + "Parameters: FILENAME \n" + + "Example: " + COMMAND_WORD + " backup"; + + private final String fileName; + + public OpenCommand(String fileName) { + this.fileName = fileName; + } + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + model.openStockList(fileName); + return new CommandResult(MESSAGE_SUCCESS); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof OpenCommand // instanceof handles nulls + && fileName.equals(((OpenCommand) other).fileName)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/RedoCommand.java b/src/main/java/seedu/address/logic/commands/RedoCommand.java index 227771a4eef6..c76c2c0022d9 100644 --- a/src/main/java/seedu/address/logic/commands/RedoCommand.java +++ b/src/main/java/seedu/address/logic/commands/RedoCommand.java @@ -1,14 +1,14 @@ package seedu.address.logic.commands; import static java.util.Objects.requireNonNull; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_ITEMS; import seedu.address.logic.CommandHistory; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; /** - * Reverts the {@code model}'s address book to its previously undone state. + * Reverts the {@code model}'s stock list to its previously undone state. */ public class RedoCommand extends Command { @@ -20,12 +20,16 @@ public class RedoCommand extends Command { public CommandResult execute(Model model, CommandHistory history) throws CommandException { requireNonNull(model); - if (!model.canRedoAddressBook()) { + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + if (!model.canRedoStockList()) { throw new CommandException(MESSAGE_FAILURE); } - model.redoAddressBook(); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + model.redoStockList(); + model.updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS); return new CommandResult(MESSAGE_SUCCESS); } } diff --git a/src/main/java/seedu/address/logic/commands/ResetAccountsCommand.java b/src/main/java/seedu/address/logic/commands/ResetAccountsCommand.java new file mode 100644 index 000000000000..0e513d12f689 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ResetAccountsCommand.java @@ -0,0 +1,40 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.AccountList; +import seedu.address.model.Model; +import seedu.address.model.account.Account; +import seedu.address.model.account.Password; +import seedu.address.model.account.Username; + +/** + * Resets the account list, to one account with "admin" as both username and password. + * User is logged out as in model. + */ +public class ResetAccountsCommand extends Command { + + public static final String COMMAND_WORD = "resetAccounts"; + public static final String MESSAGE_SUCCESS = "Account database has been reset and user logged out"; + + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + Username adminName = new Username("admin"); + Password adminPass = new Password("admin"); + Account admin = new Account(adminName, adminPass); + + model.setLoggedOutStatus(); + model.resetAccountData(new AccountList()); + model.addAccount(admin); + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/address/logic/commands/SaveCommand.java b/src/main/java/seedu/address/logic/commands/SaveCommand.java new file mode 100644 index 000000000000..cebe413a33d7 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/SaveCommand.java @@ -0,0 +1,51 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; + +/** + * Lists all items in the stock list to the user. + */ +public class SaveCommand extends Command { + + public static final String COMMAND_WORD = "save"; + + public static final String MESSAGE_SUCCESS = "Saved Stock List"; + + public static final String MESSAGE_INVALID_FILE_NAME = "Invalid file name! \n%1$s"; + + public static final String MESSAGE_EMPTY_FILE_NAME = "No file name inputted! \n%1$s"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Saves current version of stock list " + + "as an xml file with the specified file name.\n" + + "Parameters: FILENAME \n" + + "Example: " + COMMAND_WORD + " backup"; + + private final String fileName; + + public SaveCommand(String fileName) { + this.fileName = fileName; + } + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + model.saveStockList(fileName); + return new CommandResult(MESSAGE_SUCCESS); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof SaveCommand // instanceof handles nulls + && fileName.equals(((SaveCommand) other).fileName)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/SelectCommand.java b/src/main/java/seedu/address/logic/commands/SelectCommand.java index f5e8c1a8722e..6d4ce569130a 100644 --- a/src/main/java/seedu/address/logic/commands/SelectCommand.java +++ b/src/main/java/seedu/address/logic/commands/SelectCommand.java @@ -11,21 +11,22 @@ import seedu.address.logic.CommandHistory; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import seedu.address.model.person.Person; +import seedu.address.model.item.Item; /** - * Selects a person identified using it's displayed index from the address book. + * Selects a item identified using it's displayed index from the stock list. */ public class SelectCommand extends Command { public static final String COMMAND_WORD = "select"; public static final String MESSAGE_USAGE = COMMAND_WORD - + ": Selects the person identified by the index number used in the displayed person list.\n" + + ": Selects the item identified by the index number used in the displayed item list.\n" + "Parameters: INDEX (must be a positive integer)\n" + "Example: " + COMMAND_WORD + " 1"; - public static final String MESSAGE_SELECT_PERSON_SUCCESS = "Selected Person: %1$s"; + public static final String MESSAGE_SELECT_ITEM_SUCCESS = "Selected Item: %1$s"; + private final Index targetIndex; @@ -37,14 +38,19 @@ public SelectCommand(Index targetIndex) { public CommandResult execute(Model model, CommandHistory history) throws CommandException { requireNonNull(model); - List filteredPersonList = model.getFilteredPersonList(); + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + List filteredItemList = model.getFilteredItemList(); + + if (targetIndex.getZeroBased() >= filteredItemList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_ITEM_DISPLAYED_INDEX); - if (targetIndex.getZeroBased() >= filteredPersonList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); } EventsCenter.getInstance().post(new JumpToListRequestEvent(targetIndex)); - return new CommandResult(String.format(MESSAGE_SELECT_PERSON_SUCCESS, targetIndex.getOneBased())); + return new CommandResult(String.format(MESSAGE_SELECT_ITEM_SUCCESS, targetIndex.getOneBased())); } diff --git a/src/main/java/seedu/address/logic/commands/StatusCommand.java b/src/main/java/seedu/address/logic/commands/StatusCommand.java new file mode 100644 index 000000000000..5bec9f256175 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/StatusCommand.java @@ -0,0 +1,108 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_ITEMS; + +import java.util.ArrayList; +import java.util.List; + +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.item.Item; +import seedu.address.model.item.Quantity; +import seedu.address.model.item.SimpleItem; + +//@@author ChewKinWhye + +/** + * Lists all items in the stock list to the user grouped by the status. + */ +public class StatusCommand extends Command { + + public static final String COMMAND_WORD = "status"; + public static final String MESSAGE_SUCCESS = "Items listed according to status"; + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + ArrayList readyItems = new ArrayList<>(); + ArrayList onLoanItems = new ArrayList<>(); + ArrayList faultyItems = new ArrayList<>(); + + List lastShownList = model.getFilteredItemList(); + + sortSimpleItems(lastShownList, readyItems, onLoanItems, faultyItems); + + String messageOutput = getMessageOutput(readyItems, onLoanItems, faultyItems); + + model.updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS); + + return new CommandResult(messageOutput); + } + + /** + * Sorts the item in the item list into Ready, On_Loan, and Faulty. + */ + void sortSimpleItems (List lastShownList, ArrayList readyItems, + ArrayList onLoanItems, ArrayList faultyItems) { + for (Item item : lastShownList) { + if (item.getStatus().getStatusReady() > 0) { + readyItems.add(new SimpleItem(item.getName(), + new Quantity(Integer.toString(item.getStatus().getStatusReady())))); + } + if (item.getStatus().getStatusOnLoan() > 0) { + onLoanItems.add(new SimpleItem(item.getName(), + new Quantity(Integer.toString(item.getStatus().getStatusOnLoan())))); + } + if (item.getStatus().getStatusFaulty() > 0) { + faultyItems.add(new SimpleItem(item.getName(), + new Quantity(Integer.toString(item.getStatus().getStatusFaulty())))); + } + } + } + + String getMessageOutput (ArrayList readyItems, + ArrayList onLoanItems, ArrayList faultyItems) { + String messageOutput = ""; + messageOutput += MESSAGE_SUCCESS + "\n"; + + messageOutput += "Ready: "; + int counter = 0; + for (SimpleItem simpleItem : readyItems) { + counter++; + messageOutput += simpleItem.getName() + ": " + simpleItem.getQuantity().toString(); + if (counter != readyItems.size()) { + messageOutput += ", "; + } + } + + messageOutput += "\nOn Loan: "; + counter = 0; + for (SimpleItem simpleItem : onLoanItems) { + counter++; + messageOutput += simpleItem.getName() + ": " + simpleItem.getQuantity().toString(); + if (counter != onLoanItems.size()) { + messageOutput += ", "; + } + } + + + messageOutput += "\nFaulty: "; + counter = 0; + for (SimpleItem simpleItem : faultyItems) { + counter++; + messageOutput += simpleItem.getName() + ": " + simpleItem.getQuantity().toString(); + if (counter != faultyItems.size()) { + messageOutput += ", "; + } + } + + return messageOutput; + } + +} diff --git a/src/main/java/seedu/address/logic/commands/TagCommand.java b/src/main/java/seedu/address/logic/commands/TagCommand.java new file mode 100644 index 000000000000..f0ad9a7f8a8e --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/TagCommand.java @@ -0,0 +1,45 @@ +//@@author gaoqikai +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.item.TagContainsKeywordsPredicate; + +/** + * Find and show items under specific tags + * + */ +public class TagCommand extends Command { + public static final String COMMAND_WORD = "tag"; + + public static final String MESSAGE_SUCCESS = "Listed all items for your tag(s)."; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": List all items whose tag matches " + + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" + + "Parameters: KEYWORD [MORE_KEYWORDS]..." + + " Example: " + COMMAND_WORD + " Lab1"; + + private final TagContainsKeywordsPredicate predicate; + + public TagCommand(TagContainsKeywordsPredicate predicate) { + this.predicate = predicate; + } + + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + requireNonNull(model); + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + model.updateFilteredItemListByTag(predicate); + return new CommandResult( + String.format(Messages.MESSAGE_ITEMS_LISTED_OVERVIEW, model.getFilteredItemList().size())); + } +} diff --git a/src/main/java/seedu/address/logic/commands/UndoCommand.java b/src/main/java/seedu/address/logic/commands/UndoCommand.java index 40441264f346..d456a799d23f 100644 --- a/src/main/java/seedu/address/logic/commands/UndoCommand.java +++ b/src/main/java/seedu/address/logic/commands/UndoCommand.java @@ -1,14 +1,14 @@ package seedu.address.logic.commands; import static java.util.Objects.requireNonNull; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_ITEMS; import seedu.address.logic.CommandHistory; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; /** - * Reverts the {@code model}'s address book to its previous state. + * Reverts the {@code model}'s stock list to its previous state. */ public class UndoCommand extends Command { @@ -20,12 +20,16 @@ public class UndoCommand extends Command { public CommandResult execute(Model model, CommandHistory history) throws CommandException { requireNonNull(model); - if (!model.canUndoAddressBook()) { + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + if (!model.canUndoStockList()) { throw new CommandException(MESSAGE_FAILURE); } - model.undoAddressBook(); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + model.undoStockList(); + model.updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS); return new CommandResult(MESSAGE_SUCCESS); } } diff --git a/src/main/java/seedu/address/logic/commands/ViewLoanListCommand.java b/src/main/java/seedu/address/logic/commands/ViewLoanListCommand.java new file mode 100644 index 000000000000..8134cffd1faf --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ViewLoanListCommand.java @@ -0,0 +1,66 @@ +package seedu.address.logic.commands; + +import java.io.File; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; + +import seedu.address.MainApp; +import seedu.address.logic.CommandHistory; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.storage.XmlAdaptedLoanList; +import seedu.address.storage.XmlAdaptedLoanerDescription; + +//@@author ChewKinWhye + +/** + * Lists all the entries in the loan list to the user. + */ + +public class ViewLoanListCommand extends Command { + public static final String COMMAND_WORD = "viewLoanList"; + public static final String MESSAGE_SUCCESS = "Loan list displayed"; + public static final String MESSAGE_EMPTY = "Loan list is currently empty"; + + @Override + public CommandResult execute(Model model, CommandHistory history) throws CommandException { + + if (!model.getLoginStatus()) { + throw new CommandException(MESSAGE_LOGIN); + } + + if (!MainApp.getLoanListFile().exists()) { + throw new CommandException(MESSAGE_EMPTY); + } + String messageOutput = getMessageOutput(MainApp.getLoanListFile()); + return new CommandResult(messageOutput); + } + + public String getMessageOutput(File loanListFile) throws CommandException { + try { + Integer counter = 1; + String messageOutput = new String(); + messageOutput += MESSAGE_SUCCESS + "\n"; + JAXBContext context = JAXBContext.newInstance(XmlAdaptedLoanList.class); + Unmarshaller unmarshaller = context.createUnmarshaller(); + XmlAdaptedLoanList xmlAdaptedLoanList = (XmlAdaptedLoanList) unmarshaller + .unmarshal(loanListFile); + if (xmlAdaptedLoanList.getLoanList().size() == 0) { + throw new CommandException(MESSAGE_EMPTY); + } + for (XmlAdaptedLoanerDescription loanerDescription : xmlAdaptedLoanList.getLoanList()) { + messageOutput += counter + ". "; + messageOutput += loanerDescription.getLoanerName() + ": "; + messageOutput += loanerDescription.getItemName() + " "; + messageOutput += loanerDescription.getQuantity() + "\n"; + counter++; + } + return messageOutput; + } catch (JAXBException e) { + System.out.println(e.toString()); + } + return null; + } +} diff --git a/src/main/java/seedu/address/logic/parser/AddAccountCommandParser.java b/src/main/java/seedu/address/logic/parser/AddAccountCommandParser.java new file mode 100644 index 000000000000..80f250778b21 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/AddAccountCommandParser.java @@ -0,0 +1,50 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PASSWORD; +import static seedu.address.logic.parser.CliSyntax.PREFIX_USERNAME; + +import java.util.stream.Stream; + +import seedu.address.logic.commands.AddAccountCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.account.Account; +import seedu.address.model.account.Password; +import seedu.address.model.account.Username; + +/** + * Parses input arguments and creates a new AddAccountCommand object + */ +public class AddAccountCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AddAccountCommand + * and returns an AddAccountCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddAccountCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_USERNAME, PREFIX_PASSWORD); + + if (!arePrefixesPresent(argMultimap, PREFIX_USERNAME, PREFIX_PASSWORD) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddAccountCommand.MESSAGE_USAGE)); + } + + Username username = ParserUtil.parseUsername(argMultimap.getValue(PREFIX_USERNAME).get()); + Password password = ParserUtil.parsePassword(argMultimap.getValue(PREFIX_PASSWORD).get()); + + Account account = new Account(username, password); + + return new AddAccountCommand(account); + } + + /** + * 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()); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java index 3b8bfa035e83..7468f94f8b24 100644 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/AddCommandParser.java @@ -1,10 +1,9 @@ package seedu.address.logic.parser; import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MIN_QUANTITY; import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_QUANTITY; import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; import java.util.Set; @@ -12,11 +11,9 @@ import seedu.address.logic.commands.AddCommand; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; +import seedu.address.model.item.Item; +import seedu.address.model.item.Name; +import seedu.address.model.item.Quantity; import seedu.address.model.tag.Tag; /** @@ -31,22 +28,21 @@ public class AddCommandParser implements Parser { */ public AddCommand parse(String args) throws ParseException { ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_QUANTITY, PREFIX_MIN_QUANTITY, PREFIX_TAG); - if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL) + if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_QUANTITY, PREFIX_MIN_QUANTITY) || !argMultimap.getPreamble().isEmpty()) { throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); } Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); - Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()); - Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()); - Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()); + Quantity quantity = ParserUtil.parseQuantity(argMultimap.getValue(PREFIX_QUANTITY).get()); + Quantity minQuantity = ParserUtil.parseMinQuantity(argMultimap.getValue(PREFIX_MIN_QUANTITY).get()); Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); - Person person = new Person(name, phone, email, address, tagList); + Item item = new Item(name, quantity, minQuantity, tagList); - return new AddCommand(person); + return new AddCommand(item); } /** diff --git a/src/main/java/seedu/address/logic/parser/AddTagCommandParser.java b/src/main/java/seedu/address/logic/parser/AddTagCommandParser.java new file mode 100644 index 000000000000..003f164ab61b --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/AddTagCommandParser.java @@ -0,0 +1,68 @@ +//@@author gaoqikai +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.AddTagCommand; +import seedu.address.logic.commands.AddTagCommand.AddTagDescriptor; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.tag.Tag; + +/** + * Parses input arguments and creates a new AddTagCommand object + */ +public class AddTagCommandParser implements Parser { + + /** + * Parsers the given (@code String) of arguments in the context of the AddTagCommand and + * returns an AddTagCommand object for execution. + * @Throws ParseException if the user input does not conform the expected format + */ + public AddTagCommand parse(String args) throws ParseException { + requireNonNull(args); + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_TAG); + + Index index; + + AddTagDescriptor addTagDescriptor = new AddTagCommand.AddTagDescriptor(); + + parseTagsToAdd(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(addTagDescriptor::setTags); + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTagCommand.MESSAGE_USAGE), pe); + } + + if (!addTagDescriptor.haveTag()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTagCommand.MESSAGE_NO_TAG)); + } + + return new AddTagCommand(index, addTagDescriptor); + } + + /** + * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty. + * If {@code tags} contain only one element which is an empty string, it will be parsed into a + * {@code Set} containing zero tags. + */ + private Optional> parseTagsToAdd(Collection tags) throws ParseException { + assert tags != null; + + if (tags.isEmpty()) { + return Optional.empty(); + } + Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; + return Optional.of(ParserUtil.parseTags(tagSet)); + } +} diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java deleted file mode 100644 index b7d57f5db86a..000000000000 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ /dev/null @@ -1,92 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.ClearCommand; -import seedu.address.logic.commands.Command; -import seedu.address.logic.commands.DeleteCommand; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.ExitCommand; -import seedu.address.logic.commands.FindCommand; -import seedu.address.logic.commands.HelpCommand; -import seedu.address.logic.commands.HistoryCommand; -import seedu.address.logic.commands.ListCommand; -import seedu.address.logic.commands.RedoCommand; -import seedu.address.logic.commands.SelectCommand; -import seedu.address.logic.commands.UndoCommand; -import seedu.address.logic.parser.exceptions.ParseException; - -/** - * Parses user input. - */ -public class AddressBookParser { - - /** - * Used for initial separation of command word and args. - */ - private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); - - /** - * Parses user input into command for execution. - * - * @param userInput full user input string - * @return the command based on the user input - * @throws ParseException if the user input does not conform the expected format - */ - public Command parseCommand(String userInput) throws ParseException { - final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); - if (!matcher.matches()) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); - } - - final String commandWord = matcher.group("commandWord"); - final String arguments = matcher.group("arguments"); - switch (commandWord) { - - case AddCommand.COMMAND_WORD: - return new AddCommandParser().parse(arguments); - - case EditCommand.COMMAND_WORD: - return new EditCommandParser().parse(arguments); - - case SelectCommand.COMMAND_WORD: - return new SelectCommandParser().parse(arguments); - - case DeleteCommand.COMMAND_WORD: - return new DeleteCommandParser().parse(arguments); - - case ClearCommand.COMMAND_WORD: - return new ClearCommand(); - - case FindCommand.COMMAND_WORD: - return new FindCommandParser().parse(arguments); - - case ListCommand.COMMAND_WORD: - return new ListCommand(); - - case HistoryCommand.COMMAND_WORD: - return new HistoryCommand(); - - case ExitCommand.COMMAND_WORD: - return new ExitCommand(); - - case HelpCommand.COMMAND_WORD: - return new HelpCommand(); - - case UndoCommand.COMMAND_WORD: - return new UndoCommand(); - - case RedoCommand.COMMAND_WORD: - return new RedoCommand(); - - default: - throw new ParseException(MESSAGE_UNKNOWN_COMMAND); - } - } - -} diff --git a/src/main/java/seedu/address/logic/parser/ChangeStatusCommandParser.java b/src/main/java/seedu/address/logic/parser/ChangeStatusCommandParser.java new file mode 100644 index 000000000000..aebda6ead5ab --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/ChangeStatusCommandParser.java @@ -0,0 +1,59 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NEW_STATUS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ORIGINAL_STATUS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_QUANTITY; + +import java.util.stream.Stream; + +import seedu.address.logic.commands.ChangeStatusCommand; +import seedu.address.logic.commands.ChangeStatusCommand.ChangeStatusDescriptor; +import seedu.address.logic.parser.exceptions.ParseException; + +//@@author ChewKinWhye + +/** + * Parses input arguments and creates a new ChangeStatusCommand object + */ +public class ChangeStatusCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the ChangeStatusCommand + * and returns an ChangeStatusCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + + public ChangeStatusCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, + PREFIX_QUANTITY, PREFIX_ORIGINAL_STATUS, PREFIX_NEW_STATUS); + + if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_QUANTITY, PREFIX_ORIGINAL_STATUS, PREFIX_NEW_STATUS) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ChangeStatusCommand.MESSAGE_USAGE)); + } + String initialStatus = ParserUtil + .parseStatus(argMultimap.getValue(PREFIX_ORIGINAL_STATUS).get()); + String updatedStatus = ParserUtil + .parseStatus(argMultimap.getValue(PREFIX_NEW_STATUS).get()); + if (initialStatus.equals("On_Loan") || updatedStatus.equals("On_Loan")) { + throw new ParseException(String.format(ChangeStatusCommand.MESSAGE_INVALID_STATUS_FIELD)); + } + ChangeStatusDescriptor changeStatusDescriptor = new ChangeStatusDescriptor(); + changeStatusDescriptor.setName(ParserUtil + .parseName(argMultimap.getValue(PREFIX_NAME).get())); + changeStatusDescriptor.setQuantity(ParserUtil + .parseQuantity(argMultimap.getValue(PREFIX_QUANTITY).get()).toInteger()); + changeStatusDescriptor.setInitialStatus(initialStatus); + changeStatusDescriptor.setUpdatedStatus(updatedStatus); + + return new ChangeStatusCommand(changeStatusDescriptor); + } + + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } +} diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java index 75b1a9bf1190..0de8f807d36c 100644 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java @@ -7,9 +7,13 @@ public class CliSyntax { /* Prefix definitions */ public static final Prefix PREFIX_NAME = new Prefix("n/"); - public static final Prefix PREFIX_PHONE = new Prefix("p/"); - public static final Prefix PREFIX_EMAIL = new Prefix("e/"); - public static final Prefix PREFIX_ADDRESS = new Prefix("a/"); + public static final Prefix PREFIX_QUANTITY = new Prefix("q/"); + public static final Prefix PREFIX_MIN_QUANTITY = new Prefix("m/"); + public static final Prefix PREFIX_ORIGINAL_STATUS = new Prefix("os/"); + public static final Prefix PREFIX_NEW_STATUS = new Prefix("ns/"); + public static final Prefix PREFIX_LOANER = new Prefix("l/"); public static final Prefix PREFIX_TAG = new Prefix("t/"); + public static final Prefix PREFIX_PASSWORD = new Prefix("p/"); + public static final Prefix PREFIX_USERNAME = new Prefix("u/"); } diff --git a/src/main/java/seedu/address/logic/parser/DeleteAccountCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteAccountCommandParser.java new file mode 100644 index 000000000000..44863557f904 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/DeleteAccountCommandParser.java @@ -0,0 +1,29 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.DeleteAccountCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new DeleteCommand object + */ +public class DeleteAccountCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the DeleteAccountCommand + * and returns an DeleteAccountCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public DeleteAccountCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new DeleteAccountCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteAccountCommand.MESSAGE_USAGE), pe); + } + } + +} diff --git a/src/main/java/seedu/address/logic/parser/DeleteLoanListCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteLoanListCommandParser.java new file mode 100644 index 000000000000..cb20fc394809 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/DeleteLoanListCommandParser.java @@ -0,0 +1,30 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.DeleteLoanListCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +//@@author ChewKinWhye + +/** + * Parses input arguments and creates a new DeleteLoanListCommand object + */ +public class DeleteLoanListCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the DeleteLoanListCommand + * and returns an DeleteLoanListCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + + public DeleteLoanListCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new DeleteLoanListCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteLoanListCommand.MESSAGE_USAGE), pe); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/DeleteTagCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteTagCommandParser.java new file mode 100644 index 000000000000..11bfdeaa92a5 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/DeleteTagCommandParser.java @@ -0,0 +1,67 @@ +//@@author gaoqikai +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.DeleteTagCommand; +import seedu.address.logic.commands.DeleteTagCommand.DeleteTagDescriptor; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.tag.Tag; + +/** + * Parses input arguments and creates a new DeleteTagCommand object + */ +public class DeleteTagCommandParser implements Parser { + + /** + * Parsers the given (@code String) of arguments in the context of the DeleteTagCommand and + * returns an DeleteTagCommand object for execution. + * @Throws ParseException if the user input does not conform the expected format + */ + public DeleteTagCommand parse(String args) throws ParseException { + requireNonNull(args); + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_TAG); + + Index index; + + DeleteTagDescriptor deleteTagDescriptor = new DeleteTagCommand.DeleteTagDescriptor(); + + parseTagsToAdd(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(deleteTagDescriptor::setTags); + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteTagCommand.MESSAGE_USAGE), pe); + } + + if (!deleteTagDescriptor.haveTag()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteTagCommand.MESSAGE_NO_TAG)); + } + + return new DeleteTagCommand(index, deleteTagDescriptor); + } + /** + * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty. + * If {@code tags} contain only one element which is an empty string, it will be parsed into a + * {@code Set} containing zero tags. + */ + private Optional> parseTagsToAdd(Collection tags) throws ParseException { + assert tags != null; + + if (tags.isEmpty()) { + return Optional.empty(); + } + Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; + return Optional.of(ParserUtil.parseTags(tagSet)); + } +} diff --git a/src/main/java/seedu/address/logic/parser/EditAccountCommandParser.java b/src/main/java/seedu/address/logic/parser/EditAccountCommandParser.java new file mode 100644 index 000000000000..2dd7975f5a60 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/EditAccountCommandParser.java @@ -0,0 +1,52 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PASSWORD; +import static seedu.address.logic.parser.CliSyntax.PREFIX_USERNAME; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.EditAccountCommand; +import seedu.address.logic.commands.EditAccountCommand.EditAccountDescriptor; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new EditCommand object + */ +public class EditAccountCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EditAccountCommand + * and returns an EditAccountCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EditAccountCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_USERNAME, PREFIX_PASSWORD); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditAccountCommand.MESSAGE_USAGE), + pe); + } + + EditAccountDescriptor editAccountDescriptor = new EditAccountDescriptor(); + if (argMultimap.getValue(PREFIX_USERNAME).isPresent()) { + editAccountDescriptor.setUsername(ParserUtil.parseUsername(argMultimap.getValue(PREFIX_USERNAME).get())); + } + if (argMultimap.getValue(PREFIX_PASSWORD).isPresent()) { + editAccountDescriptor.setPassword(ParserUtil.parsePassword(argMultimap.getValue(PREFIX_PASSWORD).get())); + } + + if (!editAccountDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditAccountCommand.MESSAGE_NOT_EDITED); + } + + return new EditAccountCommand(index, editAccountDescriptor); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java index 845644b7dea1..7726c219eb46 100644 --- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/EditCommandParser.java @@ -2,10 +2,9 @@ import static java.util.Objects.requireNonNull; import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_MIN_QUANTITY; import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_QUANTITY; import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; import java.util.Collection; @@ -15,7 +14,7 @@ import seedu.address.commons.core.index.Index; import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; +import seedu.address.logic.commands.EditCommand.EditItemDescriptor; import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.tag.Tag; @@ -32,7 +31,7 @@ public class EditCommandParser implements Parser { public EditCommand parse(String args) throws ParseException { requireNonNull(args); ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_QUANTITY, PREFIX_MIN_QUANTITY, PREFIX_TAG); Index index; @@ -42,26 +41,25 @@ public EditCommand parse(String args) throws ParseException { throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe); } - EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); + EditItemDescriptor editItemDescriptor = new EditItemDescriptor(); if (argMultimap.getValue(PREFIX_NAME).isPresent()) { - editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); + editItemDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); } - if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { - editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get())); + if (argMultimap.getValue(PREFIX_QUANTITY).isPresent()) { + editItemDescriptor.setQuantity(ParserUtil.parseQuantity(argMultimap.getValue(PREFIX_QUANTITY).get())); } - if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) { - editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get())); + if (argMultimap.getValue(PREFIX_MIN_QUANTITY).isPresent()) { + editItemDescriptor.setMinQuantity( + ParserUtil.parseMinQuantity(argMultimap.getValue(PREFIX_MIN_QUANTITY).get()) + ); } - if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) { - editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get())); - } - parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags); + parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editItemDescriptor::setTags); - if (!editPersonDescriptor.isAnyFieldEdited()) { + if (!editItemDescriptor.isAnyFieldEdited()) { throw new ParseException(EditCommand.MESSAGE_NOT_EDITED); } - return new EditCommand(index, editPersonDescriptor); + return new EditCommand(index, editItemDescriptor); } /** diff --git a/src/main/java/seedu/address/logic/parser/FindAccountCommandParser.java b/src/main/java/seedu/address/logic/parser/FindAccountCommandParser.java new file mode 100644 index 000000000000..78415619853f --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/FindAccountCommandParser.java @@ -0,0 +1,33 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.Arrays; + +import seedu.address.logic.commands.FindAccountCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.account.UsernameContainsKeywordsPredicate; + +/** + * Parses input arguments and creates a new FindCommand object + */ +public class FindAccountCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FindAccountCommand + * and returns an FindAccountCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public FindAccountCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindAccountCommand.MESSAGE_USAGE)); + } + + String[] nameKeywords = trimmedArgs.split("\\s+"); + + return new FindAccountCommand(new UsernameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java index b186a967cb94..99a76e570915 100644 --- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/FindCommandParser.java @@ -6,7 +6,7 @@ import seedu.address.logic.commands.FindCommand; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.model.item.NameContainsKeywordsPredicate; /** * Parses input arguments and creates a new FindCommand object diff --git a/src/main/java/seedu/address/logic/parser/FoundCommandParser.java b/src/main/java/seedu/address/logic/parser/FoundCommandParser.java new file mode 100644 index 000000000000..80b4e8141512 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/FoundCommandParser.java @@ -0,0 +1,45 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import static seedu.address.logic.parser.CliSyntax.PREFIX_QUANTITY; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.FoundCommand; +import seedu.address.logic.commands.FoundCommand.FoundDescriptor; + +import seedu.address.logic.parser.exceptions.ParseException; + +//@@author HeHaowei +/** + * Parses input arguments and creates a new FoundCommand object + */ +public class FoundCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FoundCommand + * and returns an FoundCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public FoundCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_QUANTITY); + + Index targetIndex; + try { + targetIndex = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FoundCommand.MESSAGE_USAGE), pe); + } + FoundDescriptor foundDescriptor = new FoundDescriptor(); + + foundDescriptor.setFoundQuantity(ParserUtil + .parseQuantity(argMultimap.getValue(PREFIX_QUANTITY).get()).toInteger()); + + return new FoundCommand(targetIndex, foundDescriptor); + } + +} + diff --git a/src/main/java/seedu/address/logic/parser/LoanListCommandParser.java b/src/main/java/seedu/address/logic/parser/LoanListCommandParser.java new file mode 100644 index 000000000000..189d97b81d99 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/LoanListCommandParser.java @@ -0,0 +1,47 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_LOANER; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_QUANTITY; + +import java.util.stream.Stream; + +import seedu.address.logic.commands.LoanListCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.item.LoanerDescription; +import seedu.address.model.item.Name; +import seedu.address.model.item.Quantity; + +//@@author ChewKinWhye + +/** + * Parses input arguments and creates a new LoanListCommand object + */ +public class LoanListCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the LoanListCommand + * and returns an LoanListCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + + public LoanListCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_QUANTITY, PREFIX_LOANER); + + if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_QUANTITY, PREFIX_LOANER) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, LoanListCommand.MESSAGE_USAGE)); + } + Name itemName = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); + Name loanerName = ParserUtil.parseName(argMultimap.getValue(PREFIX_LOANER).get()); + Quantity quantity = ParserUtil.parseQuantity(argMultimap.getValue(PREFIX_QUANTITY).get()); + return new LoanListCommand(new LoanerDescription(itemName, loanerName, quantity)); + } + + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } +} diff --git a/src/main/java/seedu/address/logic/parser/LoginCommandParser.java b/src/main/java/seedu/address/logic/parser/LoginCommandParser.java new file mode 100644 index 000000000000..43d7c36521a2 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/LoginCommandParser.java @@ -0,0 +1,54 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PASSWORD; +import static seedu.address.logic.parser.CliSyntax.PREFIX_USERNAME; + +import java.util.stream.Stream; + +import seedu.address.logic.commands.LoginCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.account.Account; +import seedu.address.model.account.Password; +import seedu.address.model.account.Username; +import seedu.address.model.account.UsernameMatchPredicate; + +//@@author Davindran +/** + * Parses input arguments and creates a new LoginCommand object + */ +public class LoginCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the LoginCommand + * and returns an LoginCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public LoginCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_USERNAME, PREFIX_PASSWORD); + + if (!arePrefixesPresent(argMultimap, PREFIX_USERNAME, PREFIX_PASSWORD) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, LoginCommand.MESSAGE_USAGE)); + } + + Username username = ParserUtil.parseUsername(argMultimap.getValue(PREFIX_USERNAME).get()); + Password password = ParserUtil.parsePassword(argMultimap.getValue(PREFIX_PASSWORD).get()); + Account account = new Account(username, password); + + return new LoginCommand(new UsernameMatchPredicate(username.toString()), account); + + + + //return new LoginCommand(account); + } + + /** + * 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()); + } +} diff --git a/src/main/java/seedu/address/logic/parser/LostCommandParser.java b/src/main/java/seedu/address/logic/parser/LostCommandParser.java new file mode 100644 index 000000000000..81f61acf2187 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/LostCommandParser.java @@ -0,0 +1,43 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import static seedu.address.logic.parser.CliSyntax.PREFIX_QUANTITY; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.LostCommand; +import seedu.address.logic.commands.LostCommand.LostDescriptor; + +import seedu.address.logic.parser.exceptions.ParseException; + +//@@author HeHaowei +/** + * Parses input arguments and creates a new LostCommand object + */ +public class LostCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the LostCommand + * and returns an LostCommand object for execution. + * + * @throws ParseException if the user input does not conform the expected format + */ + public LostCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_QUANTITY); + + Index targetIndex; + try { + targetIndex = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, LostCommand.MESSAGE_USAGE), pe); + } + LostDescriptor lostDescriptor = new LostDescriptor(); + lostDescriptor.setLostQuantity(ParserUtil + .parseQuantity(argMultimap.getValue(PREFIX_QUANTITY).get()).toInteger()); + return new LostCommand(targetIndex, lostDescriptor); + } +} + diff --git a/src/main/java/seedu/address/logic/parser/OpenCommandParser.java b/src/main/java/seedu/address/logic/parser/OpenCommandParser.java new file mode 100644 index 000000000000..8ac3c95d495b --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/OpenCommandParser.java @@ -0,0 +1,47 @@ +package seedu.address.logic.parser; + +import java.io.File; + +import seedu.address.logic.commands.OpenCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new OpenCommand object + */ +public class OpenCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the SelectCommand + * and returns an SelectCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public OpenCommand parse(String args) throws ParseException { + try { + String fileName = ParserUtil.parseFileName(args); + fileName = fileName + ".xml"; + if ((".xml").equals(fileName)) { + throw new ParseException( + String.format(OpenCommand.MESSAGE_EMPTY_FILE_NAME, OpenCommand.MESSAGE_USAGE) + ); + } else if (!isValidFile(fileName)) { + throw new ParseException( + String.format(OpenCommand.MESSAGE_FILE_NOT_EXIST, OpenCommand.MESSAGE_USAGE) + ); + } + return new OpenCommand(fileName); + } catch (ParseException pe) { + throw new ParseException( + String.format(OpenCommand.MESSAGE_INVALID_FILE_NAME, OpenCommand.MESSAGE_USAGE), pe); + } + } + + /** + * Checks if file with given {@code fileName} exists in versions folder + * @param fileName + * @return + */ + public boolean isValidFile(String fileName) { + File tempFile = new File("versions/" + fileName); + return tempFile.isFile(); + } +} diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java index 76daf40807e2..dc22723cb83f 100644 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java @@ -9,18 +9,23 @@ import seedu.address.commons.core.index.Index; import seedu.address.commons.util.StringUtil; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; +import seedu.address.model.account.Password; +import seedu.address.model.account.Username; +import seedu.address.model.item.Name; +import seedu.address.model.item.Quantity; import seedu.address.model.tag.Tag; + /** * Contains utility methods used for parsing strings in the various *Parser classes. */ public class ParserUtil { public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer."; + public static final String MESSAGE_INVALID_QUANTITY = + "Quantities should only contain non-zero unsigned integers, and it should not be blank"; + public static final String MESSAGE_INVALID_MIN_QUANTITY = + "Minimum Quantities should only contain non-zero unsigned integers, and it should not be blank"; /** * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be @@ -51,50 +56,46 @@ public static Name parseName(String name) throws ParseException { } /** - * Parses a {@code String phone} into a {@code Phone}. + * + * Parses a {@code String quantity} into an {@code Integer quantity} * Leading and trailing whitespaces will be trimmed. * - * @throws ParseException if the given {@code phone} is invalid. + * @throws ParseException */ - public static Phone parsePhone(String phone) throws ParseException { - requireNonNull(phone); - String trimmedPhone = phone.trim(); - if (!Phone.isValidPhone(trimmedPhone)) { - throw new ParseException(Phone.MESSAGE_PHONE_CONSTRAINTS); + public static Quantity parseQuantity(String quantity) throws ParseException { + requireNonNull(quantity); + String trimmedQuantity = quantity.trim(); + if (!Quantity.isValidQuantity(trimmedQuantity)) { + throw new ParseException(MESSAGE_INVALID_QUANTITY); } - return new Phone(trimmedPhone); + return new Quantity(trimmedQuantity); } /** - * Parses a {@code String address} into an {@code Address}. + * Parses a {@code String minQuantity} into an {@code Integer minQuantity}. * Leading and trailing whitespaces will be trimmed. * - * @throws ParseException if the given {@code address} is invalid. + * @throws ParseException if the given {@code email} is invalid. */ - public static Address parseAddress(String address) throws ParseException { - requireNonNull(address); - String trimmedAddress = address.trim(); - if (!Address.isValidAddress(trimmedAddress)) { - throw new ParseException(Address.MESSAGE_ADDRESS_CONSTRAINTS); + public static Quantity parseMinQuantity(String minQuantity) throws ParseException { + requireNonNull(minQuantity); + String trimmedMinQuantity = minQuantity.trim(); + if (!Quantity.isValidQuantity(trimmedMinQuantity)) { + throw new ParseException(MESSAGE_INVALID_MIN_QUANTITY); } - return new Address(trimmedAddress); + return new Quantity(trimmedMinQuantity); } - /** - * Parses a {@code String email} into an {@code Email}. + * Parses a {@code String Status} into a {@code Status}. * Leading and trailing whitespaces will be trimmed. * - * @throws ParseException if the given {@code email} is invalid. + * @throws ParseException if the given {@code Status} is invalid. */ - public static Email parseEmail(String email) throws ParseException { - requireNonNull(email); - String trimmedEmail = email.trim(); - if (!Email.isValidEmail(trimmedEmail)) { - throw new ParseException(Email.MESSAGE_EMAIL_CONSTRAINTS); - } - return new Email(trimmedEmail); + public static String parseStatus(String status) throws ParseException { + requireNonNull(status); + String trimmedStatus = status.trim(); + return trimmedStatus; } - /** * Parses a {@code String tag} into a {@code Tag}. * Leading and trailing whitespaces will be trimmed. @@ -121,4 +122,51 @@ public static Set parseTags(Collection tags) throws ParseException } return tagSet; } + + //@@author Davindran + /** + * Parses a {@code String username} into a {@code Password}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code username} is invalid. + */ + public static Username parseUsername(String username) throws ParseException { + requireNonNull(username); + String trimmedUsername = username.trim(); + if (!Username.isValidUsername(trimmedUsername)) { + throw new ParseException(Username.MESSAGE_USERNAME_CONSTRAINTS); + } + return new Username(trimmedUsername); + } + + /** + * Parses a {@code String password} into a {@code Password}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code password} is invalid. + */ + public static Password parsePassword(String password) throws ParseException { + requireNonNull(password); + String trimmedPassword = password.trim(); + if (!Password.isValidPassword(trimmedPassword)) { + throw new ParseException(Password.MESSAGE_PASSWORD_CONSTRAINTS); + } + return new Password(trimmedPassword); + } + + //@@author kelvintankaiboon + /** + * + * Parses a {@code String fileName}. + * @throws ParseException if the given string is invalid. + */ + public static String parseFileName(String fileName) throws ParseException { + requireNonNull(fileName); + String invalidFileNameRegex = ".*[^\\w\\-. ].*"; + String trimmedName = fileName.trim(); + if (trimmedName.matches(invalidFileNameRegex)) { + throw new ParseException(Name.MESSAGE_NAME_CONSTRAINTS); + } + return trimmedName; + } } diff --git a/src/main/java/seedu/address/logic/parser/SaveCommandParser.java b/src/main/java/seedu/address/logic/parser/SaveCommandParser.java new file mode 100644 index 000000000000..abe3b0029442 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/SaveCommandParser.java @@ -0,0 +1,31 @@ +package seedu.address.logic.parser; + +import seedu.address.logic.commands.SaveCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new SaveCommand object + */ +public class SaveCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the SelectCommand + * and returns an SelectCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public SaveCommand parse(String args) throws ParseException { + try { + String fileName = ParserUtil.parseFileName(args); + fileName = fileName + ".xml"; + if (fileName.equalsIgnoreCase(".xml")) { + throw new ParseException( + String.format(SaveCommand.MESSAGE_EMPTY_FILE_NAME, SaveCommand.MESSAGE_USAGE) + ); + } + return new SaveCommand(fileName); + } catch (ParseException pe) { + throw new ParseException( + String.format(SaveCommand.MESSAGE_INVALID_FILE_NAME, SaveCommand.MESSAGE_USAGE), pe); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/StockListParser.java b/src/main/java/seedu/address/logic/parser/StockListParser.java new file mode 100644 index 000000000000..6a8f1b6849a9 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/StockListParser.java @@ -0,0 +1,146 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import seedu.address.logic.commands.AddAccountCommand; +import seedu.address.logic.commands.AddCommand; +import seedu.address.logic.commands.AddTagCommand; +import seedu.address.logic.commands.ChangeStatusCommand; +import seedu.address.logic.commands.ClearCommand; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.DeleteAccountCommand; +import seedu.address.logic.commands.DeleteCommand; +import seedu.address.logic.commands.DeleteLoanListCommand; +import seedu.address.logic.commands.DeleteTagCommand; +import seedu.address.logic.commands.EditAccountCommand; +import seedu.address.logic.commands.EditCommand; +import seedu.address.logic.commands.ExitCommand; +import seedu.address.logic.commands.FindAccountCommand; +import seedu.address.logic.commands.FindCommand; +import seedu.address.logic.commands.FoundCommand; +import seedu.address.logic.commands.HelpCommand; +import seedu.address.logic.commands.HistoryCommand; +import seedu.address.logic.commands.ListAccountsCommand; +import seedu.address.logic.commands.ListCommand; +import seedu.address.logic.commands.LoanListCommand; +import seedu.address.logic.commands.LoginCommand; +import seedu.address.logic.commands.LoginStatusCommand; +import seedu.address.logic.commands.LogoutCommand; +import seedu.address.logic.commands.LostCommand; +import seedu.address.logic.commands.LostandFoundCommand; +import seedu.address.logic.commands.OpenCommand; +import seedu.address.logic.commands.RedoCommand; +import seedu.address.logic.commands.ResetAccountsCommand; +import seedu.address.logic.commands.SaveCommand; +import seedu.address.logic.commands.SelectCommand; +import seedu.address.logic.commands.StatusCommand; +import seedu.address.logic.commands.TagCommand; +import seedu.address.logic.commands.UndoCommand; +import seedu.address.logic.commands.ViewLoanListCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses user input. + */ +public class StockListParser { + + /** + * Used for initial separation of command word and args. + */ + private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); + + /** + * Parses user input into command for execution. + * + * @param userInput full user input string + * @return the command based on the user input + * @throws ParseException if the user input does not conform the expected format + */ + public Command parseCommand(String userInput) throws ParseException { + final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); + if (!matcher.matches()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); + } + + final String commandWord = matcher.group("commandWord"); + final String arguments = matcher.group("arguments"); + switch (commandWord) { + + case HelpCommand.COMMAND_WORD: + return new HelpCommand(); + case LoginStatusCommand.COMMAND_WORD: + return new LoginStatusCommand(); + case LoginCommand.COMMAND_WORD: + return new LoginCommandParser().parse(arguments); + case LogoutCommand.COMMAND_WORD: + return new LogoutCommand(); + case AddCommand.COMMAND_WORD: + return new AddCommandParser().parse(arguments); + case AddAccountCommand.COMMAND_WORD: + return new AddAccountCommandParser().parse(arguments); + case EditCommand.COMMAND_WORD: + return new EditCommandParser().parse(arguments); + case EditAccountCommand.COMMAND_WORD: + return new EditAccountCommandParser().parse(arguments); + case DeleteAccountCommand.COMMAND_WORD: + return new DeleteAccountCommandParser().parse(arguments); + case DeleteCommand.COMMAND_WORD: + return new DeleteCommandParser().parse(arguments); + case ListCommand.COMMAND_WORD: + return new ListCommand(); + case SelectCommand.COMMAND_WORD: + return new SelectCommandParser().parse(arguments); + case ClearCommand.COMMAND_WORD: + return new ClearCommand(); + case FindCommand.COMMAND_WORD: + return new FindCommandParser().parse(arguments); + case FindAccountCommand.COMMAND_WORD: + return new FindAccountCommandParser().parse(arguments); + case ListAccountsCommand.COMMAND_WORD: + return new ListAccountsCommand(); + case HistoryCommand.COMMAND_WORD: + return new HistoryCommand(); + case ExitCommand.COMMAND_WORD: + return new ExitCommand(); + case StatusCommand.COMMAND_WORD: + return new StatusCommand(); + case ChangeStatusCommand.COMMAND_WORD: + return new ChangeStatusCommandParser().parse(arguments); + case LoanListCommand.COMMAND_WORD: + return new LoanListCommandParser().parse(arguments); + case ViewLoanListCommand.COMMAND_WORD: + return new ViewLoanListCommand(); + case DeleteLoanListCommand.COMMAND_WORD: + return new DeleteLoanListCommandParser().parse(arguments); + case TagCommand.COMMAND_WORD: + return new TagCommandParser().parse(arguments); + case AddTagCommand.COMMAND_WORD: + return new AddTagCommandParser().parse(arguments); + case DeleteTagCommand.COMMAND_WORD: + return new DeleteTagCommandParser().parse(arguments); + case LostCommand.COMMAND_WORD: + return new LostCommandParser().parse(arguments); + case FoundCommand.COMMAND_WORD: + return new FoundCommandParser().parse(arguments); + case LostandFoundCommand.COMMAND_WORD: + return new LostandFoundCommand(); + case UndoCommand.COMMAND_WORD: + return new UndoCommand(); + case RedoCommand.COMMAND_WORD: + return new RedoCommand(); + case ResetAccountsCommand.COMMAND_WORD: + return new ResetAccountsCommand(); + case SaveCommand.COMMAND_WORD: + return new SaveCommandParser().parse(arguments); + case OpenCommand.COMMAND_WORD: + return new OpenCommandParser().parse(arguments); + default: + throw new ParseException(MESSAGE_UNKNOWN_COMMAND); + } + } + +} diff --git a/src/main/java/seedu/address/logic/parser/TagCommandParser.java b/src/main/java/seedu/address/logic/parser/TagCommandParser.java new file mode 100644 index 000000000000..b63652fd09a3 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/TagCommandParser.java @@ -0,0 +1,32 @@ +//@@author gaoqikai +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.Arrays; + +import seedu.address.logic.commands.TagCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.item.TagContainsKeywordsPredicate; + +/** + * Parses input arguments and creates a new TagCommand object + */ +public class TagCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the TagCommand + * and returns an TagCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public TagCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, TagCommand.MESSAGE_USAGE)); + } + + String[] tagKeywords = trimmedArgs.split("\\s+"); + + return new TagCommand(new TagContainsKeywordsPredicate (Arrays.asList(tagKeywords))); + } +} diff --git a/src/main/java/seedu/address/model/AccountList.java b/src/main/java/seedu/address/model/AccountList.java new file mode 100644 index 000000000000..33b38a233bc5 --- /dev/null +++ b/src/main/java/seedu/address/model/AccountList.java @@ -0,0 +1,120 @@ +package seedu.address.model; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import javafx.collections.ObservableList; +import seedu.address.model.account.Account; +import seedu.address.model.account.UniqueAccountList; + +/** + * Wraps all data at the stock-list level + * Duplicates are not allowed (by .isSameItem comparison) + */ +public class AccountList implements ReadOnlyAccountList { + + private final UniqueAccountList accounts; + + /* + * The 'unusual' code block below is an non-static initialization block, sometimes used to avoid duplication + * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html + * + * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication + * among constructors. + */ + { + accounts = new UniqueAccountList(); + } + + public AccountList() {} + + /** + * Creates an AccountList using the Accounts in the {@code toBeCopied} + */ + public AccountList(ReadOnlyAccountList toBeCopied) { + this(); + resetData(toBeCopied); + } + + //// list overwrite operations + + /** + * Replaces the contents of the account list with {@code accounts}. + * {@code accounts} must not contain duplicate accounts. + */ + public void setAccounts(List accounts) { + this.accounts.setAccounts(accounts); + } + + /** + * Resets the existing data of this {@code AccountList} with {@code newData}. + */ + public void resetData(ReadOnlyAccountList newData) { + requireNonNull(newData); + + setAccounts(newData.getAccountList()); + } + + //// account-level operations + + /** + * Returns true if an account with the same identity as {@code account} exists in the account list. + */ + public boolean hasAccount(Account account) { + requireNonNull(account); + return accounts.contains(account); + } + + /** + * Adds an account to the account list. + * The account must not already exist in the account list. + */ + public void addAccount(Account p) { + accounts.add(p); + } + + /** + * Replaces the given account {@code target} in the list with {@code editedAccount}. + * {@code target} must exist in the account list. + * The item identity of {@code editedAccount} must not be the same as another existing account in the account list. + */ + public void updateAccount(Account target, Account editedAccount) { + requireNonNull(editedAccount); + + accounts.setAccount(target, editedAccount); + } + + /** + * Removes {@code key} from this {@code AccountList}. + * {@code key} must exist in the account list. + */ + public void removeAccount(Account key) { + accounts.remove(key); + } + + //// util methods + + @Override + public String toString() { + return accounts.asUnmodifiableObservableList().size() + " accounts"; + // TODO: refine later + } + + @Override + public ObservableList getAccountList() { + return accounts.asUnmodifiableObservableList(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AccountList // instanceof handles nulls + && accounts.equals(((AccountList) other).accounts)); + } + + @Override + public int hashCode() { + return accounts.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java deleted file mode 100644 index 7f85c8b9258b..000000000000 --- a/src/main/java/seedu/address/model/AddressBook.java +++ /dev/null @@ -1,120 +0,0 @@ -package seedu.address.model; - -import static java.util.Objects.requireNonNull; - -import java.util.List; - -import javafx.collections.ObservableList; -import seedu.address.model.person.Person; -import seedu.address.model.person.UniquePersonList; - -/** - * Wraps all data at the address-book level - * Duplicates are not allowed (by .isSamePerson comparison) - */ -public class AddressBook implements ReadOnlyAddressBook { - - private final UniquePersonList persons; - - /* - * The 'unusual' code block below is an non-static initialization block, sometimes used to avoid duplication - * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html - * - * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication - * among constructors. - */ - { - persons = new UniquePersonList(); - } - - public AddressBook() {} - - /** - * Creates an AddressBook using the Persons in the {@code toBeCopied} - */ - public AddressBook(ReadOnlyAddressBook toBeCopied) { - this(); - resetData(toBeCopied); - } - - //// list overwrite operations - - /** - * Replaces the contents of the person list with {@code persons}. - * {@code persons} must not contain duplicate persons. - */ - public void setPersons(List persons) { - this.persons.setPersons(persons); - } - - /** - * Resets the existing data of this {@code AddressBook} with {@code newData}. - */ - public void resetData(ReadOnlyAddressBook newData) { - requireNonNull(newData); - - setPersons(newData.getPersonList()); - } - - //// person-level operations - - /** - * Returns true if a person with the same identity as {@code person} exists in the address book. - */ - public boolean hasPerson(Person person) { - requireNonNull(person); - return persons.contains(person); - } - - /** - * Adds a person to the address book. - * The person must not already exist in the address book. - */ - public void addPerson(Person p) { - persons.add(p); - } - - /** - * Replaces the given person {@code target} in the list with {@code editedPerson}. - * {@code target} must exist in the address book. - * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. - */ - public void updatePerson(Person target, Person editedPerson) { - requireNonNull(editedPerson); - - persons.setPerson(target, editedPerson); - } - - /** - * Removes {@code key} from this {@code AddressBook}. - * {@code key} must exist in the address book. - */ - public void removePerson(Person key) { - persons.remove(key); - } - - //// util methods - - @Override - public String toString() { - return persons.asUnmodifiableObservableList().size() + " persons"; - // TODO: refine later - } - - @Override - public ObservableList getPersonList() { - return persons.asUnmodifiableObservableList(); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof AddressBook // instanceof handles nulls - && persons.equals(((AddressBook) other).persons)); - } - - @Override - public int hashCode() { - return persons.hashCode(); - } -} diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index ac4521f33199..a8583859ccbd 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -3,76 +3,124 @@ import java.util.function.Predicate; import javafx.collections.ObservableList; -import seedu.address.model.person.Person; +import seedu.address.model.account.Account; +import seedu.address.model.account.Username; +import seedu.address.model.item.Item; /** * The API of the Model component. */ public interface Model { /** {@code Predicate} that always evaluate to true */ - Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true; + Predicate PREDICATE_SHOW_ALL_ITEMS = unused -> true; + Predicate PREDICATE_SHOW_ALL_ACCOUNTS = unused -> true; /** Clears existing backing model and replaces with the provided new data. */ - void resetData(ReadOnlyAddressBook newData); + void resetData(ReadOnlyStockList newData); + void resetAccountData(ReadOnlyAccountList newData); - /** Returns the AddressBook */ - ReadOnlyAddressBook getAddressBook(); + /** Returns the StockList */ + ReadOnlyStockList getStockList(); + ReadOnlyAccountList getAccountList(); + + //@@author kelvintankaiboon + /** Saves the current version of the StockList */ + void saveStockList(String fileName); + + /** Opens the .xml file indicated by user in the browser pane*/ + void openStockList(String fileName); + //@@author /** - * Returns true if a person with the same identity as {@code person} exists in the address book. + * Returns true if a item with the same identity as {@code item} exists in the stock list. */ - boolean hasPerson(Person person); + boolean hasItem(Item item); + boolean hasAccount(Account account); /** - * Deletes the given person. - * The person must exist in the address book. + * Deletes the given item. + * The item must exist in the stock list. */ - void deletePerson(Person target); + void deleteItem(Item target); + void deleteAccount(Account target); /** - * Adds the given person. - * {@code person} must not already exist in the address book. + * Adds the given item. + * {@code item} must not already exist in the stock list. */ - void addPerson(Person person); + void addItem(Item item); + void addAccount(Account account); /** - * Replaces the given person {@code target} with {@code editedPerson}. - * {@code target} must exist in the address book. - * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. + * Replaces the given item {@code target} with {@code editedItem}. + * {@code target} must exist in the stock list. + * The item identity of {@code editedItem} must not be the same as another existing item in the stock list. */ - void updatePerson(Person target, Person editedPerson); + void updateItem(Item target, Item editedItem); + void updateAccount(Account target, Account editedAccount); + + /** Returns an unmodifiable view of the filtered item list */ + ObservableList getFilteredItemList(); + ObservableList getFilteredAccountList(); - /** Returns an unmodifiable view of the filtered person list */ - ObservableList getFilteredPersonList(); + /** + * Updates the filter of the filtered item list to filter by the given {@code predicate}. + * @throws NullPointerException if {@code predicate} is null. + */ + void updateFilteredItemList(Predicate predicate); + void updateFilteredAccountList(Predicate predicate); + //@@author gaoqikai /** - * Updates the filter of the filtered person list to filter by the given {@code predicate}. + * Updates the tag filter of the filtered item list to filter by the given tag: {@code predicate}. * @throws NullPointerException if {@code predicate} is null. */ - void updateFilteredPersonList(Predicate predicate); + void updateFilteredItemListByTag(Predicate predicate); + //@@author /** - * Returns true if the model has previous address book states to restore. + * Returns true if the model has previous stock list states to restore. */ - boolean canUndoAddressBook(); + boolean canUndoStockList(); /** - * Returns true if the model has undone address book states to restore. + * Returns true if the model has undone stock list states to restore. */ - boolean canRedoAddressBook(); + boolean canRedoStockList(); /** - * Restores the model's address book to its previous state. + * Restores the model's stock list to its previous state. */ - void undoAddressBook(); + void undoStockList(); /** - * Restores the model's address book to its previously undone state. + * Restores the model's stock list to its previously undone state. */ - void redoAddressBook(); + void redoStockList(); /** - * Saves the current address book state for undo/redo. + * Saves the current stock list state for undo/redo. */ - void commitAddressBook(); + void commitStockList(); + + /** + * Get login status to determine if user can run commands + */ + boolean getLoginStatus(); + + /** + * Get logged in user in the model + */ + String getLoggedInUser(); + + /** + * Set logout status + */ + void setLoggedOutStatus(); + + /** + * Set logged in user status + */ + void setLoggedInUser(Username username); + } diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index a664602ef5b1..9fad49a4d124 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -11,122 +11,168 @@ import javafx.collections.transformation.FilteredList; import seedu.address.commons.core.ComponentManager; import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.events.model.AddressBookChangedEvent; -import seedu.address.model.person.Person; +import seedu.address.commons.events.model.AccountListChangedEvent; +import seedu.address.commons.events.model.OpenStockListVersionEvent; +import seedu.address.commons.events.model.SaveStockListVersionEvent; +import seedu.address.commons.events.model.StockListChangedEvent; +import seedu.address.model.account.Account; +import seedu.address.model.account.LoggedInAccount; +import seedu.address.model.account.Username; +import seedu.address.model.item.Item; /** - * Represents the in-memory model of the address book data. + * Represents the in-memory model of the stock list data. */ public class ModelManager extends ComponentManager implements Model { private static final Logger logger = LogsCenter.getLogger(ModelManager.class); - private final VersionedAddressBook versionedAddressBook; - private final FilteredList filteredPersons; + private final VersionedStockList versionedStockList; + private final FilteredList filteredItems; + private final VersionedAccountList versionedAccountList; + private final FilteredList filteredAccounts; + private final LoggedInAccount loggedInAccount; /** - * Initializes a ModelManager with the given addressBook and userPrefs. + * Initializes a ModelManager with the given stockList and userPrefs. */ - public ModelManager(ReadOnlyAddressBook addressBook, UserPrefs userPrefs) { + public ModelManager(ReadOnlyStockList stockList, UserPrefs userPrefs, ReadOnlyAccountList accountList) { super(); - requireAllNonNull(addressBook, userPrefs); + requireAllNonNull(stockList, userPrefs); - logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs); + logger.fine("Initializing with stock list: " + stockList + " and user prefs " + userPrefs); - versionedAddressBook = new VersionedAddressBook(addressBook); - filteredPersons = new FilteredList<>(versionedAddressBook.getPersonList()); + versionedStockList = new VersionedStockList(stockList); + filteredItems = new FilteredList<>(versionedStockList.getItemList()); + + logger.fine("Initializing with account list: " + accountList); + + versionedAccountList = new VersionedAccountList(accountList); + filteredAccounts = new FilteredList<>(versionedAccountList.getAccountList()); + loggedInAccount = new LoggedInAccount(); } public ModelManager() { - this(new AddressBook(), new UserPrefs()); + this(new StockList(), new UserPrefs(), new AccountList()); } @Override - public void resetData(ReadOnlyAddressBook newData) { - versionedAddressBook.resetData(newData); - indicateAddressBookChanged(); + public void resetData(ReadOnlyStockList newData) { + versionedStockList.resetData(newData); + indicateStockListChanged(); } @Override - public ReadOnlyAddressBook getAddressBook() { - return versionedAddressBook; + public ReadOnlyStockList getStockList() { + return versionedStockList; } /** Raises an event to indicate the model has changed */ - private void indicateAddressBookChanged() { - raise(new AddressBookChangedEvent(versionedAddressBook)); + private void indicateStockListChanged() { + raise(new StockListChangedEvent(versionedStockList)); + } + + //@@author kelvintankaiboon + /** Raises an event to indicate that saveCommand has been called */ + private void indicateSaveStockListVersion(String fileName) { + raise(new SaveStockListVersionEvent(versionedStockList, fileName)); + } + + @Override + public void saveStockList(String fileName) { + indicateSaveStockListVersion(fileName); + } + + /** Raises an event to indicate that OpenCommand has been called */ + private void indicateOpenStockListVersion(String fileName) { + raise(new OpenStockListVersionEvent(versionedStockList, fileName)); + } + + @Override + public void openStockList(String fileName) { + indicateOpenStockListVersion(fileName); } + //@@author @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return versionedAddressBook.hasPerson(person); + public boolean hasItem(Item item) { + requireNonNull(item); + return versionedStockList.hasItem(item); } @Override - public void deletePerson(Person target) { - versionedAddressBook.removePerson(target); - indicateAddressBookChanged(); + public void deleteItem(Item target) { + versionedStockList.removeItem(target); + indicateStockListChanged(); } @Override - public void addPerson(Person person) { - versionedAddressBook.addPerson(person); - updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - indicateAddressBookChanged(); + public void addItem(Item item) { + versionedStockList.addItem(item); + updateFilteredItemList(PREDICATE_SHOW_ALL_ITEMS); + indicateStockListChanged(); } @Override - public void updatePerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); + public void updateItem(Item target, Item editedItem) { + requireAllNonNull(target, editedItem); - versionedAddressBook.updatePerson(target, editedPerson); - indicateAddressBookChanged(); + versionedStockList.updateItem(target, editedItem); + indicateStockListChanged(); } - //=========== Filtered Person List Accessors ============================================================= + //=========== Filtered Item List Accessors ============================================================= /** - * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of - * {@code versionedAddressBook} + * Returns an unmodifiable view of the list of {@code Item} backed by the internal list of + * {@code versionedStockList} */ @Override - public ObservableList getFilteredPersonList() { - return FXCollections.unmodifiableObservableList(filteredPersons); + public ObservableList getFilteredItemList() { + return FXCollections.unmodifiableObservableList(filteredItems); + } + + @Override + public void updateFilteredItemList(Predicate predicate) { + requireNonNull(predicate); + filteredItems.setPredicate(predicate); } + //@@author gaoqikai @Override - public void updateFilteredPersonList(Predicate predicate) { + //update the stock list that is filtered by tag + public void updateFilteredItemListByTag(Predicate predicate) { requireNonNull(predicate); - filteredPersons.setPredicate(predicate); + filteredItems.setPredicate(predicate); } + //@@author //=========== Undo/Redo ================================================================================= @Override - public boolean canUndoAddressBook() { - return versionedAddressBook.canUndo(); + public boolean canUndoStockList() { + return versionedStockList.canUndo(); } @Override - public boolean canRedoAddressBook() { - return versionedAddressBook.canRedo(); + public boolean canRedoStockList() { + return versionedStockList.canRedo(); } @Override - public void undoAddressBook() { - versionedAddressBook.undo(); - indicateAddressBookChanged(); + public void undoStockList() { + versionedStockList.undo(); + indicateStockListChanged(); } @Override - public void redoAddressBook() { - versionedAddressBook.redo(); - indicateAddressBookChanged(); + public void redoStockList() { + versionedStockList.redo(); + indicateStockListChanged(); } @Override - public void commitAddressBook() { - versionedAddressBook.commit(); + public void commitStockList() { + versionedStockList.commit(); } @Override @@ -143,8 +189,91 @@ public boolean equals(Object obj) { // state check ModelManager other = (ModelManager) obj; - return versionedAddressBook.equals(other.versionedAddressBook) - && filteredPersons.equals(other.filteredPersons); + return versionedStockList.equals(other.versionedStockList) + && filteredItems.equals(other.filteredItems); + } + + //=========== Account List ============================================================= + + @Override + public void resetAccountData(ReadOnlyAccountList newData) { + versionedAccountList.resetData(newData); + indicateAccountListChanged(); + } + + @Override + public ReadOnlyAccountList getAccountList() { + return versionedAccountList; + } + + /** Raises an event to indicate the model has changed */ + private void indicateAccountListChanged() { + raise(new AccountListChangedEvent(versionedAccountList)); + } + + @Override + public boolean hasAccount(Account account) { + requireNonNull(account); + return versionedAccountList.hasAccount(account); + } + + @Override + public void deleteAccount(Account target) { + versionedAccountList.removeAccount(target); + indicateAccountListChanged(); + } + + @Override + public void addAccount(Account account) { + versionedAccountList.addAccount(account); + updateFilteredAccountList(PREDICATE_SHOW_ALL_ACCOUNTS); + indicateAccountListChanged(); + } + + @Override + public void updateAccount(Account target, Account editedAccount) { + requireAllNonNull(target, editedAccount); + + versionedAccountList.updateAccount(target, editedAccount); + indicateAccountListChanged(); + } + + @Override + public boolean getLoginStatus() { + return loggedInAccount.getLoginStatus(); + } + + @Override + public String getLoggedInUser() { + return loggedInAccount.getUsername().toString(); + } + + @Override + public void setLoggedInUser(Username username) { + loggedInAccount.setLoggedInUser(username); + } + + @Override + public void setLoggedOutStatus() { + loggedInAccount.setLoggedOutStatus(); + } + + + //=========== Filtered Account List Accessors ============================================================= + + /** + * Returns an unmodifiable view of the list of {@code Item} backed by the internal list of + * {@code versionedAccountList} + */ + @Override + public ObservableList getFilteredAccountList() { + return FXCollections.unmodifiableObservableList(filteredAccounts); + } + + @Override + public void updateFilteredAccountList(Predicate predicate) { + requireNonNull(predicate); + filteredAccounts.setPredicate(predicate); } } diff --git a/src/main/java/seedu/address/model/ReadOnlyAccountList.java b/src/main/java/seedu/address/model/ReadOnlyAccountList.java new file mode 100644 index 000000000000..48d5702827de --- /dev/null +++ b/src/main/java/seedu/address/model/ReadOnlyAccountList.java @@ -0,0 +1,17 @@ +package seedu.address.model; + +import javafx.collections.ObservableList; +import seedu.address.model.account.Account; + +/** + * Unmodifiable view of an account list + */ +public interface ReadOnlyAccountList { + + /** + * Returns an unmodifiable view of the accounts list. + * This list will not contain any duplicate accounts. + */ + ObservableList getAccountList(); + +} diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java deleted file mode 100644 index 6ddc2cd9a290..000000000000 --- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java +++ /dev/null @@ -1,17 +0,0 @@ -package seedu.address.model; - -import javafx.collections.ObservableList; -import seedu.address.model.person.Person; - -/** - * Unmodifiable view of an address book - */ -public interface ReadOnlyAddressBook { - - /** - * Returns an unmodifiable view of the persons list. - * This list will not contain any duplicate persons. - */ - ObservableList getPersonList(); - -} diff --git a/src/main/java/seedu/address/model/ReadOnlyStockList.java b/src/main/java/seedu/address/model/ReadOnlyStockList.java new file mode 100644 index 000000000000..762f4734330d --- /dev/null +++ b/src/main/java/seedu/address/model/ReadOnlyStockList.java @@ -0,0 +1,17 @@ +package seedu.address.model; + +import javafx.collections.ObservableList; +import seedu.address.model.item.Item; + +/** + * Unmodifiable view of a stock list + */ +public interface ReadOnlyStockList { + + /** + * Returns an unmodifiable view of the items list. + * This list will not contain any duplicate items. + */ + ObservableList getItemList(); + +} diff --git a/src/main/java/seedu/address/model/StockList.java b/src/main/java/seedu/address/model/StockList.java new file mode 100644 index 000000000000..d7f9c63e5c8c --- /dev/null +++ b/src/main/java/seedu/address/model/StockList.java @@ -0,0 +1,119 @@ +package seedu.address.model; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import javafx.collections.ObservableList; +import seedu.address.model.item.Item; +import seedu.address.model.item.UniqueItemList; + +/** + * Wraps all data at the stock-list level + * Duplicates are not allowed (by .isSameItem comparison) + */ +public class StockList implements ReadOnlyStockList { + + private final UniqueItemList items; + + /* + * The 'unusual' code block below is an non-static initialization block, sometimes used to avoid duplication + * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html + * + * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication + * among constructors. + */ + { + items = new UniqueItemList(); + } + + public StockList() {} + + /** + * Creates an StockList using the Items in the {@code toBeCopied} + */ + public StockList(ReadOnlyStockList toBeCopied) { + this(); + resetData(toBeCopied); + } + + //// list overwrite operations + + /** + * Replaces the contents of the item list with {@code items}. + * {@code items} must not contain duplicate items. + */ + public void setItems(List items) { + this.items.setItems(items); + } + + /** + * Resets the existing data of this {@code StockList} with {@code newData}. + */ + public void resetData(ReadOnlyStockList newData) { + requireNonNull(newData); + setItems(newData.getItemList()); + } + + //// item-level operations + + /** + * Returns true if a item with the same identity as {@code item} exists in the stock list. + */ + public boolean hasItem(Item item) { + requireNonNull(item); + return items.contains(item); + } + + /** + * Adds a item to the stock list. + * The item must not already exist in the stock list. + */ + public void addItem(Item p) { + items.add(p); + } + + /** + * Replaces the given item {@code target} in the list with {@code editedItem}. + * {@code target} must exist in the stock list. + * The item identity of {@code editedItem} must not be the same as another existing item in the stock list. + */ + public void updateItem(Item target, Item editedItem) { + requireNonNull(editedItem); + + items.setItem(target, editedItem); + } + + /** + * Removes {@code key} from this {@code StockList}. + * {@code key} must exist in the stock list. + */ + public void removeItem(Item key) { + items.remove(key); + } + + //// util methods + + @Override + public String toString() { + return items.asUnmodifiableObservableList().size() + " items"; + // TODO: refine later + } + + @Override + public ObservableList getItemList() { + return items.asUnmodifiableObservableList(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof StockList // instanceof handles nulls + && items.equals(((StockList) other).items)); + } + + @Override + public int hashCode() { + return items.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/address/model/UserPrefs.java index 980b2b388852..9954485f4ee0 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/seedu/address/model/UserPrefs.java @@ -12,7 +12,8 @@ public class UserPrefs { private GuiSettings guiSettings; - private Path addressBookFilePath = Paths.get("data" , "addressbook.xml"); + private Path stockListFilePath = Paths.get("data" , "stocklist.xml"); + private Path accountListFilePath = Paths.get("data" , "accountlist.xml"); public UserPrefs() { setGuiSettings(500, 500, 0, 0); @@ -30,12 +31,16 @@ public void setGuiSettings(double width, double height, int x, int y) { guiSettings = new GuiSettings(width, height, x, y); } - public Path getAddressBookFilePath() { - return addressBookFilePath; + public Path getStockListFilePath() { + return stockListFilePath; } - public void setAddressBookFilePath(Path addressBookFilePath) { - this.addressBookFilePath = addressBookFilePath; + public Path getAccountListFilePath() { + return accountListFilePath; + } + + public void setStockListFilePath(Path stockListFilePath) { + this.stockListFilePath = stockListFilePath; } @Override @@ -50,19 +55,19 @@ public boolean equals(Object other) { UserPrefs o = (UserPrefs) other; return Objects.equals(guiSettings, o.guiSettings) - && Objects.equals(addressBookFilePath, o.addressBookFilePath); + && Objects.equals(stockListFilePath, o.stockListFilePath); } @Override public int hashCode() { - return Objects.hash(guiSettings, addressBookFilePath); + return Objects.hash(guiSettings, stockListFilePath); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Gui Settings : " + guiSettings.toString()); - sb.append("\nLocal data file location : " + addressBookFilePath); + sb.append("\nLocal data file location : " + stockListFilePath); return sb.toString(); } diff --git a/src/main/java/seedu/address/model/VersionedAddressBook.java b/src/main/java/seedu/address/model/VersionedAccountList.java similarity index 52% rename from src/main/java/seedu/address/model/VersionedAddressBook.java rename to src/main/java/seedu/address/model/VersionedAccountList.java index 227a335045d7..25787335ddd9 100644 --- a/src/main/java/seedu/address/model/VersionedAddressBook.java +++ b/src/main/java/seedu/address/model/VersionedAccountList.java @@ -4,69 +4,69 @@ import java.util.List; /** - * {@code AddressBook} that keeps track of its own history. + * {@code AccountList} that keeps track of its own history. */ -public class VersionedAddressBook extends AddressBook { +public class VersionedAccountList extends AccountList { - private final List addressBookStateList; + private final List accountListStateList; private int currentStatePointer; - public VersionedAddressBook(ReadOnlyAddressBook initialState) { + public VersionedAccountList(ReadOnlyAccountList initialState) { super(initialState); - addressBookStateList = new ArrayList<>(); - addressBookStateList.add(new AddressBook(initialState)); + accountListStateList = new ArrayList<>(); + accountListStateList.add(new AccountList(initialState)); currentStatePointer = 0; } /** - * Saves a copy of the current {@code AddressBook} state at the end of the state list. + * Saves a copy of the current {@code AccountList} state at the end of the state list. * Undone states are removed from the state list. */ public void commit() { removeStatesAfterCurrentPointer(); - addressBookStateList.add(new AddressBook(this)); + accountListStateList.add(new AccountList(this)); currentStatePointer++; } private void removeStatesAfterCurrentPointer() { - addressBookStateList.subList(currentStatePointer + 1, addressBookStateList.size()).clear(); + accountListStateList.subList(currentStatePointer + 1, accountListStateList.size()).clear(); } /** - * Restores the address book to its previous state. + * Restores the account list to its previous state. */ public void undo() { if (!canUndo()) { throw new NoUndoableStateException(); } currentStatePointer--; - resetData(addressBookStateList.get(currentStatePointer)); + resetData(accountListStateList.get(currentStatePointer)); } /** - * Restores the address book to its previously undone state. + * Restores the account list to its previously undone state. */ public void redo() { if (!canRedo()) { throw new NoRedoableStateException(); } currentStatePointer++; - resetData(addressBookStateList.get(currentStatePointer)); + resetData(accountListStateList.get(currentStatePointer)); } /** - * Returns true if {@code undo()} has address book states to undo. + * Returns true if {@code undo()} has account list states to undo. */ public boolean canUndo() { return currentStatePointer > 0; } /** - * Returns true if {@code redo()} has address book states to redo. + * Returns true if {@code redo()} has stock list states to redo. */ public boolean canRedo() { - return currentStatePointer < addressBookStateList.size() - 1; + return currentStatePointer < accountListStateList.size() - 1; } @Override @@ -77,16 +77,16 @@ public boolean equals(Object other) { } // instanceof handles nulls - if (!(other instanceof VersionedAddressBook)) { + if (!(other instanceof VersionedAccountList)) { return false; } - VersionedAddressBook otherVersionedAddressBook = (VersionedAddressBook) other; + VersionedAccountList otherVersionedAccountList = (VersionedAccountList) other; // state check - return super.equals(otherVersionedAddressBook) - && addressBookStateList.equals(otherVersionedAddressBook.addressBookStateList) - && currentStatePointer == otherVersionedAddressBook.currentStatePointer; + return super.equals(otherVersionedAccountList) + && accountListStateList.equals(otherVersionedAccountList.accountListStateList) + && currentStatePointer == otherVersionedAccountList.currentStatePointer; } /** @@ -94,7 +94,7 @@ public boolean equals(Object other) { */ public static class NoUndoableStateException extends RuntimeException { private NoUndoableStateException() { - super("Current state pointer at start of addressBookState list, unable to undo."); + super("Current state pointer at start of accountListState list, unable to undo."); } } @@ -103,7 +103,7 @@ private NoUndoableStateException() { */ public static class NoRedoableStateException extends RuntimeException { private NoRedoableStateException() { - super("Current state pointer at end of addressBookState list, unable to redo."); + super("Current state pointer at end of accountListState list, unable to redo."); } } } diff --git a/src/main/java/seedu/address/model/VersionedStockList.java b/src/main/java/seedu/address/model/VersionedStockList.java new file mode 100644 index 000000000000..cc74307639a9 --- /dev/null +++ b/src/main/java/seedu/address/model/VersionedStockList.java @@ -0,0 +1,109 @@ +package seedu.address.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * {@code StockList} that keeps track of its own history. + */ +public class VersionedStockList extends StockList { + + private final List stockListStateList; + private int currentStatePointer; + + public VersionedStockList(ReadOnlyStockList initialState) { + super(initialState); + + stockListStateList = new ArrayList<>(); + stockListStateList.add(new StockList(initialState)); + currentStatePointer = 0; + } + + /** + * Saves a copy of the current {@code StockList} state at the end of the state list. + * Undone states are removed from the state list. + */ + public void commit() { + removeStatesAfterCurrentPointer(); + stockListStateList.add(new StockList(this)); + currentStatePointer++; + } + + private void removeStatesAfterCurrentPointer() { + stockListStateList.subList(currentStatePointer + 1, stockListStateList.size()).clear(); + } + + /** + * Restores the stock list to its previous state. + */ + public void undo() { + if (!canUndo()) { + throw new NoUndoableStateException(); + } + currentStatePointer--; + resetData(stockListStateList.get(currentStatePointer)); + } + + /** + * Restores the stock list to its previously undone state. + */ + public void redo() { + if (!canRedo()) { + throw new NoRedoableStateException(); + } + currentStatePointer++; + resetData(stockListStateList.get(currentStatePointer)); + } + + /** + * Returns true if {@code undo()} has stock list states to undo. + */ + public boolean canUndo() { + return currentStatePointer > 0; + } + + /** + * Returns true if {@code redo()} has stock list states to redo. + */ + public boolean canRedo() { + return currentStatePointer < stockListStateList.size() - 1; + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof VersionedStockList)) { + return false; + } + + VersionedStockList otherVersionedStockList = (VersionedStockList) other; + + // state check + return super.equals(otherVersionedStockList) + && stockListStateList.equals(otherVersionedStockList.stockListStateList) + && currentStatePointer == otherVersionedStockList.currentStatePointer; + } + + /** + * Thrown when trying to {@code undo()} but can't. + */ + public static class NoUndoableStateException extends RuntimeException { + private NoUndoableStateException() { + super("Current state pointer at start of stockListState list, unable to undo."); + } + } + + /** + * Thrown when trying to {@code redo()} but can't. + */ + public static class NoRedoableStateException extends RuntimeException { + private NoRedoableStateException() { + super("Current state pointer at end of stockListState list, unable to redo."); + } + } +} diff --git a/src/main/java/seedu/address/model/account/Account.java b/src/main/java/seedu/address/model/account/Account.java new file mode 100644 index 000000000000..cb1017fddbe6 --- /dev/null +++ b/src/main/java/seedu/address/model/account/Account.java @@ -0,0 +1,76 @@ +package seedu.address.model.account; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Objects; + +//@@author Davindran +/** + * Represents an Account in the stock list. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Account { + + private final Username username; + private final Password password; + + public Account(Username username, Password password) { + requireAllNonNull(username, password); + this.username = username; + this.password = password; + } + + public Username getUsername() { + return username; + } + + public Password getPassword() { + return password; + } + + /** + * Returns true if both items of the same name have at least one other identity field that is the same. + * This defines a weaker notion of equality between two items. + */ + public boolean isSameAccount(Account otherAccount) { + if (otherAccount == this) { + return true; + } + + return otherAccount != null + && (otherAccount.getUsername().fullUsername.toLowerCase()).equals(getUsername().fullUsername + .toLowerCase()); + } + + /** + * Returns true if both items have the same identity and data fields. + * This defines a stronger notion of equality between two items. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Account)) { + return false; + } + + Account otherAccount = (Account) other; + return (otherAccount.getUsername().fullUsername.toLowerCase()).equals(getUsername().fullUsername.toLowerCase()); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(username, password); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getUsername()); + return builder.toString(); + } + +} diff --git a/src/main/java/seedu/address/model/account/LoggedInAccount.java b/src/main/java/seedu/address/model/account/LoggedInAccount.java new file mode 100644 index 000000000000..f11848a75f6b --- /dev/null +++ b/src/main/java/seedu/address/model/account/LoggedInAccount.java @@ -0,0 +1,80 @@ +package seedu.address.model.account; + +import java.util.Objects; + +/** + * Represents a logged in Account in the model. + */ +public class LoggedInAccount { + + private Username username; + private boolean loginStatus; + + public LoggedInAccount() { + this.username = null; + this.loginStatus = false; + } + + public Username getUsername() { + return username; + } + + public boolean getLoginStatus() { + return loginStatus; + } + + /** + * Returns true if both items of the same name have at least one other identity field that is the same. + * This defines a weaker notion of equality between two items. + */ + public boolean isSameAccount(LoggedInAccount otherAccount) { + if (otherAccount == this) { + return true; + } + + return otherAccount != null + && (otherAccount.getUsername().fullUsername.toLowerCase()).equals(getUsername().fullUsername + .toLowerCase()); + } + + /** + * Returns true if both items have the same identity and data fields. + * This defines a stronger notion of equality between two items. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof LoggedInAccount)) { + return false; + } + + LoggedInAccount otherAccount = (LoggedInAccount) other; + return (otherAccount.getUsername().fullUsername.toLowerCase()).equals(getUsername().fullUsername.toLowerCase()); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(username); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getUsername()); + return builder.toString(); + } + + public void setLoggedInUser(Username username) { + this.username = username; + this.loginStatus = true; + } + + public void setLoggedOutStatus() { + this.username = null; + this.loginStatus = false; + } +} diff --git a/src/main/java/seedu/address/model/account/Password.java b/src/main/java/seedu/address/model/account/Password.java new file mode 100644 index 000000000000..e40f12c7fc44 --- /dev/null +++ b/src/main/java/seedu/address/model/account/Password.java @@ -0,0 +1,59 @@ +package seedu.address.model.account; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +//@@author Davindran +/** + * Represents an Account's password in the stock list login command. + * Guarantees: immutable; is valid as declared in {@link #isValidPassword(String)} + */ +public class Password { + + public static final String MESSAGE_PASSWORD_CONSTRAINTS = + "Passwords should have no spaces and cannot be empty"; + + /* + * The first character of the password must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String PASSWORD_VALIDATION_REGEX = "^[^\\s]+$"; + + public final String fullPassword; + + /** + * Constructs a {@code Password}. + * + * @param password A valid password. + */ + public Password(String password) { + requireNonNull(password); + checkArgument(isValidPassword(password), MESSAGE_PASSWORD_CONSTRAINTS); + fullPassword = password; + } + + /** + * Returns true if a given string is a valid password. + */ + public static boolean isValidPassword(String test) { + return test.matches(PASSWORD_VALIDATION_REGEX); + } + + + @Override + public String toString() { + return fullPassword; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Password // instanceof handles nulls + && fullPassword.equals(((Password) other).fullPassword)); // state check + } + + @Override + public int hashCode() { + return fullPassword.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/account/UniqueAccountList.java b/src/main/java/seedu/address/model/account/UniqueAccountList.java new file mode 100644 index 000000000000..ddfba8ea6997 --- /dev/null +++ b/src/main/java/seedu/address/model/account/UniqueAccountList.java @@ -0,0 +1,133 @@ +package seedu.address.model.account; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.address.model.account.exceptions.AccountNotFoundException; +import seedu.address.model.account.exceptions.DuplicateAccountException; + +/** + * A list of accounts that enforces uniqueness between its elements and does not allow nulls. + * An account is considered unique by comparing using {@code Account#isSameAccount(Account)}. As such, adding and + * updating of accounts uses Account#isSameAccount(Account) for equality so as to ensure that the account being added + * or updated is unique in terms of identity in the UniqueAccountList. However, the removal of an account uses + * Account#equals(Object) so as to ensure that the account with exactly the same fields will be removed. + * + * Supports a minimal set of list operations. + * + * @see Account#isSameAccount(Account) + */ +public class UniqueAccountList { + private final ObservableList internalList = FXCollections.observableArrayList(); + + /** + * Returns true if the list contains an equivalent account as the given argument. + */ + public boolean contains(Account toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameAccount); + } + + /** + * Adds an account to the list. + * The account must not already exist in the list. + */ + public void add(Account toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicateAccountException(); + } + internalList.add(toAdd); + } + + /** + * Replaces the account {@code target} in the list with {@code editedAccount}. + * {@code target} must exist in the list. + * The account identity of {@code editedAccount} must not be the same as another existing account in the list. + */ + public void setAccount(Account target, Account editedAccount) { + requireAllNonNull(target, editedAccount); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new AccountNotFoundException(); + } + + if (!target.isSameAccount(editedAccount) && contains(editedAccount)) { + throw new DuplicateAccountException(); + } + + internalList.set(index, editedAccount); + } + + /** + * Removes the equivalent account from the list. + * The account must exist in the list. + */ + public void remove(Account toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new AccountNotFoundException(); + } + } + + public void setAccounts(UniqueAccountList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code accounts}. + * {@code accounts} must not contain duplicate accounts. + */ + public void setAccounts(List accounts) { + requireAllNonNull(accounts); + if (!accountsAreUnique(accounts)) { + throw new DuplicateAccountException(); + } + + internalList.setAll(accounts); + } + + /** + * Returns the backing list as an unmodifiable {@code ObservableList}. + */ + public ObservableList asUnmodifiableObservableList() { + return FXCollections.unmodifiableObservableList(internalList); + } + + //@Override + //public Iterator iterator() { + // return internalList.iterator(); + //} + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueAccountList // instanceof handles nulls + && internalList.equals(((UniqueAccountList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code accounts} contains only unique accounts. + */ + private boolean accountsAreUnique(List accounts) { + for (int i = 0; i < accounts.size() - 1; i++) { + for (int j = i + 1; j < accounts.size(); j++) { + if (accounts.get(i).isSameAccount(accounts.get(j))) { + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/seedu/address/model/account/Username.java b/src/main/java/seedu/address/model/account/Username.java new file mode 100644 index 000000000000..46bc2962c562 --- /dev/null +++ b/src/main/java/seedu/address/model/account/Username.java @@ -0,0 +1,59 @@ +package seedu.address.model.account; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +//@@author Davindran +/** + * Represents an Account's username in the stock list login command. + * Guarantees: immutable; is valid as declared in {@link #isValidUsername(String)} + */ +public class Username { + + public static final String MESSAGE_USERNAME_CONSTRAINTS = + "Usernames should only contain alphanumeric characters and spaces, and it should not be blank"; + + /* + * The first character of the username must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String USERNAME_VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*"; + + public final String fullUsername; + + /** + * Constructs a {@code Username}. + * + * @param username A valid username. + */ + public Username(String username) { + requireNonNull(username); + checkArgument(isValidUsername(username), MESSAGE_USERNAME_CONSTRAINTS); + fullUsername = username; + } + + /** + * Returns true if a given string is a valid username. + */ + public static boolean isValidUsername(String test) { + return test.matches(USERNAME_VALIDATION_REGEX); + } + + + @Override + public String toString() { + return fullUsername; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Username // instanceof handles nulls + && fullUsername.equals(((Username) other).fullUsername)); // state check + } + + @Override + public int hashCode() { + return fullUsername.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/account/UsernameContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/account/UsernameContainsKeywordsPredicate.java new file mode 100644 index 000000000000..356297707b9c --- /dev/null +++ b/src/main/java/seedu/address/model/account/UsernameContainsKeywordsPredicate.java @@ -0,0 +1,29 @@ +package seedu.address.model.account; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Tests that a {@code Account}'s {@code Username} matches any of the keywords given. + */ +public class UsernameContainsKeywordsPredicate implements Predicate { + private final List keywords; + + public UsernameContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Account account) { + return keywords.stream() + .anyMatch(keyword -> account.getUsername().fullUsername.toLowerCase().contains(keyword.toLowerCase())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UsernameContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((UsernameContainsKeywordsPredicate) other).keywords)); // state check + } + +} diff --git a/src/main/java/seedu/address/model/account/UsernameMatchPredicate.java b/src/main/java/seedu/address/model/account/UsernameMatchPredicate.java new file mode 100644 index 000000000000..53731bb2f538 --- /dev/null +++ b/src/main/java/seedu/address/model/account/UsernameMatchPredicate.java @@ -0,0 +1,27 @@ +package seedu.address.model.account; + +import java.util.function.Predicate; + +/** + * Tests that a {@code Account}'s {@code Username} matches any of the keywords given. + */ +public class UsernameMatchPredicate implements Predicate { + private final String name; + + public UsernameMatchPredicate(String name) { + this.name = name; + } + + @Override + public boolean test(Account account) { + return name.equals(account.getUsername().toString()); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UsernameMatchPredicate // instanceof handles nulls + && name.equals(((UsernameMatchPredicate) other).name)); // state check + } + +} diff --git a/src/main/java/seedu/address/model/account/exceptions/AccountNotFoundException.java b/src/main/java/seedu/address/model/account/exceptions/AccountNotFoundException.java new file mode 100644 index 000000000000..2bf70c78ec82 --- /dev/null +++ b/src/main/java/seedu/address/model/account/exceptions/AccountNotFoundException.java @@ -0,0 +1,6 @@ +package seedu.address.model.account.exceptions; + +/** + * Signals that the operation is unable to find the specified item. + */ +public class AccountNotFoundException extends RuntimeException {} diff --git a/src/main/java/seedu/address/model/account/exceptions/DuplicateAccountException.java b/src/main/java/seedu/address/model/account/exceptions/DuplicateAccountException.java new file mode 100644 index 000000000000..61b88566caa1 --- /dev/null +++ b/src/main/java/seedu/address/model/account/exceptions/DuplicateAccountException.java @@ -0,0 +1,11 @@ +package seedu.address.model.account.exceptions; + +/** + * Signals that the operation will result in duplicate Accounts (Accounts are considered duplicates if they have the + * same name). + */ +public class DuplicateAccountException extends RuntimeException { + public DuplicateAccountException() { + super("Operation would result in duplicate accounts"); + } +} diff --git a/src/main/java/seedu/address/model/item/Item.java b/src/main/java/seedu/address/model/item/Item.java new file mode 100644 index 000000000000..e0291fd5d626 --- /dev/null +++ b/src/main/java/seedu/address/model/item/Item.java @@ -0,0 +1,153 @@ +package seedu.address.model.item; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import seedu.address.model.tag.Tag; + +/** + * Represents a Item in the Stock List. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Item { + + // Identity + // fields + private final Name name; + private final Quantity quantity; + private final Quantity minQuantity; + + // Data fields + private final Status status = new Status(); + private final Loststatus loststatus = new Loststatus(); + private final Set tags = new HashSet<>(); + + /** + * Every field must be present and not null. + */ + public Item(Name name, Quantity quantity, Quantity minQuantity, Set tags) { + requireAllNonNull(name, quantity, minQuantity, tags); + this.name = name; + this.quantity = quantity; + this.minQuantity = minQuantity; + this.status.setDefaultValues(quantity.toInteger()); + this.loststatus.setDefaultValues(quantity.toInteger()); + this.tags.addAll(tags); + } + + public Item(Name name, Quantity quantity, Quantity minQuantity, Status status, Set tags) { + requireAllNonNull(name, quantity, minQuantity, tags); + this.name = name; + this.quantity = quantity; + this.minQuantity = minQuantity; + this.status.setStatusReady(status.getStatusReady()); + this.status.setStatusOnLoan(status.getStatusOnLoan()); + this.status.setStatusFaulty(status.getStatusFaulty()); + this.tags.addAll(tags); + } + + public Item(Name name, Quantity quantity, Quantity minQuantity, + Loststatus loststatus, Status status, Set tags) { + requireAllNonNull(name, quantity, minQuantity, tags); + this.name = name; + this.quantity = quantity; + this.minQuantity = minQuantity; + this.loststatus.setLoststatusFound(loststatus.getLoststatusFound()); + this.loststatus.setLoststatusLost(loststatus.getLoststatusLost()); + this.status.setStatusReady(status.getStatusReady()); + this.status.setStatusOnLoan(status.getStatusOnLoan()); + this.status.setStatusFaulty(status.getStatusFaulty()); + this.tags.addAll(tags); + } + + public Name getName() { + return name; + } + + public Quantity getQuantity() { + return quantity; + } + + public Quantity getMinQuantity() { + return minQuantity; + } + + public Status getStatus() { + return status; + } + + public Loststatus getLoststatus() { + return loststatus; } + + /** + * Returns an immutable tag set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + */ + public Set getTags() { + return Collections.unmodifiableSet(tags); + } + + /** + * Returns true if both items of the same name have at least one other identity field that is the same. + * This defines a weaker notion of equality between two items. + */ + public boolean isSameItem(Item otherItem) { + if (otherItem == this) { + return true; + } + + return otherItem != null + && (otherItem.getName().fullName.toLowerCase()).equals(getName().fullName.toLowerCase()); + } + + /** + * Returns true if both items have the same identity and data fields. + * This defines a stronger notion of equality between two items. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Item)) { + return false; + } + + Item otherItem = (Item) other; + return (otherItem.getName().fullName.toLowerCase()).equals(getName().fullName.toLowerCase()); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(name, quantity, minQuantity, status, loststatus, tags); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getName()) + .append(" Name: ") + .append(getName()) + .append(" Quantity: ") + .append(getQuantity()) + .append(" Minimum Quantity Required In Stocks: ") + .append(getMinQuantity()) + .append(" Status: Ready | ") + .append(getStatus().getStatusReady()) + .append(", On-Loan | ") + .append(getStatus().getStatusOnLoan()) + .append(", Faulty | ") + .append(getStatus().getStatusFaulty()) + .append(", Lost | ") + .append(getLoststatus().getLoststatusLost()) + .append(" Tags: "); + getTags().forEach(builder::append); + return builder.toString(); + } +} diff --git a/src/main/java/seedu/address/model/item/LoanerDescription.java b/src/main/java/seedu/address/model/item/LoanerDescription.java new file mode 100644 index 000000000000..ac0433355242 --- /dev/null +++ b/src/main/java/seedu/address/model/item/LoanerDescription.java @@ -0,0 +1,49 @@ +package seedu.address.model.item; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +//@@author ChewKinWhye + +/** + * Represents a loaner in the loan list. + */ + +public class LoanerDescription { + private final Name itemName; + private final Name loanerName; + private final Quantity quantity; + + public LoanerDescription(Name itemName, Name loanerName, Quantity quantity) { + requireAllNonNull(itemName, loanerName, quantity); + this.itemName = itemName; + this.loanerName = loanerName; + this.quantity = quantity; + } + public LoanerDescription(LoanerDescription loaner) { + itemName = loaner.getItemName(); + loanerName = loaner.getLoanerName(); + quantity = loaner.getQuantity(); + } + public Name getItemName() { + return itemName; + } + + public Quantity getQuantity() { + return quantity; + } + public Name getLoanerName() { + return loanerName; + } + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getLoanerName()) + .append(" loanerName: ") + .append(getLoanerName()) + .append(" itemName: ") + .append(getItemName()) + .append(" Quantity: ") + .append(getQuantity()); + return builder.toString(); + } +} diff --git a/src/main/java/seedu/address/model/item/Loststatus.java b/src/main/java/seedu/address/model/item/Loststatus.java new file mode 100644 index 000000000000..3df5790d76b5 --- /dev/null +++ b/src/main/java/seedu/address/model/item/Loststatus.java @@ -0,0 +1,40 @@ +package seedu.address.model.item; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents an Item's loststatus in the stock list. + */ +//@@author HeHaowei +public class Loststatus { + public static final int LOSTSTATUS_LOST = 0; + public static final int LOSTSTATUS_FOUND = 1; + private final List loststatus = new ArrayList<>(2); + + public Loststatus() { + this(0, 0); } + public Loststatus(int lost, int found) { + loststatus.add(lost); + loststatus.add(found); + } + public Integer getLoststatusLost() { + return loststatus.get(LOSTSTATUS_LOST); } + public Integer getLoststatusFound() { + return loststatus.get(LOSTSTATUS_FOUND); + } + + public void setLoststatusLost(Integer lost) { + loststatus.set(LOSTSTATUS_LOST, lost); + } + public void setLoststatusFound(Integer found) { + loststatus.set(LOSTSTATUS_FOUND, found); + } + public void setDefaultValues(int quantity) { + loststatus.set(LOSTSTATUS_FOUND, quantity); + loststatus.set(LOSTSTATUS_LOST, 0); + + } + +} + diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/address/model/item/Name.java similarity index 94% rename from src/main/java/seedu/address/model/person/Name.java rename to src/main/java/seedu/address/model/item/Name.java index 9982393dabb5..3b5c7a733572 100644 --- a/src/main/java/seedu/address/model/person/Name.java +++ b/src/main/java/seedu/address/model/item/Name.java @@ -1,10 +1,10 @@ -package seedu.address.model.person; +package seedu.address.model.item; import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.AppUtil.checkArgument; /** - * Represents a Person's name in the address book. + * Represents an Item's name in the stock list. * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} */ public class Name { diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/item/NameContainsKeywordsPredicate.java similarity index 67% rename from src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java rename to src/main/java/seedu/address/model/item/NameContainsKeywordsPredicate.java index c9b5868427ca..bb0d1f069d0a 100644 --- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java +++ b/src/main/java/seedu/address/model/item/NameContainsKeywordsPredicate.java @@ -1,14 +1,12 @@ -package seedu.address.model.person; +package seedu.address.model.item; import java.util.List; import java.util.function.Predicate; -import seedu.address.commons.util.StringUtil; - /** - * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. + * Tests that a {@code Item}'s {@code Name} matches any of the keywords given. */ -public class NameContainsKeywordsPredicate implements Predicate { +public class NameContainsKeywordsPredicate implements Predicate { private final List keywords; public NameContainsKeywordsPredicate(List keywords) { @@ -16,9 +14,9 @@ public NameContainsKeywordsPredicate(List keywords) { } @Override - public boolean test(Person person) { + public boolean test(Item item) { return keywords.stream() - .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword)); + .anyMatch(keyword -> item.getName().fullName.toLowerCase().contains(keyword.toLowerCase())); } @Override diff --git a/src/main/java/seedu/address/model/item/Quantity.java b/src/main/java/seedu/address/model/item/Quantity.java new file mode 100644 index 000000000000..c9a6aee96856 --- /dev/null +++ b/src/main/java/seedu/address/model/item/Quantity.java @@ -0,0 +1,60 @@ +package seedu.address.model.item; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +import seedu.address.commons.util.StringUtil; + +/** + * Represents an Item's quantity in the stock list. + * Guarantees: immutable; is valid as declared in {@link #isValidQuantity(String)} + */ +public class Quantity { + + public static final String MESSAGE_QUANTITY_CONSTRAINTS = + "Quantities should only contain non-zero unsigned integers, and it should not be blank"; + public static final String MESSAGE_MIN_QUANTITY_CONSTRAINTS = + "Minimum Quantities should only contain non-zero unsigned integers, and it should not be blank"; + + + public final Integer quantity; + + /** + * Constructs a {@code Quantity}. + * + * @param inputQuantity A valid quantity. + */ + public Quantity(String inputQuantity) { + requireNonNull(inputQuantity); + checkArgument(isValidQuantity(inputQuantity), MESSAGE_QUANTITY_CONSTRAINTS); + quantity = Integer.parseInt(inputQuantity); + } + + /** + * Returns true if a given integer is a valid quantity. + */ + public static boolean isValidQuantity(String test) { + return StringUtil.isNonZeroUnsignedInteger(test); + } + + @Override + public String toString() { + return quantity.toString(); + } + + public Integer toInteger() { + return quantity; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Quantity // instanceof handles nulls + && quantity.equals(((Quantity) other).quantity)); // state check + } + + @Override + public int hashCode() { + return quantity.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/item/SimpleItem.java b/src/main/java/seedu/address/model/item/SimpleItem.java new file mode 100644 index 000000000000..c0670490125b --- /dev/null +++ b/src/main/java/seedu/address/model/item/SimpleItem.java @@ -0,0 +1,42 @@ +package seedu.address.model.item; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +//@@author ChewKinWhye + +/** + * Simplified item with fields removed to be used in the status command + */ + +public class SimpleItem { + private final Name name; + private final Quantity quantity; + + public SimpleItem(Name name, Quantity quantity) { + requireAllNonNull(name, quantity); + this.name = name; + this.quantity = quantity; + } + public Name getName() { + return name; + } + + public Quantity getQuantity() { + return quantity; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof SimpleItem)) { + return false; + } + + SimpleItem otherItem = (SimpleItem) other; + return (otherItem.getName().toString().equals(getName().toString()) + && otherItem.getQuantity().toString().equals(getQuantity().toString())); + } +} diff --git a/src/main/java/seedu/address/model/item/Status.java b/src/main/java/seedu/address/model/item/Status.java new file mode 100644 index 000000000000..c1d98097f7cb --- /dev/null +++ b/src/main/java/seedu/address/model/item/Status.java @@ -0,0 +1,50 @@ +package seedu.address.model.item; + +import java.util.ArrayList; +import java.util.List; + +//@@author ChewKinWhye + +/** + * Represents an Item's status in the stock list. + */ +public class Status { + public static final int STATUS_READY = 0; + public static final int STATUS_ON_LOAN = 1; + public static final int STATUS_FAULTY = 2; + private final List status = new ArrayList<>(3); + + public Status() { + this(0, 0, 0); + } + public Status(int ready, int onLoan, int faulty) { + status.add(ready); + status.add(onLoan); + status.add(faulty); + } + public Integer getStatusReady() { + return status.get(STATUS_READY); + } + public Integer getStatusOnLoan() { + return status.get(STATUS_ON_LOAN); + } + public Integer getStatusFaulty() { + return status.get(STATUS_FAULTY); + } + + public void setStatusReady(Integer ready) { + status.set(STATUS_READY, ready); + } + public void setStatusOnLoan(Integer onLoan) { + status.set(STATUS_ON_LOAN, onLoan); + } + public void setStatusFaulty(Integer faulty) { + status.set(STATUS_FAULTY, faulty); + } + public void setDefaultValues(int quantity) { + status.set(STATUS_READY, quantity); + status.set(STATUS_ON_LOAN, 0); + status.set(STATUS_FAULTY, 0); + } +} + diff --git a/src/main/java/seedu/address/model/item/TagContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/item/TagContainsKeywordsPredicate.java new file mode 100644 index 000000000000..90dc455b17da --- /dev/null +++ b/src/main/java/seedu/address/model/item/TagContainsKeywordsPredicate.java @@ -0,0 +1,30 @@ +//@@author gaoqikai +package seedu.address.model.item; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Tests that a {@code Item}'s {@code Tags} matches any of the keywords given. + */ +public class TagContainsKeywordsPredicate implements Predicate { + private final List keywords; + + public TagContainsKeywordsPredicate (List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Item item) { + return keywords.stream() + .anyMatch(keyword -> item.getTags().toString().toLowerCase().contains(keyword.toLowerCase())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TagContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((TagContainsKeywordsPredicate) other).keywords)); // state check + } + +} diff --git a/src/main/java/seedu/address/model/item/UniqueItemList.java b/src/main/java/seedu/address/model/item/UniqueItemList.java new file mode 100644 index 000000000000..03f503de8d7d --- /dev/null +++ b/src/main/java/seedu/address/model/item/UniqueItemList.java @@ -0,0 +1,135 @@ +package seedu.address.model.item; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.address.model.item.exceptions.DuplicateItemException; +import seedu.address.model.item.exceptions.ItemNotFoundException; + +/** + * A list of items that enforces uniqueness between its elements and does not allow nulls. + * A item is considered unique by comparing using {@code Item#isSameItem(Item)}. As such, adding and updating of + * items uses Item#isSameItem(Item) for equality so as to ensure that the item being added or updated is + * unique in terms of identity in the UniqueItemList. However, the removal of a item uses Item#equals(Object) so + * as to ensure that the item with exactly the same fields will be removed. + * + * Supports a minimal set of list operations. + * + * @see Item#isSameItem(Item) + */ +public class UniqueItemList implements Iterable { + + private final ObservableList internalList = FXCollections.observableArrayList(); + + /** + * Returns true if the list contains an equivalent item as the given argument. + */ + public boolean contains(Item toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameItem); + } + + /** + * Adds a item to the list. + * The item must not already exist in the list. + */ + public void add(Item toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicateItemException(); + } + internalList.add(toAdd); + } + + /** + * Replaces the item {@code target} in the list with {@code editedItem}. + * {@code target} must exist in the list. + * The item identity of {@code editedItem} must not be the same as another existing item in the list. + */ + public void setItem(Item target, Item editedItem) { + requireAllNonNull(target, editedItem); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new ItemNotFoundException(); + } + + if (!target.isSameItem(editedItem) && contains(editedItem)) { + throw new DuplicateItemException(); + } + + internalList.set(index, editedItem); + } + + /** + * Removes the equivalent item from the list. + * The item must exist in the list. + */ + public void remove(Item toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new ItemNotFoundException(); + } + } + + public void setItems(UniqueItemList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code items}. + * {@code items} must not contain duplicate items. + */ + public void setItems(List items) { + requireAllNonNull(items); + if (!itemsAreUnique(items)) { + throw new DuplicateItemException(); + } + + internalList.setAll(items); + } + + /** + * Returns the backing list as an unmodifiable {@code ObservableList}. + */ + public ObservableList asUnmodifiableObservableList() { + return FXCollections.unmodifiableObservableList(internalList); + } + + @Override + public Iterator iterator() { + return internalList.iterator(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueItemList // instanceof handles nulls + && internalList.equals(((UniqueItemList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code items} contains only unique items. + */ + private boolean itemsAreUnique(List items) { + for (int i = 0; i < items.size() - 1; i++) { + for (int j = i + 1; j < items.size(); j++) { + if (items.get(i).isSameItem(items.get(j))) { + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/seedu/address/model/item/exceptions/DuplicateItemException.java b/src/main/java/seedu/address/model/item/exceptions/DuplicateItemException.java new file mode 100644 index 000000000000..71865b09093b --- /dev/null +++ b/src/main/java/seedu/address/model/item/exceptions/DuplicateItemException.java @@ -0,0 +1,11 @@ +package seedu.address.model.item.exceptions; + +/** + * Signals that the operation will result in duplicate Items (Items are considered duplicates if they have the same + * name). + */ +public class DuplicateItemException extends RuntimeException { + public DuplicateItemException() { + super("Operation would result in duplicate items"); + } +} diff --git a/src/main/java/seedu/address/model/item/exceptions/ItemNotFoundException.java b/src/main/java/seedu/address/model/item/exceptions/ItemNotFoundException.java new file mode 100644 index 000000000000..8816e6eb5040 --- /dev/null +++ b/src/main/java/seedu/address/model/item/exceptions/ItemNotFoundException.java @@ -0,0 +1,6 @@ +package seedu.address.model.item.exceptions; + +/** + * Signals that the operation is unable to find the specified item. + */ +public class ItemNotFoundException extends RuntimeException {} diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/seedu/address/model/person/Address.java deleted file mode 100644 index a1409233ceb9..000000000000 --- a/src/main/java/seedu/address/model/person/Address.java +++ /dev/null @@ -1,58 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's address in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)} - */ -public class Address { - - public static final String MESSAGE_ADDRESS_CONSTRAINTS = - "Addresses can take any values, and it should not be blank"; - - /* - * The first character of the address must not be a whitespace, - * otherwise " " (a blank string) becomes a valid input. - */ - public static final String ADDRESS_VALIDATION_REGEX = "[^\\s].*"; - - public final String value; - - /** - * Constructs an {@code Address}. - * - * @param address A valid address. - */ - public Address(String address) { - requireNonNull(address); - checkArgument(isValidAddress(address), MESSAGE_ADDRESS_CONSTRAINTS); - value = address; - } - - /** - * Returns true if a given string is a valid email. - */ - public static boolean isValidAddress(String test) { - return test.matches(ADDRESS_VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Address // instanceof handles nulls - && value.equals(((Address) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/address/model/person/Email.java deleted file mode 100644 index 38a7629e9a2d..000000000000 --- a/src/main/java/seedu/address/model/person/Email.java +++ /dev/null @@ -1,67 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's email in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)} - */ -public class Email { - - private static final String SPECIAL_CHARACTERS = "!#$%&'*+/=?`{|}~^.-"; - public static final String MESSAGE_EMAIL_CONSTRAINTS = "Emails should be of the format local-part@domain " - + "and adhere to the following constraints:\n" - + "1. The local-part should only contain alphanumeric characters and these special characters, excluding " - + "the parentheses, (" + SPECIAL_CHARACTERS + ") .\n" - + "2. This is followed by a '@' and then a domain name. " - + "The domain name must:\n" - + " - be at least 2 characters long\n" - + " - start and end with alphanumeric characters\n" - + " - consist of alphanumeric characters, a period or a hyphen for the characters in between, if any."; - // alphanumeric and special characters - private static final String LOCAL_PART_REGEX = "^[\\w" + SPECIAL_CHARACTERS + "]+"; - private static final String DOMAIN_FIRST_CHARACTER_REGEX = "[^\\W_]"; // alphanumeric characters except underscore - private static final String DOMAIN_MIDDLE_REGEX = "[a-zA-Z0-9.-]*"; // alphanumeric, period and hyphen - private static final String DOMAIN_LAST_CHARACTER_REGEX = "[^\\W_]$"; - public static final String EMAIL_VALIDATION_REGEX = LOCAL_PART_REGEX + "@" - + DOMAIN_FIRST_CHARACTER_REGEX + DOMAIN_MIDDLE_REGEX + DOMAIN_LAST_CHARACTER_REGEX; - - public final String value; - - /** - * Constructs an {@code Email}. - * - * @param email A valid email address. - */ - public Email(String email) { - requireNonNull(email); - checkArgument(isValidEmail(email), MESSAGE_EMAIL_CONSTRAINTS); - value = email; - } - - /** - * Returns if a given string is a valid email. - */ - public static boolean isValidEmail(String test) { - return test.matches(EMAIL_VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Email // instanceof handles nulls - && value.equals(((Email) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java deleted file mode 100644 index 557a7a60cd51..000000000000 --- a/src/main/java/seedu/address/model/person/Person.java +++ /dev/null @@ -1,120 +0,0 @@ -package seedu.address.model.person; - -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -import seedu.address.model.tag.Tag; - -/** - * Represents a Person in the address book. - * Guarantees: details are present and not null, field values are validated, immutable. - */ -public class Person { - - // Identity fields - private final Name name; - private final Phone phone; - private final Email email; - - // Data fields - private final Address address; - private final Set tags = new HashSet<>(); - - /** - * Every field must be present and not null. - */ - public Person(Name name, Phone phone, Email email, Address address, Set tags) { - requireAllNonNull(name, phone, email, address, tags); - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - this.tags.addAll(tags); - } - - public Name getName() { - return name; - } - - public Phone getPhone() { - return phone; - } - - public Email getEmail() { - return email; - } - - public Address getAddress() { - return address; - } - - /** - * Returns an immutable tag set, which throws {@code UnsupportedOperationException} - * if modification is attempted. - */ - public Set getTags() { - return Collections.unmodifiableSet(tags); - } - - /** - * Returns true if both persons of the same name have at least one other identity field that is the same. - * This defines a weaker notion of equality between two persons. - */ - public boolean isSamePerson(Person otherPerson) { - if (otherPerson == this) { - return true; - } - - return otherPerson != null - && otherPerson.getName().equals(getName()) - && (otherPerson.getPhone().equals(getPhone()) || otherPerson.getEmail().equals(getEmail())); - } - - /** - * Returns true if both persons have the same identity and data fields. - * This defines a stronger notion of equality between two persons. - */ - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - if (!(other instanceof Person)) { - return false; - } - - Person otherPerson = (Person) other; - return otherPerson.getName().equals(getName()) - && otherPerson.getPhone().equals(getPhone()) - && otherPerson.getEmail().equals(getEmail()) - && otherPerson.getAddress().equals(getAddress()) - && otherPerson.getTags().equals(getTags()); - } - - @Override - public int hashCode() { - // use this method for custom fields hashing instead of implementing your own - return Objects.hash(name, phone, email, address, tags); - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append(getName()) - .append(" Phone: ") - .append(getPhone()) - .append(" Email: ") - .append(getEmail()) - .append(" Address: ") - .append(getAddress()) - .append(" Tags: "); - getTags().forEach(builder::append); - return builder.toString(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/address/model/person/Phone.java deleted file mode 100644 index a22e51653835..000000000000 --- a/src/main/java/seedu/address/model/person/Phone.java +++ /dev/null @@ -1,53 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's phone number in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)} - */ -public class Phone { - - - public static final String MESSAGE_PHONE_CONSTRAINTS = - "Phone numbers should only contain numbers, and it should be at least 3 digits long"; - public static final String PHONE_VALIDATION_REGEX = "\\d{3,}"; - public final String value; - - /** - * Constructs a {@code Phone}. - * - * @param phone A valid phone number. - */ - public Phone(String phone) { - requireNonNull(phone); - checkArgument(isValidPhone(phone), MESSAGE_PHONE_CONSTRAINTS); - value = phone; - } - - /** - * Returns true if a given string is a valid phone number. - */ - public static boolean isValidPhone(String test) { - return test.matches(PHONE_VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Phone // instanceof handles nulls - && value.equals(((Phone) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java deleted file mode 100644 index 5856aa42e6b5..000000000000 --- a/src/main/java/seedu/address/model/person/UniquePersonList.java +++ /dev/null @@ -1,135 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.util.Iterator; -import java.util.List; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.model.person.exceptions.PersonNotFoundException; - -/** - * A list of persons that enforces uniqueness between its elements and does not allow nulls. - * A person is considered unique by comparing using {@code Person#isSamePerson(Person)}. As such, adding and updating of - * persons uses Person#isSamePerson(Person) for equality so as to ensure that the person being added or updated is - * unique in terms of identity in the UniquePersonList. However, the removal of a person uses Person#equals(Object) so - * as to ensure that the person with exactly the same fields will be removed. - * - * Supports a minimal set of list operations. - * - * @see Person#isSamePerson(Person) - */ -public class UniquePersonList implements Iterable { - - private final ObservableList internalList = FXCollections.observableArrayList(); - - /** - * Returns true if the list contains an equivalent person as the given argument. - */ - public boolean contains(Person toCheck) { - requireNonNull(toCheck); - return internalList.stream().anyMatch(toCheck::isSamePerson); - } - - /** - * Adds a person to the list. - * The person must not already exist in the list. - */ - public void add(Person toAdd) { - requireNonNull(toAdd); - if (contains(toAdd)) { - throw new DuplicatePersonException(); - } - internalList.add(toAdd); - } - - /** - * Replaces the person {@code target} in the list with {@code editedPerson}. - * {@code target} must exist in the list. - * The person identity of {@code editedPerson} must not be the same as another existing person in the list. - */ - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); - - int index = internalList.indexOf(target); - if (index == -1) { - throw new PersonNotFoundException(); - } - - if (!target.isSamePerson(editedPerson) && contains(editedPerson)) { - throw new DuplicatePersonException(); - } - - internalList.set(index, editedPerson); - } - - /** - * Removes the equivalent person from the list. - * The person must exist in the list. - */ - public void remove(Person toRemove) { - requireNonNull(toRemove); - if (!internalList.remove(toRemove)) { - throw new PersonNotFoundException(); - } - } - - public void setPersons(UniquePersonList replacement) { - requireNonNull(replacement); - internalList.setAll(replacement.internalList); - } - - /** - * Replaces the contents of this list with {@code persons}. - * {@code persons} must not contain duplicate persons. - */ - public void setPersons(List persons) { - requireAllNonNull(persons); - if (!personsAreUnique(persons)) { - throw new DuplicatePersonException(); - } - - internalList.setAll(persons); - } - - /** - * Returns the backing list as an unmodifiable {@code ObservableList}. - */ - public ObservableList asUnmodifiableObservableList() { - return FXCollections.unmodifiableObservableList(internalList); - } - - @Override - public Iterator iterator() { - return internalList.iterator(); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof UniquePersonList // instanceof handles nulls - && internalList.equals(((UniquePersonList) other).internalList)); - } - - @Override - public int hashCode() { - return internalList.hashCode(); - } - - /** - * Returns true if {@code persons} contains only unique persons. - */ - private boolean personsAreUnique(List persons) { - for (int i = 0; i < persons.size() - 1; i++) { - for (int j = i + 1; j < persons.size(); j++) { - if (persons.get(i).isSamePerson(persons.get(j))) { - return false; - } - } - } - return true; - } -} diff --git a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java b/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java deleted file mode 100644 index d7290f594423..000000000000 --- a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java +++ /dev/null @@ -1,11 +0,0 @@ -package seedu.address.model.person.exceptions; - -/** - * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if they have the same - * identity). - */ -public class DuplicatePersonException extends RuntimeException { - public DuplicatePersonException() { - super("Operation would result in duplicate persons"); - } -} diff --git a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java b/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java deleted file mode 100644 index fa764426ca73..000000000000 --- a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java +++ /dev/null @@ -1,6 +0,0 @@ -package seedu.address.model.person.exceptions; - -/** - * Signals that the operation is unable to find the specified person. - */ -public class PersonNotFoundException extends RuntimeException {} diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/address/model/tag/Tag.java index 8cdff2773ac9..2e728f15e88d 100644 --- a/src/main/java/seedu/address/model/tag/Tag.java +++ b/src/main/java/seedu/address/model/tag/Tag.java @@ -4,7 +4,7 @@ import static seedu.address.commons.util.AppUtil.checkArgument; /** - * Represents a Tag in the address book. + * Represents a Tag in the stock list. * Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)} */ public class Tag { diff --git a/src/main/java/seedu/address/model/util/SampleAccountDataUtil.java b/src/main/java/seedu/address/model/util/SampleAccountDataUtil.java new file mode 100644 index 000000000000..b7767ffc0a0b --- /dev/null +++ b/src/main/java/seedu/address/model/util/SampleAccountDataUtil.java @@ -0,0 +1,27 @@ +package seedu.address.model.util; + +import seedu.address.model.AccountList; +import seedu.address.model.ReadOnlyAccountList; +import seedu.address.model.account.Account; +import seedu.address.model.account.Password; +import seedu.address.model.account.Username; + +/** + * Contains utility methods for populating {@code AccountList} with sample data. + */ +public class SampleAccountDataUtil { + public static Account[] getSampleAccounts() { + return new Account[] { + new Account(new Username("admin"), new Password("admin")) + }; + } + + public static ReadOnlyAccountList getSampleAccountList() { + AccountList sampleSl = new AccountList(); + for (Account sampleAccount : getSampleAccounts()) { + sampleSl.addAccount(sampleAccount); + } + return sampleSl; + } + +} diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java index 1806da4facfa..5214102ddee4 100644 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java @@ -4,48 +4,30 @@ import java.util.Set; import java.util.stream.Collectors; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; +import seedu.address.model.ReadOnlyStockList; +import seedu.address.model.StockList; +import seedu.address.model.item.Item; +import seedu.address.model.item.Name; +import seedu.address.model.item.Quantity; import seedu.address.model.tag.Tag; /** - * Contains utility methods for populating {@code AddressBook} with sample data. + * Contains utility methods for populating {@code StockList} with sample data. */ public class SampleDataUtil { - public static Person[] getSamplePersons() { - return new Person[] { - new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"), - new Address("Blk 30 Geylang Street 29, #06-40"), - getTagSet("friends")), - new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"), - new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), - getTagSet("colleagues", "friends")), - new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"), - new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), - getTagSet("neighbours")), - new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"), - new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), - getTagSet("family")), - new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"), - new Address("Blk 47 Tampines Street 20, #17-35"), - getTagSet("classmates")), - new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"), - new Address("Blk 45 Aljunied Street 85, #11-31"), - getTagSet("colleagues")) + public static Item[] getSampleItems() { + return new Item[] { + new Item(new Name("Arduino"), new Quantity("20"), new Quantity("5"), getTagSet("Lab1")), + new Item(new Name("Raspberry Pi"), new Quantity("50"), new Quantity("15"), getTagSet("Lab2")) }; } - public static ReadOnlyAddressBook getSampleAddressBook() { - AddressBook sampleAb = new AddressBook(); - for (Person samplePerson : getSamplePersons()) { - sampleAb.addPerson(samplePerson); + public static ReadOnlyStockList getSampleStockList() { + StockList sampleSl = new StockList(); + for (Item sampleItem : getSampleItems()) { + sampleSl.addItem(sampleItem); } - return sampleAb; + return sampleSl; } /** diff --git a/src/main/java/seedu/address/storage/AddressBookStorage.java b/src/main/java/seedu/address/storage/AccountListStorage.java similarity index 52% rename from src/main/java/seedu/address/storage/AddressBookStorage.java rename to src/main/java/seedu/address/storage/AccountListStorage.java index 4599182b3f92..f02647a273ce 100644 --- a/src/main/java/seedu/address/storage/AddressBookStorage.java +++ b/src/main/java/seedu/address/storage/AccountListStorage.java @@ -5,41 +5,41 @@ import java.util.Optional; import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.ReadOnlyAccountList; /** - * Represents a storage for {@link seedu.address.model.AddressBook}. + * Represents a storage for {@link seedu.address.model.AccountList}. */ -public interface AddressBookStorage { +public interface AccountListStorage { /** * Returns the file path of the data file. */ - Path getAddressBookFilePath(); + Path getAccountListFilePath(); /** - * Returns AddressBook data as a {@link ReadOnlyAddressBook}. + * Returns AccountList data as a {@link ReadOnlyAccountList}. * Returns {@code Optional.empty()} if storage file is not found. * @throws DataConversionException if the data in storage is not in the expected format. * @throws IOException if there was any problem when reading from the storage. */ - Optional readAddressBook() throws DataConversionException, IOException; + Optional readAccountList() throws DataConversionException, IOException; /** - * @see #getAddressBookFilePath() + * @see #getAccountListFilePath() */ - Optional readAddressBook(Path filePath) throws DataConversionException, IOException; + Optional readAccountList(Path filePath) throws DataConversionException, IOException; /** - * Saves the given {@link ReadOnlyAddressBook} to the storage. - * @param addressBook cannot be null. + * Saves the given {@link ReadOnlyAccountList} to the storage. + * @param accountList cannot be null. * @throws IOException if there was any problem writing to the file. */ - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; + void saveAccountList(ReadOnlyAccountList accountList) throws IOException; /** - * @see #saveAddressBook(ReadOnlyAddressBook) + * @see #saveAccountList(ReadOnlyAccountList) */ - void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException; + void saveAccountList(ReadOnlyAccountList accountList, Path filePath) throws IOException; } diff --git a/src/main/java/seedu/address/storage/StockListStorage.java b/src/main/java/seedu/address/storage/StockListStorage.java new file mode 100644 index 000000000000..bf10fbf65778 --- /dev/null +++ b/src/main/java/seedu/address/storage/StockListStorage.java @@ -0,0 +1,47 @@ +package seedu.address.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.address.commons.exceptions.DataConversionException; +import seedu.address.model.ReadOnlyStockList; + +/** + * Represents a storage for {@link seedu.address.model.StockList}. + */ +public interface StockListStorage { + + /** + * Returns the file path of the data file. + */ + Path getStockListFilePath(); + + /** + * Returns StockList data as a {@link ReadOnlyStockList}. + * Returns {@code Optional.empty()} if storage file is not found. + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ + Optional readStockList() throws DataConversionException, IOException; + + /** + * @see #getStockListFilePath() + */ + Optional readStockList(Path filePath) throws DataConversionException, IOException; + + /** + * Saves the given {@link ReadOnlyStockList} to the storage. + * @param stockList cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveStockList(ReadOnlyStockList stockList) throws IOException; + + /** + * @see #saveStockList(ReadOnlyStockList) + */ + void saveStockList(ReadOnlyStockList stockList, Path filePath) throws IOException; + + void saveStockListVersion(ReadOnlyStockList stockList, Path filePath) throws IOException; + +} diff --git a/src/main/java/seedu/address/storage/Storage.java b/src/main/java/seedu/address/storage/Storage.java index 28791127999b..d62dc3150660 100644 --- a/src/main/java/seedu/address/storage/Storage.java +++ b/src/main/java/seedu/address/storage/Storage.java @@ -4,16 +4,20 @@ import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.events.model.AddressBookChangedEvent; +import seedu.address.commons.events.model.AccountListChangedEvent; +import seedu.address.commons.events.model.SaveStockListVersionEvent; +import seedu.address.commons.events.model.StockListChangedEvent; import seedu.address.commons.events.storage.DataSavingExceptionEvent; import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.ReadOnlyAccountList; +import seedu.address.model.ReadOnlyStockList; import seedu.address.model.UserPrefs; /** * API of the Storage component */ -public interface Storage extends AddressBookStorage, UserPrefsStorage { +public interface Storage extends StockListStorage, UserPrefsStorage, AccountListStorage { + @Override Optional readUserPrefs() throws DataConversionException, IOException; @@ -22,18 +26,36 @@ public interface Storage extends AddressBookStorage, UserPrefsStorage { void saveUserPrefs(UserPrefs userPrefs) throws IOException; @Override - Path getAddressBookFilePath(); + Path getStockListFilePath(); + + @Override + Path getAccountListFilePath(); + + @Override + Optional readStockList() throws DataConversionException, IOException; + + @Override + Optional readAccountList() throws DataConversionException, IOException; @Override - Optional readAddressBook() throws DataConversionException, IOException; + void saveStockList(ReadOnlyStockList stockList) throws IOException; @Override - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; + void saveAccountList(ReadOnlyAccountList accountList) throws IOException; /** - * Saves the current version of the Address Book to the hard disk. + * Saves the current version of the Stock List to the hard disk. * Creates the data file if it is missing. * Raises {@link DataSavingExceptionEvent} if there was an error during saving. */ - void handleAddressBookChangedEvent(AddressBookChangedEvent abce); + void handleStockListChangedEvent(StockListChangedEvent stockList); + + /** + * Saves the current version of the Account List to the hard disk. + * Creates the data file if it is missing. + * Raises {@link DataSavingExceptionEvent} if there was an error during saving. + */ + void handleAccountListChangedEvent(AccountListChangedEvent accountList); + + void handleSaveStockListVersionEvent(SaveStockListVersionEvent stockList); } diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java index b0df908a76a7..b4804b6e4d3d 100644 --- a/src/main/java/seedu/address/storage/StorageManager.java +++ b/src/main/java/seedu/address/storage/StorageManager.java @@ -2,6 +2,7 @@ import java.io.IOException; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Optional; import java.util.logging.Logger; @@ -9,26 +10,32 @@ import seedu.address.commons.core.ComponentManager; import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.events.model.AddressBookChangedEvent; +import seedu.address.commons.events.model.AccountListChangedEvent; +import seedu.address.commons.events.model.SaveStockListVersionEvent; +import seedu.address.commons.events.model.StockListChangedEvent; import seedu.address.commons.events.storage.DataSavingExceptionEvent; import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.ReadOnlyAccountList; +import seedu.address.model.ReadOnlyStockList; import seedu.address.model.UserPrefs; /** - * Manages storage of AddressBook data in local storage. + * Manages storage of StockList data in local storage. */ public class StorageManager extends ComponentManager implements Storage { private static final Logger logger = LogsCenter.getLogger(StorageManager.class); - private AddressBookStorage addressBookStorage; + private StockListStorage stockListStorage; private UserPrefsStorage userPrefsStorage; + private AccountListStorage accountListStorage; - public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) { + public StorageManager(StockListStorage stockListStorage, UserPrefsStorage userPrefsStorage, AccountListStorage + accountListStorage) { super(); - this.addressBookStorage = addressBookStorage; + this.stockListStorage = stockListStorage; this.userPrefsStorage = userPrefsStorage; + this.accountListStorage = accountListStorage; } // ================ UserPrefs methods ============================== @@ -49,42 +56,100 @@ public void saveUserPrefs(UserPrefs userPrefs) throws IOException { } - // ================ AddressBook methods ============================== + // ================ StockList methods ============================== @Override - public Path getAddressBookFilePath() { - return addressBookStorage.getAddressBookFilePath(); + public Path getStockListFilePath() { + return stockListStorage.getStockListFilePath(); } @Override - public Optional readAddressBook() throws DataConversionException, IOException { - return readAddressBook(addressBookStorage.getAddressBookFilePath()); + public Optional readStockList() throws DataConversionException, IOException { + return readStockList(stockListStorage.getStockListFilePath()); } @Override - public Optional readAddressBook(Path filePath) throws DataConversionException, IOException { + public Optional readStockList(Path filePath) throws DataConversionException, IOException { logger.fine("Attempting to read data from file: " + filePath); - return addressBookStorage.readAddressBook(filePath); + return stockListStorage.readStockList(filePath); } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, addressBookStorage.getAddressBookFilePath()); + public void saveStockList(ReadOnlyStockList stockList) throws IOException { + saveStockList(stockList, stockListStorage.getStockListFilePath()); } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { + public void saveStockList(ReadOnlyStockList stockList, Path filePath) throws IOException { logger.fine("Attempting to write to data file: " + filePath); - addressBookStorage.saveAddressBook(addressBook, filePath); + stockListStorage.saveStockList(stockList, filePath); } + //@@author kelvintankaiboon + @Override + public void saveStockListVersion(ReadOnlyStockList stockList, Path filePath) throws IOException { + logger.fine("Attempting to write to data file: " + filePath); + stockListStorage.saveStockListVersion(stockList, filePath); + } + + @Override + @Subscribe + public void handleSaveStockListVersionEvent(SaveStockListVersionEvent event) { + logger.info(LogsCenter.getEventHandlingLogMessage(event, "Saving current version to file")); + try { + saveStockListVersion(event.data, Paths.get("versions", event.fileName)); + } catch (IOException e) { + raise(new DataSavingExceptionEvent(e)); + } + } + //@@author + + @Override + @Subscribe + public void handleStockListChangedEvent(StockListChangedEvent event) { + logger.info(LogsCenter.getEventHandlingLogMessage(event, "Local data changed, saving to file")); + try { + saveStockList(event.data); + } catch (IOException e) { + raise(new DataSavingExceptionEvent(e)); + } + } + + // ================ AccountList methods ============================== + + @Override + public Path getAccountListFilePath() { + return accountListStorage.getAccountListFilePath(); + } + + @Override + public Optional readAccountList() throws DataConversionException, IOException { + return readAccountList(accountListStorage.getAccountListFilePath()); + } + + @Override + public Optional readAccountList(Path filePath) throws DataConversionException, IOException { + logger.fine("Attempting to read data from file: " + filePath); + return accountListStorage.readAccountList(filePath); + } + + @Override + public void saveAccountList(ReadOnlyAccountList accountList) throws IOException { + saveAccountList(accountList, accountListStorage.getAccountListFilePath()); + } + + @Override + public void saveAccountList(ReadOnlyAccountList accountList, Path filePath) throws IOException { + logger.fine("Attempting to write to data file: " + filePath); + accountListStorage.saveAccountList(accountList, filePath); + } @Override @Subscribe - public void handleAddressBookChangedEvent(AddressBookChangedEvent event) { + public void handleAccountListChangedEvent(AccountListChangedEvent event) { logger.info(LogsCenter.getEventHandlingLogMessage(event, "Local data changed, saving to file")); try { - saveAddressBook(event.data); + saveAccountList(event.data); } catch (IOException e) { raise(new DataSavingExceptionEvent(e)); } diff --git a/src/main/java/seedu/address/storage/XmlAddressBookStorage.java b/src/main/java/seedu/address/storage/XmlAccountListStorage.java similarity index 53% rename from src/main/java/seedu/address/storage/XmlAddressBookStorage.java rename to src/main/java/seedu/address/storage/XmlAccountListStorage.java index ecf0e7ec23a8..202ea19a8116 100644 --- a/src/main/java/seedu/address/storage/XmlAddressBookStorage.java +++ b/src/main/java/seedu/address/storage/XmlAccountListStorage.java @@ -13,47 +13,50 @@ import seedu.address.commons.exceptions.DataConversionException; import seedu.address.commons.exceptions.IllegalValueException; import seedu.address.commons.util.FileUtil; -import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.ReadOnlyAccountList; /** - * A class to access AddressBook data stored as an xml file on the hard disk. + * A class to access AccountList data stored as an xml file on the hard disk. */ -public class XmlAddressBookStorage implements AddressBookStorage { +public class XmlAccountListStorage implements AccountListStorage { + private static final Logger logger = LogsCenter.getLogger(XmlAccountListStorage.class); - private static final Logger logger = LogsCenter.getLogger(XmlAddressBookStorage.class); + private final Path filePath; - private Path filePath; - - public XmlAddressBookStorage(Path filePath) { + /** + * Reused from https://github.com/se-edu/addressbook-level4 solutions + * @param filePath + */ + public XmlAccountListStorage(Path filePath) { this.filePath = filePath; } - public Path getAddressBookFilePath() { + public Path getAccountListFilePath() { return filePath; } @Override - public Optional readAddressBook() throws DataConversionException, IOException { - return readAddressBook(filePath); + public Optional readAccountList() throws DataConversionException, IOException { + return readAccountList(filePath); } /** - * Similar to {@link #readAddressBook()} + * Similar to {@link #readAccountList()} * @param filePath location of the data. Cannot be null * @throws DataConversionException if the file is not in the correct format. */ - public Optional readAddressBook(Path filePath) throws DataConversionException, - FileNotFoundException { + public Optional readAccountList(Path filePath) throws DataConversionException, + FileNotFoundException { requireNonNull(filePath); if (!Files.exists(filePath)) { - logger.info("AddressBook file " + filePath + " not found"); + logger.info("AccountList file " + filePath + " not found"); return Optional.empty(); } - XmlSerializableAddressBook xmlAddressBook = XmlFileStorage.loadDataFromSaveFile(filePath); + XmlSerializableAccountList xmlAccountList = XmlFileStorage.loadAccountListDataFromSaveFile(filePath); try { - return Optional.of(xmlAddressBook.toModelType()); + return Optional.of(xmlAccountList.toModelType()); } catch (IllegalValueException ive) { logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); throw new DataConversionException(ive); @@ -61,20 +64,20 @@ public Optional readAddressBook(Path filePath) throws DataC } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, filePath); + public void saveAccountList(ReadOnlyAccountList accountList) throws IOException { + saveAccountList(accountList, filePath); } /** - * Similar to {@link #saveAddressBook(ReadOnlyAddressBook)} + * Similar to {@link #saveAccountList(ReadOnlyAccountList)} * @param filePath location of the data. Cannot be null */ - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { - requireNonNull(addressBook); + public void saveAccountList(ReadOnlyAccountList accountList, Path filePath) throws IOException { + requireNonNull(accountList); requireNonNull(filePath); FileUtil.createIfMissing(filePath); - XmlFileStorage.saveDataToFile(filePath, new XmlSerializableAddressBook(addressBook)); + XmlFileStorage.saveAccountListDataToFile(filePath, new XmlSerializableAccountList(accountList)); } } diff --git a/src/main/java/seedu/address/storage/XmlAdaptedAccount.java b/src/main/java/seedu/address/storage/XmlAdaptedAccount.java new file mode 100644 index 000000000000..80a9d28afaec --- /dev/null +++ b/src/main/java/seedu/address/storage/XmlAdaptedAccount.java @@ -0,0 +1,90 @@ +package seedu.address.storage; + +import java.util.Objects; + +import javax.xml.bind.annotation.XmlElement; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.account.Account; +import seedu.address.model.account.Password; +import seedu.address.model.account.Username; + +/** + * JAXB-friendly version of the Account. + */ +public class XmlAdaptedAccount { + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Account's %s field is missing!"; + + @XmlElement(required = true) + private String username; + @XmlElement(required = true) + private String password; + + + /** + * Constructs an XmlAdaptedAccount. + * This is the no-arg constructor that is required by JAXB. + */ + public XmlAdaptedAccount() {} + + /** + * Constructs an {@code XmlAdaptedItem} with the given item details. + */ + public XmlAdaptedAccount(String username, String password) { + this.username = username; + this.password = password; + } + + /** + * Converts a given Account into this class for JAXB use. + * + * @param source future changes to this will not affect the created XmlAdaptedItem + */ + public XmlAdaptedAccount(Account source) { + username = source.getUsername().fullUsername; + password = source.getPassword().toString(); + } + + /** + * Converts this jaxb-friendly adapted account object into the model's Account object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted account + */ + public Account toModelType() throws IllegalValueException { + + if (username == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Username.class + .getSimpleName())); + } + if (!Username.isValidUsername(username)) { + throw new IllegalValueException(Username.MESSAGE_USERNAME_CONSTRAINTS); + } + final Username modelUsername = new Username(username); + + if (password == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Password.class + .getSimpleName())); + } + if (!Password.isValidPassword(password)) { + throw new IllegalValueException(Password.MESSAGE_PASSWORD_CONSTRAINTS); + } + final Password modelPassword = new Password(password); + + return new Account(modelUsername, modelPassword); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof XmlAdaptedAccount)) { + return false; + } + + XmlAdaptedAccount otherAccount = (XmlAdaptedAccount) other; + return Objects.equals(username, otherAccount.username) + && Objects.equals(password, otherAccount.password); + } +} diff --git a/src/main/java/seedu/address/storage/XmlAdaptedItem.java b/src/main/java/seedu/address/storage/XmlAdaptedItem.java new file mode 100644 index 000000000000..89fe6d4eaaf2 --- /dev/null +++ b/src/main/java/seedu/address/storage/XmlAdaptedItem.java @@ -0,0 +1,146 @@ +package seedu.address.storage; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.xml.bind.annotation.XmlElement; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.item.Item; +import seedu.address.model.item.Loststatus; +import seedu.address.model.item.Name; +import seedu.address.model.item.Quantity; +import seedu.address.model.item.Status; +import seedu.address.model.tag.Tag; + +/** + * JAXB-friendly version of the Item. + */ +public class XmlAdaptedItem { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Item's %s field is missing!"; + + @XmlElement(required = true) + private String name; + @XmlElement(required = true) + private String quantity; + @XmlElement(required = true) + private String minQuantity; + @XmlElement(required = true) + private Status status = new Status(); + @XmlElement(required = true) + private Loststatus loststatus = new Loststatus(); + + @XmlElement + private List tagged = new ArrayList<>(); + + /** + * Constructs an XmlAdaptedItem. + * This is the no-arg constructor that is required by JAXB. + */ + public XmlAdaptedItem() {} + + /** + * Constructs an {@code XmlAdaptedItem} with the given item details. + */ + public XmlAdaptedItem(String name, String quantity, String minQuantity, + Loststatus loststatus, Status status, List tagged) { + this.name = name; + this.quantity = quantity; + this.minQuantity = minQuantity; + if (loststatus != null) { + this.loststatus = loststatus; + } + if (status != null) { + this.status = status; + } + if (tagged != null) { + this.tagged = new ArrayList<>(tagged); + } + } + + /** + * Converts a given Item into this class for JAXB use. + * + * @param source future changes to this will not affect the created XmlAdaptedItem + */ + public XmlAdaptedItem(Item source) { + name = source.getName().fullName; + quantity = source.getQuantity().toString(); + minQuantity = source.getMinQuantity().toString(); + loststatus = source.getLoststatus(); + status = source.getStatus(); + tagged = source.getTags().stream() + .map(XmlAdaptedTag::new) + .collect(Collectors.toList()); + } + + public Loststatus loststatus() { + return loststatus; + } + + public Status getStatus() { + return status; + } + /** + * Converts this jaxb-friendly adapted item object into the model's Item object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted item + */ + public Item toModelType() throws IllegalValueException { + final List itemTags = new ArrayList<>(); + for (XmlAdaptedTag tag : tagged) { + itemTags.add(tag.toModelType()); + } + + if (name == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); + } + if (!Name.isValidName(name)) { + throw new IllegalValueException(Name.MESSAGE_NAME_CONSTRAINTS); + } + final Name modelName = new Name(name); + + if (quantity == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, "Quantity")); + } + if (Integer.parseInt(quantity) <= 0) { + throw new IllegalValueException(Quantity.MESSAGE_QUANTITY_CONSTRAINTS); + } + final Quantity modelQuantity = new Quantity(quantity.toString()); + + if (minQuantity == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, "Minimum Quantity")); + } + if (Integer.parseInt(minQuantity) <= 0) { + throw new IllegalValueException(Quantity.MESSAGE_QUANTITY_CONSTRAINTS); + } + final Quantity modelMinQuantity = new Quantity(minQuantity.toString()); + + final Set modelTags = new HashSet<>(itemTags); + return new Item(modelName, modelQuantity, modelMinQuantity, loststatus, status, modelTags); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof XmlAdaptedItem)) { + return false; + } + + XmlAdaptedItem otherItem = (XmlAdaptedItem) other; + return Objects.equals(name, otherItem.name) + && Objects.equals(quantity, otherItem.quantity) + && Objects.equals(minQuantity, otherItem.minQuantity) + && Objects.equals(loststatus, otherItem.loststatus) + && Objects.equals(status, otherItem.status) + && tagged.equals(otherItem.tagged); + } +} diff --git a/src/main/java/seedu/address/storage/XmlAdaptedLoanList.java b/src/main/java/seedu/address/storage/XmlAdaptedLoanList.java new file mode 100644 index 000000000000..7c390426d1ac --- /dev/null +++ b/src/main/java/seedu/address/storage/XmlAdaptedLoanList.java @@ -0,0 +1,37 @@ +package seedu.address.storage; + +import java.util.ArrayList; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +//@@author ChewKinWhye + +/** + * JAXB-friendly loan list. + */ + +@XmlRootElement(name = "LoanList") +@XmlAccessorType(XmlAccessType.FIELD) + +public class XmlAdaptedLoanList { + @XmlElement(name = "Loaner") + private ArrayList xmlAdaptedLoanList = null; + + public XmlAdaptedLoanList() { + xmlAdaptedLoanList = new ArrayList<>(); + } + public XmlAdaptedLoanList(ArrayList xmlAdaptedLoanList) { + this.xmlAdaptedLoanList = xmlAdaptedLoanList; + } + public ArrayList getLoanList() { + return xmlAdaptedLoanList; + } + + public void addLoaner(XmlAdaptedLoanerDescription toAdd) { + xmlAdaptedLoanList.add(toAdd); + } + +} diff --git a/src/main/java/seedu/address/storage/XmlAdaptedLoanerDescription.java b/src/main/java/seedu/address/storage/XmlAdaptedLoanerDescription.java new file mode 100644 index 000000000000..194fc88253c5 --- /dev/null +++ b/src/main/java/seedu/address/storage/XmlAdaptedLoanerDescription.java @@ -0,0 +1,59 @@ +package seedu.address.storage; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; + +import seedu.address.model.item.LoanerDescription; + +//@@author ChewKinWhye + +/** + * JAXB-friendly version of the LoanerDescription. + */ +@XmlRootElement(name = "Loaner") +@XmlAccessorType(XmlAccessType.FIELD) + +public class XmlAdaptedLoanerDescription { + private String itemName; + private String loanerName; + private Integer quantity; + + public XmlAdaptedLoanerDescription() { + + } + public XmlAdaptedLoanerDescription(String itemName, String loanerName, Integer quantity) { + this.itemName = itemName; + this.loanerName = loanerName; + this.quantity = quantity; + } + public XmlAdaptedLoanerDescription(LoanerDescription loaner) { + itemName = loaner.getItemName().toString(); + loanerName = loaner.getLoanerName().toString(); + quantity = loaner.getQuantity().toInteger(); + } + + public String getItemName() { + return itemName; + } + + public void setItemName(String itemName) { + this.itemName = itemName; + } + + public String getLoanerName() { + return loanerName; + } + + public void setLoanerName(String loanerName) { + this.loanerName = loanerName; + } + + public Integer getQuantity() { + return quantity; + } + + public void setQuantity(Integer quantity) { + this.quantity = quantity; + } +} diff --git a/src/main/java/seedu/address/storage/XmlAdaptedPerson.java b/src/main/java/seedu/address/storage/XmlAdaptedPerson.java deleted file mode 100644 index c03785e5700f..000000000000 --- a/src/main/java/seedu/address/storage/XmlAdaptedPerson.java +++ /dev/null @@ -1,137 +0,0 @@ -package seedu.address.storage; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; - -import javax.xml.bind.annotation.XmlElement; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * JAXB-friendly version of the Person. - */ -public class XmlAdaptedPerson { - - public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!"; - - @XmlElement(required = true) - private String name; - @XmlElement(required = true) - private String phone; - @XmlElement(required = true) - private String email; - @XmlElement(required = true) - private String address; - - @XmlElement - private List tagged = new ArrayList<>(); - - /** - * Constructs an XmlAdaptedPerson. - * This is the no-arg constructor that is required by JAXB. - */ - public XmlAdaptedPerson() {} - - /** - * Constructs an {@code XmlAdaptedPerson} with the given person details. - */ - public XmlAdaptedPerson(String name, String phone, String email, String address, List tagged) { - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - if (tagged != null) { - this.tagged = new ArrayList<>(tagged); - } - } - - /** - * Converts a given Person into this class for JAXB use. - * - * @param source future changes to this will not affect the created XmlAdaptedPerson - */ - public XmlAdaptedPerson(Person source) { - name = source.getName().fullName; - phone = source.getPhone().value; - email = source.getEmail().value; - address = source.getAddress().value; - tagged = source.getTags().stream() - .map(XmlAdaptedTag::new) - .collect(Collectors.toList()); - } - - /** - * Converts this jaxb-friendly adapted person object into the model's Person object. - * - * @throws IllegalValueException if there were any data constraints violated in the adapted person - */ - public Person toModelType() throws IllegalValueException { - final List personTags = new ArrayList<>(); - for (XmlAdaptedTag tag : tagged) { - personTags.add(tag.toModelType()); - } - - if (name == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); - } - if (!Name.isValidName(name)) { - throw new IllegalValueException(Name.MESSAGE_NAME_CONSTRAINTS); - } - final Name modelName = new Name(name); - - if (phone == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName())); - } - if (!Phone.isValidPhone(phone)) { - throw new IllegalValueException(Phone.MESSAGE_PHONE_CONSTRAINTS); - } - final Phone modelPhone = new Phone(phone); - - if (email == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName())); - } - if (!Email.isValidEmail(email)) { - throw new IllegalValueException(Email.MESSAGE_EMAIL_CONSTRAINTS); - } - final Email modelEmail = new Email(email); - - if (address == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName())); - } - if (!Address.isValidAddress(address)) { - throw new IllegalValueException(Address.MESSAGE_ADDRESS_CONSTRAINTS); - } - final Address modelAddress = new Address(address); - - final Set modelTags = new HashSet<>(personTags); - return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags); - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - if (!(other instanceof XmlAdaptedPerson)) { - return false; - } - - XmlAdaptedPerson otherPerson = (XmlAdaptedPerson) other; - return Objects.equals(name, otherPerson.name) - && Objects.equals(phone, otherPerson.phone) - && Objects.equals(email, otherPerson.email) - && Objects.equals(address, otherPerson.address) - && tagged.equals(otherPerson.tagged); - } -} diff --git a/src/main/java/seedu/address/storage/XmlAdaptedTag.java b/src/main/java/seedu/address/storage/XmlAdaptedTag.java index d3e2d8be9c4f..132ee0d103ec 100644 --- a/src/main/java/seedu/address/storage/XmlAdaptedTag.java +++ b/src/main/java/seedu/address/storage/XmlAdaptedTag.java @@ -38,7 +38,7 @@ public XmlAdaptedTag(Tag source) { /** * Converts this jaxb-friendly adapted tag object into the model's Tag object. * - * @throws IllegalValueException if there were any data constraints violated in the adapted person + * @throws IllegalValueException if there were any data constraints violated in the adapted item */ public Tag toModelType() throws IllegalValueException { if (!Tag.isValidTagName(tagName)) { diff --git a/src/main/java/seedu/address/storage/XmlFileStorage.java b/src/main/java/seedu/address/storage/XmlFileStorage.java index d8f65dc036ab..1356b74011dd 100644 --- a/src/main/java/seedu/address/storage/XmlFileStorage.java +++ b/src/main/java/seedu/address/storage/XmlFileStorage.java @@ -9,28 +9,52 @@ import seedu.address.commons.util.XmlUtil; /** - * Stores addressbook data in an XML file + * Stores stocklist data in an XML file */ public class XmlFileStorage { /** - * Saves the given addressbook data to the specified file. + * Saves the given stocklist data to the specified file. */ - public static void saveDataToFile(Path file, XmlSerializableAddressBook addressBook) + public static void saveStockListDataToFile(Path file, XmlSerializableStockList stockList) throws FileNotFoundException { try { - XmlUtil.saveDataToFile(file, addressBook); + XmlUtil.saveDataToFile(file, stockList); } catch (JAXBException e) { throw new AssertionError("Unexpected exception " + e.getMessage(), e); } } /** - * Returns address book in the file or an empty address book + * Saves the given account list data to the specified file. */ - public static XmlSerializableAddressBook loadDataFromSaveFile(Path file) throws DataConversionException, + public static void saveAccountListDataToFile(Path file, XmlSerializableAccountList accountList) + throws FileNotFoundException { + try { + XmlUtil.saveDataToFile(file, accountList); + } catch (JAXBException e) { + throw new AssertionError("Unexpected exception " + e.getMessage(), e); + } + } + + /** + * Returns stock list in the file or an empty stock list + */ + public static XmlSerializableStockList loadStockListDataFromSaveFile(Path file) throws DataConversionException, FileNotFoundException { try { - return XmlUtil.getDataFromFile(file, XmlSerializableAddressBook.class); + return XmlUtil.getDataFromFile(file, XmlSerializableStockList.class); + } catch (JAXBException e) { + throw new DataConversionException(e); + } + } + + /** + * Returns account list in the file or an empty account list + */ + public static XmlSerializableAccountList loadAccountListDataFromSaveFile(Path file) throws DataConversionException, + FileNotFoundException { + try { + return XmlUtil.getDataFromFile(file, XmlSerializableAccountList.class); } catch (JAXBException e) { throw new DataConversionException(e); } diff --git a/src/main/java/seedu/address/storage/XmlSerializableAccountList.java b/src/main/java/seedu/address/storage/XmlSerializableAccountList.java new file mode 100644 index 000000000000..46f7e62fbb66 --- /dev/null +++ b/src/main/java/seedu/address/storage/XmlSerializableAccountList.java @@ -0,0 +1,70 @@ +package seedu.address.storage; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.AccountList; +import seedu.address.model.ReadOnlyAccountList; +import seedu.address.model.account.Account; + +/** + * An Immutable AccountList that is serializable to XML format + */ +@XmlRootElement(name = "accountlist") +public class XmlSerializableAccountList { + public static final String MESSAGE_DUPLICATE_ACCOUNT = "Account list contains duplicate account(s)."; + + @XmlElement + private List accounts; + + /** + * Creates an empty XmlSerializableAccountList. + * This empty constructor is required for marshalling. + */ + public XmlSerializableAccountList() { + accounts = new ArrayList<>(); + } + + /** + * Conversion + */ + public XmlSerializableAccountList(ReadOnlyAccountList src) { + this(); + accounts.addAll(src.getAccountList().stream().map(XmlAdaptedAccount::new).collect(Collectors.toList())); + } + + /** + * Converts account list into the model's {@code AccountList} object. + * + * @throws IllegalValueException if there were any data constraints violated or duplicates in the + * {@code XmlAdaptedAccount}. + */ + public AccountList toModelType() throws IllegalValueException { + AccountList accountList = new AccountList(); + for (XmlAdaptedAccount p : accounts) { + Account account = p.toModelType(); + if (accountList.hasAccount(account)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_ACCOUNT); + } + accountList.addAccount(account); + } + return accountList; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof XmlSerializableAccountList)) { + return false; + } + return accounts.equals(((XmlSerializableAccountList) other).accounts); + } +} diff --git a/src/main/java/seedu/address/storage/XmlSerializableAddressBook.java b/src/main/java/seedu/address/storage/XmlSerializableAddressBook.java deleted file mode 100644 index b85fa4a8f07e..000000000000 --- a/src/main/java/seedu/address/storage/XmlSerializableAddressBook.java +++ /dev/null @@ -1,71 +0,0 @@ -package seedu.address.storage; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; - -/** - * An Immutable AddressBook that is serializable to XML format - */ -@XmlRootElement(name = "addressbook") -public class XmlSerializableAddressBook { - - public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s)."; - - @XmlElement - private List persons; - - /** - * Creates an empty XmlSerializableAddressBook. - * This empty constructor is required for marshalling. - */ - public XmlSerializableAddressBook() { - persons = new ArrayList<>(); - } - - /** - * Conversion - */ - public XmlSerializableAddressBook(ReadOnlyAddressBook src) { - this(); - persons.addAll(src.getPersonList().stream().map(XmlAdaptedPerson::new).collect(Collectors.toList())); - } - - /** - * Converts this addressbook into the model's {@code AddressBook} object. - * - * @throws IllegalValueException if there were any data constraints violated or duplicates in the - * {@code XmlAdaptedPerson}. - */ - public AddressBook toModelType() throws IllegalValueException { - AddressBook addressBook = new AddressBook(); - for (XmlAdaptedPerson p : persons) { - Person person = p.toModelType(); - if (addressBook.hasPerson(person)) { - throw new IllegalValueException(MESSAGE_DUPLICATE_PERSON); - } - addressBook.addPerson(person); - } - return addressBook; - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - if (!(other instanceof XmlSerializableAddressBook)) { - return false; - } - return persons.equals(((XmlSerializableAddressBook) other).persons); - } -} diff --git a/src/main/java/seedu/address/storage/XmlSerializableStockList.java b/src/main/java/seedu/address/storage/XmlSerializableStockList.java new file mode 100644 index 000000000000..b8aa78c2fcca --- /dev/null +++ b/src/main/java/seedu/address/storage/XmlSerializableStockList.java @@ -0,0 +1,71 @@ +package seedu.address.storage; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.ReadOnlyStockList; +import seedu.address.model.StockList; +import seedu.address.model.item.Item; + +/** + * An Immutable StockList that is serializable to XML format + */ +@XmlRootElement(name = "stocklist") +public class XmlSerializableStockList { + + public static final String MESSAGE_DUPLICATE_ITEM = "Items list contains duplicate item(s)."; + + @XmlElement + private List items; + + /** + * Creates an empty XmlSerializableStockList. + * This empty constructor is required for marshalling. + */ + public XmlSerializableStockList() { + items = new ArrayList<>(); + } + + /** + * Conversion + */ + public XmlSerializableStockList(ReadOnlyStockList src) { + this(); + items.addAll(src.getItemList().stream().map(XmlAdaptedItem::new).collect(Collectors.toList())); + } + + /** + * Converts this stocklist into the model's {@code StockList} object. + * + * @throws IllegalValueException if there were any data constraints violated or duplicates in the + * {@code XmlAdaptedItem}. + */ + public StockList toModelType() throws IllegalValueException { + StockList stockList = new StockList(); + for (XmlAdaptedItem p : items) { + Item item = p.toModelType(); + if (stockList.hasItem(item)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_ITEM); + } + stockList.addItem(item); + } + return stockList; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof XmlSerializableStockList)) { + return false; + } + return items.equals(((XmlSerializableStockList) other).items); + } +} diff --git a/src/main/java/seedu/address/storage/XmlStockListStorage.java b/src/main/java/seedu/address/storage/XmlStockListStorage.java new file mode 100644 index 000000000000..ad2c865484ea --- /dev/null +++ b/src/main/java/seedu/address/storage/XmlStockListStorage.java @@ -0,0 +1,92 @@ +package seedu.address.storage; + +import static java.util.Objects.requireNonNull; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.exceptions.DataConversionException; +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.commons.util.FileUtil; +import seedu.address.model.ReadOnlyStockList; + +/** + * A class to access StockList data stored as an xml file on the hard disk. + */ +public class XmlStockListStorage implements StockListStorage { + + private static final Logger logger = LogsCenter.getLogger(XmlStockListStorage.class); + + private final Path filePath; + + /** + * Reused from https://github.com/se-edu/addressbook-level4 solutions + * @param filePath + */ + public XmlStockListStorage(Path filePath) { + this.filePath = filePath; + } + + public Path getStockListFilePath() { + return filePath; + } + + @Override + public Optional readStockList() throws DataConversionException, IOException { + return readStockList(filePath); + } + + /** + * Similar to {@link #readStockList()} + * @param filePath location of the data. Cannot be null + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional readStockList(Path filePath) throws DataConversionException, + FileNotFoundException { + requireNonNull(filePath); + + if (!Files.exists(filePath)) { + logger.info("StockList file " + filePath + " not found"); + return Optional.empty(); + } + + XmlSerializableStockList xmlStockList = XmlFileStorage.loadStockListDataFromSaveFile(filePath); + try { + return Optional.of(xmlStockList.toModelType()); + } catch (IllegalValueException ive) { + logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); + throw new DataConversionException(ive); + } + } + + @Override + public void saveStockList(ReadOnlyStockList stockList) throws IOException { + saveStockList(stockList, filePath); + } + + /** + * Similar to {@link #saveStockList(ReadOnlyStockList)} + * @param filePath location of the data. Cannot be null + */ + public void saveStockList(ReadOnlyStockList stockList, Path filePath) throws IOException { + requireNonNull(stockList); + requireNonNull(filePath); + + FileUtil.createIfMissing(filePath); + XmlFileStorage.saveStockListDataToFile(filePath, new XmlSerializableStockList(stockList)); + } + //@@author kelvintankaiboon + @Override + public void saveStockListVersion(ReadOnlyStockList stockList, Path filePath) throws IOException { + requireNonNull(stockList); + requireNonNull(filePath); + + FileUtil.createIfMissing(filePath); + XmlFileStorage.saveStockListDataToFile(filePath, new XmlSerializableStockList(stockList)); + } +} diff --git a/src/main/java/seedu/address/ui/BrowserPanel.java b/src/main/java/seedu/address/ui/BrowserPanel.java index b43de90a2b9f..8e7d99b145c5 100644 --- a/src/main/java/seedu/address/ui/BrowserPanel.java +++ b/src/main/java/seedu/address/ui/BrowserPanel.java @@ -1,8 +1,17 @@ package seedu.address.ui; +import java.io.File; +import java.io.StringWriter; import java.net.URL; import java.util.logging.Logger; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + import com.google.common.eventbus.Subscribe; import javafx.application.Platform; @@ -12,8 +21,7 @@ import javafx.scene.web.WebView; import seedu.address.MainApp; import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.events.ui.PersonPanelSelectionChangedEvent; -import seedu.address.model.person.Person; +import seedu.address.commons.events.model.OpenStockListVersionEvent; /** * The Browser Panel of the App. @@ -23,7 +31,6 @@ public class BrowserPanel extends UiPart { public static final String DEFAULT_PAGE = "default.html"; public static final String SEARCH_PAGE_URL = "https://se-edu.github.io/addressbook-level4/DummySearchPage.html?name="; - private static final String FXML = "BrowserPanel.fxml"; private final Logger logger = LogsCenter.getLogger(getClass()); @@ -41,10 +48,6 @@ public BrowserPanel() { registerAsAnEventHandler(this); } - private void loadPersonPage(Person person) { - loadPage(SEARCH_PAGE_URL + person.getName().fullName); - } - public void loadPage(String url) { Platform.runLater(() -> browser.getEngine().load(url)); } @@ -57,6 +60,21 @@ private void loadDefaultPage() { loadPage(defaultPage.toExternalForm()); } + /** + * Loads the specified .xml file as a .html page. + */ + private void loadFileAsPage(String fileName) { + File jarPath = new File(MainApp.class.getProtectionDomain().getCodeSource().getLocation().getPath()); + String filePath = jarPath.getParent(); + String pathXsl = getClass().getResource("/docs/template.xsl").toString(); + try { + transformXml((filePath + "\\versions\\" + fileName), pathXsl); + } catch (Exception e) { + //handle exception + } + } + + /** * Frees resources allocated to the browser. */ @@ -65,8 +83,30 @@ public void freeResources() { } @Subscribe - private void handlePersonPanelSelectionChangedEvent(PersonPanelSelectionChangedEvent event) { + private void handleOpenStockListVersionEvent (OpenStockListVersionEvent event) { logger.info(LogsCenter.getEventHandlingLogMessage(event)); - loadPersonPage(event.getNewSelection()); + loadFileAsPage(event.fileName); + } + + /** + * Transforms the given .xml file with the given .xsl template and outputs onto the browser + * @param pathXml + * @param pathXsl + * @throws Exception + */ + private void transformXml (String pathXml, String pathXsl) throws Exception { + Source source = new StreamSource(pathXml); + Source xsl = new StreamSource(pathXsl); + final StreamResult output = new StreamResult(new StringWriter()); + TransformerFactory factory = TransformerFactory.newInstance(); + Transformer transformer = factory.newTransformer(xsl); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + transformer.setOutputProperty(OutputKeys.METHOD, "html"); + transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", + "4"); + transformer.transform(source, output); + browser.getEngine().loadContent(output.getWriter().toString()); } } diff --git a/src/main/java/seedu/address/ui/ItemCard.java b/src/main/java/seedu/address/ui/ItemCard.java new file mode 100644 index 000000000000..36bdcaebfc75 --- /dev/null +++ b/src/main/java/seedu/address/ui/ItemCard.java @@ -0,0 +1,97 @@ +package seedu.address.ui; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.address.model.item.Item; + + +/** + * An UI component that displays information of a {@code Item}. + */ +public class ItemCard extends UiPart { + + private static final String FXML = "ItemListCard.fxml"; + private static final String QUANTITY_TEXT = "Quantity : "; + private static final String MIN_QUANTITY_TEXT = "Minimum Quantity : "; + private static final String STATUS_READY_TEXT = "Status-Ready : "; + private static final String STATUS_ON_LOAN_TEXT = "Status-On_Loan : "; + private static final String STATUS_FAULTY_TEXT = "Status-Faulty : "; + + /** + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on StockList level 4 + */ + + public final Item item; + + @FXML + private HBox cardPane; + @FXML + private Label name; + @FXML + private Label id; + @FXML + private Label quantity; + @FXML + private Label minQuantity; + @FXML + private Label statusReady; + @FXML + private Label statusOnLoan; + @FXML + private Label statusFaulty; + @FXML + private Label quantityText; + @FXML + private Label minQuantityText; + @FXML + private Label statusReadyText; + @FXML + private Label statusOnLoanText; + @FXML + private Label statusFaultyText; + @FXML + private FlowPane tags; + + public ItemCard(Item item, int displayedIndex) { + super(FXML); + this.item = item; + id.setText(displayedIndex + ". "); + name.setText(item.getName().fullName); + quantity.setText(item.getQuantity().toString()); + minQuantity.setText(item.getMinQuantity().toString()); + statusReady.setText(item.getStatus().getStatusReady().toString()); + statusOnLoan.setText(item.getStatus().getStatusOnLoan().toString()); + statusFaulty.setText(item.getStatus().getStatusFaulty().toString()); + item.getTags().forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); + quantityText.setText(QUANTITY_TEXT); + minQuantityText.setText(MIN_QUANTITY_TEXT); + statusReadyText.setText(STATUS_READY_TEXT); + statusOnLoanText.setText(STATUS_ON_LOAN_TEXT); + statusFaultyText.setText(STATUS_FAULTY_TEXT); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof ItemCard)) { + return false; + } + + // state check + ItemCard card = (ItemCard) other; + return id.getText().equals(card.id.getText()) + && item.equals(card.item); + } +} diff --git a/src/main/java/seedu/address/ui/ItemListPanel.java b/src/main/java/seedu/address/ui/ItemListPanel.java new file mode 100644 index 000000000000..1c315531a339 --- /dev/null +++ b/src/main/java/seedu/address/ui/ItemListPanel.java @@ -0,0 +1,83 @@ +package seedu.address.ui; + +import java.util.logging.Logger; + +import com.google.common.eventbus.Subscribe; + +import javafx.application.Platform; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.layout.Region; +import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.events.ui.ItemPanelSelectionChangedEvent; +import seedu.address.commons.events.ui.JumpToListRequestEvent; +import seedu.address.model.item.Item; + +/** + * Panel containing the list of items. + */ +public class ItemListPanel extends UiPart { + private static final String FXML = "ItemListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(ItemListPanel.class); + + @FXML + private ListView itemListView; + + public ItemListPanel(ObservableList itemList) { + super(FXML); + setConnections(itemList); + registerAsAnEventHandler(this); + } + + private void setConnections(ObservableList itemList) { + itemListView.setItems(itemList); + itemListView.setCellFactory(listView -> new ItemListViewCell()); + setEventHandlerForSelectionChangeEvent(); + } + + private void setEventHandlerForSelectionChangeEvent() { + itemListView.getSelectionModel().selectedItemProperty() + .addListener((observable, oldValue, newValue) -> { + if (newValue != null) { + logger.fine("Selection in item list panel changed to : '" + newValue + "'"); + raise(new ItemPanelSelectionChangedEvent(newValue)); + } + }); + } + + /** + * Scrolls to the {@code ItemCard} at the {@code index} and selects it. + */ + private void scrollTo(int index) { + Platform.runLater(() -> { + itemListView.scrollTo(index); + itemListView.getSelectionModel().clearAndSelect(index); + }); + } + + @Subscribe + private void handleJumpToListRequestEvent(JumpToListRequestEvent event) { + logger.info(LogsCenter.getEventHandlingLogMessage(event)); + scrollTo(event.targetIndex); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Item} using a {@code ItemCard}. + */ + class ItemListViewCell extends ListCell { + @Override + protected void updateItem(Item item, boolean empty) { + super.updateItem(item, empty); + + if (empty || item == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new ItemCard(item, getIndex() + 1).getRoot()); + } + } + } + +} diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java index 0e361a4d7baf..d17dc23c3e62 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/address/ui/MainWindow.java @@ -35,7 +35,7 @@ public class MainWindow extends UiPart { // Independent Ui parts residing in this Ui container private BrowserPanel browserPanel; - private PersonListPanel personListPanel; + private ItemListPanel itemListPanel; private Config config; private UserPrefs prefs; private HelpWindow helpWindow; @@ -50,7 +50,7 @@ public class MainWindow extends UiPart { private MenuItem helpMenuItem; @FXML - private StackPane personListPanelPlaceholder; + private StackPane itemListPanelPlaceholder; @FXML private StackPane resultDisplayPlaceholder; @@ -122,13 +122,13 @@ void fillInnerParts() { browserPanel = new BrowserPanel(); browserPlaceholder.getChildren().add(browserPanel.getRoot()); - personListPanel = new PersonListPanel(logic.getFilteredPersonList()); - personListPanelPlaceholder.getChildren().add(personListPanel.getRoot()); + itemListPanel = new ItemListPanel(logic.getFilteredItemList()); + itemListPanelPlaceholder.getChildren().add(itemListPanel.getRoot()); ResultDisplay resultDisplay = new ResultDisplay(); resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot()); - StatusBarFooter statusBarFooter = new StatusBarFooter(prefs.getAddressBookFilePath()); + StatusBarFooter statusBarFooter = new StatusBarFooter(prefs.getStockListFilePath()); statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot()); CommandBox commandBox = new CommandBox(logic); @@ -187,8 +187,8 @@ private void handleExit() { raise(new ExitAppRequestEvent()); } - public PersonListPanel getPersonListPanel() { - return personListPanel; + public ItemListPanel getItemListPanel() { + return itemListPanel; } void releaseResources() { diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java deleted file mode 100644 index f6727ea83abd..000000000000 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ /dev/null @@ -1,70 +0,0 @@ -package seedu.address.ui; - -import javafx.fxml.FXML; -import javafx.scene.control.Label; -import javafx.scene.layout.FlowPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Region; -import seedu.address.model.person.Person; - -/** - * An UI component that displays information of a {@code Person}. - */ -public class PersonCard extends UiPart { - - private static final String FXML = "PersonListCard.fxml"; - - /** - * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. - * As a consequence, UI elements' variable names cannot be set to such keywords - * or an exception will be thrown by JavaFX during runtime. - * - * @see The issue on AddressBook level 4 - */ - - public final Person person; - - @FXML - private HBox cardPane; - @FXML - private Label name; - @FXML - private Label id; - @FXML - private Label phone; - @FXML - private Label address; - @FXML - private Label email; - @FXML - private FlowPane tags; - - public PersonCard(Person person, int displayedIndex) { - super(FXML); - this.person = person; - id.setText(displayedIndex + ". "); - name.setText(person.getName().fullName); - phone.setText(person.getPhone().value); - address.setText(person.getAddress().value); - email.setText(person.getEmail().value); - person.getTags().forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); - } - - @Override - public boolean equals(Object other) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof PersonCard)) { - return false; - } - - // state check - PersonCard card = (PersonCard) other; - return id.getText().equals(card.id.getText()) - && person.equals(card.person); - } -} diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/address/ui/PersonListPanel.java deleted file mode 100644 index 80080adb4305..000000000000 --- a/src/main/java/seedu/address/ui/PersonListPanel.java +++ /dev/null @@ -1,83 +0,0 @@ -package seedu.address.ui; - -import java.util.logging.Logger; - -import com.google.common.eventbus.Subscribe; - -import javafx.application.Platform; -import javafx.collections.ObservableList; -import javafx.fxml.FXML; -import javafx.scene.control.ListCell; -import javafx.scene.control.ListView; -import javafx.scene.layout.Region; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.events.ui.JumpToListRequestEvent; -import seedu.address.commons.events.ui.PersonPanelSelectionChangedEvent; -import seedu.address.model.person.Person; - -/** - * Panel containing the list of persons. - */ -public class PersonListPanel extends UiPart { - private static final String FXML = "PersonListPanel.fxml"; - private final Logger logger = LogsCenter.getLogger(PersonListPanel.class); - - @FXML - private ListView personListView; - - public PersonListPanel(ObservableList personList) { - super(FXML); - setConnections(personList); - registerAsAnEventHandler(this); - } - - private void setConnections(ObservableList personList) { - personListView.setItems(personList); - personListView.setCellFactory(listView -> new PersonListViewCell()); - setEventHandlerForSelectionChangeEvent(); - } - - private void setEventHandlerForSelectionChangeEvent() { - personListView.getSelectionModel().selectedItemProperty() - .addListener((observable, oldValue, newValue) -> { - if (newValue != null) { - logger.fine("Selection in person list panel changed to : '" + newValue + "'"); - raise(new PersonPanelSelectionChangedEvent(newValue)); - } - }); - } - - /** - * Scrolls to the {@code PersonCard} at the {@code index} and selects it. - */ - private void scrollTo(int index) { - Platform.runLater(() -> { - personListView.scrollTo(index); - personListView.getSelectionModel().clearAndSelect(index); - }); - } - - @Subscribe - private void handleJumpToListRequestEvent(JumpToListRequestEvent event) { - logger.info(LogsCenter.getEventHandlingLogMessage(event)); - scrollTo(event.targetIndex); - } - - /** - * Custom {@code ListCell} that displays the graphics of a {@code Person} using a {@code PersonCard}. - */ - class PersonListViewCell extends ListCell { - @Override - protected void updateItem(Person person, boolean empty) { - super.updateItem(person, empty); - - if (empty || person == null) { - setGraphic(null); - setText(null); - } else { - setGraphic(new PersonCard(person, getIndex() + 1).getRoot()); - } - } - } - -} diff --git a/src/main/java/seedu/address/ui/StatusBarFooter.java b/src/main/java/seedu/address/ui/StatusBarFooter.java index f6ba29502422..1c1d970e0a6b 100644 --- a/src/main/java/seedu/address/ui/StatusBarFooter.java +++ b/src/main/java/seedu/address/ui/StatusBarFooter.java @@ -14,7 +14,8 @@ import javafx.fxml.FXML; import javafx.scene.layout.Region; import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.events.model.AddressBookChangedEvent; +import seedu.address.commons.events.model.AccountListChangedEvent; +import seedu.address.commons.events.model.StockListChangedEvent; /** * A ui for the status bar that is displayed at the footer of the application. @@ -74,10 +75,20 @@ private void setSyncStatus(String status) { } @Subscribe - public void handleAddressBookChangedEvent(AddressBookChangedEvent abce) { + public void handleStockListChangedEvent(StockListChangedEvent stockList) { long now = clock.millis(); String lastUpdated = new Date(now).toString(); - logger.info(LogsCenter.getEventHandlingLogMessage(abce, "Setting last updated status to " + lastUpdated)); + logger.info(LogsCenter.getEventHandlingLogMessage(stockList, "Setting last updated status to " + + lastUpdated)); + setSyncStatus(String.format(SYNC_STATUS_UPDATED, lastUpdated)); + } + + @Subscribe + public void handleAccountListChangedEvent(AccountListChangedEvent accountList) { + long now = clock.millis(); + String lastUpdated = new Date(now).toString(); + logger.info(LogsCenter.getEventHandlingLogMessage(accountList, "Setting last updated status to " + + lastUpdated)); setSyncStatus(String.format(SYNC_STATUS_UPDATED, lastUpdated)); } } diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/seedu/address/ui/UiManager.java index 3fd3c17be156..330468040647 100644 --- a/src/main/java/seedu/address/ui/UiManager.java +++ b/src/main/java/seedu/address/ui/UiManager.java @@ -30,7 +30,7 @@ public class UiManager extends ComponentManager implements Ui { public static final String FILE_OPS_ERROR_DIALOG_CONTENT_MESSAGE = "Could not save data to file"; private static final Logger logger = LogsCenter.getLogger(UiManager.class); - private static final String ICON_APPLICATION = "/images/address_book_32.png"; + private static final String ICON_APPLICATION = "/images/stock_list_32.png"; private Logic logic; private Config config; diff --git a/src/main/java/seedu/address/ui/UiPart.java b/src/main/java/seedu/address/ui/UiPart.java index 5c237e57154b..a76d38932674 100644 --- a/src/main/java/seedu/address/ui/UiPart.java +++ b/src/main/java/seedu/address/ui/UiPart.java @@ -26,7 +26,7 @@ public abstract class UiPart { * The FXML file must not specify the {@code fx:controller} attribute. */ public UiPart(URL fxmlFileUrl) { - loadFxmlFile(fxmlFileUrl, null); + loadFxmlFile(fxmlFileUrl, getRoot()); } /** diff --git a/src/main/resources/docs/template.xsl b/src/main/resources/docs/template.xsl new file mode 100644 index 000000000000..6a945a624999 --- /dev/null +++ b/src/main/resources/docs/template.xsl @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameQuantityMinimum QuantityStatus-ReadyStatus-On LoanStatus-FaultyLost
+ + +
+
diff --git a/src/main/resources/images/address_book_32.png b/src/main/resources/images/address_book_32.png deleted file mode 100644 index 29810cf1fd93..000000000000 Binary files a/src/main/resources/images/address_book_32.png and /dev/null differ diff --git a/src/main/resources/images/stock_list_32.png b/src/main/resources/images/stock_list_32.png new file mode 100644 index 000000000000..7297209a5bf2 Binary files /dev/null and b/src/main/resources/images/stock_list_32.png differ diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css index c8941ea18263..1b740184167a 100644 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/view/DarkTheme.css @@ -327,7 +327,7 @@ -fx-text-fill: white; } -#filterField, #personListPanel, #personWebpage { +#filterField, #itemListPanel, #itemWebpage { -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0); } diff --git a/src/main/resources/view/ItemListCard.fxml b/src/main/resources/view/ItemListCard.fxml new file mode 100644 index 000000000000..df42807f7fe8 --- /dev/null +++ b/src/main/resources/view/ItemListCard.fxml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/ItemListPanel.fxml b/src/main/resources/view/ItemListPanel.fxml new file mode 100644 index 000000000000..de68a9c66eb9 --- /dev/null +++ b/src/main/resources/view/ItemListPanel.fxml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index daf386d8f5b8..98e1d9e88a4f 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -14,7 +14,7 @@ - + @@ -47,11 +47,11 @@ - + - + diff --git a/src/main/resources/view/PersonListPanel.fxml b/src/main/resources/view/PersonListPanel.fxml index 8836d323cc5d..d2e3252670a0 100644 --- a/src/main/resources/view/PersonListPanel.fxml +++ b/src/main/resources/view/PersonListPanel.fxml @@ -4,5 +4,5 @@ - + diff --git a/src/main/resources/view/default.html b/src/main/resources/view/default.html index c49aa0f61682..1940a3d5218a 100644 --- a/src/main/resources/view/default.html +++ b/src/main/resources/view/default.html @@ -3,7 +3,6 @@ - diff --git a/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json b/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json index a312cecf8bad..54df085bde7a 100644 --- a/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json +++ b/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json @@ -9,5 +9,5 @@ "z" : 99 } }, - "addressBookFilePath" : "addressbook.xml" + "stockListFilePath" : "stocklist.xml" } diff --git a/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json b/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json index 412dbd7cac65..3a3f87ed2ca3 100644 --- a/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json +++ b/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json @@ -7,5 +7,5 @@ "y" : 100 } }, - "addressBookFilePath" : "addressbook.xml" + "stockListFilePath" : "stocklist.xml" } diff --git a/src/test/data/XmlAddressBookStorageTest/invalidAndValidPersonAddressBook.xml b/src/test/data/XmlAddressBookStorageTest/invalidAndValidPersonAddressBook.xml deleted file mode 100644 index 41e411568a5f..000000000000 --- a/src/test/data/XmlAddressBookStorageTest/invalidAndValidPersonAddressBook.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Hans Muster - 9482424 - hans@example.com -
4th street
-
- - - Hans Muster - 948asdf2424 - hans@example.com -
4th street
-
-
diff --git a/src/test/data/XmlAddressBookStorageTest/invalidPersonAddressBook.xml b/src/test/data/XmlAddressBookStorageTest/invalidPersonAddressBook.xml deleted file mode 100644 index cfa128e72828..000000000000 --- a/src/test/data/XmlAddressBookStorageTest/invalidPersonAddressBook.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - Ha!ns Mu@ster - 9482424 - hans@example.com -
4th street
-
-
diff --git a/src/test/data/XmlSerializableAddressBookTest/duplicatePersonAddressBook.xml b/src/test/data/XmlSerializableAddressBookTest/duplicatePersonAddressBook.xml deleted file mode 100644 index ac02230263d3..000000000000 --- a/src/test/data/XmlSerializableAddressBookTest/duplicatePersonAddressBook.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - Alice Pauline - 94351253 - alice@example.com -
123, Jurong West Ave 6, #08-111
- friends -
- - - - Alice Pauline - 94351253 - pauline@example.com -
4th street
-
- -
diff --git a/src/test/data/XmlSerializableAddressBookTest/invalidPersonAddressBook.xml b/src/test/data/XmlSerializableAddressBookTest/invalidPersonAddressBook.xml deleted file mode 100644 index 13d5b1cb1c8a..000000000000 --- a/src/test/data/XmlSerializableAddressBookTest/invalidPersonAddressBook.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - Hans Muster - 9482424 - hans@exam!32ple -
4th street
-
-
diff --git a/src/test/data/XmlSerializableAddressBookTest/typicalPersonsAddressBook.xml b/src/test/data/XmlSerializableAddressBookTest/typicalPersonsAddressBook.xml deleted file mode 100644 index d812b05e32bb..000000000000 --- a/src/test/data/XmlSerializableAddressBookTest/typicalPersonsAddressBook.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - Alice Pauline - 94351253 - alice@example.com -
123, Jurong West Ave 6, #08-111
- friends -
- - Benson Meier - 98765432 - johnd@example.com -
311, Clementi Ave 2, #02-25
- owesMoney - friends -
- - Carl Kurz - 95352563 - heinz@example.com -
wall street
-
- - Daniel Meier - 87652533 - cornelia@example.com -
10th street
- friends -
- - Elle Meyer - 9482224 - werner@example.com -
michegan ave
-
- - Fiona Kunz - 9482427 - lydia@example.com -
little tokyo
-
- - George Best - 9482442 - anna@example.com -
4th street
-
-
diff --git a/src/test/data/XmlSerializableStockListTest/duplicateItemStockList.xml b/src/test/data/XmlSerializableStockListTest/duplicateItemStockList.xml new file mode 100644 index 000000000000..bfcad54833f4 --- /dev/null +++ b/src/test/data/XmlSerializableStockListTest/duplicateItemStockList.xml @@ -0,0 +1,25 @@ + + + + + Arduino + 20 + 5 + 20 + 0 + 0 + Lab1 + + + + + Arduino + 20 + 5 + 20 + 0 + 0 + Lab1 + + + diff --git a/src/test/data/XmlSerializableStockListTest/invalidItemStockList.xml b/src/test/data/XmlSerializableStockListTest/invalidItemStockList.xml new file mode 100644 index 000000000000..4bea45f4b5ac --- /dev/null +++ b/src/test/data/XmlSerializableStockListTest/invalidItemStockList.xml @@ -0,0 +1,13 @@ + + + + + Arduino + -1 + 5 + 20 + 0 + 0 + Lab1 + + diff --git a/src/test/data/XmlSerializableStockListTest/typicalItemsStockList.xml b/src/test/data/XmlSerializableStockListTest/typicalItemsStockList.xml new file mode 100644 index 000000000000..889589c393b2 --- /dev/null +++ b/src/test/data/XmlSerializableStockListTest/typicalItemsStockList.xml @@ -0,0 +1,31 @@ + + + + + Arduino + 20 + 5 + 20 + 0 + 0 + Lab1 + + + RPLidar + 25 + 10 + 50 + 0 + 0 + lab1 + + + Motor + 100 + 20 + 30 + 0 + 0 + lab3 + + diff --git a/src/test/data/XmlAddressBookStorageTest/NotXmlFormatAddressBook.xml b/src/test/data/XmlStockListStorageTest/NotXmlFormatStockList.xml similarity index 100% rename from src/test/data/XmlAddressBookStorageTest/NotXmlFormatAddressBook.xml rename to src/test/data/XmlStockListStorageTest/NotXmlFormatStockList.xml diff --git a/src/test/data/XmlStockListStorageTest/invalidAndValidItemStockList.xml b/src/test/data/XmlStockListStorageTest/invalidAndValidItemStockList.xml new file mode 100644 index 000000000000..2f4018982f6d --- /dev/null +++ b/src/test/data/XmlStockListStorageTest/invalidAndValidItemStockList.xml @@ -0,0 +1,23 @@ + + + + + Arduino + 20 + 5 + 20 + 0 + 0 + Lab1 + + + + Arduino + -1 + 5 + 20 + 0 + 0 + Lab1 + + diff --git a/src/test/data/XmlStockListStorageTest/invalidItemStockList.xml b/src/test/data/XmlStockListStorageTest/invalidItemStockList.xml new file mode 100644 index 000000000000..4bea45f4b5ac --- /dev/null +++ b/src/test/data/XmlStockListStorageTest/invalidItemStockList.xml @@ -0,0 +1,13 @@ + + + + + Arduino + -1 + 5 + 20 + 0 + 0 + Lab1 + + diff --git a/src/test/data/XmlUtilTest/invalidItemField.xml b/src/test/data/XmlUtilTest/invalidItemField.xml new file mode 100644 index 000000000000..eacc65dfe11a --- /dev/null +++ b/src/test/data/XmlUtilTest/invalidItemField.xml @@ -0,0 +1,13 @@ + + + + Arduino + -1 + 5 + + 0 + 0 + 20 + + Lab1 + diff --git a/src/test/data/XmlUtilTest/invalidPersonField.xml b/src/test/data/XmlUtilTest/invalidPersonField.xml deleted file mode 100644 index ba49c971e884..000000000000 --- a/src/test/data/XmlUtilTest/invalidPersonField.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - Hans Muster - 9482asf424 - hans@example -
4th street
- friends -
diff --git a/src/test/data/XmlUtilTest/missingItemField.xml b/src/test/data/XmlUtilTest/missingItemField.xml new file mode 100644 index 000000000000..e84d4daa5660 --- /dev/null +++ b/src/test/data/XmlUtilTest/missingItemField.xml @@ -0,0 +1,12 @@ + + + + 20 + 5 + + 0 + 0 + 20 + + Lab1 + diff --git a/src/test/data/XmlUtilTest/missingPersonField.xml b/src/test/data/XmlUtilTest/missingPersonField.xml deleted file mode 100644 index c0da5c86d080..000000000000 --- a/src/test/data/XmlUtilTest/missingPersonField.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - 9482424 - hans@example -
4th street
- friends -
diff --git a/src/test/data/XmlUtilTest/tempAddressBook.xml b/src/test/data/XmlUtilTest/tempAddressBook.xml deleted file mode 100644 index 4773cf598f4b..000000000000 --- a/src/test/data/XmlUtilTest/tempAddressBook.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - 1 - John - Doe - - - - - - diff --git a/src/test/data/XmlUtilTest/tempStockList.xml b/src/test/data/XmlUtilTest/tempStockList.xml new file mode 100644 index 000000000000..6f45dcd4afd1 --- /dev/null +++ b/src/test/data/XmlUtilTest/tempStockList.xml @@ -0,0 +1,15 @@ + + + + 1 + Arduino + 20 + 5 + + 0 + 0 + 20 + + Lab1 + + diff --git a/src/test/data/XmlUtilTest/validAddressBook.xml b/src/test/data/XmlUtilTest/validAddressBook.xml deleted file mode 100644 index 6265778674d3..000000000000 --- a/src/test/data/XmlUtilTest/validAddressBook.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - Hans Muster - 9482424 - hans@example.com -
4th street
-
- - Ruth Mueller - 87249245 - ruth@example.com -
81th street
-
- - Heinz Kurz - 95352563 - heinz@example.com -
wall street
-
- - Cornelia Meier - 87652533 - cornelia@example.com -
10th street
-
- - Werner Meyer - 9482224 - werner@example.com -
michegan ave
-
- - Lydia Kunz - 9482427 - lydia@example.com -
little tokyo
-
- - Anna Best - 9482442 - anna@example.com -
4th street
-
- - Stefan Meier - 8482424 - stefan@example.com -
little india
-
- - Martin Mueller - 8482131 - hans@example.com -
chicago ave
-
-
diff --git a/src/test/data/XmlUtilTest/validItem.xml b/src/test/data/XmlUtilTest/validItem.xml new file mode 100644 index 000000000000..97d60dd6c3c3 --- /dev/null +++ b/src/test/data/XmlUtilTest/validItem.xml @@ -0,0 +1,10 @@ + + + Arduino + 20 + 5 + 20 + 0 + 0 + Lab1 + diff --git a/src/test/data/XmlUtilTest/validPerson.xml b/src/test/data/XmlUtilTest/validPerson.xml deleted file mode 100644 index c029008d54f4..000000000000 --- a/src/test/data/XmlUtilTest/validPerson.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - Hans Muster - 9482424 - hans@example -
4th street
- friends -
diff --git a/src/test/data/XmlUtilTest/validStockList.xml b/src/test/data/XmlUtilTest/validStockList.xml new file mode 100644 index 000000000000..22fbd6762c8d --- /dev/null +++ b/src/test/data/XmlUtilTest/validStockList.xml @@ -0,0 +1,30 @@ + + + + Arduino + 20 + 5 + 20 + 0 + 0 + Lab1 + + + Raspberry Pi + 50 + 15 + 50 + 0 + 0 + lab2 + + + RPLidar + 30 + 10 + 30 + 0 + 0 + lab3 + + diff --git a/src/test/java/guitests/guihandles/PersonCardHandle.java b/src/test/java/guitests/guihandles/ItemCardHandle.java similarity index 52% rename from src/test/java/guitests/guihandles/PersonCardHandle.java rename to src/test/java/guitests/guihandles/ItemCardHandle.java index 1789735e49a8..cfe98cac7339 100644 --- a/src/test/java/guitests/guihandles/PersonCardHandle.java +++ b/src/test/java/guitests/guihandles/ItemCardHandle.java @@ -8,34 +8,34 @@ import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.layout.Region; -import seedu.address.model.person.Person; +import seedu.address.model.item.Item; /** - * Provides a handle to a person card in the person list panel. + * Provides a handle to a item card in the item list panel. */ -public class PersonCardHandle extends NodeHandle { +public class ItemCardHandle extends NodeHandle { private static final String ID_FIELD_ID = "#id"; private static final String NAME_FIELD_ID = "#name"; - private static final String ADDRESS_FIELD_ID = "#address"; - private static final String PHONE_FIELD_ID = "#phone"; - private static final String EMAIL_FIELD_ID = "#email"; + private static final String QUANTITY_FIELD_ID = "#quantity"; + private static final String MIN_QUANTITY_FIELD_ID = "#min_quantity"; + private static final String STATUS_FIELD_ID = "#status"; private static final String TAGS_FIELD_ID = "#tags"; private final Label idLabel; private final Label nameLabel; - private final Label addressLabel; - private final Label phoneLabel; - private final Label emailLabel; + private final Label quantityLabel; + private final Label minQuantityLabel; + private final Label statusLabel; private final List