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