org.jbpm.designer.bpmn2.validation.BPMN2SyntaxChecker.java Source code

Java tutorial

Introduction

Here is the source code for org.jbpm.designer.bpmn2.validation.BPMN2SyntaxChecker.java

Source

/*
 * Copyright 2015 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * 
 *      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.
*/

package org.jbpm.designer.bpmn2.validation;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import bpsim.*;
import bpsim.impl.BpsimFactoryImpl;
import org.drools.core.xml.SemanticModules;
import org.eclipse.bpmn2.*;
import org.eclipse.bpmn2.Process;
import org.eclipse.emf.ecore.util.FeatureMap;
import org.jboss.drools.impl.DroolsFactoryImpl;
import org.jbpm.bpmn2.xml.BPMNDISemanticModule;
import org.jbpm.bpmn2.xml.BPMNSemanticModule;
import org.jbpm.compiler.xml.XmlProcessReader;
import org.jbpm.designer.web.profile.IDiagramProfile;
import org.jbpm.process.core.validation.ProcessValidationError;
import org.jbpm.ruleflow.core.validation.RuleFlowProcessValidator;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BPMN2SyntaxChecker implements SyntaxChecker {
    public static final String BPMN2_TYPE = "BPMN2";
    public static final String SIMULATION_TYPE = "Simulation";
    public static final String PROCESS_TYPE = "Process";
    private static final Logger _logger = LoggerFactory.getLogger(BPMN2SyntaxChecker.class);

    protected Map<String, List<ValidationSyntaxError>> errors = new HashMap<String, List<ValidationSyntaxError>>();
    private String json;
    private String preprocessingData;
    private IDiagramProfile profile;
    private String defaultResourceId = "processerrors";

    public BPMN2SyntaxChecker(String json, String preprocessingData, IDiagramProfile profile) {
        this.json = json;
        this.preprocessingData = preprocessingData;
        this.profile = profile;
    }

    public void checkSyntax() {
        DroolsFactoryImpl.init();
        BpsimFactoryImpl.init();

        Process process = null;
        try {
            Definitions def = profile.createMarshaller().getDefinitions(json, preprocessingData);
            List<RootElement> rootElements = def.getRootElements();
            Scenario defaultScenario = getDefaultScenario(def);

            for (RootElement root : rootElements) {
                if (root instanceof Process) {
                    process = (Process) root;

                    if (isEmpty(process.getId())) {
                        addError(defaultResourceId,
                                new ValidationSyntaxError(process, BPMN2_TYPE, "Process has no id."));
                    } else if (!SyntaxCheckerUtils.isNCName(process.getId())) {
                        addError(defaultResourceId, new ValidationSyntaxError(process, BPMN2_TYPE,
                                "Invalid process id. See http://www.w3.org/TR/REC-xml-names/#NT-NCName for more info."));
                    } else {
                        defaultResourceId = process.getId();
                    }

                    String pname;
                    Iterator<FeatureMap.Entry> iter = process.getAnyAttribute().iterator();
                    boolean foundPackageName = false;
                    while (iter.hasNext()) {
                        FeatureMap.Entry entry = iter.next();
                        if (entry.getEStructuralFeature().getName().equals("packageName")) {
                            foundPackageName = true;
                            pname = (String) entry.getValue();
                            if (isEmpty(pname)) {
                                addError(defaultResourceId, new ValidationSyntaxError(process, BPMN2_TYPE,
                                        SyntaxCheckerErrors.PROCESS_HAS_NO_PACKAGE_NAME));
                            }
                            if (!isValidPackageName(pname)) {
                                addError(defaultResourceId, new ValidationSyntaxError(process, BPMN2_TYPE,
                                        "Package name contains invalid characters."));
                            }
                        }
                    }
                    if (!foundPackageName) {
                        addError(defaultResourceId, new ValidationSyntaxError(process, BPMN2_TYPE,
                                SyntaxCheckerErrors.PROCESS_HAS_NO_PACKAGE_NAME));
                    }

                    if (isEmpty(process.getName())) {
                        addError(defaultResourceId,
                                new ValidationSyntaxError(process, BPMN2_TYPE, "Process has no name."));
                    }

                    List<Property> processProperties = process.getProperties();
                    if (processProperties != null && processProperties.size() > 0) {
                        for (Property prop : processProperties) {
                            String propId = prop.getId();
                            Pattern pattern = Pattern.compile("\\s");
                            Matcher matcher = pattern.matcher(propId);
                            if (matcher.find()) {
                                addError(defaultResourceId, new ValidationSyntaxError(process, BPMN2_TYPE,
                                        "Process variable \"" + propId + "\" contains white spaces."));
                            }
                        }
                    }

                    boolean foundStartEvent = false;
                    boolean foundEndEvent = false;
                    List<FlowElement> flowElements = process.getFlowElements();
                    for (FlowElement fe : flowElements) {
                        if (fe instanceof StartEvent) {
                            foundStartEvent = true;
                        }
                        if (fe instanceof EndEvent) {
                            foundEndEvent = true;
                        }
                    }
                    if (!foundStartEvent && !isAdHocProcess(process)) {
                        addError(defaultResourceId,
                                new ValidationSyntaxError(process, BPMN2_TYPE, "Process has no start node."));
                    }
                    if (!foundEndEvent && !isAdHocProcess(process)) {
                        addError(defaultResourceId,
                                new ValidationSyntaxError(process, BPMN2_TYPE, "Process has no end node."));
                    }

                    checkFlowElements(process, process, defaultScenario);
                }
            }
        } catch (Exception e) {
            addError(defaultResourceId,
                    new ValidationSyntaxError(process, PROCESS_TYPE, "Could not parse BPMN2 process."));
        }

        // if there are no suggestions add RuleFlowProcessValidator process errors
        if (this.errors.size() < 1) {
            try {
                SemanticModules modules = new SemanticModules();
                modules.addSemanticModule(new BPMNSemanticModule());
                modules.addSemanticModule(new BPMNDISemanticModule());
                XmlProcessReader xmlReader = new XmlProcessReader(modules, getClass().getClassLoader());
                List<org.kie.api.definition.process.Process> processes = xmlReader
                        .read(new StringReader(profile.createMarshaller().parseModel(json, preprocessingData)));
                if (processes != null) {
                    ProcessValidationError[] errors = RuleFlowProcessValidator.getInstance()
                            .validateProcess((org.jbpm.ruleflow.core.RuleFlowProcess) processes.get(0));
                    for (ProcessValidationError er : errors) {
                        addError(defaultResourceId,
                                new ValidationSyntaxError(process, PROCESS_TYPE, er.getMessage()));
                    }
                }
            } catch (Exception e) {
                _logger.warn("Could not parse to RuleFlowProcess.");
                addError(defaultResourceId, new ValidationSyntaxError(process, PROCESS_TYPE,
                        "Could not parse BPMN2 to RuleFlowProcess."));
            }
        }

    }

    private void checkFlowElements(FlowElementsContainer container, Process process, Scenario defaultScenario) {

        for (FlowElement fe : container.getFlowElements()) {
            if (fe instanceof StartEvent) {
                checkStartEvent((StartEvent) fe, container, process);
            } else if (fe instanceof EndEvent) {
                checkEndEvent((EndEvent) fe, container, process);
            } else {
                if (fe instanceof FlowNode) {
                    checkFlowNode((FlowNode) fe, container, process);
                }
            }

            if (fe instanceof BusinessRuleTask) {
                checkBusinessRuleTask((BusinessRuleTask) fe);
            }

            if (fe instanceof ScriptTask) {
                checkScriptTask((ScriptTask) fe);
            }

            if (fe instanceof ReceiveTask) {
                checkReceiveTask((ReceiveTask) fe);
            }

            if (fe instanceof SendTask) {
                checkSendTask((SendTask) fe);
            }

            if (fe instanceof ServiceTask) {
                checkServiceTask((ServiceTask) fe);
            }

            if (fe instanceof UserTask) {
                checkUserTask((UserTask) fe, defaultScenario);
            }

            if (fe instanceof Task) {
                checkTask((Task) fe, defaultScenario);
            }

            if (fe instanceof CatchEvent) {
                checkCatchEvent((CatchEvent) fe);
            }

            if (fe instanceof ThrowEvent) {
                checkThrowEvent((ThrowEvent) fe);
            }

            if (fe instanceof SequenceFlow) {
                checkSequenceFlow((SequenceFlow) fe);
            }

            if (fe instanceof Gateway) {
                checkGateway((Gateway) fe, defaultScenario);
            }

            if (fe instanceof CallActivity) {
                checkCallActivity((CallActivity) fe);
            }

            if (fe instanceof DataObject) {
                checkDataObject((DataObject) fe);
            }

            if (fe instanceof SubProcess) {
                checkFlowElements((SubProcess) fe, process, defaultScenario);
            }
        }
    }

    private void checkStartEvent(StartEvent startEvent, FlowElementsContainer container, Process process) {

        if (startEvent.getOutgoing() == null || startEvent.getOutgoing().isEmpty()) {
            if (container instanceof Process) {
                if (!isAdHocProcess(process)) {
                    addError(startEvent, new ValidationSyntaxError(startEvent, BPMN2_TYPE,
                            SyntaxCheckerErrors.START_NODE_NO_OUTGOING_CONNECTIONS));
                }
            } else if (container instanceof SubProcess) {
                if (!(container instanceof AdHocSubProcess)) {
                    addError(startEvent, new ValidationSyntaxError(startEvent, BPMN2_TYPE,
                            SyntaxCheckerErrors.START_NODE_NO_OUTGOING_CONNECTIONS));
                }
            } else {
                addError(startEvent, new ValidationSyntaxError(startEvent, BPMN2_TYPE,
                        SyntaxCheckerErrors.START_NODE_NO_OUTGOING_CONNECTIONS));
            }
        }
    }

    private void checkEndEvent(EndEvent endEvent, FlowElementsContainer container, Process process) {

        if (endEvent.getIncoming() == null || endEvent.getIncoming().isEmpty()) {
            if (container instanceof Process) {
                if (!isAdHocProcess(process)) {
                    addError(endEvent, new ValidationSyntaxError(endEvent, BPMN2_TYPE,
                            SyntaxCheckerErrors.END_NODE_NO_INCOMING_CONNECTIONS));
                }
            } else if (container instanceof SubProcess) {
                if (!(container instanceof AdHocSubProcess)) {
                    addError(endEvent, new ValidationSyntaxError(endEvent, BPMN2_TYPE,
                            SyntaxCheckerErrors.END_NODE_NO_INCOMING_CONNECTIONS));
                }
            } else {
                addError(endEvent, new ValidationSyntaxError(endEvent, BPMN2_TYPE,
                        SyntaxCheckerErrors.END_NODE_NO_INCOMING_CONNECTIONS));
            }
        }
    }

    private void checkFlowNode(FlowNode flowNode, FlowElementsContainer container, Process process) {
        if ((flowNode.getOutgoing() == null || flowNode.getOutgoing().isEmpty()) && !isAdHocProcess(process)
                && !(flowNode instanceof BoundaryEvent) && !(flowNode instanceof EventSubprocess)) {
            if (container instanceof Process) {
                if (!isAdHocProcess(process)) {
                    if (!isCompensatingFlowNodeInProcess(flowNode, (Process) container)) {
                        addError(flowNode, new ValidationSyntaxError(flowNode, BPMN2_TYPE,
                                SyntaxCheckerErrors.NODE_NO_OUTGOING_CONNECTIONS));
                    }
                }
            } else if (container instanceof SubProcess) {
                if (!(container instanceof AdHocSubProcess)) {
                    if (!isCompensatingFlowNodeInSubprocess(flowNode, (SubProcess) container)) {
                        addError(flowNode, new ValidationSyntaxError(flowNode, BPMN2_TYPE,
                                SyntaxCheckerErrors.NODE_NO_OUTGOING_CONNECTIONS));
                    }
                }
            } else {
                addError(flowNode, new ValidationSyntaxError(flowNode, BPMN2_TYPE,
                        SyntaxCheckerErrors.NODE_NO_OUTGOING_CONNECTIONS));
            }
        }
        if (!(flowNode instanceof BoundaryEvent)) {
            if ((flowNode.getIncoming() == null || flowNode.getIncoming().isEmpty()) && !isAdHocProcess(process)) {
                if (container instanceof Process) {
                    if (!isAdHocProcess(process) && !(flowNode instanceof EventSubprocess)) {
                        if (!isCompensatingFlowNodeInProcess(flowNode, (Process) container)) {
                            addError(flowNode, new ValidationSyntaxError(flowNode, BPMN2_TYPE,
                                    SyntaxCheckerErrors.NODE_NO_INCOMING_CONNECTIONS));
                        }
                    }
                } else if (container instanceof SubProcess) {
                    if (!(container instanceof AdHocSubProcess)) {
                        if (!isCompensatingFlowNodeInSubprocess(flowNode, (SubProcess) container)) {
                            addError(flowNode, new ValidationSyntaxError(flowNode, BPMN2_TYPE,
                                    SyntaxCheckerErrors.NODE_NO_INCOMING_CONNECTIONS));
                        }
                    }
                } else {
                    addError(flowNode, new ValidationSyntaxError(flowNode, BPMN2_TYPE,
                            SyntaxCheckerErrors.NODE_NO_INCOMING_CONNECTIONS));
                }
            }
        }
    }

    private void checkBusinessRuleTask(BusinessRuleTask businessRuleTask) {
        Iterator<FeatureMap.Entry> biter = businessRuleTask.getAnyAttribute().iterator();
        boolean foundRuleflowGroup = false;
        while (biter.hasNext()) {
            FeatureMap.Entry entry = biter.next();
            if (entry.getEStructuralFeature().getName().equals("ruleFlowGroup")) {
                foundRuleflowGroup = true;
                String ruleflowGroup = (String) entry.getValue();
                if (isEmpty(ruleflowGroup)) {
                    addError(businessRuleTask, new ValidationSyntaxError(businessRuleTask, BPMN2_TYPE,
                            SyntaxCheckerErrors.BUSINESS_RULE_TASK_NO_RULEFLOW_GROUP));
                }
            }
        }
        if (!foundRuleflowGroup) {
            addError(businessRuleTask, new ValidationSyntaxError(businessRuleTask, BPMN2_TYPE,
                    SyntaxCheckerErrors.BUSINESS_RULE_TASK_NO_RULEFLOW_GROUP));
        }
    }

    private void checkScriptTask(ScriptTask scriptTask) {
        if (isEmpty(scriptTask.getScript())) {
            addError(scriptTask,
                    new ValidationSyntaxError(scriptTask, BPMN2_TYPE, SyntaxCheckerErrors.SCRIPT_TASK_NO_SCRIPT));
        }
        if (isEmpty(scriptTask.getScriptFormat())) {
            addError(scriptTask, new ValidationSyntaxError(scriptTask, BPMN2_TYPE,
                    SyntaxCheckerErrors.SCRIPT_TASK_NO_SCRIPT_FORMAT));
        }
    }

    private void checkReceiveTask(ReceiveTask receiveTask) {
        checkMessageRefOfTask(receiveTask, receiveTask.getMessageRef());
    }

    private void checkSendTask(SendTask sendTask) {
        checkMessageRefOfTask(sendTask, sendTask.getMessageRef());
    }

    private void checkMessageRefOfTask(Task task, Message message) {
        if (message == null) {
            addError(task, new ValidationSyntaxError(task, BPMN2_TYPE, SyntaxCheckerErrors.TASK_NO_MESSAGE));
        }
    }

    private void checkServiceTask(ServiceTask serviceTask) {

        if (serviceTask.getOperationRef() == null) {
            addError(serviceTask, new ValidationSyntaxError(serviceTask, BPMN2_TYPE,
                    SyntaxCheckerErrors.SERVICE_TASK_NO_OPERATION));
        }
    }

    private void checkUserTask(UserTask userTask, Scenario defaultScenario) {

        String taskName = null;
        boolean foundTaskName = false;

        if (userTask.getIoSpecification() != null && userTask.getIoSpecification().getDataInputs() != null) {
            List<DataInput> taskDataInputs = userTask.getIoSpecification().getDataInputs();
            for (DataInput din : taskDataInputs) {
                if (din.getName() != null && din.getName().equals("TaskName")) {
                    List<DataInputAssociation> taskDataInputAssociations = userTask.getDataInputAssociations();
                    for (DataInputAssociation dia : taskDataInputAssociations) {
                        if (dia.getTargetRef().getId() != null
                                && (dia.getTargetRef().getId().equals(din.getId()))) {
                            foundTaskName = true;
                            taskName = ((FormalExpression) dia.getAssignment().get(0).getFrom()).getBody();
                            if (isEmpty(taskName)) {
                                addError(userTask, new ValidationSyntaxError(userTask, BPMN2_TYPE,
                                        SyntaxCheckerErrors.USER_TASK_HAS_NO_TASK_NAME));
                            }
                        }
                    }
                    break;
                }
            }
        }
        if (!foundTaskName) {
            addError(userTask, new ValidationSyntaxError(userTask, BPMN2_TYPE,
                    SyntaxCheckerErrors.USER_TASK_HAS_NO_TASK_NAME));
        }

        // simulation validation
        for (ElementParameters eleType : getElementParameters(defaultScenario, userTask)) {
            if (eleType.getResourceParameters() != null) {
                ResourceParameters resourceParams = eleType.getResourceParameters();
                if (resourceParams.getQuantity() != null) {
                    FloatingParameterType quantityVal = (FloatingParameterType) resourceParams.getQuantity()
                            .getParameterValue().get(0);
                    double val = quantityVal.getValue();
                    if (val < 0) {
                        addError(userTask, new ValidationSyntaxError(userTask, SIMULATION_TYPE,
                                SyntaxCheckerErrors.STAFF_AVAILABILITY_MUST_BE_POSITIVE));
                    }
                }
            }
        }
    }

    private void checkTask(Task task, Scenario defaultScenario) {
        // simulation validation
        for (ElementParameters eleType : getElementParameters(defaultScenario, task)) {
            if (eleType.getCostParameters() != null) {
                CostParameters costParams = eleType.getCostParameters();
                if (costParams.getUnitCost() != null) {
                    FloatingParameterType unitCostVal = (FloatingParameterType) costParams.getUnitCost()
                            .getParameterValue().get(0);
                    Double val = unitCostVal.getValue();
                    if (val.doubleValue() < 0) {
                        addError(task, new ValidationSyntaxError(task, SIMULATION_TYPE,
                                SyntaxCheckerErrors.COST_PER_TIME_UNIT_MUST_BE_POSITIVE));
                    }
                }
            }
            if (eleType.getResourceParameters() != null) {
                ResourceParameters resourceParams = eleType.getResourceParameters();
                if (resourceParams.getQuantity() != null) {
                    FloatingParameterType workingHoursVal = (FloatingParameterType) resourceParams.getQuantity()
                            .getParameterValue().get(0);
                    if (workingHoursVal.getValue() < 0) {
                        addError(task, new ValidationSyntaxError(task, SIMULATION_TYPE,
                                SyntaxCheckerErrors.WORKING_HOURS_MUST_BE_POSITIVE));
                    }
                }
            }
        }
    }

    private void checkCatchEvent(CatchEvent catchEvent) {

        List<EventDefinition> eventdefs = catchEvent.getEventDefinitions();
        for (EventDefinition ed : eventdefs) {
            checkEventDefinition(catchEvent, ed, "Catch");
        }
    }

    private void checkThrowEvent(ThrowEvent throwEvent) {

        List<EventDefinition> eventdefs = throwEvent.getEventDefinitions();
        for (EventDefinition ed : eventdefs) {
            checkEventDefinition(throwEvent, ed, "Throw");
        }
    }

    private void checkEventDefinition(Event event, EventDefinition eventDefinition, String errorPrefix) {
        if (eventDefinition instanceof TimerEventDefinition) {
            TimerEventDefinition ted = (TimerEventDefinition) eventDefinition;
            if (ted.getTimeDate() != null && ted.getTimeDuration() != null && ted.getTimeCycle() != null) {
                addError(event, new ValidationSyntaxError(event, BPMN2_TYPE,
                        errorPrefix + " Event has timeDate and timeDuration and timeCycle defined."));
            } else if (ted.getTimeDate() != null && ted.getTimeDuration() != null) {
                addError(event, new ValidationSyntaxError(event, BPMN2_TYPE,
                        errorPrefix + " Event has both timeDate and timeDuration defined."));
            } else if (ted.getTimeDate() != null && ted.getTimeCycle() != null) {
                addError(event, new ValidationSyntaxError(event, BPMN2_TYPE,
                        errorPrefix + " Event has both timeDate and timeCycle defined."));
            } else if (ted.getTimeDuration() != null && ted.getTimeCycle() != null) {
                addError(event, new ValidationSyntaxError(event, BPMN2_TYPE,
                        errorPrefix + " Event has both timeduration and timecycle defined."));
            }

            if (ted.getTimeDate() == null && ted.getTimeDuration() == null && ted.getTimeCycle() == null) {
                addError(event, new ValidationSyntaxError(event, BPMN2_TYPE,
                        errorPrefix + " Event has no timeDate or timeDuration or timeCycle defined."));
            }
        } else if (eventDefinition instanceof SignalEventDefinition) {
            if (((SignalEventDefinition) eventDefinition).getSignalRef() == null) {
                addError(event,
                        new ValidationSyntaxError(event, BPMN2_TYPE, errorPrefix + " Event has no signalref."));
            }
        } else if (eventDefinition instanceof ErrorEventDefinition) {
            if (((ErrorEventDefinition) eventDefinition).getErrorRef() == null
                    || ((ErrorEventDefinition) eventDefinition).getErrorRef().getErrorCode() == null) {
                addError(event, new ValidationSyntaxError(event, BPMN2_TYPE,
                        errorPrefix + SyntaxCheckerErrors.EVENT_HAS_NO_ERROR_REF));
            }
        } else if (eventDefinition instanceof ConditionalEventDefinition) {
            FormalExpression conditionalExp = (FormalExpression) ((ConditionalEventDefinition) eventDefinition)
                    .getCondition();
            if (conditionalExp.getBody() == null) {
                addError(event, new ValidationSyntaxError(event, BPMN2_TYPE,
                        errorPrefix + " Event has no condition expression."));
            }
        } else if (eventDefinition instanceof EscalationEventDefinition) {
            if (((EscalationEventDefinition) eventDefinition).getEscalationRef() == null) {
                addError(event,
                        new ValidationSyntaxError(event, BPMN2_TYPE, errorPrefix + " Event has no escalationref."));
            }
        } else if (eventDefinition instanceof MessageEventDefinition) {
            if (((MessageEventDefinition) eventDefinition).getMessageRef() == null) {
                addError(event,
                        new ValidationSyntaxError(event, BPMN2_TYPE, errorPrefix + " Event has no messageref."));
            }
        } else if (eventDefinition instanceof CompensateEventDefinition) {
            if (((CompensateEventDefinition) eventDefinition).getActivityRef() == null) {
                addError(event,
                        new ValidationSyntaxError(event, BPMN2_TYPE, errorPrefix + " Event has no activityref."));
            }
        }
    }

    private void checkGateway(Gateway gateway, Scenario defaultScenario) {

        if (gateway.getGatewayDirection() == null
                || gateway.getGatewayDirection().getValue() == GatewayDirection.UNSPECIFIED.getValue()) {
            addError(gateway,
                    new ValidationSyntaxError(gateway, BPMN2_TYPE, "Gateway does not specify a valid direction."));
        }
        if (gateway instanceof ExclusiveGateway) {
            if (gateway.getGatewayDirection().getValue() != GatewayDirection.DIVERGING.getValue()
                    && gateway.getGatewayDirection().getValue() != GatewayDirection.CONVERGING.getValue()) {
                addError(gateway, new ValidationSyntaxError(gateway, BPMN2_TYPE,
                        "Invalid Gateway direction for Exclusing Gateway. It should be 'Converging' or 'Diverging'."));
            }
            checkDefaultGate(gateway, ((ExclusiveGateway) gateway).getDefault());
        }
        if (gateway instanceof EventBasedGateway) {
            if (gateway.getGatewayDirection().getValue() != GatewayDirection.DIVERGING.getValue()) {
                addError(gateway, new ValidationSyntaxError(gateway, BPMN2_TYPE,
                        "Invalid Gateway direction for EventBased Gateway. It should be 'Diverging'."));
            }
        }
        if (gateway instanceof ParallelGateway) {
            if (gateway.getGatewayDirection().getValue() != GatewayDirection.DIVERGING.getValue()
                    && gateway.getGatewayDirection().getValue() != GatewayDirection.CONVERGING.getValue()) {
                addError(gateway, new ValidationSyntaxError(gateway, BPMN2_TYPE,
                        "Invalid Gateway direction for Parallel Gateway. It should be 'Converging' or 'Diverging'."));
            }
        }
        if (gateway instanceof InclusiveGateway) {
            if (gateway.getGatewayDirection().getValue() != GatewayDirection.DIVERGING.getValue()) {
                addError(gateway, new ValidationSyntaxError(gateway, BPMN2_TYPE,
                        "Invalid Gateway direction for Inclusive Gateway. It should be 'Diverging'."));
            }
            checkDefaultGate(gateway, ((InclusiveGateway) gateway).getDefault());
        }
        if (gateway instanceof ComplexGateway) {
            if (gateway.getGatewayDirection().getValue() != GatewayDirection.DIVERGING.getValue()
                    && gateway.getGatewayDirection().getValue() != GatewayDirection.CONVERGING.getValue()) {
                addError(gateway, new ValidationSyntaxError(gateway, BPMN2_TYPE,
                        "Invalid Gateway direction for Complex Gateway. It should be 'Converging' or 'Diverging'."));
            }
        }

        if ((gateway instanceof ExclusiveGateway || gateway instanceof InclusiveGateway)
                && (gateway.getGatewayDirection().getValue() == GatewayDirection.DIVERGING.getValue())) {
            List<SequenceFlow> outgoingFlows = gateway.getOutgoing();
            if (outgoingFlows != null && outgoingFlows.size() > 0) {
                for (SequenceFlow flow : outgoingFlows) {
                    if (flow.getConditionExpression() == null) {
                        addError(flow, new ValidationSyntaxError(flow, BPMN2_TYPE,
                                SyntaxCheckerErrors.SEQUENCE_FLOW_NO_CONDITION_EXPRESSION_DEFINED));
                    } else {
                        if (flow.getConditionExpression() instanceof FormalExpression) {
                            FormalExpression formalExp = (FormalExpression) flow.getConditionExpression();
                            if (formalExp.getBody() == null && formalExp.getBody().isEmpty()) {
                                addError(flow, new ValidationSyntaxError(flow, BPMN2_TYPE,
                                        SyntaxCheckerErrors.SEQUENCE_FLOW_NO_CONDITION_EXPRESSION_DEFINED));
                            }
                        } else {
                            addError(flow, new ValidationSyntaxError(flow, BPMN2_TYPE,
                                    "Invalid condition expression on sequence flow."));
                        }
                    }
                }
            }
        }

        // simulation validation
        List<SequenceFlow> outgoingGwSequenceFlows = gateway.getOutgoing();
        if (outgoingGwSequenceFlows != null && outgoingGwSequenceFlows.size() > 0) {
            double probabilitySum = 0;
            boolean defaultSimulationOutgoingFlow = false;
            for (SequenceFlow sf : outgoingGwSequenceFlows) {
                for (ElementParameters eleType : getElementParameters(defaultScenario, sf)) {
                    if (eleType.getControlParameters() != null
                            && eleType.getControlParameters().getProbability() != null) {
                        FloatingParameterType valType = (FloatingParameterType) eleType.getControlParameters()
                                .getProbability().getParameterValue().get(0);
                        if (valType.getValue() < 0) {
                            addError(sf, new ValidationSyntaxError(sf, SIMULATION_TYPE,
                                    SyntaxCheckerErrors.PROBABILITY_MUST_BE_POSITIVE));
                        } else {
                            if (valType.getValue() == 100) {
                                defaultSimulationOutgoingFlow = true;
                            }
                            probabilitySum += valType.getValue();
                        }
                    } else {
                        addError(sf, new ValidationSyntaxError(sf, SIMULATION_TYPE,
                                SyntaxCheckerErrors.SEQUENCE_FLOW_NO_PROBABILITY_DEFINED));
                    }
                }

            }
            if (!(gateway instanceof ParallelGateway)) {
                if (gateway instanceof InclusiveGateway) {
                    if (!defaultSimulationOutgoingFlow) {
                        addError(gateway, new ValidationSyntaxError(gateway, SIMULATION_TYPE,
                                SyntaxCheckerErrors.AT_LEAST_ONE_OUTGOING_PROBABILITY_VALUE_100));
                    }
                } else {
                    if (probabilitySum != 100) {
                        addError(gateway, new ValidationSyntaxError(gateway, SIMULATION_TYPE,
                                SyntaxCheckerErrors.THE_SUM_OF_PROBABILITIES_MUST_BE_EQUAL_100));
                    }
                }
            }
        }
    }

    private void checkSequenceFlow(SequenceFlow sequenceFlow) {
        if (sequenceFlow.getSourceRef() == null) {
            addError(sequenceFlow,
                    new ValidationSyntaxError(sequenceFlow, BPMN2_TYPE, "An Edge must have a source node."));
        }
        if (sequenceFlow.getTargetRef() == null) {
            addError(sequenceFlow,
                    new ValidationSyntaxError(sequenceFlow, BPMN2_TYPE, "An Edge must have a target node."));
        }
    }

    private void checkCallActivity(CallActivity callActivity) {

        if (callActivity.getCalledElement() == null || callActivity.getCalledElement().isEmpty()) {
            addError(callActivity, new ValidationSyntaxError(callActivity, BPMN2_TYPE,
                    SyntaxCheckerErrors.NO_CALLED_ELEMENT_SPECIFIED));
        }
    }

    private void checkDataObject(DataObject dataObject) {
        if (dataObject.getName() == null || dataObject.getName().isEmpty()) {
            addError(dataObject,
                    new ValidationSyntaxError(dataObject, BPMN2_TYPE, "Data Object has no name defined."));
        } else {
            if (containsWhiteSpace(dataObject.getName())) {
                addError(dataObject, new ValidationSyntaxError(dataObject, BPMN2_TYPE,
                        "Data Object name contains white spaces."));
            }
        }
    }

    private void checkDefaultGate(Gateway gateway, SequenceFlow defaultSequenceFlow) {
        if (defaultSequenceFlow != null
                && (gateway.getOutgoing() != null && !gateway.getOutgoing().contains(defaultSequenceFlow))) {
            addError(gateway,
                    new ValidationSyntaxError(gateway, BPMN2_TYPE, SyntaxCheckerErrors.NOT_VALID_DEFAULT_GATE));
        }
    }

    public Map<String, List<ValidationSyntaxError>> getErrors() {
        return errors;
    }

    public JSONObject getErrorsAsJson() {
        JSONObject jsonObject = new JSONObject();
        for (Entry<String, List<ValidationSyntaxError>> error : this.getErrors().entrySet()) {
            try {
                JSONArray errorsArray = new JSONArray();
                for (ValidationSyntaxError se : error.getValue()) {
                    errorsArray.put(se.toJSON());
                }
                jsonObject.put(error.getKey(), errorsArray);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return jsonObject;
    }

    public boolean errorsFound() {
        return errors.size() > 0;
    }

    public void clearErrors() {
        errors.clear();
    }

    private void addError(BaseElement element, ValidationSyntaxError error) {
        addError(element.getId(), error);
    }

    private void addError(String resourceId, ValidationSyntaxError error) {
        if (errors.containsKey(resourceId) && errors.get(resourceId) != null) {
            errors.get(resourceId).add(error);
        } else {
            List<ValidationSyntaxError> value = new ArrayList<ValidationSyntaxError>();
            value.add(error);
            errors.put(resourceId, value);
        }
    }

    private static boolean isEmpty(final CharSequence str) {
        if (str == null || str.length() == 0) {
            return true;
        }
        for (int i = 0, length = str.length(); i < length; i++) {
            if (str.charAt(i) != ' ') {
                return false;
            }
        }
        return true;
    }

    private boolean isAdHocProcess(Process process) {
        Iterator<FeatureMap.Entry> iter = process.getAnyAttribute().iterator();
        while (iter.hasNext()) {
            FeatureMap.Entry entry = iter.next();
            if (entry.getEStructuralFeature().getName().equals("adHoc")) {
                return Boolean.parseBoolean(((String) entry.getValue()).trim());
            }
        }
        return false;
    }

    public boolean isCompensatingFlowNodeInSubprocess(FlowNode node, SubProcess subProcess) {
        //text annotations are flow elements now not artifacts so omit them
        if (node instanceof TextAnnotation) {
            return true;
        }

        for (Artifact artifact : subProcess.getArtifacts()) {
            if (artifact instanceof Association) {
                Association association = (Association) artifact;
                if (association.getTargetRef().getId().equals(node.getId())) {
                    return true;
                }
            }
        }

        return false;
    }

    public boolean isCompensatingFlowNodeInProcess(FlowNode node, Process process) {
        for (Artifact artifact : process.getArtifacts()) {
            if (artifact instanceof Association) {
                Association association = (Association) artifact;
                if (association.getTargetRef().getId().equals(node.getId())) {
                    return true;
                }
            }
        }

        return false;
    }

    private boolean containsWhiteSpace(String testString) {
        if (testString != null) {
            for (int i = 0; i < testString.length(); i++) {
                if (Character.isWhitespace(testString.charAt(i))) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean isValidPackageName(String pkg) {
        Pattern p = Pattern.compile("^[a-zA-Z_\\$][\\w\\$]*(?:\\.[a-zA-Z_\\$][\\w\\$]*)*$");
        return p.matcher(pkg).matches();
    }

    private Scenario getDefaultScenario(Definitions def) {
        if (def.getRelationships() != null && def.getRelationships().size() > 0) {
            // current support for single relationship
            Relationship relationship = def.getRelationships().get(0);
            for (ExtensionAttributeValue extattrval : relationship.getExtensionValues()) {
                FeatureMap extensionElements = extattrval.getValue();
                @SuppressWarnings("unchecked")
                List<BPSimDataType> bpsimExtensions = (List<BPSimDataType>) extensionElements
                        .get(BpsimPackage.Literals.DOCUMENT_ROOT__BP_SIM_DATA, true);
                if (bpsimExtensions != null && bpsimExtensions.size() > 0) {
                    BPSimDataType processAnalysis = bpsimExtensions.get(0);
                    if (processAnalysis.getScenario() != null && processAnalysis.getScenario().size() > 0) {
                        return processAnalysis.getScenario().get(0);
                    }
                }
            }
        }
        return null;
    }

    private List<ElementParameters> getElementParameters(Scenario scenario, BaseElement element) {
        List<ElementParameters> elementParameters = new ArrayList<>();
        if (scenario != null && scenario.getElementParameters() != null) {
            for (ElementParameters eleType : scenario.getElementParameters()) {
                if (eleType.getElementRef().equals(element.getId())) {
                    elementParameters.add(eleType);
                }
            }
        }
        return elementParameters;
    }

    public class ValidationSyntaxError {
        private BaseElement element;
        private String error;
        private String type;

        public ValidationSyntaxError(BaseElement element, String type, String error) {
            this.element = element;
            this.error = error;
            this.type = type;
        }

        public JSONObject toJSON() throws JSONException {
            JSONObject errorJSON = new JSONObject();
            errorJSON.put("id", this.element == null ? "" : this.element.getId());
            errorJSON.put("type", type);
            errorJSON.put("error", this.error);

            return errorJSON;
        }

        public BaseElement getElement() {
            return this.element;
        }

        public String getError() {
            return this.error;
        }
    }
}