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.BufferedReader;
    19  import java.io.File;
    20  import java.io.FileReader;
    21  import java.io.IOException;
    22  import java.io.InputStreamReader;
    23  import java.io.StringReader;
    24  import java.net.URL;
    25  import java.net.URLConnection;
    26
    27  import org.apache.commons.jexl.parser.ASTJexlScript;
    28  import org.apache.commons.jexl.parser.ParseException;
    29  import org.apache.commons.jexl.parser.Parser;
    30  import org.apache.commons.jexl.parser.SimpleNode;
    31  import org.apache.commons.jexl.parser.TokenMgrError;
    32  import org.apache.commons.logging.Log;
    33  import org.apache.commons.logging.LogFactory;
    34
    35  /***
    36   * <p>
    37   * Creates {@link Script}s.  To create a JEXL Script, pass
    38   * valid JEXL syntax to the static createScript() method:
    39   * </p>
    40   *
    41   * <pre>
    42   * String jexl = "y = x * 12 + 44; y = y * 4;";
    43   * Script script = ScriptFactory.createScript( jexl );
    44   * </pre>
    45   *
    46   * <p>
    47   * When an {@link Script} is created, the JEXL syntax is
    48   * parsed and verified.
    49   * </p>
    50   * @since 1.1
    51   * @version $Id: ScriptFactory.java 429175 2006-08-06 19:05:23Z rahul $
    52   */
    53  public class ScriptFactory {
    54
    55      /*** The Log to which all ScriptFactory messages will be logged.*/
    56      protected static Log log =
    57          LogFactory.getLog("org.apache.commons.jexl.ScriptFactory");
    58
    59      /***
    60       * The singleton ScriptFactory also holds a single instance of
    61       * {@link Parser}. When parsing expressions, ScriptFactory
    62       * synchronizes on Parser.
    63       */
    64      protected static Parser parser = new Parser(new StringReader(";"));
    65
    66      /***
    67       * ScriptFactory is a singleton and this is the private
    68       * instance fufilling that pattern.
    69       */
    70      protected static ScriptFactory factory = new ScriptFactory();
    71
    72      /***
    73       * Private constructor, the single instance is always obtained
    74       * with a call to getInstance().
    75       */
    76      private ScriptFactory() {
    77
    78      }
    79
    80      /***
    81       * Returns the single instance of ScriptFactory.
    82       * @return the instance of ScriptFactory.
    83       */
    84      protected static  ScriptFactory getInstance() {
    85          return factory;
    86      }
    87
    88      /***
    89       * Creates a Script from a String containing valid JEXL syntax.
    90       * This method parses the script which validates the syntax.
    91       *
    92       * @param scriptText A String containing valid JEXL syntax
    93       * @return A {@link Script} which can be executed with a
    94       *      {@link JexlContext}.
    95       * @throws Exception An exception can be thrown if there is a
    96       *      problem parsing the script.
    97       */
    98      public static Script createScript(String scriptText) throws Exception {
    99          return getInstance().createNewScript(scriptText);
    100     }
    101
    102     /***
    103      * Creates a Script from a {@link File} containing valid JEXL syntax.
    104      * This method parses the script and validates the syntax.
    105      *
    106      * @param scriptFile A {@link File} containing valid JEXL syntax.
    107      *      Must not be null. Must be a readable file.
    108      * @return A {@link Script} which can be executed with a
    109      *      {@link JexlContext}.
    110      * @throws Exception An exception can be thrown if there is a problem
    111      *      parsing the script.
    112      */
    113     public static Script createScript(File scriptFile) throws Exception {
    114         if (scriptFile == null) {
    115             throw new NullPointerException("scriptFile is null");
    116         }
    117         if (!scriptFile.canRead()) {
    118             throw new IOException("Can't read scriptFile ("
    119                 + scriptFile.getCanonicalPath() + ")");
    120         }
    121         BufferedReader reader = new BufferedReader(new FileReader(scriptFile));
    122         return createScript(readerToString(reader));
    123
    124     }
    125
    126     /***
    127      * Creates a Script from a {@link URL} containing valid JEXL syntax.
    128      * This method parses the script and validates the syntax.
    129      *
    130      * @param scriptUrl A {@link URL} containing valid JEXL syntax.
    131      *      Must not be null. Must be a readable file.
    132      * @return A {@link Script} which can be executed with a
    133      *      {@link JexlContext}.
    134      * @throws Exception An exception can be thrown if there is a problem
    135      *      parsing the script.
    136      */
    137     public static Script createScript(URL scriptUrl) throws Exception {
    138         if (scriptUrl == null) {
    139             throw new NullPointerException("scriptUrl is null");
    140         }
    141         URLConnection connection = scriptUrl.openConnection();
    142
    143         BufferedReader reader = new BufferedReader(
    144             new InputStreamReader(connection.getInputStream()));
    145         return createScript(readerToString(reader));
    146     }
    147
    148     /***
    149      *  Creates a new Script based on the string.
    150      *
    151      *  @param scriptText valid Jexl script
    152      *  @return Script a new script
    153      *  @throws Exception for a variety of reasons - mostly malformed scripts
    154      */
    155     protected Script createNewScript(String scriptText) throws Exception {
    156         String cleanText = cleanScript(scriptText);
    157         SimpleNode script;
    158         // Parse the Expression
    159         synchronized (parser) {
    160             log.debug("Parsing script: " + cleanText);
    161             try {
    162                 script = parser.parse(new StringReader(cleanText));
    163             } catch (TokenMgrError tme) {
    164                 throw new ParseException(tme.getMessage());
    165             }
    166         }
    167         if (script instanceof ASTJexlScript) {
    168             return new ScriptImpl(cleanText, (ASTJexlScript) script);
    169         } else {
    170             throw new IllegalStateException("Parsed script is not "
    171                 + "an ASTJexlScript");
    172         }
    173     }
    174
    175     /***
    176      * @todo move to ParseUtils?
    177      * Trims the expression and adds a semi-colon if missing.
    178      * @param script to clean
    179      * @return trimmed expression ending in a semi-colon
    180      */
    181     private String cleanScript(String script) {
    182         String expr = script.trim();
    183         if (!expr.endsWith(";")) {
    184             expr += ";";
    185         }
    186         return expr;
    187     }
    188
    189     /***
    190      * Read a buffered reader into a StringBuffer and return a String with
    191      * the contents of the reader.
    192      * @param reader to be read.
    193      * @return the contents of the reader as a String.
    194      * @throws IOException on any error reading the reader.
    195      */
    196     private static String readerToString(BufferedReader reader)
    197         throws IOException {
    198         StringBuffer buffer = new StringBuffer();
    199         try {
    200             String line = null;
    201             while ((line = reader.readLine()) != null) {
    202                 buffer.append(line).append('\n');
    203             }
    204             return buffer.toString();
    205         } finally {
    206             reader.close();
    207         }
    208
    209     }
    210
    211 }