/*
 * #%L
 * fujion
 * %%
 * Copyright (C) 2020 Fujion Framework
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * #L%
 */
package org.fujion.script;

import org.apache.commons.lang3.StringUtils;
import org.fujion.common.Assert;
import org.fujion.common.MiscUtil;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.util.Map;

/**
 * Base class for JSR-223 compliant scripts.
 */
public abstract class BaseScript implements IScriptLanguage {

    /**
     * Wrapper for a script.
     */
    public class ParsedScript implements IParsedScript {

        private final String source;

        public ParsedScript(String source) {
            this.source = StringUtils.trimToEmpty(source);
        }

        @Override
        public Object run(Map<String, Object> variables) {
            ScriptEngine engine = SCRIPT_ENGINE_MANAGER.getEngineByName(engineName);
            Assert.state(engine != null, () -> displayName + " scripting engine was not found.");

            if (variables != null) {
                for (Map.Entry<String, Object> entry : variables.entrySet()) {
                    engine.put(entry.getKey(), entry.getValue());
                }
            }

            try {
                return engine.eval(source);
            } catch (ScriptException e) {
                throw MiscUtil.toUnchecked(e);
            }
        }

    }

    public static final ScriptEngineManager SCRIPT_ENGINE_MANAGER = new ScriptEngineManager();

    private final String type;

    private final String engineName;

    private final String displayName;

    protected BaseScript(
            String type,
            String engineName,
            String displayName) {
        this.type = type;
        this.engineName = engineName;
        this.displayName = displayName;
    }

    /**
     * @see IScriptLanguage#getType()
     */
    @Override
    public String getType() {
        return type;
    }

    /**
     * @see IScriptLanguage#parse(String)
     */
    @Override
    public IParsedScript parse(String source) {
        return new ParsedScript(source);
    }

}

@@ -800,98 +800,9 @@
 ipt.
-ScriptEngine;%0Aimport javax.script.ScriptEngineManager;%0Aimport javax.script.ScriptException
+*
 ;%0Aim
@@ -1075,24 +1075,122 @@
 ng source;%0A%0A
+        private final CompiledScript compiledScript;%0A%0A        private final ScriptEngine engine;%0A%0A
         publ
@@ -1293,113 +1293,17 @@
     
-%7D%0A%0A        @Override%0A        public Object run(Map%3CString, Object%3E variables) %7B%0A            ScriptEngine 
+    this.
 engi
@@ -1403,29 +1403,11 @@
 ll, 
-() -%3E displayName + %22
+%22%25s
  scr
@@ -1435,16 +1435,29 @@
  found.%22
+, displayName
 );%0A%0A    
@@ -1468,175 +1468,485 @@
     
-if (variables != null) %7B%0A                for (Map.Entry%3CString, Object%3E entry : variables.entrySet()) %7B%0A                    engine.put(entry.getKey(), entry.getValue()
+try %7B%0A                compiledScript = engine instanceof Compilable ? ((Compilable) engine).compile(source) : null;%0A            %7D catch (ScriptException e) %7B%0A                throw MiscUtil.toUnchecked(e);%0A            %7D%0A%0A        %7D%0A%0A        @Override%0A        public Object run(Map%3CString, Object%3E variables) %7B%0A            Bindings bindings = null;%0A%0A            if (variables != null) %7B%0A                bindings = engine.createBindings();%0A                bindings.putAll(variables
 );%0A 
@@ -1951,30 +1951,45 @@
 %0A           
+ %7D%0A%0A
      
-%7D
+       try %7B
 %0A           
@@ -1989,34 +1989,173 @@
         
-%7D%0A%0A            try
+    if (compiledScript != null) %7B%0A                    return bindings == null ? compiledScript.eval() : compiledScript.eval(bindings);%0A                %7D else
  %7B%0A     
@@ -2169,35 +2169,108 @@
     
+ 
+   
 return 
-engine.eval(source);
+bindings == null ? engine.eval(source) : engine.eval(source, bindings);%0A                %7D
 %0A   

Support compilable scripts.