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" <br/>
034     * Extension: "jexl"
035     * </p>
036     * <p>
037     * See
038     * <a href="http://java.sun.com/javase/6/docs/api/javax/script/package-summary.html">Java Scripting API</a>
039     * Javadoc.
040     * @since 2.0
041     */
042    public class JexlScriptEngineFactory implements ScriptEngineFactory {
043    
044        /** {@inheritDoc} */
045        public String getEngineName() {
046            return "JEXL Engine";
047        }
048    
049        /** {@inheritDoc} */
050        public String getEngineVersion() {
051            return "1.0"; // ensure this is updated if function changes are made to this class
052        }
053    
054        /** {@inheritDoc} */
055        public String getLanguageName() {
056            return "JEXL";
057        }
058    
059        /** {@inheritDoc} */
060        public String getLanguageVersion() {
061            return "2.0"; // TODO this should be derived from the actual version
062        }
063    
064        /** {@inheritDoc} */
065        public String getMethodCallSyntax(String obj, String m, String... args) {
066            StringBuilder sb = new StringBuilder();
067            sb.append(obj);
068            sb.append('.');
069            sb.append(m);
070            sb.append('(');
071            boolean needComma = false;
072            for(String arg : args){
073                if (needComma) {
074                    sb.append(',');
075                }
076                sb.append(arg);
077                needComma = true;
078            }
079            sb.append(')');
080            return sb.toString();
081        }
082    
083        /** {@inheritDoc} */
084        public List<String> getExtensions() {
085            return Collections.unmodifiableList(Arrays.asList("jexl"));
086        }
087    
088        /** {@inheritDoc} */
089        public List<String> getMimeTypes() {
090            return Collections.unmodifiableList(Arrays.asList("application/x-jexl"));
091        }
092    
093        /** {@inheritDoc} */
094        public List<String> getNames() {
095            return Collections.unmodifiableList(Arrays.asList("JEXL", "Jexl", "jexl"));
096        }
097    
098        /** {@inheritDoc} */
099        public String getOutputStatement(String toDisplay) {
100            if (toDisplay == null) {
101                return "JEXL.out.print(null)";
102            } else {
103                return "JEXL.out.print("+StringParser.escapeString(toDisplay)+")";
104            }
105        }
106    
107        /** {@inheritDoc} */
108        public Object getParameter(String key) {
109            if (key.equals(ScriptEngine.ENGINE)) {
110                return getEngineName();
111            } else if (key.equals(ScriptEngine.ENGINE_VERSION)) {
112                return getEngineVersion();
113            } else if (key.equals(ScriptEngine.NAME)) {
114                return getNames();
115            } else if (key.equals(ScriptEngine.LANGUAGE)) {
116                return getLanguageName();
117            } else if(key.equals(ScriptEngine.LANGUAGE_VERSION)) {
118                return getLanguageVersion();
119            } else if (key.equals("THREADING")) {
120                /*
121                 * To implement multithreading, the scripting engine context (inherited from AbstractScriptEngine)
122                 * would need to be made thread-safe; so would the setContext/getContext methods.
123                 * It is easier to share the underlying Uberspect and JEXL engine instance, especially
124                 * with an expression cache.
125                 */
126                return null;
127            }
128            return null;
129        }
130    
131        /** {@inheritDoc} */
132        public String getProgram(String... statements) {
133            StringBuilder sb = new StringBuilder();
134            for(String statement : statements){
135                sb.append(JexlEngine.cleanExpression(statement));
136                if (!statement.endsWith(";")){
137                    sb.append(';');
138                }
139            }
140            return sb.toString();
141        }
142    
143        /** {@inheritDoc} */
144        public ScriptEngine getScriptEngine() {
145            JexlScriptEngine engine = new JexlScriptEngine(this);
146            return engine;
147        }
148    
149    }