com.mirth.connect.client.ui.util.VariableListUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.mirth.connect.client.ui.util.VariableListUtil.java

Source

/*
 * Copyright (c) Mirth Corporation. All rights reserved.
 * 
 * http://www.mirthcorp.com
 * 
 * The software in this package is published under the terms of the MPL license a copy of which has
 * been included with this distribution in the LICENSE.txt file.
 */

package com.mirth.connect.client.ui.util;

import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringEscapeUtils;

import com.mirth.connect.model.Connector;
import com.mirth.connect.model.Rule;
import com.mirth.connect.model.Step;
import com.mirth.connect.model.Transformer;

public class VariableListUtil {
    final static String COMMENT_SIMPLE_PATTERN = "//.*";
    final static String COMMENT_BLOCK_PATTERN = "/\\*(?:.|[\\n\\r])*?\\*/";

    /*
     * This regular expression uses alternation to capture either the
     * "xxxxxMap.put" syntax, or the "$x('key'," syntax. Kleene closures for
     * whitespace are used in between every method token since it is legal
     * JavaScript. Instead of checking ['"] once at the beginning and end, it
     * checks once and then uses a backreference later on. That way you can
     * capture keys like "Foo's Bar". It also accounts for backslashes before
     * any subsequent backreferences so that "Foo\"s Bar" would still be
     * captured. In the "$x" case, the regular expression also performs a
     * lookahead to ensure that there is a comma after the first argument,
     * indicating that it is the "put" version of the method, not the "get"
     * version.
     */
    final static String GLOBAL_AND_CHANNEL_VARIABLE_PATTERN = "(?<![A-Za-z0-9_$])(?:channel|global|globalChannel|response)Map\\s*\\.\\s*put\\s*\\(\\s*(['\"])(((?!(?<!\\\\)\\1).)*)\\1|(?<![A-Za-z0-9_$])\\$(?:g|gc|c|r)\\s*\\(\\s*(['\"])(((?!(?<!\\\\)\\4).)*)\\4(?=\\s*,)";
    final static String LOCAL_VARIABLE_PATTERN = "(?<![A-Za-z0-9_$])(?:channel|global|globalChannel|response|connector)Map\\s*\\.\\s*put\\s*\\(\\s*(['\"])(((?!(?<!\\\\)\\1).)*)\\1|(?<![A-Za-z0-9_$])\\$(?:g|gc|c|r|co)\\s*\\(\\s*(['\"])(((?!(?<!\\\\)\\4).)*)\\4(?=\\s*,)";
    final static int FULL_NAME_MATCHER_INDEX = 2;
    final static int SHORT_NAME_MATCHER_INDEX = 5;

    public static void getStepVariables(Set<String> targetSet, Transformer transformer, boolean includeLocalVars) {
        getStepVariables(targetSet, transformer, includeLocalVars, -1);
    }

    /*
     * Gets all steps that have variables that should show up in the global
     * variable list
     */
    public static void getStepVariables(Set<String> targetSet, Transformer transformer, boolean includeLocalVars,
            int row) {

        // add only the global variables
        List<Step> connectorSteps = transformer.getSteps();
        Iterator<Step> stepIterator = connectorSteps.iterator();
        String varPattern = GLOBAL_AND_CHANNEL_VARIABLE_PATTERN;
        if (includeLocalVars) {
            varPattern = LOCAL_VARIABLE_PATTERN;
        }
        int currentRow = 0;
        while (stepIterator.hasNext()) {
            if (row > -1 && row <= currentRow) {
                break;
            }
            Pattern pattern = Pattern.compile(varPattern);
            String scriptWithoutComments = getScriptWithoutComments(stepIterator.next().getScript());

            Matcher matcher = pattern.matcher(scriptWithoutComments);
            while (matcher.find()) {
                targetSet.add(getMapKey(matcher));
            }
            currentRow++;
        }
    }

    public static void getRuleVariables(Set<String> targetSet, Connector connector, boolean includeLocalVars) {
        getRuleVariables(targetSet, connector, includeLocalVars, -1);
    }

    /*
     * Gets all rules that have variables that should show up in the global
     * variable list
     */
    public static void getRuleVariables(Set<String> targetSet, Connector connector, boolean includeLocalVars,
            int row) {

        // add only the global variables
        List<Rule> connectorRules = connector.getFilter().getRules();
        Iterator<Rule> ruleIterator = connectorRules.iterator();
        String varPattern = GLOBAL_AND_CHANNEL_VARIABLE_PATTERN;
        if (includeLocalVars) {
            varPattern = LOCAL_VARIABLE_PATTERN;
        }
        int currentRow = 0;
        while (ruleIterator.hasNext()) {
            if (row > -1 && row <= currentRow) {
                break;
            }
            Pattern pattern = Pattern.compile(varPattern);
            String scriptWithoutComments = getScriptWithoutComments(ruleIterator.next().getScript());

            Matcher matcher = pattern.matcher(scriptWithoutComments);
            while (matcher.find()) {
                targetSet.add(getMapKey(matcher));
            }
            currentRow++;
        }
    }

    private static String getScriptWithoutComments(String script) {
        String scriptWithoutSimpleComments = null;
        String scriptWithoutCommentBlocks = null;
        String scriptWithoutComments = null;

        try {
            scriptWithoutSimpleComments = script.replaceAll(COMMENT_SIMPLE_PATTERN, "");
            scriptWithoutCommentBlocks = scriptWithoutSimpleComments.replaceAll(COMMENT_BLOCK_PATTERN, "");
        } catch (Throwable e) {
            // Catch stackoverflow bug in java http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6337993
        }

        if (scriptWithoutCommentBlocks != null) {
            scriptWithoutComments = scriptWithoutCommentBlocks;
        } else if (scriptWithoutSimpleComments != null) {
            scriptWithoutComments = scriptWithoutSimpleComments;
        } else {
            scriptWithoutComments = script;
        }

        return scriptWithoutComments;
    }

    private static String getMapKey(Matcher matcher) {
        /*
         * Since multiple capturing groups are used and the final key could
         * reside on either side of the alternation, we use two specific group
         * indices (2 and 5), one for the full "xxxxxMap" case and one for
         * the short "$x" case. We also replace JavaScript-specific escape
         * sequences like \', \", etc.
         */
        String key = matcher.group(FULL_NAME_MATCHER_INDEX);
        if (key == null) {
            key = matcher.group(SHORT_NAME_MATCHER_INDEX);
        }
        return StringEscapeUtils.unescapeEcmaScript(key);
    }
}