Skip to content

Commit 09cc878

Browse files
authored
Merge pull request nus-cs2103-AY1819S2#69 from JiaHaoLim/master
[v1.2] Open, Save, Import, Export
2 parents c6570cf + a0760a0 commit 09cc878

9 files changed

+338
-4
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package seedu.address.logic.commands;
2+
3+
import static java.util.Objects.requireNonNull;
4+
import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
5+
6+
import java.io.IOException;
7+
import java.util.ArrayList;
8+
import java.util.HashSet;
9+
import java.util.logging.Logger;
10+
11+
import seedu.address.MainApp;
12+
import seedu.address.commons.core.LogsCenter;
13+
import seedu.address.logic.CommandHistory;
14+
import seedu.address.model.Model;
15+
import seedu.address.model.ModelManager;
16+
import seedu.address.model.person.Person;
17+
import seedu.address.storage.AddressBookStorage;
18+
import seedu.address.storage.JsonAddressBookStorage;
19+
import seedu.address.storage.ParsedInOut;
20+
import seedu.address.storage.StorageManager;
21+
22+
/**
23+
* Exports records to a text file.
24+
*/
25+
public class ExportCommand extends Command {
26+
27+
public static final String COMMAND_WORD = "export";
28+
29+
public static final String MESSAGE_USAGE = COMMAND_WORD
30+
+ ": Exports specific patients by index to text file in the \"data\" folder, "
31+
+ "overwriting if filename exists \n"
32+
+ "Parameters: FILENAME INDEX_RANGE(must be a positive integer)\n"
33+
+ "Example: " + COMMAND_WORD + " records1.json + 1-5"
34+
+ "Example: " + COMMAND_WORD + " records1.json + 1,3,5";
35+
36+
public static final String MESSAGE_SUCCESS = "Exported the records!";
37+
private static final String MESSAGE_FAILURE = "Problem while writing to the file.";
38+
39+
private final ParsedInOut parsedInput;
40+
41+
public ExportCommand(ParsedInOut parsedInput) {
42+
this.parsedInput = parsedInput;
43+
}
44+
45+
@Override
46+
public CommandResult execute(Model model, CommandHistory history) {
47+
requireNonNull(model);
48+
writeFile(createTempAddressBook(model, parsedInput.getParsedIndex()));
49+
model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
50+
return new CommandResult(MESSAGE_SUCCESS);
51+
}
52+
53+
/**
54+
* writeFile() writes or overwrites a file with the contents of the current address book.
55+
*/
56+
private void writeFile(Model model) {
57+
58+
AddressBookStorage addressBookStorage = new JsonAddressBookStorage(parsedInput.getFile().toPath());
59+
60+
StorageManager storage = new StorageManager(addressBookStorage, null);
61+
62+
final Logger logger = LogsCenter.getLogger(MainApp.class);
63+
64+
try {
65+
storage.saveAddressBook(model.getAddressBook());
66+
} catch (IOException e) {
67+
logger.warning(MESSAGE_FAILURE);
68+
}
69+
}
70+
71+
/**
72+
* createTempAddressBook() creates a temporary address book populated with the specified patients in parsedInput[1]
73+
* @param model
74+
* @param parsedIndex
75+
* @return
76+
*/
77+
private ModelManager createTempAddressBook(Model model, HashSet<Integer> parsedIndex) {
78+
ModelManager tempModel = new ModelManager();
79+
80+
tempModel.setAddressBook(model.getAddressBook());
81+
ArrayList<Person> deleteList = new ArrayList<>();
82+
83+
for (int i = 0; i < tempModel.getFilteredPersonList().size(); i++) {
84+
if (!parsedIndex.contains(i)) {
85+
deleteList.add(tempModel.getFilteredPersonList().get(i));
86+
}
87+
}
88+
89+
for (Person personToDelete : deleteList) {
90+
tempModel.deletePerson(personToDelete);
91+
}
92+
93+
return tempModel;
94+
}
95+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package seedu.address.logic.commands;
2+
3+
import static java.util.Objects.requireNonNull;
4+
import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
5+
6+
import java.io.IOException;
7+
import java.util.HashSet;
8+
import java.util.Optional;
9+
import java.util.logging.Logger;
10+
11+
import seedu.address.MainApp;
12+
import seedu.address.commons.core.LogsCenter;
13+
import seedu.address.commons.exceptions.DataConversionException;
14+
import seedu.address.logic.CommandHistory;
15+
import seedu.address.model.AddressBook;
16+
import seedu.address.model.Model;
17+
import seedu.address.model.ReadOnlyAddressBook;
18+
import seedu.address.model.util.SampleDataUtil;
19+
import seedu.address.storage.AddressBookStorage;
20+
import seedu.address.storage.JsonAddressBookStorage;
21+
import seedu.address.storage.ParsedInOut;
22+
import seedu.address.storage.StorageManager;
23+
24+
/**
25+
* Imports records to a text file.
26+
*/
27+
public class ImportCommand extends Command {
28+
29+
public static final String COMMAND_WORD = "import";
30+
31+
public static final String MESSAGE_USAGE = COMMAND_WORD
32+
+ ": Imports specific patients by index to text file in the \"data\" folder, "
33+
+ "overwriting if filename exists \n"
34+
+ "Parameters: FILENAME INDEX_RANGE(must be a positive integer)\n"
35+
+ "Example: " + COMMAND_WORD + " records1.json + 1-5"
36+
+ "Example: " + COMMAND_WORD + " records1.json + 1,3,5";
37+
38+
public static final String MESSAGE_SUCCESS = "Imported the records!";
39+
40+
private final ParsedInOut parsedInput;
41+
42+
public ImportCommand(ParsedInOut parsedInput) {
43+
this.parsedInput = parsedInput;
44+
}
45+
46+
@Override
47+
public CommandResult execute(Model model, CommandHistory history) {
48+
requireNonNull(model);
49+
readFile(model, parsedInput.getParsedIndex());
50+
model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
51+
model.commitAddressBook();
52+
return new CommandResult(MESSAGE_SUCCESS);
53+
}
54+
55+
/**
56+
* readFile() appends the current address book with the contents of the file.
57+
*/
58+
private void readFile(Model model, HashSet<Integer> parsedIndex) {
59+
AddressBookStorage importStorage = new JsonAddressBookStorage(parsedInput.getFile().toPath());
60+
61+
StorageManager importStorageManager = new StorageManager(importStorage, null);
62+
63+
final Logger logger = LogsCenter.getLogger(MainApp.class);
64+
65+
Optional<ReadOnlyAddressBook> importOptional;
66+
ReadOnlyAddressBook importData;
67+
68+
try {
69+
importOptional = importStorageManager.readAddressBook();
70+
if (!importOptional.isPresent()) {
71+
logger.info("Data file not found. Will be starting with a sample AddressBook");
72+
}
73+
importData = importOptional.orElseGet(SampleDataUtil::getSampleAddressBook);
74+
} catch (DataConversionException e) {
75+
logger.warning("Data file not in the correct format.");
76+
importData = new AddressBook();
77+
} catch (IOException e) {
78+
logger.warning("Problem while reading from the file.");
79+
importData = new AddressBook();
80+
}
81+
82+
for (int i = 0; i < importData.getPersonList().size(); i++) {
83+
if (parsedIndex.contains(i) && !model.hasPerson(importData.getPersonList().get(i))) {
84+
model.addPerson(importData.getPersonList().get(i));
85+
}
86+
}
87+
}
88+
}

src/main/java/seedu/address/logic/parser/AddressBookParser.java

+11
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313
import seedu.address.logic.commands.DeleteCommand;
1414
import seedu.address.logic.commands.EditCommand;
1515
import seedu.address.logic.commands.ExitCommand;
16+
import seedu.address.logic.commands.ExportCommand;
1617
import seedu.address.logic.commands.FindCommand;
1718
import seedu.address.logic.commands.HelpCommand;
1819
import seedu.address.logic.commands.HistoryCommand;
20+
import seedu.address.logic.commands.ImportCommand;
1921
import seedu.address.logic.commands.ListCommand;
2022
import seedu.address.logic.commands.OpenCommand;
2123
import seedu.address.logic.commands.RedoCommand;
@@ -98,12 +100,21 @@ public Command parseCommand(String userInput) throws ParseException {
98100
case SaveCommand.COMMAND_WORD:
99101
return new SaveCommandParser().parse(arguments);
100102

103+
case ImportCommand.COMMAND_WORD:
104+
return new ImportCommandParser().parse(arguments);
105+
106+
case ExportCommand.COMMAND_WORD:
107+
return new ExportCommandParser().parse(arguments);
108+
101109
case TaskAddCommand.COMMAND_WORD:
102110
return new TaskAddCommandParser().parse(arguments);
111+
103112
case TaskEditCommand.COMMAND_WORD:
104113
return new TaskEditCommandParser().parse(arguments);
114+
105115
case TaskDeleteCommand.COMMAND_WORD:
106116
return new TaskDeleteCommandParser().parse(arguments);
117+
107118
default:
108119
throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
109120
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package seedu.address.logic.parser;
2+
3+
import seedu.address.logic.commands.ExportCommand;
4+
import seedu.address.logic.parser.exceptions.ParseException;
5+
6+
/**
7+
* Parses input arguments and creates a new ExportCommand object.
8+
*/
9+
public class ExportCommandParser implements Parser<ExportCommand> {
10+
11+
/**
12+
* Parses the given argument {@code String} in the context of the ExportCommand
13+
* and returns an ExportCommand object for execution.
14+
* @throws ParseException if the user input does not conform the expected format
15+
*/
16+
public ExportCommand parse(String args) throws ParseException {
17+
try {
18+
return new ExportCommand(ParserUtil.parseImportExport(args));
19+
} catch (ParseException pe) {
20+
throw new ParseException(pe.getMessage());
21+
}
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package seedu.address.logic.parser;
2+
3+
import java.io.File;
4+
5+
import seedu.address.logic.commands.ImportCommand;
6+
import seedu.address.logic.parser.exceptions.ParseException;
7+
import seedu.address.storage.ParsedInOut;
8+
9+
/**
10+
* Parses input arguments and creates a new ImportCommand object.
11+
*/
12+
public class ImportCommandParser implements Parser<ImportCommand> {
13+
14+
/**
15+
* Parses the given argument {@code String} in the context of the ImportCommand
16+
* and returns an ImportCommand object for execution.
17+
* @throws ParseException if the user input does not conform the expected format
18+
*/
19+
public ImportCommand parse(String args) throws ParseException {
20+
try {
21+
ParsedInOut parsedInOut = ParserUtil.parseImportExport(args);
22+
importValidation(parsedInOut.getFile());
23+
return new ImportCommand(parsedInOut);
24+
} catch (ParseException pe) {
25+
throw new ParseException(pe.getMessage());
26+
}
27+
}
28+
29+
private void importValidation(File file) throws ParseException {
30+
if (!file.exists() || !file.isFile() || !file.canRead()) {
31+
throw new ParseException("File is invalid");
32+
}
33+
}
34+
}

src/main/java/seedu/address/logic/parser/OpenCommandParser.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public class OpenCommandParser implements Parser<OpenCommand> {
1717
*/
1818
public OpenCommand parse(String args) throws ParseException {
1919
try {
20-
File file = ParserUtil.parseFile(args);
20+
File file = ParserUtil.parseOpenSave(args);
2121
openValidation(file);
2222
return new OpenCommand(file);
2323
} catch (ParseException pe) {

src/main/java/seedu/address/logic/parser/ParserUtil.java

+60-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import java.util.Collection;
77
import java.util.HashSet;
88
import java.util.Set;
9+
import java.util.regex.Matcher;
10+
import java.util.regex.Pattern;
911

1012
import seedu.address.commons.core.index.Index;
1113
import seedu.address.commons.util.StringUtil;
@@ -19,6 +21,7 @@
1921
import seedu.address.model.person.Phone;
2022
import seedu.address.model.tag.Tag;
2123
import seedu.address.model.task.Title;
24+
import seedu.address.storage.ParsedInOut;
2225

2326
/**
2427
* Contains utility methods used for parsing strings in the various *Parser classes.
@@ -205,10 +208,10 @@ public static Set<Tag> parseTags(Collection<String> tags) throws ParseException
205208
*
206209
* @throws ParseException if the given {@code file} is invalid.
207210
*/
208-
public static File parseFile(String filePath) throws ParseException {
211+
public static File parseOpenSave(String filePath) throws ParseException {
209212
requireNonNull(filePath);
210213
filePath = filePath.trim();
211-
final String validationRegex = "\\p{Alnum}+.(txt|xml|json)$";
214+
final String validationRegex = "^([\\w-\\\\\\s.\\(\\)]+)+\\.(txt|xml|json)$";
212215

213216
if (!filePath.matches(validationRegex)) {
214217
throw new ParseException("File name is invalid");
@@ -220,4 +223,59 @@ public static File parseFile(String filePath) throws ParseException {
220223

221224
return file;
222225
}
226+
227+
/**
228+
* Parses a {@code String file} and {@code Index} into a {@code ParsedIO}.
229+
*
230+
* @throws ParseException if the given {@code file} is invalid.
231+
*/
232+
public static ParsedInOut parseImportExport(String input) throws ParseException {
233+
requireNonNull(input);
234+
input = input.trim();
235+
final String validationRegex = "^([\\w-\\\\\\s.\\(\\)]+)+\\.(txt|xml|json)+\\s?([0-9,-]*)?$";
236+
237+
if (!input.matches(validationRegex)) {
238+
throw new ParseException("File name is invalid or no index given");
239+
}
240+
241+
String newPath = "data\\";
242+
final Pattern splitRegex = Pattern.compile("([\\w-\\\\\\s.\\(\\)]+)+\\.(txt|xml|json)+\\s?([0-9,-]*)?");
243+
Matcher splitMatcher = splitRegex.matcher(input);
244+
String filepath = "";
245+
String indexRange = "";
246+
247+
if (splitMatcher.find()) {
248+
filepath = splitMatcher.group(1).concat(".");
249+
filepath = filepath.concat(splitMatcher.group(2));
250+
filepath = newPath.concat(filepath);
251+
indexRange = splitMatcher.group(3);
252+
} else {
253+
// This shouldn't be possible after validationRegex
254+
throw new ParseException("File name is invalid or no index given");
255+
}
256+
257+
HashSet<Integer> parsedIndex = new HashSet<>();
258+
259+
String[] splitInput = indexRange.trim().split(",");
260+
String[] splitRange;
261+
final String singleNumberRegex = "^\\d+$";
262+
final String rangeNumberRegex = "^(\\d+)-(\\d+)$";
263+
264+
for (String string : splitInput) {
265+
if (string.matches(singleNumberRegex)) {
266+
// -1 because indexes displayed to user starts with 1, not 0
267+
parsedIndex.add(Integer.parseInt(string) - 1);
268+
} else if (string.matches(rangeNumberRegex)) {
269+
splitRange = string.split("-");
270+
for (int i = Integer.parseInt(splitRange[0]); i < Integer.parseInt(splitRange[1]) + 1; i++) {
271+
// -1 because indexes displayed to user starts with 1, not 0
272+
parsedIndex.add(i - 1);
273+
}
274+
} else {
275+
throw new ParseException("Invalid index range!");
276+
}
277+
}
278+
279+
return new ParsedInOut(new File(filepath), parsedIndex);
280+
}
223281
}

src/main/java/seedu/address/logic/parser/SaveCommandParser.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public class SaveCommandParser implements Parser<SaveCommand> {
1717
*/
1818
public SaveCommand parse(String args) throws ParseException {
1919
try {
20-
File file = ParserUtil.parseFile(args);
20+
File file = ParserUtil.parseOpenSave(args);
2121
return new SaveCommand(file);
2222
} catch (ParseException pe) {
2323
throw new ParseException(pe.getMessage());

0 commit comments

Comments
 (0)