View Javadoc

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