package mireka.pop;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* CommandParser extracts the command name from the command received from the
* POP3 client and provides functions for parsing the arguments.
*/
public class CommandParser {
private final String line;
/**
* It treats space as part of the single argument, and not as a separator.
*/
private static Pattern singleExtendedArgumentCommandPattern = Pattern
.compile("\\p{Graph}{3,4} (\\p{Print}+)");
private static Pattern singleArgumentCommandPattern = Pattern
.compile("\\p{Graph}{3,4} (\\p{Graph}+)");
private static Pattern argumentPattern = Pattern.compile("\\p{Graph}+");
public CommandParser(String line) {
this.line = line;
}
public String parseSingleExtendedArgument() throws CommandSyntaxException {
Matcher matcher = singleExtendedArgumentCommandPattern.matcher(line);
if (!matcher.matches())
throw new CommandSyntaxException(
"Syntax error: command with exactly one argument is expected");
return matcher.group(1);
}
public String parseSingleArgument() throws CommandSyntaxException {
Matcher matcher = singleArgumentCommandPattern.matcher(line);
if (!matcher.matches())
throw new CommandSyntaxException(
"Syntax error: command with exactly one argument is expected");
return matcher.group(1);
}
public List<String> parseArguments() throws CommandSyntaxException {
StringTokenizer tokenizer = new StringTokenizer(line, " ", true);
// skip command
if (!tokenizer.hasMoreTokens())
throw new CommandSyntaxException(""); // empty command
tokenizer.nextToken(); // command
List<String> result = new ArrayList<String>();
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
if (!token.equals(" "))
throw new CommandSyntaxException(
"Syntax error: arguments must be separated by a "
+ "single space character");
if (!tokenizer.hasMoreTokens())
throw new CommandSyntaxException(
"Syntax error: there are one or more spaces at the end of the command line");
token = tokenizer.nextToken();
if (!argumentPattern.matcher(token).matches())
throw new CommandSyntaxException(
"Syntax error: an argument must consist of printable US-ASCII "
+ "characters.");
result.add(token);
}
return result;
}
public Integer parseSingleOptionalNumericArgument()
throws CommandSyntaxException {
List<String> args = parseArguments();
if (args.isEmpty())
return null;
if (args.size() >= 2)
throw new CommandSyntaxException(
"Syntax error: Either no argument or a "
+ "single numeric argument is expected");
try {
return Integer.valueOf(args.get(0));
} catch (NumberFormatException e) {
throw new CommandSyntaxException(
"Syntax error: Either no argument or a "
+ "single numeric argument is expected");
}
}
public int parseSingleNumericArgument() throws CommandSyntaxException {
List<String> args = parseArguments();
if (args.size() != 1)
throw new CommandSyntaxException(
"Syntax error: A single numeric argument is expected");
try {
return Integer.valueOf(args.get(0));
} catch (NumberFormatException e) {
throw new CommandSyntaxException(
"Syntax error: A single numeric argument is expected");
}
}
public String extractCommand() throws CommandSyntaxException {
StringBuilder buffer = new StringBuilder();
int i = 0;
while (true) {
if (i >= line.length())
break;
char ch = line.charAt(i);
if (ch == ' ')
break;
if (i >= 4)
throw new CommandSyntaxException(
"Syntax error: command name is too long");
buffer.append(ch);
i++;
}
if (buffer.length() < 3)
throw new CommandSyntaxException(
"Syntax error: command name is too short");
return buffer.toString();
}
}