/*
 * 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.commons.jexl;

import java.io.StringReader;

import org.apache.commons.jexl.parser.ParseException;
import org.apache.commons.jexl.parser.Parser;
import org.apache.commons.jexl.parser.SimpleNode;
import org.apache.commons.jexl.parser.TokenMgrError;
import org.apache.commons.jexl.util.Introspector;
import org.apache.commons.jexl.util.introspection.Uberspect;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * <p>
 * Creates Expression objects.  To create a JEXL Expression object, pass
 * valid JEXL syntax to the static createExpression() method:
 * </p>
 *
 * <pre>
 * String jexl = "array[1]";
 * Expression expression = ExpressionFactory.createExpression( jexl );
 * </pre>
 *
 * <p>
 * When an {@link Expression} object is created, the JEXL syntax is
 * parsed and verified.  If the supplied expression is neither an
 * expression nor a reference, an exception is thrown from createException().
 * </p>
 * @since 1.0
 * @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
 * @version $Id$
 */
public class ExpressionFactory {
    /**
     * The Log to which all ExpressionFactory messages will be logged.
     */
    protected static final Log LOG =
        LogFactory.getLog("org.apache.commons.jexl.ExpressionFactory");

    /**
     * The singleton ExpressionFactory also holds a single instance of
     * {@link Parser}.
     * When parsing expressions, ExpressionFactory synchronizes on Parser.
     */
    protected final Parser parser =
            new Parser(new StringReader(";")); //$NON-NLS-1$

    /**
     * ExpressionFactory is a singleton and this is the private
     * instance fulfilling that pattern.
     */
    protected static final ExpressionFactory EF = new ExpressionFactory();

    /**
     * Private constructor, the single instance is always obtained
     * with a call to getInstance().
     */
    private ExpressionFactory() {
        this(Introspector.getUberspect());
    }

    /**
     * Creates an expression factory using the provided {@link Uberspect}.
     * @param uberspect to allow different introspection behaviour
     */
    public ExpressionFactory(Uberspect uberspect) {
        parser.setUberspect(uberspect);
    }

    /**
     * Returns the single instance of ExpressionFactory.
     * @return the instance of ExpressionFactory.
     */
    public static ExpressionFactory getInstance() {
        return EF;
    }

    /**
     * Creates an Expression from a String containing valid
     * JEXL syntax.  This method parses the expression which
     * must contain either a reference or an expression.
     * @param expression A String containing valid JEXL syntax
     * @return An Expression object which can be evaluated with a JexlContext
     * @throws Exception An exception can be thrown if there is a problem
     *      parsing this expression, or if the expression is neither an
     *      expression or a reference.
     */
    public static Expression createExpression(String expression)
        throws Exception {
        return getInstance().createNewExpression(expression);
    }


    /**
     *  Creates a new Expression based on the expression string.
     *
     *  @param expression valid Jexl expression
     *  @return Expression
     *  @throws Exception for a variety of reasons - mostly malformed
     *          Jexl expression
     */
    public Expression createNewExpression(final String expression)
        throws Exception {

        String expr = cleanExpression(expression);

        // Parse the Expression
        SimpleNode tree;
        synchronized (parser) {
            LOG.debug("Parsing expression: " + expr);
            try {
                tree = parser.parse(new StringReader(expr));
            } catch (TokenMgrError tme) {
                throw new ParseException(tme.getMessage());
            }
        }

        if (tree.jjtGetNumChildren() > 1 && LOG.isWarnEnabled()) {
            LOG.warn("The JEXL Expression created will be a reference"
                + " to the first expression from the supplied script: \""
                + expression + "\" ");
        }

        // Must be a simple reference, expression, statement or if, otherwise
        // throw an exception.
        SimpleNode node = (SimpleNode) tree.jjtGetChild(0);

        Interpreter interpreter = new Interpreter(
            null,
            Introspector.getUberspect(), 
            new JexlArithmetic());
        // TODO: remove this from the parser.
        // interpreter.setUberspect(parser.getUberspect());
        return new ExpressionImpl(expression, node, interpreter);
    }

    /**
     * Trims the expression and adds a semi-colon if missing.
     * @param expression to clean
     * @return trimmed expression ending in a semi-colon
     */
    private String cleanExpression(String expression) {
        String expr = expression.trim();
        if (!expr.endsWith(";")) {
            expr += ";";
        }
        return expr;
    }
}

@@ -3575,32 +3575,37 @@
 %0A     * @throws 
+Parse
 Exception An exc
@@ -3838,32 +3838,37 @@
 %0A        throws 
+Parse
 Exception %7B%0A    
@@ -4100,24 +4100,29 @@
  *  @throws 
+Parse
 Exception fo
@@ -4127,63 +4127,17 @@
 for 
-a variety of reasons - mostly malformed%0A     *         
+malformed
  Jex
@@ -4223,32 +4223,24 @@
 ression)
-%0A       
  throws 
 Exceptio
@@ -4231,16 +4231,21 @@
  throws 
+Parse
 Exceptio
@@ -4621,24 +4621,173 @@
 Message());%0A
+            %7D catch (ParseException e) %7B%0A                throw e;%0A            %7D catch (Exception e) %7B%0A                throw new RuntimeException(e);%0A
             

Remove more throws Exception JEXL-33