forked from nus-cs2103-AY1617S1/addressbook-level4
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Parser.java
266 lines (216 loc) · 9.06 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
259
260
261
262
263
264
265
266
package seedu.address.logic.parser;
import seedu.address.logic.commands.*;
import seedu.address.commons.util.StringUtil;
import seedu.address.commons.exceptions.IllegalValueException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND;
/**
* Parses user input.
*/
public class Parser {
/**
* Used for initial separation of command word and args.
*/
private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?<commandWord>\\S+)(?<arguments>.*)");
private static final Pattern PERSON_INDEX_ARGS_FORMAT = Pattern.compile("(?<targetIndex>.+)");
private static final Pattern KEYWORDS_ARGS_FORMAT =
Pattern.compile("(?<keywords>\\S+(?:\\s+\\S+)*)"); // one or more keywords separated by whitespace
private static final Pattern PERSON_DATA_ARGS_FORMAT = // '/' forward slashes are reserved for delimiter prefixes
Pattern.compile("(?<name>[^/]+)"
+ "( d/(?<description>[^/]+)){0,1}"
+ "( date/(?<date>[^/]+)){0,1}"
+ "(?<tagArguments>(?: t/[^/]+)*)"); // variable number of tags
private static final Pattern EDIT_DATA_ARGS_FORMAT = // '/' forward slashes are reserved for delimiter prefixes
Pattern.compile("(?<index>[\\d]+)"
+ "( (?<name>[^/]+)){0,1}"
+ "( d/(?<description>[^/]+)){0,1}"
+ "( date/(?<date>[^/]+)){0,1}"
+ "(?<tagArguments>(?: t/[^/]+)*)"); // variable number of tags
// private static final Pattern USA_DATE_FORMAT =
// Pattern.compile("(?<MM>(0?[1-9]|1[012]))"
// + "\\."
// + "(?<DD>(0?[1-9]|[12][0-9]|3[01]))"
// + "\\."
// + "(?<YY>\\d{2})");
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 SelectCommand.COMMAND_WORD:
return prepareSelect(arguments);
case DeleteCommand.COMMAND_WORD:
return prepareDelete(arguments);
case EditCommand.COMMAND_WORD:
return prepareEdit(arguments);
case ClearCommand.COMMAND_WORD:
return new ClearCommand();
case FindCommand.COMMAND_WORD:
return prepareFind(arguments);
case ListCommand.COMMAND_WORD:
return new ListCommand();
case ExitCommand.COMMAND_WORD:
return new ExitCommand();
case HelpCommand.COMMAND_WORD:
return new HelpCommand();
default:
return new IncorrectCommand(MESSAGE_UNKNOWN_COMMAND);
}
}
/**
* 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 {
List<java.util.Date> dateList = nattyParse(matcher);
return new AddCommand(
matcher.group("name"),
matcher.group("description"),
dateList,
getTagsFromArgs(matcher.group("tagArguments"))
);
} catch (IllegalValueException ive) {
return new IncorrectCommand(ive.getMessage());
}
}
private List<java.util.Date> nattyParse(final Matcher matcher) throws IllegalValueException {
TimeZone tz = TimeZone.getDefault();
com.joestelmach.natty.Parser natty = new com.joestelmach.natty.Parser(tz);
List<java.util.Date> dateList;
// NPE could be thrown here -> floating task no date no time
// natty.parse() can return null too
if (matcher.group("date") == null){
// return empty list
dateList = new ArrayList <java.util.Date> ();
}
else if (natty.parse(matcher.group("date")).isEmpty()){
throw new IllegalValueException(seedu.address.model.person.Date.MESSAGE_DATE_CONSTRAINTS);
}
else {
dateList = natty.parse(matcher.group("date")).get(0).getDates();
}
return dateList;
}
/**
* 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) {
Optional<Integer> index = parseIndex(args);
if(!index.isPresent()){
return new IncorrectCommand(
String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE));
}
return new DeleteCommand(index.get());
}
/**
* Parses arguments in the context of the edit person command.
*
* @param args full command args string
* @return the prepared command
*/
private Command prepareEdit(String args) {
final Matcher matcher = EDIT_DATA_ARGS_FORMAT.matcher(args.trim());
// Validate arg string format
if (!matcher.matches()) {
return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE));
}
try {
List<java.util.Date> dateList = nattyParse(matcher);
return new EditCommand(
Integer.parseInt(matcher.group("index")),
matcher.group("name"),
matcher.group("description"),
matcher.group("date"),
matcher.group("time"),
getTagsFromArgs(matcher.group("tagArguments"))
);
} catch (IllegalValueException ive) {
return new IncorrectCommand(ive.getMessage());
}
}
/**
* Parses arguments in the context of the select person command.
*
* @param args full command args string
* @return the prepared command
*/
private Command prepareSelect(String args) {
Optional<Integer> index = parseIndex(args);
if(!index.isPresent()){
return new IncorrectCommand(
String.format(MESSAGE_INVALID_COMMAND_FORMAT, SelectCommand.MESSAGE_USAGE));
}
return new SelectCommand(index.get());
}
/**
* Returns the specified index in the {@code command} IF a positive unsigned integer is given as the index.
* Returns an {@code Optional.empty()} otherwise.
*/
private Optional<Integer> parseIndex(String command) {
final Matcher matcher = PERSON_INDEX_ARGS_FORMAT.matcher(command.trim());
if (!matcher.matches()) {
return Optional.empty();
}
String index = matcher.group("targetIndex");
if(!StringUtil.isUnsignedInteger(index)){
return Optional.empty();
}
return Optional.of(Integer.parseInt(index));
}
/**
* 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);
}
}