Package org.freeplane.plugin.script

Source Code of org.freeplane.plugin.script.GroovyScript

/*
*  Freeplane - mind map editor
*  Copyright (C) 2012 Dimitry
*
*  This file author is Dimitry
*
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation, either version 2 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.freeplane.plugin.script;

import groovy.lang.Binding;
import groovy.lang.GroovyRuntimeException;
import groovy.lang.GroovyShell;
import groovy.lang.Script;

import java.io.File;
import java.io.PrintStream;
import java.util.regex.Matcher;

import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.freeplane.core.resources.ResourceController;
import org.freeplane.features.map.NodeModel;
import org.freeplane.features.mode.Controller;
import org.freeplane.main.application.FreeplaneSecurityManager;
import org.freeplane.plugin.script.proxy.ProxyFactory;

/**
* Special scripting implementation for Groovy.
*/
public class GroovyScript implements IScript {
    final private Object script;
    private final ScriptingPermissions specificPermissions;
    private Script compiledScript;
    private Throwable errorsInScript;
    private IFreeplaneScriptErrorHandler errorHandler;
    private PrintStream outStream;
    private ScriptContext scriptContext;
    private CompileTimeStrategy compileTimeStrategy;

    public GroovyScript(String script) {
        this((Object) script);
    }

    public GroovyScript(File script) {
        this((Object) script);
        compileTimeStrategy = new CompileTimeStrategy(script);
    }

    public GroovyScript(String script, ScriptingPermissions permissions) {
        this((Object) script, permissions);
    }

    public GroovyScript(File script, ScriptingPermissions permissions) {
        this((Object) script, permissions);
        compileTimeStrategy = new CompileTimeStrategy(script);
    }

    private GroovyScript(Object script, ScriptingPermissions permissions) {
        super();
        this.script = script;
        this.specificPermissions = permissions;
        compiledScript = null;
        errorsInScript = null;
        errorHandler = ScriptResources.IGNORING_SCRIPT_ERROR_HANDLER;
        outStream = System.out;
        scriptContext = null;
        compileTimeStrategy = new CompileTimeStrategy(null);
    }

    private GroovyScript(Object script) {
        this(script, null);
    }

    @Override
    public IScript setErrorHandler(IFreeplaneScriptErrorHandler pErrorHandler) {
        this.errorHandler = pErrorHandler;
        return this;
    }

    @Override
    public IScript setOutStream(PrintStream outStream) {
        this.outStream = outStream;
        return this;
    }

    @Override
    public IScript setScriptContext(ScriptContext scriptContext) {
        this.scriptContext = scriptContext;
        return this;
    }

    @Override
    public Object getScript() {
        return script;
    }

    public Script getCompiledScript() {
        return compiledScript;
    }

    @Override
    public Object execute(final NodeModel node) {
        try {
            if (errorsInScript != null && compileTimeStrategy.canUseOldCompiledScript()) {
                throw new ExecuteScriptException(errorsInScript.getMessage(), errorsInScript);
            }
            final ScriptingSecurityManager scriptingSecurityManager = createScriptingSecurityManager();
            final ScriptingPermissions originalScriptingPermissions = new ScriptingPermissions(ResourceController
                .getResourceController().getProperties());
            final FreeplaneSecurityManager securityManager = (FreeplaneSecurityManager) System.getSecurityManager();
            final boolean needToSetFinalSecurityManager = securityManager.needToSetFinalSecurityManager();
            final PrintStream oldOut = System.out;
            try {
                compileAndCache();
                final Binding binding = createBinding(node);
                compiledScript.setBinding(binding);
                if (needToSetFinalSecurityManager)
                    securityManager.setFinalSecurityManager(scriptingSecurityManager);
                System.setOut(outStream);
                return compiledScript.run();
            }
            finally {
                System.setOut(oldOut);
                if (needToSetFinalSecurityManager && securityManager.hasFinalSecurityManager())
                    securityManager.removeFinalSecurityManager(scriptingSecurityManager);
                /* restore preferences (and assure that the values are unchanged!). */
                originalScriptingPermissions.restorePermissions();
            }
        }
        catch (final GroovyRuntimeException e) {
            handleScriptRuntimeException(e);
            // :fixme: This throw is only reached, if handleScriptRuntimeException
            // does not raise an exception. Should it be here at all?
            // And if: Shouldn't it raise an ExecuteScriptException?
            throw new RuntimeException(e);
        }
        catch (final Throwable e) {
            if (Controller.getCurrentController().getSelection() != null)
                Controller.getCurrentModeController().getMapController().select(node);
            throw new ExecuteScriptException(e.getMessage(), e);
        }
    }

    private ScriptingSecurityManager createScriptingSecurityManager() {
        return new ScriptSecurity(script, specificPermissions, outStream).getScriptingSecurityManager();
    }

  private Script compileAndCache() throws Throwable {
    if (compileTimeStrategy.canUseOldCompiledScript())
      return compiledScript;
    removeOldScript();
    errorsInScript = null;
    if (script instanceof Script)
      return (Script) script;
    else
      try {
        final Binding binding = createBindingForCompilation();
        final ClassLoader classLoader = GroovyScript.class.getClassLoader();
        final GroovyShell shell = new GroovyShell(classLoader, binding, createCompilerConfiguration());
        compileTimeStrategy.scriptCompileStart();
        if (script instanceof String)
          compiledScript = shell.parse((String) script);
        else if (script instanceof File)
          compiledScript = shell.parse((File) script);
        else
          throw new IllegalArgumentException();
        compileTimeStrategy.scriptCompiled();
        return compiledScript;
      }
      catch (Throwable e) {
        errorsInScript = e;
        throw e;
      }
  }

  private void removeOldScript() {
    if (compiledScript != null) {
      InvokerHelper.removeClass(compiledScript.getClass());
      compiledScript = null;
    }
  }

    private Binding createBinding(final NodeModel node) {
        final Binding binding = new Binding();
        binding.setVariable("c", ProxyFactory.createController(scriptContext));
        binding.setVariable("node", ProxyFactory.createNode(node, scriptContext));
        return binding;
    }

  private Binding createBindingForCompilation() {
        final Binding binding = new Binding();
        binding.setVariable("c", null);
        binding.setVariable("node", null);
        return binding;
    }

    private void handleScriptRuntimeException(final GroovyRuntimeException e) {
        outStream.print("message: " + e.getMessage());
        final ModuleNode module = e.getModule();
        final ASTNode astNode = e.getNode();
        int lineNumber = -1;
        if (module != null) {
            lineNumber = module.getLineNumber();
        }
        else if (astNode != null) {
            lineNumber = astNode.getLineNumber();
        }
        else {
            lineNumber = findLineNumberInString(e.getMessage(), lineNumber);
        }
        outStream.print("Line number: " + lineNumber);
        errorHandler.gotoLine(lineNumber);
        throw new ExecuteScriptException(e.getMessage() + " at line " + lineNumber, e);
    }

    static CompilerConfiguration createCompilerConfiguration() {
        CompilerConfiguration config = new CompilerConfiguration();
        config.setScriptBaseClass(FreeplaneScriptBaseClass.class.getName());
        if (!(ScriptResources.getClasspath() == null || ScriptResources.getClasspath().isEmpty())) {
            config.setClasspathList(ScriptResources.getClasspath());
        }
        return config;
    }

    private int findLineNumberInString(final String resultString, int lineNumber) {
        final java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(".*@ line ([0-9]+).*",
            java.util.regex.Pattern.DOTALL);
        final Matcher matcher = pattern.matcher(resultString);
        if (matcher.matches()) {
            lineNumber = Integer.parseInt(matcher.group(1));
        }
        return lineNumber;
    }

  @Override
    protected void finalize() throws Throwable {
      removeOldScript();
      super.finalize();
    }

    @Override
    public boolean permissionsEquals(ScriptingPermissions permissions) {
        if (this.specificPermissions == null)
            return this.specificPermissions == permissions;
        else
            return this.specificPermissions.equals(permissions);
    }
}
TOP

Related Classes of org.freeplane.plugin.script.GroovyScript

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.