View Javadoc

1   /*
2    * Copyright 2002-2006 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.apache.commons.jexl;
17  
18  import java.io.StringReader;
19  
20  import org.apache.commons.jexl.parser.ASTExpressionExpression;
21  import org.apache.commons.jexl.parser.ASTForeachStatement;
22  import org.apache.commons.jexl.parser.ASTIfStatement;
23  import org.apache.commons.jexl.parser.ASTReferenceExpression;
24  import org.apache.commons.jexl.parser.ASTStatementExpression;
25  import org.apache.commons.jexl.parser.ASTWhileStatement;
26  import org.apache.commons.jexl.parser.ParseException;
27  import org.apache.commons.jexl.parser.Parser;
28  import org.apache.commons.jexl.parser.SimpleNode;
29  import org.apache.commons.jexl.parser.TokenMgrError;
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  
33  /***
34   * <p>
35   * Creates Expression objects.  To create a JEXL Expression object, pass
36   * valid JEXL syntax to the static createExpression() method:
37   * </p>
38   *
39   * <pre>
40   * String jexl = "array[1]";
41   * Expression expression = ExpressionFactory.createExpression( jexl );
42   * </pre>
43   *
44   * <p>
45   * When an {@link Expression} object is created, the JEXL syntax is
46   * parsed and verified.  If the supplied expression is neither an
47   * expression nor a reference, an exception is thrown from createException().
48   * </p>
49   * @since 1.0
50   * @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
51   * @version $Id: ExpressionFactory.java 429169 2006-08-06 18:36:29Z rahul $
52   */
53  public class ExpressionFactory {
54      /***
55       * The Log to which all ExpressionFactory messages will be logged.
56       */
57      protected static Log log =
58          LogFactory.getLog("org.apache.commons.jexl.ExpressionFactory");
59  
60      /***
61       * The singleton ExpressionFactory also holds a single instance of
62       * {@link Parser}.
63       * When parsing expressions, ExpressionFactory synchronizes on Parser.
64       */
65      protected static Parser parser =
66              new Parser(new StringReader(";")); //$NON-NLS-1$
67  
68      /***
69       * ExpressionFactory is a singleton and this is the private
70       * instance fufilling that pattern.
71       */
72      protected static ExpressionFactory ef = new ExpressionFactory();
73  
74      /***
75       * Private constructor, the single instance is always obtained
76       * with a call to getInstance().
77       */
78      private ExpressionFactory() {
79      }
80  
81      /***
82       * Returns the single instance of ExpressionFactory.
83       * @return the instance of ExpressionFactory.
84       */
85      protected static  ExpressionFactory getInstance() {
86          return ef;
87      }
88  
89      /***
90       * Creates an Expression from a String containing valid
91       * JEXL syntax.  This method parses the expression which
92       * must contain either a reference or an expression.
93       * @param expression A String containing valid JEXL syntax
94       * @return An Expression object which can be evaluated with a JexlContext
95       * @throws Exception An exception can be thrown if there is a problem
96       *      parsing this expression, or if the expression is neither an
97       *      expression or a reference.
98       */
99      public static Expression createExpression(String expression)
100         throws Exception {
101         return getInstance().createNewExpression(expression);
102     }
103 
104 
105     /***
106      *  Creates a new Expression based on the expression string.
107      *
108      *  @param expression valid Jexl expression
109      *  @return Expression
110      *  @throws Exception for a variety of reasons - mostly malformed
111      *          Jexl expression
112      */
113     protected Expression createNewExpression(final String expression)
114         throws Exception {
115 
116         String expr = cleanExpression(expression);
117 
118         // Parse the Expression
119         SimpleNode tree;
120         synchronized (parser) {
121             log.debug("Parsing expression: " + expr);
122             try {
123                 tree = parser.parse(new StringReader(expr));
124             } catch (TokenMgrError tme) {
125                 throw new ParseException(tme.getMessage());
126             }
127         }
128 
129         if (tree.jjtGetNumChildren() > 1 && log.isWarnEnabled()) {
130             log.warn("The JEXL Expression created will be a reference"
131                 + " to the first expression from the supplied script: \""
132                 + expression + "\" ");
133         }
134 
135         // Must be a simple reference, expression, statement or if, otherwise
136         // throw an exception.
137         SimpleNode node = (SimpleNode) tree.jjtGetChild(0);
138 
139         // TODO: Can we get rid of these checks?
140         if (node instanceof ASTReferenceExpression
141             || node instanceof ASTExpressionExpression
142             || node instanceof ASTStatementExpression
143             || node instanceof ASTIfStatement
144             || node instanceof ASTWhileStatement
145             || node instanceof ASTForeachStatement
146             ) {
147             return new ExpressionImpl(expression, node);
148         }
149         log.error("Invalid Expression, node of type: "
150             + node.getClass().getName());
151         throw new Exception("Invalid Expression: not a Reference, Expression, "
152             + "Statement or If");
153     }
154 
155     /***
156      * Trims the expression and adds a semi-colon if missing.
157      * @param expression to clean
158      * @return trimmed expression ending in a semi-colon
159      */
160     private String cleanExpression(String expression) {
161         String expr = expression.trim();
162         if (!expr.endsWith(";")) {
163             expr += ";";
164         }
165         return expr;
166     }
167 }