All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.viadee.bpm.vPAV.output.JsOutputWriter Maven / Gradle / Ivy

Go to download

The tool checks Camunda projects for consistency and discovers errors in process-driven applications. Called as a Maven plugin or JUnit test, it discovers esp. inconsistencies of a given BPMN model in the classpath and the sourcecode of an underlying java project, such as a delegate reference to a non-existing java class or a non-existing Spring bean.

There is a newer version: 3.0.8
Show newest version
/**
 * BSD 3-Clause License
 *
 * Copyright © 2018, viadee Unternehmensberatung GmbH
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package de.viadee.bpm.vPAV.output;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.logging.Logger;

import de.viadee.bpm.vPAV.processing.model.data.ProcessVariable;
import de.viadee.bpm.vPAV.processing.model.data.*;

import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;

import de.viadee.bpm.vPAV.RuntimeConfig;
import de.viadee.bpm.vPAV.constants.BpmnConstants;
import de.viadee.bpm.vPAV.constants.ConfigConstants;
import de.viadee.bpm.vPAV.processing.model.graph.Path;

/**
 * Create the JavaScript file for HTML-output; Needs: issues and bpmnFile names
 */
public class JsOutputWriter implements IssueOutputWriter {

    private static Logger logger = Logger.getLogger(JsOutputWriter.class.getName());
    
    private Map ignoredIssuesMap = new HashMap<>();
    
    private Map wrongCheckersMap = new HashMap<>();
    
    private Set modelPaths = new HashSet<>();

    /**
     * Writes the output as JavaScript to the vPAV output folder
     */
    @Override
    public void write(final Collection issues) throws OutputWriterException {    	
    	
        final String json = transformToJsonDatastructure(issues, BpmnConstants.VPAV_ELEMENTS_TO_MARK);
        final String json_noIssues = transformToJsonDatastructure(getNoIssues(issues),
                BpmnConstants.VPAV_NO_ISSUES_ELEMENTS);
        final String bpmn = transformToXMLDatastructure();
        final String wrongCheckers = transformToJsDatastructure(getWrongCheckersMap());
        final String defaultCheckers = transformDefaultRulesToJsDatastructure(
                extractExternalCheckers(
                        RuntimeConfig.getInstance().getActiveRules()));
        final String issueSeverity = transformSeverityToJsDatastructure(createIssueSeverity(issues));
        final String ignoredIssues = transformIgnoredIssuesToJsDatastructure(getIgnoredIssuesMap());

        writeJS(json, json_noIssues, bpmn, wrongCheckers, defaultCheckers, issueSeverity, ignoredIssues);
    }

    public void prepareMaps(final Map wrongCheckers, final Map ignoredIssues, final Set modelPath) {
    	this.setWrongCheckersMap(wrongCheckers);
    	this.setIgnoredIssuesMap(ignoredIssues);
    	this.setModelPaths(modelPath);
    }


	/**
     * Creates list which contains elements with multiple issues and the marks it with highest severity
     * 
     * @param issues
     */
    private Map createIssueSeverity(final Collection issues) {
        Map issueSeverity = new HashMap();
        for (CheckerIssue issue : issues) {
            if (!issueSeverity.containsKey(issue.getElementId())) {
                issueSeverity.put(issue.getElementId(), issue.getClassification());
            } else if (issueSeverity.containsKey(issue.getElementId())
                    && issueSeverity.get(issue.getElementId()).equals(CriticalityEnum.WARNING)) {
                if (issue.getClassification().equals(CriticalityEnum.ERROR)) {
                    issueSeverity.put(issue.getElementId(), issue.getClassification());
                }
            }
        }

        return issueSeverity;
    }

    /**
     * Extract external rules from active ruleset
     * 
     * @param activeRules
     * @return
     */
    private ArrayList extractExternalCheckers(final ArrayList activeRules) {
        final ArrayList defaultRules = new ArrayList();
        for (String entry : RuntimeConfig.getInstance().getViadeeRules()) {
            if (activeRules.contains(entry)) {
                defaultRules.add(entry);
            }
        }
        return defaultRules;
    }

    /**
     * 
     * @param json
     * @param json_noIssues
     * @param bpmn
     * @throws OutputWriterException
     */
    private void writeJS(final String json, final String json_noIssues, final String bpmn, final String wrongCheckers,
            final String defaultCheckers, final String issueSeverity, final String ignoredIssues)
            throws OutputWriterException {
        if (json != null && !json.isEmpty()) {
            try {
                final FileWriter file = new FileWriter(ConfigConstants.VALIDATION_JS_MODEL_OUTPUT);
                file.write(bpmn);
                file.close();

                final OutputStreamWriter osWriter = new OutputStreamWriter(
                        new FileOutputStream(ConfigConstants.VALIDATION_JS_OUTPUT), StandardCharsets.UTF_8);
                osWriter.write(json);
                osWriter.close();

                final OutputStreamWriter osWriterSuccess = new OutputStreamWriter(
                        new FileOutputStream(ConfigConstants.VALIDATION_JS_SUCCESS_OUTPUT), StandardCharsets.UTF_8);
                osWriterSuccess.write(json_noIssues);
                osWriterSuccess.close();

                if ((wrongCheckers != null && !wrongCheckers.isEmpty())
                        && (defaultCheckers != null && !defaultCheckers.isEmpty())) {
                    final OutputStreamWriter wrongAndDefaultCheckers = new OutputStreamWriter(
                            new FileOutputStream(ConfigConstants.VALIDATION_CHECKERS), StandardCharsets.UTF_8);
                    wrongAndDefaultCheckers.write(wrongCheckers);
                    wrongAndDefaultCheckers.write(defaultCheckers);
                    wrongAndDefaultCheckers.close();
                } else if ((wrongCheckers == null || wrongCheckers.isEmpty())
                        && (defaultCheckers != null && !defaultCheckers.isEmpty())) {
                    final OutputStreamWriter defaultCheckerJS = new OutputStreamWriter(
                            new FileOutputStream(ConfigConstants.VALIDATION_CHECKERS), StandardCharsets.UTF_8);
                    defaultCheckerJS.write(defaultCheckers);
                    defaultCheckerJS.close();
                }

                if (issueSeverity != null && !issueSeverity.isEmpty()) {
                    final OutputStreamWriter issueSeverityWriter = new OutputStreamWriter(
                            new FileOutputStream(ConfigConstants.VALIDATION_ISSUE_SEVERITY), StandardCharsets.UTF_8);
                    issueSeverityWriter.write(issueSeverity);
                    issueSeverityWriter.close();
                }
                if (ignoredIssues != null && !ignoredIssues.isEmpty()) {
                    final OutputStreamWriter ignoredIssuesWriter = new OutputStreamWriter(
                            new FileOutputStream(ConfigConstants.VALIDATION_IGNORED_ISSUES_OUTPUT),
                            StandardCharsets.UTF_8);
                    ignoredIssuesWriter.write(ignoredIssues);
                    ignoredIssuesWriter.close();
                }

            } catch (final IOException ex) {
                throw new OutputWriterException("js output couldn't be written", ex);
            }
        }
    }

    /**
     * write javascript file with elements which have variables
     *
     * @param elements
     *            Collection of BPMN elements across all models
     * @throws OutputWriterException
     *             javascript couldnt be written
     */
    public void writeVars(Collection elements, Collection processVariables) throws OutputWriterException {
        try {
            FileWriter writer = new FileWriter(ConfigConstants.VALIDATION_JS_PROCESSVARIABLES, true);

            // write elements containing operations
            JsonArray jsonElements = elements.stream()
                    .map(JsOutputWriter::transformElementToJsonIncludingProcessVariables)
                    .filter(o -> o.has("elementId"))
                    .collect(JsonArray::new, JsonArray::add, JsonArray::addAll);
            StringBuilder jsFile = new StringBuilder();
            jsFile.append("var proz_vars = ")
                    .append(new GsonBuilder().setPrettyPrinting().create().toJson(jsonElements))
                    .append(";\n\n");


            JsonArray jsonVariables = processVariables.stream()
                    .map(JsOutputWriter::transformProcessVariablesToJson)
                    .collect(JsonArray::new, JsonArray::add, JsonArray::addAll);
            jsFile.append("var processVariables = ")
                    .append(new GsonBuilder().setPrettyPrinting().create().toJson(jsonVariables))
                    .append(";");

            writer.write(jsFile.toString());
            writer.close();
        } catch (IOException e) {
            logger.warning("Processvariables couldn't be written");
        }
    }

    private static JsonObject transformElementToJsonIncludingProcessVariables(BpmnElement element) {
        final JsonObject obj = new JsonObject();
        if (!element.getProcessVariables().isEmpty()) {
            // elementID
            obj.addProperty("elementId", element.getBaseElement().getId());
            // bpmnFile
            obj.addProperty(BpmnConstants.VPAV_BPMN_FILE, replace(File.separator, "\\", element.getProcessdefinition()));
            // element Name
            if (element.getBaseElement().getAttributeValue("name") != null)
                obj.addProperty("elementName", element.getBaseElement().getAttributeValue(BpmnConstants.ATTR_NAME));

            Function processVariableToJson = o -> {
                final JsonObject jsonOperation = new JsonObject();
                jsonOperation.addProperty("name", o.getName());
                jsonOperation.addProperty("fieldType", o.getFieldType().getDescription());
                jsonOperation.addProperty("elementChapter", o.getChapter().toString());
                return jsonOperation;
            };
            obj.add("read", element.getProcessVariables().values().stream()
                    .filter(o -> o.getOperation() == VariableOperation.READ)
                    .map(processVariableToJson).collect(JsonArray::new, JsonArray::add, JsonArray::addAll));
            obj.add("write", element.getProcessVariables().values().stream()
                    .filter(o -> o.getOperation() == VariableOperation.WRITE)
                    .map(processVariableToJson).collect(JsonArray::new, JsonArray::add, JsonArray::addAll));
            obj.add("delete", element.getProcessVariables().values().stream()
                    .filter(o -> o.getOperation() == VariableOperation.DELETE)
                    .map(processVariableToJson).collect(JsonArray::new, JsonArray::add, JsonArray::addAll));
        }
        return obj;
    }

    /**
     * Transforms a process variable to a json object
     *
     * @param processVariable
     * @return
     */
    private static JsonObject transformProcessVariablesToJson(final ProcessVariable processVariable) {
        final JsonObject obj = new JsonObject();
        obj.addProperty("name", processVariable.getName());
        if (processVariable.getOperations().size() > 0) {
            String bpmnFile = processVariable.getOperations().get(0).getElement().getProcessdefinition();
            obj.addProperty(BpmnConstants.VPAV_BPMN_FILE, replace(File.separator, "\\", bpmnFile));
        }
        Function processVariableToJson = o -> {
            final JsonObject jsonOperation = new JsonObject();
            jsonOperation.addProperty("elementId", o.getElement().getBaseElement().getId());
            jsonOperation.addProperty("elementName", o.getElement().getBaseElement().getAttributeValue("name"));
            jsonOperation.addProperty("fieldType", o.getFieldType().getDescription());
            jsonOperation.addProperty("elementChapter", o.getChapter().toString());
            return jsonOperation;
        };
        obj.add("read", processVariable.getReads().stream().map(processVariableToJson).collect(JsonArray::new, JsonArray::add, JsonArray::addAll));
        obj.add("write", processVariable.getWrites().stream().map(processVariableToJson).collect(JsonArray::new, JsonArray::add, JsonArray::addAll));
        obj.add("delete", processVariable.getDeletes().stream().map(processVariableToJson).collect(JsonArray::new, JsonArray::add, JsonArray::addAll));
        return obj;
    }

    /**
     * Check all checkers for successful verification
     *
     * @param issues
     *            list of all issues
     * @return list with checkers without issues
     */
    private Collection getNoIssues(final Collection issues) {
        Collection newIssues = new ArrayList();

        for (final String bpmnFilename : getModelPaths()) {
            Collection modelIssues = new ArrayList();
            modelIssues.addAll(issues);

            for (CheckerIssue issue : issues) {
                String prettyBpmnFilename = replace(File.separator, "\\", issue.getBpmnFile());
                if (!prettyBpmnFilename.equals(ConfigConstants.JS_BASEPATH + bpmnFilename))
                    modelIssues.remove(issue);
            }

            for (final String ruleName : RuntimeConfig.getInstance().getActiveRules()) {
                Collection ruleIssues = new ArrayList();
                ruleIssues.addAll(modelIssues);
                for (CheckerIssue issue : modelIssues) {
                    if (!issue.getRuleName().equals(ruleName))
                        ruleIssues.remove(issue);
                }
                if (ruleIssues.isEmpty())
                    newIssues.add(new CheckerIssue(ruleName, null, CriticalityEnum.SUCCESS,
                            (ConfigConstants.JS_BASEPATH + bpmnFilename), null, "", "", null, null, null,
                            "No issues found", null));
            }
        }

        return newIssues;
    }

    /**
     * Transforms the path and filename to XML
     *
     * @return output
     * @throws OutputWriterException
     */
    private String transformToXMLDatastructure() throws OutputWriterException {
        String output = "var diagramXMLSource = [\n";

        try {
            for (final String bpmnFilename : getModelPaths()) {
                String prettyBpmnFileName = replace(File.separator, "\\\\", bpmnFilename);
                output += "{\"name\":\"" + prettyBpmnFileName + "\",\n \"xml\": \"";
                output += convertBpmnFile(ConfigConstants.BASEPATH + bpmnFilename);
                output += "\"},\n";
            }
        } catch (IOException e) {
            throw new OutputWriterException("bpmnFile not found");
        }
        return output + "];\n";
    }

    /**
     *
     * @param search
     * @param replace
     * @param str
     * @return str
     */
    private static String replace(String search, String replace, String str) {
        int start = str.indexOf(search);

        while (start != -1) {
            str = str.substring(0, start) + replace + str.substring(start + search.length(), str.length());
            start = str.indexOf(search, start + replace.length());
        }
        return (str);
    }

    /**
     * Cleans bad unicode chars in a string
     *
     * @param path
     * @return s
     * @throws IOException
     */
    private String convertBpmnFile(String path) throws IOException {
        byte[] encoded = Files.readAllBytes(Paths.get(path));
        String s = new String(encoded);
        s = s.replace("\"", "\\\""); // replace " with \"
        s = s.replace('\n', ' '); // delete all \n
        s = s.replace('\r', ' '); // delete all \r
        s = s.replaceAll(">\\u0020*<", "><");
        s = s.replaceAll(">\\u0027*<", "><");
        return s;
    }

    /**
     * Transforms the collection of issues into JSON format
     *
     * @param issues
     * @return
     */
    private String transformToJsonDatastructure(final Collection issues, String varName) {
        final JsonArray jsonIssues = new JsonArray();
        if (issues != null && issues.size() > 0) {
            for (final CheckerIssue issue : issues) {
                final JsonObject obj = new JsonObject();
                obj.addProperty(BpmnConstants.VPAV_ID, issue.getId());
                obj.addProperty(BpmnConstants.VPAV_BPMN_FILE, replace(File.separator, "\\", issue.getBpmnFile()));
                obj.addProperty(BpmnConstants.VPAV_RULE_NAME, issue.getRuleName());
                obj.addProperty(BpmnConstants.VPAV_RULE_DESCRIPTION, issue.getRuleDescription());
                obj.addProperty(BpmnConstants.VPAV_ELEMENT_ID, issue.getElementId());
                obj.addProperty(BpmnConstants.VPAV_ELEMENT_NAME, issue.getElementName());
                obj.addProperty(BpmnConstants.VPAV_CLASSIFICATION, issue.getClassification().name());
                obj.addProperty(BpmnConstants.VPAV_RESOURCE_FILE, issue.getResourceFile());
                obj.addProperty(BpmnConstants.VPAV_VARIABLE, issue.getVariable());
                obj.addProperty(BpmnConstants.VPAV_ANOMALY,
                        issue.getAnomaly() == null ? null : issue.getAnomaly().getDescription());
                final JsonArray jsonPaths = new JsonArray();
                final List paths = issue.getInvalidPaths();
                if (paths != null && paths.size() > 0) {
                    for (final Path path : paths) {
                        final JsonArray jsonPath = new JsonArray();
                        final List elements = path.getElements();
                        for (BpmnElement element : elements) {
                            final JsonObject jsonElement = new JsonObject();
                            final String id = element.getBaseElement().getId();
                            final String name = element.getBaseElement().getAttributeValue(BpmnConstants.ATTR_NAME);
                            jsonElement.addProperty(BpmnConstants.VPAV_ELEMENT_ID, id);
                            jsonElement.addProperty(BpmnConstants.VPAV_ELEMENT_NAME,
                                    name == null ? null : name.replaceAll("\n", ""));
                            jsonPath.add(jsonElement);
                        }
                        jsonPaths.add(jsonPath);
                    }
                }
                obj.add(BpmnConstants.VPAV_PATHS, jsonPaths);
                obj.addProperty(BpmnConstants.VPAV_MESSAGE, issue.getMessage());
                obj.addProperty(BpmnConstants.VPAV_ELEMENT_DESCRIPTION, issue.getElementDescription());
                jsonIssues.add(obj);
            }
        }
        return ("var " + varName + " = " + new GsonBuilder().setPrettyPrinting().create().toJson(jsonIssues) + ";");
    }

    /**
     * Transforms the collection of wrong checkers into JSON format
     *
     * @param issues
     * @return
     */
    private String transformToJsDatastructure(final Map wrongCheckers) {
        final String varName = "unlocatedCheckers";
        final JsonArray jsonIssues = new JsonArray();
        if (wrongCheckers != null && wrongCheckers.size() > 0) {
            for (Map.Entry entry : wrongCheckers.entrySet()) {
                final JsonObject obj = new JsonObject();
                obj.addProperty(ConfigConstants.RULENAME, entry.getKey());
                obj.addProperty(ConfigConstants.MESSAGE, entry.getValue());
                jsonIssues.add(obj);
            }
        }
        return ("var " + varName + " = " + new GsonBuilder().setPrettyPrinting().create().toJson(jsonIssues) + ";");
    }

    /**
     * Transforms the collection of issue severities into JSON format
     *
     * @param issues
     * @return
     */
    private String transformSeverityToJsDatastructure(final Map issues) {
        final String varName = "issueSeverity";
        final JsonArray jsonIssues = new JsonArray();
        if (issues != null && issues.size() > 0) {
            for (Map.Entry entry : issues.entrySet()) {
                final JsonObject obj = new JsonObject();
                obj.addProperty(BpmnConstants.ATTR_ID, entry.getKey());
                obj.addProperty(ConfigConstants.CRITICALITY, entry.getValue().name().toString());
                jsonIssues.add(obj);
            }
        }
        return ("var " + varName + " = " + new GsonBuilder().setPrettyPrinting().create().toJson(jsonIssues) + ";");
    }

    /**
     * Transforms the map of ignored issues into JSON format
     * 
     * @param ignoredIssues
     * @return
     */
    private String transformIgnoredIssuesToJsDatastructure(Map ignoredIssues) {
        final String ignoredIssuesList = "ignoredIssues";
        final JsonArray ignoredIssesJson = new JsonArray();

        if (ignoredIssues != null && ignoredIssues.size() > 0) {
            for (Map.Entry entry : ignoredIssues.entrySet()) {
                final JsonObject obj = new JsonObject();
                obj.addProperty("ID", entry.getKey());
                obj.addProperty("Comment", entry.getValue());
                ignoredIssesJson.add(obj);
            }
        }
        return ("var " + ignoredIssuesList + " = "
                + new GsonBuilder().setPrettyPrinting().create().toJson(ignoredIssues) + ";");
    }

    /**
     * 
     * @param wrongCheckers
     * @return
     */
    private String transformDefaultRulesToJsDatastructure(final ArrayList defaultCheckers) {
        final String varName = "defaultCheckers";
        final JsonArray jsonIssues = new JsonArray();
        if (defaultCheckers != null && defaultCheckers.size() > 0) {
            for (String entry : defaultCheckers) {
                final JsonObject obj = new JsonObject();
                obj.addProperty(ConfigConstants.RULENAME, entry);
                jsonIssues.add(obj);
            }
        }
        return ("\n var " + varName + " = " + new GsonBuilder().setPrettyPrinting().create().toJson(jsonIssues) + ";");
    }

	public Map getIgnoredIssuesMap() {
		return ignoredIssuesMap;
	}

	public void setIgnoredIssuesMap(Map ignoredIssuesMap) {
		this.ignoredIssuesMap = ignoredIssuesMap;
	}

	public Map getWrongCheckersMap() {
		return wrongCheckersMap;
	}

	public void setWrongCheckersMap(Map wrongCheckersMap) {
		this.wrongCheckersMap = wrongCheckersMap;
	}

	public Set getModelPaths() {
		return modelPaths;
	}

	public void setModelPaths(Set modelPaths) {
		this.modelPaths = modelPaths;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy