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 }