001: /*
002: * Copyright 2002-2006 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.jexl;
017:
018: import java.io.BufferedReader;
019: import java.io.File;
020: import java.io.FileReader;
021: import java.io.IOException;
022: import java.io.InputStreamReader;
023: import java.io.StringReader;
024: import java.net.URL;
025: import java.net.URLConnection;
026:
027: import org.apache.commons.jexl.parser.ASTJexlScript;
028: import org.apache.commons.jexl.parser.ParseException;
029: import org.apache.commons.jexl.parser.Parser;
030: import org.apache.commons.jexl.parser.SimpleNode;
031: import org.apache.commons.jexl.parser.TokenMgrError;
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
034:
035: /**
036: * <p>
037: * Creates {@link Script}s. To create a JEXL Script, pass
038: * valid JEXL syntax to the static createScript() method:
039: * </p>
040: *
041: * <pre>
042: * String jexl = "y = x * 12 + 44; y = y * 4;";
043: * Script script = ScriptFactory.createScript( jexl );
044: * </pre>
045: *
046: * <p>
047: * When an {@link Script} is created, the JEXL syntax is
048: * parsed and verified.
049: * </p>
050: * @since 1.1
051: * @version $Id: ScriptFactory.java 429175 2006-08-06 19:05:23Z rahul $
052: */
053: public class ScriptFactory {
054:
055: /** The Log to which all ScriptFactory messages will be logged.*/
056: protected static Log log = LogFactory
057: .getLog("org.apache.commons.jexl.ScriptFactory");
058:
059: /**
060: * The singleton ScriptFactory also holds a single instance of
061: * {@link Parser}. When parsing expressions, ScriptFactory
062: * synchronizes on Parser.
063: */
064: protected static Parser parser = new Parser(new StringReader(";"));
065:
066: /**
067: * ScriptFactory is a singleton and this is the private
068: * instance fufilling that pattern.
069: */
070: protected static ScriptFactory factory = new ScriptFactory();
071:
072: /**
073: * Private constructor, the single instance is always obtained
074: * with a call to getInstance().
075: */
076: private ScriptFactory() {
077:
078: }
079:
080: /**
081: * Returns the single instance of ScriptFactory.
082: * @return the instance of ScriptFactory.
083: */
084: protected static ScriptFactory getInstance() {
085: return factory;
086: }
087:
088: /**
089: * Creates a Script from a String containing valid JEXL syntax.
090: * This method parses the script which validates the syntax.
091: *
092: * @param scriptText A String containing valid JEXL syntax
093: * @return A {@link Script} which can be executed with a
094: * {@link JexlContext}.
095: * @throws Exception An exception can be thrown if there is a
096: * problem parsing the script.
097: */
098: public static Script createScript(String scriptText)
099: throws Exception {
100: return getInstance().createNewScript(scriptText);
101: }
102:
103: /**
104: * Creates a Script from a {@link File} containing valid JEXL syntax.
105: * This method parses the script and validates the syntax.
106: *
107: * @param scriptFile A {@link File} containing valid JEXL syntax.
108: * Must not be null. Must be a readable file.
109: * @return A {@link Script} which can be executed with a
110: * {@link JexlContext}.
111: * @throws Exception An exception can be thrown if there is a problem
112: * parsing the script.
113: */
114: public static Script createScript(File scriptFile) throws Exception {
115: if (scriptFile == null) {
116: throw new NullPointerException("scriptFile is null");
117: }
118: if (!scriptFile.canRead()) {
119: throw new IOException("Can't read scriptFile ("
120: + scriptFile.getCanonicalPath() + ")");
121: }
122: BufferedReader reader = new BufferedReader(new FileReader(
123: scriptFile));
124: return createScript(readerToString(reader));
125:
126: }
127:
128: /**
129: * Creates a Script from a {@link URL} containing valid JEXL syntax.
130: * This method parses the script and validates the syntax.
131: *
132: * @param scriptUrl A {@link URL} containing valid JEXL syntax.
133: * Must not be null. Must be a readable file.
134: * @return A {@link Script} which can be executed with a
135: * {@link JexlContext}.
136: * @throws Exception An exception can be thrown if there is a problem
137: * parsing the script.
138: */
139: public static Script createScript(URL scriptUrl) throws Exception {
140: if (scriptUrl == null) {
141: throw new NullPointerException("scriptUrl is null");
142: }
143: URLConnection connection = scriptUrl.openConnection();
144:
145: BufferedReader reader = new BufferedReader(
146: new InputStreamReader(connection.getInputStream()));
147: return createScript(readerToString(reader));
148: }
149:
150: /**
151: * Creates a new Script based on the string.
152: *
153: * @param scriptText valid Jexl script
154: * @return Script a new script
155: * @throws Exception for a variety of reasons - mostly malformed scripts
156: */
157: protected Script createNewScript(String scriptText)
158: throws Exception {
159: String cleanText = cleanScript(scriptText);
160: SimpleNode script;
161: // Parse the Expression
162: synchronized (parser) {
163: log.debug("Parsing script: " + cleanText);
164: try {
165: script = parser.parse(new StringReader(cleanText));
166: } catch (TokenMgrError tme) {
167: throw new ParseException(tme.getMessage());
168: }
169: }
170: if (script instanceof ASTJexlScript) {
171: return new ScriptImpl(cleanText, (ASTJexlScript) script);
172: } else {
173: throw new IllegalStateException("Parsed script is not "
174: + "an ASTJexlScript");
175: }
176: }
177:
178: /**
179: * @todo move to ParseUtils?
180: * Trims the expression and adds a semi-colon if missing.
181: * @param script to clean
182: * @return trimmed expression ending in a semi-colon
183: */
184: private String cleanScript(String script) {
185: String expr = script.trim();
186: if (!expr.endsWith(";")) {
187: expr += ";";
188: }
189: return expr;
190: }
191:
192: /**
193: * Read a buffered reader into a StringBuffer and return a String with
194: * the contents of the reader.
195: * @param reader to be read.
196: * @return the contents of the reader as a String.
197: * @throws IOException on any error reading the reader.
198: */
199: private static String readerToString(BufferedReader reader)
200: throws IOException {
201: StringBuffer buffer = new StringBuffer();
202: try {
203: String line = null;
204: while ((line = reader.readLine()) != null) {
205: buffer.append(line).append('\n');
206: }
207: return buffer.toString();
208: } finally {
209: reader.close();
210: }
211:
212: }
213:
214: }
|