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