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