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
015package org.apache.hivemind.conditional;
016
017import 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 */
026public 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}