001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.catalina.ssi;
018:
019: /**
020: * Parses an expression string to return the individual tokens. This is
021: * patterned similar to the StreamTokenizer in the JDK but customized for SSI
022: * conditional expression parsing.
023: *
024: * @version $Revision: 531303 $
025: * @author Paul Speed
026: */
027: public class ExpressionTokenizer {
028: public static final int TOKEN_STRING = 0;
029: public static final int TOKEN_AND = 1;
030: public static final int TOKEN_OR = 2;
031: public static final int TOKEN_NOT = 3;
032: public static final int TOKEN_EQ = 4;
033: public static final int TOKEN_NOT_EQ = 5;
034: public static final int TOKEN_RBRACE = 6;
035: public static final int TOKEN_LBRACE = 7;
036: public static final int TOKEN_GE = 8;
037: public static final int TOKEN_LE = 9;
038: public static final int TOKEN_GT = 10;
039: public static final int TOKEN_LT = 11;
040: public static final int TOKEN_END = 12;
041: private char[] expr;
042: private String tokenVal = null;
043: private int index;
044: private int length;
045:
046: /**
047: * Creates a new parser for the specified expression.
048: */
049: public ExpressionTokenizer(String expr) {
050: this .expr = expr.trim().toCharArray();
051: this .length = this .expr.length;
052: }
053:
054: /**
055: * Returns true if there are more tokens.
056: */
057: public boolean hasMoreTokens() {
058: return index < length;
059: }
060:
061: /**
062: * Returns the current index for error reporting purposes.
063: */
064: public int getIndex() {
065: return index;
066: }
067:
068: protected boolean isMetaChar(char c) {
069: return Character.isWhitespace(c) || c == '(' || c == ')'
070: || c == '!' || c == '<' || c == '>' || c == '|'
071: || c == '&' || c == '=';
072: }
073:
074: /**
075: * Returns the next token type and initializes any state variables
076: * accordingly.
077: */
078: public int nextToken() {
079: // Skip any leading white space
080: while (index < length && Character.isWhitespace(expr[index]))
081: index++;
082: // Clear the current token val
083: tokenVal = null;
084: if (index == length)
085: return TOKEN_END; // End of string
086: int start = index;
087: char currentChar = expr[index];
088: char nextChar = (char) 0;
089: index++;
090: if (index < length)
091: nextChar = expr[index];
092: // Check for a known token start
093: switch (currentChar) {
094: case '(':
095: return TOKEN_LBRACE;
096: case ')':
097: return TOKEN_RBRACE;
098: case '=':
099: return TOKEN_EQ;
100: case '!':
101: if (nextChar == '=') {
102: index++;
103: return TOKEN_NOT_EQ;
104: } else {
105: return TOKEN_NOT;
106: }
107: case '|':
108: if (nextChar == '|') {
109: index++;
110: return TOKEN_OR;
111: }
112: break;
113: case '&':
114: if (nextChar == '&') {
115: index++;
116: return TOKEN_AND;
117: }
118: break;
119: case '>':
120: if (nextChar == '=') {
121: index++;
122: return TOKEN_GE; // Greater than or equal
123: } else {
124: return TOKEN_GT; // Greater than
125: }
126: case '<':
127: if (nextChar == '=') {
128: index++;
129: return TOKEN_LE; // Less than or equal
130: } else {
131: return TOKEN_LT; // Less than
132: }
133: default:
134: // Otherwise it's a string
135: break;
136: }
137: int end = index;
138: // If it's a quoted string then end is the next unescaped quote
139: if (currentChar == '"' || currentChar == '\'') {
140: char endChar = currentChar;
141: boolean escaped = false;
142: start++;
143: for (; index < length; index++) {
144: if (expr[index] == '\\' && !escaped) {
145: escaped = true;
146: continue;
147: }
148: if (expr[index] == endChar && !escaped)
149: break;
150: escaped = false;
151: }
152: end = index;
153: index++; // Skip the end quote
154: } else {
155: // End is the next whitespace character
156: for (; index < length; index++) {
157: if (isMetaChar(expr[index]))
158: break;
159: }
160: end = index;
161: }
162: // Extract the string from the array
163: this .tokenVal = new String(expr, start, end - start);
164: return TOKEN_STRING;
165: }
166:
167: /**
168: * Returns the String value of the token if it was type TOKEN_STRING.
169: * Otherwise null is returned.
170: */
171: public String getTokenValue() {
172: return tokenVal;
173: }
174: }
|