001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.jexl2.scripting;
019    
020    import java.util.Arrays;
021    import java.util.Collections;
022    import java.util.List;
023    
024    import javax.script.ScriptEngine;
025    import javax.script.ScriptEngineFactory;
026    import org.apache.commons.jexl2.JexlEngine;
027    import org.apache.commons.jexl2.parser.StringParser;
028    
029    /**
030     * Implements the Jexl ScriptEngineFactory for JSF-223.
031     * <p>
032     * Supports the following:<br.>
033     * Language short names: "JEXL", "Jexl", "jexl", "JEXL2", "Jexl2", "jexl2" <br/>
034     * File Extensions: ".jexl", ".jexl2"<br/>
035     * "jexl2" etc. were added for engineVersion="2.0".
036     * </p>
037     * <p>
038     * See
039     * <a href="http://java.sun.com/javase/6/docs/api/javax/script/package-summary.html">Java Scripting API</a>
040     * Javadoc.
041     * @since 2.0
042     */
043    public class JexlScriptEngineFactory implements ScriptEngineFactory {
044    
045        /** {@inheritDoc} */
046        public String getEngineName() {
047            return "JEXL Engine";
048        }
049    
050        /** {@inheritDoc} */
051        public String getEngineVersion() {
052            return "2.0"; // ensure this is updated if function changes are made to this class
053        }
054    
055        /** {@inheritDoc} */
056        public String getLanguageName() {
057            return "JEXL";
058        }
059    
060        /** {@inheritDoc} */
061        public String getLanguageVersion() {
062            return "2.0"; // TODO this should be derived from the actual version
063        }
064    
065        /** {@inheritDoc} */
066        public String getMethodCallSyntax(String obj, String m, String[] args) {
067            StringBuilder sb = new StringBuilder();
068            sb.append(obj);
069            sb.append('.');
070            sb.append(m);
071            sb.append('(');
072            boolean needComma = false;
073            for(String arg : args){
074                if (needComma) {
075                    sb.append(',');
076                }
077                sb.append(arg);
078                needComma = true;
079            }
080            sb.append(')');
081            return sb.toString();
082        }
083    
084        /** {@inheritDoc} */
085        public List<String> getExtensions() {
086            return Collections.unmodifiableList(Arrays.asList("jexl", "jexl2"));
087        }
088    
089        /** {@inheritDoc} */
090        public List<String> getMimeTypes() {
091            return Collections.unmodifiableList(Arrays.asList("application/x-jexl", "application/x-jexl2"));
092        }
093    
094        /** {@inheritDoc} */
095        public List<String> getNames() {
096            return Collections.unmodifiableList(Arrays.asList("JEXL", "Jexl", "jexl", "JEXL2", "Jexl2", "jexl2"));
097        }
098    
099        /** {@inheritDoc} */
100        public String getOutputStatement(String toDisplay) {
101            if (toDisplay == null) {
102                return "JEXL.out.print(null)";
103            } else {
104                return "JEXL.out.print("+StringParser.escapeString(toDisplay, '\'')+")";
105            }
106        }
107    
108        /** {@inheritDoc} */
109        public Object getParameter(String key) {
110            if (key.equals(ScriptEngine.ENGINE)) {
111                return getEngineName();
112            } else if (key.equals(ScriptEngine.ENGINE_VERSION)) {
113                return getEngineVersion();
114            } else if (key.equals(ScriptEngine.NAME)) {
115                return getNames();
116            } else if (key.equals(ScriptEngine.LANGUAGE)) {
117                return getLanguageName();
118            } else if(key.equals(ScriptEngine.LANGUAGE_VERSION)) {
119                return getLanguageVersion();
120            } else if (key.equals("THREADING")) {
121                /*
122                 * To implement multithreading, the scripting engine context (inherited from AbstractScriptEngine)
123                 * would need to be made thread-safe; so would the setContext/getContext methods.
124                 * It is easier to share the underlying Uberspect and JEXL engine instance, especially
125                 * with an expression cache.
126                 */
127                return null;
128            }
129            return null;
130        }
131    
132        /** {@inheritDoc} */
133        public String getProgram(String[] statements) {
134            StringBuilder sb = new StringBuilder();
135            for(String statement : statements){
136                sb.append(JexlEngine.cleanExpression(statement));
137                if (!statement.endsWith(";")){
138                    sb.append(';');
139                }
140            }
141            return sb.toString();
142        }
143    
144        /** {@inheritDoc} */
145        public ScriptEngine getScriptEngine() {
146            JexlScriptEngine engine = new JexlScriptEngine(this);
147            return engine;
148        }
149    
150    }