/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.catalina.ssi; /** * Parses an expression string to return the individual tokens. This is * patterned similar to the StreamTokenizer in the JDK but customized for SSI * conditional expression parsing. * * @author Paul Speed */ public class ExpressionTokenizer { public static final int TOKEN_STRING = 0; public static final int TOKEN_AND = 1; public static final int TOKEN_OR = 2; public static final int TOKEN_NOT = 3; public static final int TOKEN_EQ = 4; public static final int TOKEN_NOT_EQ = 5; public static final int TOKEN_RBRACE = 6; public static final int TOKEN_LBRACE = 7; public static final int TOKEN_GE = 8; public static final int TOKEN_LE = 9; public static final int TOKEN_GT = 10; public static final int TOKEN_LT = 11; public static final int TOKEN_END = 12; private final char[] expr; private String tokenVal = null; private int index; private final int length; /** * Creates a new parser for the specified expression. * @param expr The expression */ public ExpressionTokenizer(String expr) { this.expr = expr.trim().toCharArray(); this.length = this.expr.length; } /** * @return <code>true</code> if there are more tokens. */ public boolean hasMoreTokens() { return index < length; } /** * @return the current index for error reporting purposes. */ public int getIndex() { return index; } protected boolean isMetaChar(char c) { return Character.isWhitespace(c) || c == '(' || c == ')' || c == '!' || c == '<' || c == '>' || c == '|' || c == '&' || c == '='; } /** * @return the next token type and initializes any state variables * accordingly. */ public int nextToken() { // Skip any leading white space while (index < length && Character.isWhitespace(expr[index])) index++; // Clear the current token val tokenVal = null; if (index == length) return TOKEN_END; // End of string int start = index; char currentChar = expr[index]; char nextChar = (char)0; index++; if (index < length) nextChar = expr[index]; // Check for a known token start switch (currentChar) { case '(' : return TOKEN_LBRACE; case ')' : return TOKEN_RBRACE; case '=' : return TOKEN_EQ; case '!' : if (nextChar == '=') { index++; return TOKEN_NOT_EQ; } return TOKEN_NOT; case '|' : if (nextChar == '|') { index++; return TOKEN_OR; } break; case '&' : if (nextChar == '&') { index++; return TOKEN_AND; } break; case '>' : if (nextChar == '=') { index++; return TOKEN_GE; // Greater than or equal } return TOKEN_GT; // Greater than case '<' : if (nextChar == '=') { index++; return TOKEN_LE; // Less than or equal } return TOKEN_LT; // Less than default : // Otherwise it's a string break; } int end = index; if (currentChar == '"' || currentChar == '\'') { // It's a quoted string and the end is the next unescaped quote char endChar = currentChar; boolean escaped = false; start++; for (; index < length; index++) { if (expr[index] == '\\' && !escaped) { escaped = true; continue; } if (expr[index] == endChar && !escaped) break; escaped = false; } end = index; index++; // Skip the end quote } else if (currentChar == '/') { // It's a regular expression and the end is the next unescaped / char endChar = currentChar; boolean escaped = false; for (; index < length; index++) { if (expr[index] == '\\' && !escaped) { escaped = true; continue; } if (expr[index] == endChar && !escaped) break; escaped = false; } end = ++index; } else { // End is the next whitespace character for (; index < length; index++) { if (isMetaChar(expr[index])) break; } end = index; } // Extract the string from the array this.tokenVal = new String(expr, start, end - start); return TOKEN_STRING; } /** * @return the String value of the token if it was type TOKEN_STRING. * Otherwise null is returned. */ public String getTokenValue() { return tokenVal; } }