/src/main/java/org/andya/confluence/utils/parse/ExpressionParser.java
Java | 184 lines | 89 code | 14 blank | 81 comment | 32 complexity | ec3d20fc71e12b249b8739953c4dc6a8 MD5 | raw file
- /*
- * Copyright (c) 2006, 2007 Andy Armstrong, Kelsey Grant and other contributors.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * * The names of contributors may not
- * be used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
- package org.andya.confluence.utils.parse;
-
- import com.atlassian.renderer.v2.macro.MacroException;
-
- import java.util.LinkedList;
- import java.util.StringTokenizer;
-
- /**
- * This parses an expression used to determine which pages to include
- * in a metadata report. The language used is as follows:
- *
- * <ul>
- * <li><OR> ,
- * <li><AND> +
- * <li><OPEN> (
- * <li><CLOSE> )
- * <li><LABEL> String Literal
- * </ul>
- *
- * @author Kelsey Grant
- *
- */
- public class ExpressionParser {
-
- /** Token Type for '(' */
- private static final int TT_OPEN = 0;
- /** Token Type for ')' */
- private static final int TT_CLOSE = 1;
- /** Token Type for a literal */
- private static final int TT_LABEL = 2;
- /** Token Type for ',' */
- private static final int TT_OR = 3;
- /** Token Type for '+' */
- private static final int TT_AND = 4;
-
- /**
- * Parse the given expression into a tree structure representing
- * the abstract syntax. This can then be iterated by using an
- * ExpressionVisitor
- *
- * @param expression the expression to be parsed
- * @return an expression tree
- * @throws MacroException if the syntax was invalid
- */
- public Expression parse(String expression) throws MacroException {
- final LinkedList<Token> tokenStream = parseTokens(expression, new LinkedList<Token>());
- Expression exp = parseExpression(tokenStream);
- if (!tokenStream.isEmpty()) {
- throw new MacroException("Encountered extra tokens - '" + tokenStream.toString() + "'");
- }
- return exp;
- }
-
- /**
- * Parse out the given expression from a stream of tokens
- *
- * @param tokenStream the tokens
- * @return the parsed expression
- * @throws MacroException
- */
- Expression parseExpression(LinkedList<Token> tokenStream) throws MacroException {
- if(tokenStream.isEmpty()) {
- throw new MacroException("Expected '(' or label, was no expression!");
- }
- Token t = (Token)tokenStream.removeFirst();
- final Expression first;
-
- // either open parentheses, so recurse down, or a literal
- if(t.type == TT_OPEN) {
- first = parseExpression(tokenStream);
- if(tokenStream.isEmpty()) {
- throw new MacroException("Expected ')', was end of stream");
- }
- t = (Token)tokenStream.removeFirst();
- if(t.type != TT_CLOSE) {
- throw new MacroException("Expected ')', was '" + t.token + "'");
- }
- } else if (t.type == TT_LABEL) {
- first = new LabelExpression(t.token);
- } else {
- throw new MacroException("Expected '(' or label, was '" + t.token + "'");
- }
- if(tokenStream.isEmpty()) {
- return first;
- }
- // after a literal, we have an optional operator followed by expression
- t = (Token)tokenStream.getFirst(); //peek just now...
- if(t.type == TT_OR || t.type == TT_AND) {
- tokenStream.removeFirst(); // kill the or
- Expression right = parseExpression(tokenStream);
- if (t.type == TT_OR) {
- return new OrExpression(first, right);
- }
- return new AndExpression(first, right);
- }
- // we got something else. who knows?
- return first;
- }
-
- /**
- * Create the token stream from the expression. This is the lexing step
- * of the parse
- *
- * @param expression the expression to be tokenized
- * @param tokenList an existing list of tokens to be added to
- * @return the list of tokens with the tokens from expression added
- */
- LinkedList<Token> parseTokens(String expression, LinkedList<Token> tokenList) {
- expression = expression.trim();
- if(expression.length() == 0) {
- return tokenList;
- }
- final char ch = expression.charAt(0);
- final Token t;
- if(ch == '(') {
- t = new Token(TT_OPEN, "(");
- } else if (ch == ')') {
- t = new Token(TT_CLOSE, ")");
- } else if (ch == ',') {
- t = new Token(TT_OR, ",");
- } else if (ch == '+') {
- t = new Token(TT_AND, "+");
- } else {
- // it must be a label literal. Consume until we hit one of the specials.
- final String label = consume(expression);
- t = new Token(TT_LABEL, label);
- }
-
- tokenList.add(t);
- return parseTokens(expression.substring(t.token.length()), tokenList);
- }
-
- /**
- * Consumes the given expression until a known special is encountered
- *
- * @param expression the expression to be consumed
- * @return the literal that was consumed
- */
- private String consume(String expression) {
- return new StringTokenizer(expression, ",+()").nextToken();
- }
-
- private static final class Token {
- private final int type;
- private final String token;
-
- public Token(int type, String token) {
- this.type = type;
- this.token = token;
- }
-
- public String toString() {
- return token;
- }
- }
- }