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