001: /*
002: * Copyright 2002-2006 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.jexl;
017:
018: import java.io.StringReader;
019:
020: import org.apache.commons.jexl.parser.ASTExpressionExpression;
021: import org.apache.commons.jexl.parser.ASTForeachStatement;
022: import org.apache.commons.jexl.parser.ASTIfStatement;
023: import org.apache.commons.jexl.parser.ASTReferenceExpression;
024: import org.apache.commons.jexl.parser.ASTStatementExpression;
025: import org.apache.commons.jexl.parser.ASTWhileStatement;
026: import org.apache.commons.jexl.parser.ParseException;
027: import org.apache.commons.jexl.parser.Parser;
028: import org.apache.commons.jexl.parser.SimpleNode;
029: import org.apache.commons.jexl.parser.TokenMgrError;
030: import org.apache.commons.logging.Log;
031: import org.apache.commons.logging.LogFactory;
032:
033: /**
034: * <p>
035: * Creates Expression objects. To create a JEXL Expression object, pass
036: * valid JEXL syntax to the static createExpression() method:
037: * </p>
038: *
039: * <pre>
040: * String jexl = "array[1]";
041: * Expression expression = ExpressionFactory.createExpression( jexl );
042: * </pre>
043: *
044: * <p>
045: * When an {@link Expression} object is created, the JEXL syntax is
046: * parsed and verified. If the supplied expression is neither an
047: * expression nor a reference, an exception is thrown from createException().
048: * </p>
049: * @since 1.0
050: * @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
051: * @version $Id: ExpressionFactory.java 429169 2006-08-06 18:36:29Z rahul $
052: */
053: public class ExpressionFactory {
054: /**
055: * The Log to which all ExpressionFactory messages will be logged.
056: */
057: protected static Log log = LogFactory
058: .getLog("org.apache.commons.jexl.ExpressionFactory");
059:
060: /**
061: * The singleton ExpressionFactory also holds a single instance of
062: * {@link Parser}.
063: * When parsing expressions, ExpressionFactory synchronizes on Parser.
064: */
065: protected static Parser parser = new Parser(new StringReader(";")); //$NON-NLS-1$
066:
067: /**
068: * ExpressionFactory is a singleton and this is the private
069: * instance fufilling that pattern.
070: */
071: protected static ExpressionFactory ef = new ExpressionFactory();
072:
073: /**
074: * Private constructor, the single instance is always obtained
075: * with a call to getInstance().
076: */
077: private ExpressionFactory() {
078: }
079:
080: /**
081: * Returns the single instance of ExpressionFactory.
082: * @return the instance of ExpressionFactory.
083: */
084: protected static ExpressionFactory getInstance() {
085: return ef;
086: }
087:
088: /**
089: * Creates an Expression from a String containing valid
090: * JEXL syntax. This method parses the expression which
091: * must contain either a reference or an expression.
092: * @param expression A String containing valid JEXL syntax
093: * @return An Expression object which can be evaluated with a JexlContext
094: * @throws Exception An exception can be thrown if there is a problem
095: * parsing this expression, or if the expression is neither an
096: * expression or a reference.
097: */
098: public static Expression createExpression(String expression)
099: throws Exception {
100: return getInstance().createNewExpression(expression);
101: }
102:
103: /**
104: * Creates a new Expression based on the expression string.
105: *
106: * @param expression valid Jexl expression
107: * @return Expression
108: * @throws Exception for a variety of reasons - mostly malformed
109: * Jexl expression
110: */
111: protected Expression createNewExpression(final String expression)
112: throws Exception {
113:
114: String expr = cleanExpression(expression);
115:
116: // Parse the Expression
117: SimpleNode tree;
118: synchronized (parser) {
119: log.debug("Parsing expression: " + expr);
120: try {
121: tree = parser.parse(new StringReader(expr));
122: } catch (TokenMgrError tme) {
123: throw new ParseException(tme.getMessage());
124: }
125: }
126:
127: if (tree.jjtGetNumChildren() > 1 && log.isWarnEnabled()) {
128: log
129: .warn("The JEXL Expression created will be a reference"
130: + " to the first expression from the supplied script: \""
131: + expression + "\" ");
132: }
133:
134: // Must be a simple reference, expression, statement or if, otherwise
135: // throw an exception.
136: SimpleNode node = (SimpleNode) tree.jjtGetChild(0);
137:
138: // TODO: Can we get rid of these checks?
139: if (node instanceof ASTReferenceExpression
140: || node instanceof ASTExpressionExpression
141: || node instanceof ASTStatementExpression
142: || node instanceof ASTIfStatement
143: || node instanceof ASTWhileStatement
144: || node instanceof ASTForeachStatement) {
145: return new ExpressionImpl(expression, node);
146: }
147: log.error("Invalid Expression, node of type: "
148: + node.getClass().getName());
149: throw new Exception(
150: "Invalid Expression: not a Reference, Expression, "
151: + "Statement or If");
152: }
153:
154: /**
155: * Trims the expression and adds a semi-colon if missing.
156: * @param expression to clean
157: * @return trimmed expression ending in a semi-colon
158: */
159: private String cleanExpression(String expression) {
160: String expr = expression.trim();
161: if (!expr.endsWith(";")) {
162: expr += ";";
163: }
164: return expr;
165: }
166: }
|