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