001: package example;
002:
003: import com.caucho.util.L10N;
004: import java.util.logging.Logger;
005: import java.util.logging.Level;
006:
007: import com.caucho.vfs.ReadStream;
008: import java.io.IOException;
009: import java.util.HashMap;
010:
011: /**
012: * Parse a request made using the magic8ball protocol, returning
013: * appropriate objects that are instances of AbstractCommand.
014: *
015: * This class is both a parser and a command factory.
016: */
017: public class Parser {
018: static protected final Logger log = Logger.getLogger(Parser.class
019: .getName());
020: static final L10N L = new L10N(Parser.class);
021:
022: // set in init() for each new parse
023: private ReadStream _readStream;
024: String _error;
025:
026: // parsing buffer
027: private StringBuffer _buffer = new StringBuffer();
028:
029: // commands
030: private HashMap _commands = new HashMap();
031:
032: public Parser() {
033: _commands.put("set-prophet", new SetProphetCommand());
034: _commands.put("ask", new AskCommand());
035: }
036:
037: public void init(ReadStream readStream) {
038: _readStream = readStream;
039: _error = null;
040: }
041:
042: /**
043: * Parse one command out of the ReadStream.
044: * Once the last command is encountered, null is returned.
045: *
046: * @return null if there were no more commands to parse, or there is a parse
047: * error.
048: */
049: public AbstractCommand nextCommand() throws IOException {
050: String cmd = parseToken();
051: if (cmd == null) {
052: return null;
053: }
054: AbstractCommand command = (AbstractCommand) _commands.get(cmd
055: .toLowerCase());
056:
057: if (command == null) {
058: _error = "Unknown command `" + cmd + "'";
059: } else {
060: command.parse(this );
061: if (command.isError()) {
062: _error = command.getError();
063: command = null;
064: }
065: }
066:
067: return command;
068: }
069:
070: public boolean isError() {
071: return _error != null;
072: }
073:
074: public String getError() {
075: return _error;
076: }
077:
078: /**
079: * @return true if ch is an indication that the end-of-stream was reached
080: */
081: boolean isEOS(int ch) {
082: return ch < 0;
083: }
084:
085: /**
086: * @return true if ch is a whitespace character or end-of-stream indicator
087: */
088: boolean isWhitespace(int ch) {
089: return isEOS(ch) || Character.isWhitespace((char) ch);
090: }
091:
092: /**
093: * Eat whitespace characters out of readStream
094: *
095: * @return the first non-whitespace character (which
096: * is still in the _readStream), or -1 if end of stream encountered.
097: */
098: int eatWhitespace() throws IOException {
099: int ch;
100: while (!isEOS(ch = _readStream.read())) {
101: if (!isWhitespace(ch)) {
102: _readStream.unread();
103: break;
104: }
105: }
106: return ch;
107: }
108:
109: /**
110: * Parse optional whitespace then a token containing no whitespace.
111: *
112: * @return the token, or null if there are no tokens left on the stream
113: */
114:
115: String parseToken() throws IOException {
116: String token = null;
117:
118: int ch = eatWhitespace();
119:
120: if (ch >= 0) {
121: _buffer.setLength(0);
122:
123: while (!isEOS(ch = _readStream.read())) {
124: if (isWhitespace(ch)) {
125: _readStream.unread();
126: break;
127: }
128: _buffer.append((char) ch);
129: }
130: token = _buffer.toString();
131: }
132:
133: return token;
134: }
135: }
|