001 // Copyright 2004, 2005 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.hivemind.conditional;
016
017 import org.apache.hivemind.util.Defense;
018
019 /**
020 * Parser for conditional expressions. This class is not threadsafe; it is inexpensive to create,
021 * however, and can be discarded after parsing one or more expressions.
022 *
023 * @author Howard M. Lewis Ship
024 * @since 1.1
025 */
026 public class Parser
027 {
028 private String _input;
029
030 private Lexer _lexer;
031
032 private Token _nextToken;
033
034 private boolean _onDeck;
035
036 // No reason to have multiple instances of these, since they are always
037 // identical (one of the advantages of the NodeImpl being purely structural.
038
039 private static final Evaluator NOT_EVALUATOR = new NotEvaluator();
040
041 private static final Evaluator OR_EVALUATOR = new OrEvaluator();
042
043 private static final Evaluator AND_EVALUATOR = new AndEvaluator();
044
045 public Node parse(String input)
046 {
047 Defense.notNull(input, "input");
048
049 try
050 {
051 _input = input;
052 _lexer = new Lexer(input);
053
054 Node result = expression();
055
056 Token token = next();
057
058 if (token != null)
059 throw new RuntimeException(ConditionalMessages.unparsedToken(token, _input));
060
061 return result;
062 }
063 finally
064 {
065 _input = null;
066 _nextToken = null;
067 _lexer = null;
068 _onDeck = false;
069 }
070 }
071
072 private Token next()
073 {
074 Token result = _onDeck ? _nextToken : _lexer.next();
075
076 _onDeck = false;
077 _nextToken = null;
078
079 return result;
080 }
081
082 private Token match(TokenType expected)
083 {
084 Token actual = next();
085
086 if (actual == null)
087 throw new RuntimeException(ConditionalMessages.unexpectedEndOfInput(_input));
088
089 if (actual.getType() != expected)
090 throw new RuntimeException(ConditionalMessages.unexpectedToken(expected, actual
091 .getType(), _input));
092
093 return actual;
094 }
095
096 private Token peek()
097 {
098 if (! _onDeck)
099 {
100 _nextToken = _lexer.next();
101 _onDeck = true;
102 }
103
104 return _nextToken;
105 }
106
107 private TokenType peekType()
108 {
109 Token next = peek();
110
111 return next == null ? null : next.getType();
112 }
113
114 private boolean isPeek(TokenType type)
115 {
116 return peekType() == type;
117 }
118
119 private Node expression()
120 {
121 Node lnode = term();
122
123 if (isPeek(TokenType.OR))
124 {
125 next();
126
127 Node rnode = expression();
128
129 return new NodeImpl(lnode, rnode, OR_EVALUATOR);
130 }
131
132 if (isPeek(TokenType.AND))
133 {
134 next();
135
136 Node rnode = expression();
137
138 return new NodeImpl(lnode, rnode, AND_EVALUATOR);
139 }
140
141 return lnode;
142 }
143
144 private Node term()
145 {
146 if (isPeek(TokenType.OPAREN))
147 {
148 next();
149
150 Node result = expression();
151
152 match(TokenType.CPAREN);
153
154 return result;
155 }
156
157 if (isPeek(TokenType.NOT))
158 {
159 next();
160
161 match(TokenType.OPAREN);
162
163 Node expression = expression();
164
165 match(TokenType.CPAREN);
166
167 return new NodeImpl(expression, null, NOT_EVALUATOR);
168 }
169
170 if (isPeek(TokenType.PROPERTY))
171 {
172 next();
173
174 Token symbolToken = match(TokenType.SYMBOL);
175
176 Evaluator ev = new PropertyEvaluator(symbolToken.getValue());
177
178 return new NodeImpl(ev);
179 }
180
181 if (isPeek(TokenType.CLASS))
182 {
183 next();
184
185 Token symbolToken = match(TokenType.SYMBOL);
186
187 Evaluator ev = new ClassNameEvaluator(symbolToken.getValue());
188
189 return new NodeImpl(ev);
190 }
191
192 throw new RuntimeException(ConditionalMessages.unparsedToken(next(), _input));
193 }
194 }