forked from nus-cs2103-AY1718S2/addressbook-level2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Parser.java
258 lines (215 loc) · 8.92 KB
/
Parser.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
package seedu.addressbook.parser;
import static seedu.addressbook.common.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.addressbook.common.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import seedu.addressbook.commands.AddCommand;
import seedu.addressbook.commands.ClearCommand;
import seedu.addressbook.commands.Command;
import seedu.addressbook.commands.DeleteCommand;
import seedu.addressbook.commands.ExitCommand;
import seedu.addressbook.commands.FindCommand;
import seedu.addressbook.commands.HelpCommand;
import seedu.addressbook.commands.IncorrectCommand;
import seedu.addressbook.commands.ListCommand;
import seedu.addressbook.commands.SortCommand;
import seedu.addressbook.commands.ViewAllCommand;
import seedu.addressbook.commands.ViewCommand;
import seedu.addressbook.data.exception.IllegalValueException;
/**
* Parses user input.
*/
public class Parser {
public static final Pattern PERSON_INDEX_ARGS_FORMAT = Pattern.compile("(?<targetIndex>.+)");
public static final Pattern KEYWORDS_ARGS_FORMAT =
Pattern.compile("(?<keywords>\\S+(?:\\s+\\S+)*)"); // one or more keywords separated by whitespace
public static final Pattern PERSON_DATA_ARGS_FORMAT = // '/' forward slashes are reserved for delimiter prefixes
Pattern.compile("(?<name>[^/]+)"
+ " (?<isPhonePrivate>p?)p/(?<phone>[^/]+)"
+ " (?<isEmailPrivate>p?)e/(?<email>[^/]+)"
+ " (?<isAddressPrivate>p?)a/(?<address>[^/]+)"
+ "(?<tagArguments>(?: t/[^/]+)*)"); // variable number of tags
/**
* Signals that the user input could not be parsed.
*/
public static class ParseException extends Exception {
ParseException(String message) {
super(message);
}
}
/**
* Used for initial separation of command word and args.
*/
public static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?<commandWord>\\S+)(?<arguments>.*)");
public Parser() {}
/**
* Parses user input into command for execution.
*
* @param userInput full user input string
* @return the command based on the user input
*/
public Command parseCommand(String userInput) {
final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim());
if (!matcher.matches()) {
return new IncorrectCommand(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 prepareAdd(arguments);
case DeleteCommand.COMMAND_WORD:
return prepareDelete(arguments);
case ClearCommand.COMMAND_WORD:
return new ClearCommand();
case FindCommand.COMMAND_WORD:
return prepareFind(arguments);
case ListCommand.COMMAND_WORD:
return new ListCommand();
case SortCommand.COMMAND_WORD:
return new SortCommand();
case ViewCommand.COMMAND_WORD:
return prepareView(arguments);
case ViewAllCommand.COMMAND_WORD:
return prepareViewAll(arguments);
case ExitCommand.COMMAND_WORD:
return new ExitCommand();
case HelpCommand.COMMAND_WORD: // Fallthrough
default:
return new HelpCommand();
}
}
/**
* Parses arguments in the context of the add person command.
*
* @param args full command args string
* @return the prepared command
*/
private Command prepareAdd(String args) {
final Matcher matcher = PERSON_DATA_ARGS_FORMAT.matcher(args.trim());
// Validate arg string format
if (!matcher.matches()) {
return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
}
try {
return new AddCommand(
matcher.group("name"),
matcher.group("phone"),
isPrivatePrefixPresent(matcher.group("isPhonePrivate")),
matcher.group("email"),
isPrivatePrefixPresent(matcher.group("isEmailPrivate")),
matcher.group("address"),
isPrivatePrefixPresent(matcher.group("isAddressPrivate")),
getTagsFromArgs(matcher.group("tagArguments"))
);
} catch (IllegalValueException ive) {
return new IncorrectCommand(ive.getMessage());
}
}
/**
* Returns true if the private prefix is present for a contact detail in the add command's arguments string.
*/
private static boolean isPrivatePrefixPresent(String matchedPrefix) {
return matchedPrefix.equals("p");
}
/**
* Extracts the new person's tags from the add command's tag arguments string.
* Merges duplicate tag strings.
*/
private static Set<String> getTagsFromArgs(String tagArguments) throws IllegalValueException {
// no tags
if (tagArguments.isEmpty()) {
return Collections.emptySet();
}
// replace first delimiter prefix, then split
final Collection<String> tagStrings = Arrays.asList(tagArguments.replaceFirst(" t/", "").split(" t/"));
return new HashSet<>(tagStrings);
}
/**
* Parses arguments in the context of the delete person command.
*
* @param args full command args string
* @return the prepared command
*/
private Command prepareDelete(String args) {
try {
final int targetIndex = parseArgsAsDisplayedIndex(args);
return new DeleteCommand(targetIndex);
} catch (ParseException pe) {
return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE));
} catch (NumberFormatException nfe) {
return new IncorrectCommand(MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
}
}
/**
* Parses arguments in the context of the view command.
*
* @param args full command args string
* @return the prepared command
*/
private Command prepareView(String args) {
try {
final int targetIndex = parseArgsAsDisplayedIndex(args);
return new ViewCommand(targetIndex);
} catch (ParseException pe) {
return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
ViewCommand.MESSAGE_USAGE));
} catch (NumberFormatException nfe) {
return new IncorrectCommand(MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
}
}
/**
* Parses arguments in the context of the view all command.
*
* @param args full command args string
* @return the prepared command
*/
private Command prepareViewAll(String args) {
try {
final int targetIndex = parseArgsAsDisplayedIndex(args);
return new ViewAllCommand(targetIndex);
} catch (ParseException pe) {
return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
ViewAllCommand.MESSAGE_USAGE));
} catch (NumberFormatException nfe) {
return new IncorrectCommand(MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
}
}
/**
* Parses the given arguments string as a single index number.
*
* @param args arguments string to parse as index number
* @return the parsed index number
* @throws ParseException if no region of the args string could be found for the index
* @throws NumberFormatException the args string region is not a valid number
*/
private int parseArgsAsDisplayedIndex(String args) throws ParseException, NumberFormatException {
final Matcher matcher = PERSON_INDEX_ARGS_FORMAT.matcher(args.trim());
if (!matcher.matches()) {
throw new ParseException("Could not find index number to parse");
}
return Integer.parseInt(matcher.group("targetIndex"));
}
/**
* Parses arguments in the context of the find person command.
*
* @param args full command args string
* @return the prepared command
*/
private Command prepareFind(String args) {
final Matcher matcher = KEYWORDS_ARGS_FORMAT.matcher(args.trim());
if (!matcher.matches()) {
return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
FindCommand.MESSAGE_USAGE));
}
// keywords delimited by whitespace
final String[] keywords = matcher.group("keywords").split("\\s+");
final Set<String> keywordSet = new HashSet<>(Arrays.asList(keywords));
return new FindCommand(keywordSet);
}
}