<commit_before>/*******************************************************************************
 * Copyright (c) 2015-2016 Oak Ridge National Laboratory.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/
package org.csstudio.display.builder.runtime.script.internal;

import static org.csstudio.display.builder.runtime.RuntimePlugin.logger;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Queue;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.logging.Level;

import org.csstudio.display.builder.model.properties.ScriptInfo;
import org.csstudio.display.builder.model.util.NamedDaemonPool;

/** Script (Jython, Javascript) Support
 *
 *  <p>Each instance of the support module maintains one interpreter instance.
 *  Script files are parsed/compiled (possibly slow) and can then be executed
 *  multiple times (hopefully faster).
 *
 *  <p>Scripts are executed on one thread per support/interpreter.
 *
 *  @author <NAME>
 */
@SuppressWarnings("nls")
public class ScriptSupport
{
    /** Single thread script executor, shared by Jython and Javascript */
    private final ExecutorService executor = Executors.newSingleThreadExecutor(new NamedDaemonPool("ScriptSupport"));

    /** Futures of submitted scripts to allow cancellation */
    private final Queue<Future<Object>> active_scripts = new ConcurrentLinkedQueue<>();

    // Script supports.
    // Could provide two executors, one for jython and one for javascript,
    // but each one needs to be single-threaded because there's only one interpreter
    // with only one global variable for 'window' etc.
    private final PythonScriptSupport python;
    private final JythonScriptSupport jython;
    private final JavaScriptSupport javascript;

    public ScriptSupport() throws Exception
    {
        python = new PythonScriptSupport(this);
        jython = new JythonScriptSupport(this);
        javascript = new JavaScriptSupport(this);
    }

    /** Prepare script file for submission
     *
     *  @param path Path to the script. May be <code>null</null>.
     *              Added to the script engine's search path
     *              if not null to allow access to other scripts
     *              in the same location.
     *  @param name Name of script, used for messages
     *              and to identify the type of script (*.py, *.js)
     *  @param stream Stream for the script content
     *  @return {@link Script}
     *  @throws Exception on error
     */
    public Script compile(final String path, final String name, final InputStream stream) throws Exception
    {
        if (ScriptInfo.isPython(path, name))
            return python.compile(path, name);
        final InputStream script_stream = patchScript(name, stream);
        if (ScriptInfo.isJython(name))
            return jython.compile(path, name, script_stream);
        else if (ScriptInfo.isJavaScript(name))
            return javascript.compile(name, script_stream);
        throw new Exception("Cannot compile '" + name + "'");
    }

    /** Update legacy package names
     *  @param path Name of script (file name, URL)
     *  @param stream Stream for the script content
     *  @return Patched stream
     *  @throws Exception on error
     */
    private InputStream patchScript(final String path, final InputStream stream) throws Exception
    {
        boolean warned = false;

        final StringBuilder buf = new StringBuilder();
        final BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
        String line;
        while ((line = reader.readLine()) != null)
        {
            // Replace "from org.csstudio.opibuilder.scriptUtil" or
            // "import org.csstudio.opibuilder.scriptUtil",
            // but skip if this is indented within code that tries to handle portability, e.g.:
            // if 'getVersion' in dir(widget):
            //     .. new API
            // else:
            //     from org.csstudio.opibuilder.scriptUtil
            if (line.length() > 0      &&
                line.charAt(0) != ' '  &&
                line.charAt(0) != '\t' &&
                line.contains("org.csstudio.opibuilder.scriptUtil"))
            {
                if (! warned)
                {
                    logger.log(Level.SEVERE,<commit_msg>Script support: Reduce log level of legacy PVUtil import<commit_after>/*******************************************************************************
 * Copyright (c) 2015-2016 Oak Ridge National Laboratory.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/
package org.csstudio.display.builder.runtime.script.internal;

import static org.csstudio.display.builder.runtime.RuntimePlugin.logger;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Queue;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.logging.Level;

import org.csstudio.display.builder.model.properties.ScriptInfo;
import org.csstudio.display.builder.model.util.NamedDaemonPool;

/** Script (Jython, Javascript) Support
 *
 *  <p>Each instance of the support module maintains one interpreter instance.
 *  Script files are parsed/compiled (possibly slow) and can then be executed
 *  multiple times (hopefully faster).
 *
 *  <p>Scripts are executed on one thread per support/interpreter.
 *
 *  @author <NAME>
 */
@SuppressWarnings("nls")
public class ScriptSupport
{
    /** Single thread script executor, shared by Jython and Javascript */
    private final ExecutorService executor = Executors.newSingleThreadExecutor(new NamedDaemonPool("ScriptSupport"));

    /** Futures of submitted scripts to allow cancellation */
    private final Queue<Future<Object>> active_scripts = new ConcurrentLinkedQueue<>();

    // Script supports.
    // Could provide two executors, one for jython and one for javascript,
    // but each one needs to be single-threaded because there's only one interpreter
    // with only one global variable for 'window' etc.
    private final PythonScriptSupport python;
    private final JythonScriptSupport jython;
    private final JavaScriptSupport javascript;

    public ScriptSupport() throws Exception
    {
        python = new PythonScriptSupport(this);
        jython = new JythonScriptSupport(this);
        javascript = new JavaScriptSupport(this);
    }

    /** Prepare script file for submission
     *
     *  @param path Path to the script. May be <code>null</null>.
     *              Added to the script engine's search path
     *              if not null to allow access to other scripts
     *              in the same location.
     *  @param name Name of script, used for messages
     *              and to identify the type of script (*.py, *.js)
     *  @param stream Stream for the script content
     *  @return {@link Script}
     *  @throws Exception on error
     */
    public Script compile(final String path, final String name, final InputStream stream) throws Exception
    {
        if (ScriptInfo.isPython(path, name))
            return python.compile(path, name);
        final InputStream script_stream = patchScript(name, stream);
        if (ScriptInfo.isJython(name))
            return jython.compile(path, name, script_stream);
        else if (ScriptInfo.isJavaScript(name))
            return javascript.compile(name, script_stream);
        throw new Exception("Cannot compile '" + name + "'");
    }

    /** Update legacy package names
     *  @param path Name of script (file name, URL)
     *  @param stream Stream for the script content
     *  @return Patched stream
     *  @throws Exception on error
     */
    private InputStream patchScript(final String path, final InputStream stream) throws Exception
    {
        boolean warned = false;

        final StringBuilder buf = new StringBuilder();
        final BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
        String line;
        while ((line = reader.readLine()) != null)
        {
            // Replace "from org.csstudio.opibuilder.scriptUtil" or
            // "import org.csstudio.opibuilder.scriptUtil",
            // but skip if this is indented within code that tries to handle portability, e.g.:
            // if 'getVersion' in dir(widget):
            //     .. new API
            // else:
            //     from org.csstudio.opibuilder.scriptUtil
            if (line.length() > 0      &&
                line.charAt(0) != ' '  &&
                line.charAt(0) != '\t' &&
                line.contains("org.csstudio.opibuilder.scriptUtil"))
            {
                if (! warned)
                {   // This message can be quite frequent, and in most cases the display will work just fine,
                    // to don't use WARNING let alone SEVERE
                    logger.log(Level.INFO,
{'fraction_non_alphanumeric': 0.11805414207205374, 'fraction_numerical': 0.003053124363932424, 'mean_word_length': 2.8416731821735732, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2776181', 'n_tokens_mistral': 2553, 'n_tokens_neox': 2447, 'n_words': 828}