1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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 }