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

org.jbpm.compiler.canonical.AbstractNodeVisitor Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   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.compiler.canonical;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.jbpm.compiler.canonical.builtin.ReturnValueEvaluatorBuilderService;
import org.jbpm.process.builder.action.ActionCompilerRegistry;
import org.jbpm.process.core.Context;
import org.jbpm.process.core.ContextContainer;
import org.jbpm.process.core.ContextResolver;
import org.jbpm.process.core.context.variable.Mappable;
import org.jbpm.process.core.context.variable.Variable;
import org.jbpm.process.core.context.variable.VariableScope;
import org.jbpm.process.core.datatype.DataTypeResolver;
import org.jbpm.process.instance.impl.actions.HandleEscalationAction;
import org.jbpm.process.instance.impl.actions.ProduceEventAction;
import org.jbpm.process.instance.impl.actions.SignalProcessInstanceAction;
import org.jbpm.ruleflow.core.Metadata;
import org.jbpm.ruleflow.core.factory.MappableNodeFactory;
import org.jbpm.util.JbpmClassLoaderUtil;
import org.jbpm.workflow.core.impl.ConnectionImpl;
import org.jbpm.workflow.core.impl.DataAssociation;
import org.jbpm.workflow.core.impl.DataAssociation.DataAssociationType;
import org.jbpm.workflow.core.impl.DataDefinition;
import org.jbpm.workflow.core.impl.DroolsConsequenceAction;
import org.jbpm.workflow.core.impl.ExtendedNodeImpl;
import org.jbpm.workflow.core.impl.NodeImpl;
import org.jbpm.workflow.core.node.Assignment;
import org.jbpm.workflow.core.node.HumanTaskNode;
import org.jbpm.workflow.core.node.StartNode;
import org.jbpm.workflow.core.node.Transformation;
import org.kie.api.definition.process.Connection;
import org.kie.api.definition.process.Node;

import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.expr.AssignExpr;
import com.github.javaparser.ast.expr.CastExpr;
import com.github.javaparser.ast.expr.ClassExpr;
import com.github.javaparser.ast.expr.EnclosedExpr;
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.ast.expr.LambdaExpr;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.NameExpr;
import com.github.javaparser.ast.expr.NullLiteralExpr;
import com.github.javaparser.ast.expr.ObjectCreationExpr;
import com.github.javaparser.ast.expr.StringLiteralExpr;
import com.github.javaparser.ast.expr.VariableDeclarationExpr;
import com.github.javaparser.ast.stmt.BlockStmt;
import com.github.javaparser.ast.stmt.ExpressionStmt;
import com.github.javaparser.ast.stmt.ReturnStmt;
import com.github.javaparser.ast.stmt.Statement;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.ast.type.UnknownType;
import com.github.javaparser.ast.type.WildcardType;

import static com.github.javaparser.StaticJavaParser.parseClassOrInterfaceType;
import static java.util.Collections.singletonList;
import static org.drools.util.StringUtils.ucFirst;
import static org.jbpm.ruleflow.core.Metadata.CUSTOM_AUTO_START;
import static org.jbpm.ruleflow.core.Metadata.HIDDEN;
import static org.jbpm.ruleflow.core.factory.NodeFactory.METHOD_DONE;
import static org.jbpm.ruleflow.core.factory.NodeFactory.METHOD_NAME;
import static org.kie.kogito.internal.utils.ConversionUtils.sanitizeString;

public abstract class AbstractNodeVisitor extends AbstractVisitor {

    protected abstract String getNodeKey();

    public void visitNode(T node, BlockStmt body, VariableScope variableScope, ProcessMetaData metadata) {
        visitNode(FACTORY_FIELD_NAME, node, body, variableScope, metadata);
        if (isAdHocNode(node) && !(node instanceof HumanTaskNode)) {
            metadata.addSignal(node.getName(), null);
        }
        if (isExtendedNode(node)) {
            ExtendedNodeImpl extendedNodeImpl = (ExtendedNodeImpl) node;
            addScript(extendedNodeImpl, body, ON_ACTION_SCRIPT_METHOD, ExtendedNodeImpl.EVENT_NODE_ENTER);
            addScript(extendedNodeImpl, body, ON_ACTION_SCRIPT_METHOD, ExtendedNodeImpl.EVENT_NODE_EXIT);
        }
    }

    private void addScript(ExtendedNodeImpl extendedNodeImpl, BlockStmt body, String factoryMethod, String actionType) {
        if (!extendedNodeImpl.hasActions(actionType)) {
            return;
        }
        List scripts = extendedNodeImpl.getActions(actionType).stream()
                .filter(Predicate.not(Objects::isNull))
                .filter(DroolsConsequenceAction.class::isInstance)
                .map(DroolsConsequenceAction.class::cast)
                .filter(e -> e.getConsequence() != null && !e.getConsequence().isBlank())
                .toList();

        for (DroolsConsequenceAction script : scripts) {
            body.addStatement(getFactoryMethod(getNodeId((T) extendedNodeImpl), factoryMethod,
                    new StringLiteralExpr(actionType),
                    new StringLiteralExpr(script.getDialect()),
                    new StringLiteralExpr(sanitizeString(script.getConsequence())),
                    buildDroolsConsequenceAction(extendedNodeImpl, script.getDialect(), script.getConsequence())));
            ;
        }
    }

    private Expression buildDroolsConsequenceAction(ExtendedNodeImpl extendedNodeImpl, String dialect, String script) {
        if (script == null) {
            return new NullLiteralExpr();
        }
        return ActionCompilerRegistry.instance().find(dialect).buildAction(extendedNodeImpl, script);
    }

    private boolean isExtendedNode(T node) {
        return node instanceof ExtendedNodeImpl;
    }

    private boolean isAdHocNode(Node node) {
        return (node.getIncomingConnections() == null || node.getIncomingConnections().isEmpty())
                && !(node instanceof StartNode)
                && !Boolean.parseBoolean((String) node.getMetaData().get(CUSTOM_AUTO_START));
    }

    protected String getNodeId(T node) {
        return getNodeKey() + node.getId().toSanitizeString();
    }

    public void visitNode(String factoryField, T node, BlockStmt body, VariableScope variableScope, ProcessMetaData metadata) {
    }

    protected MethodCallExpr getNameMethod(T node, String defaultName) {
        return getFactoryMethod(getNodeId(node), METHOD_NAME, new StringLiteralExpr(getOrDefault(node.getName(), defaultName)));
    }

    protected MethodCallExpr getDoneMethod(String object) {
        return getFactoryMethod(object, METHOD_DONE);
    }

    protected AssignExpr getAssignedFactoryMethod(String factoryField, Class typeClass, String variableName, String methodName, Expression... args) {
        return getAssignedFactoryMethod(factoryField, typeClass, variableName, methodName, new WildcardType(), args);
    }

    public Expression buildDataResolver(String type) {
        return new MethodCallExpr(null, "org.jbpm.process.core.datatype.DataTypeResolver.fromClass",
                new NodeList<>(new ClassExpr(parseClassOrInterfaceType(type))));
    }

    protected AssignExpr getAssignedFactoryMethod(String factoryField, Class typeClass, String variableName, String methodName, Type parentType, Expression... args) {
        ClassOrInterfaceType type = new ClassOrInterfaceType(null, typeClass.getCanonicalName());

        type.setTypeArguments(parentType);

        MethodCallExpr variableMethod = new MethodCallExpr(new NameExpr(factoryField), methodName);

        for (Expression arg : args) {
            variableMethod.addArgument(arg);
        }

        return new AssignExpr(
                new VariableDeclarationExpr(type, variableName),
                variableMethod,
                AssignExpr.Operator.ASSIGN);

    }

    public static Statement makeAssignment(Variable v) {
        String name = v.getSanitizedName();
        return makeAssignment(name, v);
    }

    public static Statement makeAssignment(String targetLocalVariable, Variable processVariable) {
        ClassOrInterfaceType type = parseClassOrInterfaceType(processVariable.getType().getStringType());
        // `type` `name` = (`type`) `kcontext.getVariable
        AssignExpr assignExpr = new AssignExpr(
                new VariableDeclarationExpr(type, targetLocalVariable),
                new CastExpr(
                        type,
                        new MethodCallExpr(
                                new NameExpr(KCONTEXT_VAR),
                                "getVariable")
                                        .addArgument(new StringLiteralExpr(targetLocalVariable))),
                AssignExpr.Operator.ASSIGN);
        return new ExpressionStmt(assignExpr);
    }

    protected Statement makeAssignmentFromModel(Variable v) {
        return makeAssignmentFromModel(v, v.getSanitizedName());
    }

    protected Statement makeAssignmentFromModel(Variable v, String name) {
        ClassOrInterfaceType type = parseClassOrInterfaceType(v.getType().getStringType());
        // `type` `name` = (`type`) `model.get
        AssignExpr assignExpr = new AssignExpr(
                new VariableDeclarationExpr(type, name),
                new CastExpr(
                        type,
                        new MethodCallExpr(
                                new NameExpr("model"),
                                "get" + ucFirst(name))),
                AssignExpr.Operator.ASSIGN);

        return new ExpressionStmt(assignExpr);
    }

    protected void addNodeMappings(Mappable node, BlockStmt body, String variableName) {
        for (DataAssociation entry : node.getInAssociations()) {
            body.addStatement(getFactoryMethod(variableName, MappableNodeFactory.METHOD_IN_ASSOCIATION, buildDataAssociationExpression((NodeImpl) node, entry)));
        }
        for (DataAssociation entry : node.getOutAssociations()) {
            body.addStatement(getFactoryMethod(variableName, MappableNodeFactory.METHOD_OUT_ASSOCIATION, buildDataAssociationExpression((NodeImpl) node, entry)));
        }
    }

    protected Expression buildDataAssociationsExpression(NodeImpl node, List dataAssociations) {
        NodeList expressions = NodeList.nodeList(dataAssociations.stream().map(da -> buildDataAssociationExpression(node, da)).collect(Collectors.toList()));
        return new MethodCallExpr(null, "java.util.Arrays.asList", NodeList.nodeList(expressions));
    }

    protected Expression buildDataAssociationExpression(NodeImpl node, DataAssociation dataAssociation) {
        List sourceExpr = dataAssociation.getSources();
        DataDefinition targetExpr = dataAssociation.getTarget();
        Transformation transformation = dataAssociation.getTransformation();
        List assignments = dataAssociation.getAssignments();
        return toDataAssociation(toDataDef(sourceExpr), toDataDef(targetExpr), toAssignmentExpr(assignments),
                toTransformation(node, dataAssociation.getType(), sourceExpr, singletonList(targetExpr), transformation));
    }

    private Expression toAssignmentExpr(List assignments) {
        if (assignments == null || assignments.isEmpty()) {
            return new NullLiteralExpr();
        }

        List expressions = new ArrayList<>();
        for (Assignment assignment : assignments) {
            Expression lang = assignment.getDialect() != null ? new StringLiteralExpr(assignment.getDialect()) : new NullLiteralExpr();
            Expression from = toDataDef(assignment.getFrom());
            Expression to = toDataDef(assignment.getTo());
            ClassOrInterfaceType clazz = new ClassOrInterfaceType(null, "org.jbpm.workflow.core.node.Assignment");
            expressions.add(new ObjectCreationExpr(null, clazz, NodeList.nodeList(lang, from, to)));
        }

        return new MethodCallExpr(null, "java.util.Arrays.asList", NodeList.nodeList(expressions));
    }

    protected Expression toTransformation(NodeImpl node, DataAssociationType type, List inputs, List outputs, Transformation transformation) {
        if (transformation == null) {
            return new NullLiteralExpr();
        }

        Expression lang = new StringLiteralExpr(transformation.getLanguage());
        Expression expression = new StringLiteralExpr(sanitizeString(transformation.getExpression()));

        ContextResolver contextResolver = type.equals(DataAssociationType.INPUT) ? node : wrapContextResolver(node, inputs);

        ReturnValueEvaluatorBuilderService service = ReturnValueEvaluatorBuilderService.instance();
        Expression returnValueEvaluatorExpression = service.build(contextResolver, transformation.getLanguage(), transformation.getExpression(), Object.class, null);
        ClassOrInterfaceType clazz = StaticJavaParser.parseClassOrInterfaceType(Transformation.class.getName());
        return new ObjectCreationExpr(null, clazz, NodeList.nodeList(lang, expression, returnValueEvaluatorExpression));

    }

    private ContextResolver wrapContextResolver(NodeImpl node, List variables) {
        VariableScope variableScope = new VariableScope();

        for (DataDefinition variable : variables) {
            Variable var = new Variable();
            var.setId(variable.getId());
            var.setName(variable.getLabel());
            var.setType(DataTypeResolver.fromType(variable.getType(), JbpmClassLoaderUtil.findClassLoader()));
            variableScope.addVariable(var);
        }
        return new ContextResolver() {

            @Override
            public Context resolveContext(String contextId, Object param) {
                if (VariableScope.VARIABLE_SCOPE.equals(contextId)) {
                    return variableScope.resolveContext(param);
                }
                return null;
            }

        };
    }

    protected Expression toDataAssociation(Expression sourceExprs, Expression target, Expression transformation, Expression assignments) {
        ClassOrInterfaceType clazz = new ClassOrInterfaceType(null, "org.jbpm.workflow.core.impl.DataAssociation");
        return new ObjectCreationExpr(null, clazz, NodeList.nodeList(sourceExprs, target, transformation, assignments));
    }

    private Expression toDataDef(List sourceExpr) {
        List expressions = sourceExpr.stream().map(this::toDataDef).collect(Collectors.toList());
        return new MethodCallExpr(null, "java.util.Arrays.asList", NodeList.nodeList(expressions));
    }

    private Expression toDataDef(DataDefinition sourceExpr) {
        if (sourceExpr == null) {
            return new NullLiteralExpr();
        }
        Expression id = new StringLiteralExpr(sourceExpr.getId());
        Expression label = new StringLiteralExpr(escape(sourceExpr.getLabel()));
        Expression type = new StringLiteralExpr(sourceExpr.getType());
        Expression expression = sourceExpr.getExpression() != null ? new StringLiteralExpr(escape(sourceExpr.getExpression())) : new NullLiteralExpr();
        ClassOrInterfaceType clazz = new ClassOrInterfaceType(null, "org.jbpm.workflow.core.impl.DataDefinition");
        return new ObjectCreationExpr(null, clazz, NodeList.nodeList(id, label, type, expression));
    }

    private String escape(String escape) {
        return escape.replace("\"", "\\\"");
    }

    protected String extractVariableFromExpression(String variableExpression) {
        if (variableExpression.startsWith("#{")) {
            return variableExpression.substring(2, variableExpression.indexOf('.'));
        }
        return variableExpression;
    }

    protected void visitConnections(String factoryField, Node[] nodes, BlockStmt body) {
        List connections = new ArrayList<>();
        for (Node node : nodes) {
            for (List connectionList : node.getIncomingConnections().values()) {
                connections.addAll(connectionList);
            }
        }
        for (Connection connection : connections) {
            visitConnection(factoryField, connection, body);
        }
    }

    protected void visitConnection(String factoryField, Connection connection, BlockStmt body) {
        // if the connection is a hidden one (compensations), don't dump
        Object hidden = ((ConnectionImpl) connection).getMetaData(HIDDEN);
        if (hidden != null && ((Boolean) hidden)) {
            return;
        }

        body.addStatement(getFactoryMethod(factoryField, "connection", getWorkflowElementConstructor(connection.getFrom().getId()),
                getWorkflowElementConstructor(connection.getTo().getId()),
                new StringLiteralExpr(getOrDefault(connection.getUniqueId(), ""))));
    }

    protected static LambdaExpr createLambdaExpr(String consequence, VariableScope scope) {
        BlockStmt conditionBody = new BlockStmt();
        List variables = scope.getVariables();
        variables.stream()
                .map(ActionNodeVisitor::makeAssignment)
                .forEach(conditionBody::addStatement);

        conditionBody.addStatement(new ReturnStmt(new EnclosedExpr(new NameExpr(consequence))));

        return new LambdaExpr(
                new Parameter(new UnknownType(), KCONTEXT_VAR), // (kcontext) ->
                conditionBody);
    }

    public static ObjectCreationExpr buildSignalAction(String signalName, String variable, String inputVariable, String scope) {
        return new ObjectCreationExpr(null,
                parseClassOrInterfaceType(SignalProcessInstanceAction.class.getCanonicalName()),
                new NodeList<>(new StringLiteralExpr(signalName), variable != null ? new StringLiteralExpr(variable.replace("\"", "\\\""))
                        : new CastExpr(
                                parseClassOrInterfaceType(String.class.getCanonicalName()), new NullLiteralExpr()),
                        inputVariable != null ? new StringLiteralExpr(inputVariable) : new NullLiteralExpr(),
                        scope != null ? new StringLiteralExpr(scope)
                                : new CastExpr(
                                        parseClassOrInterfaceType(String.class.getCanonicalName()), new NullLiteralExpr())));
    }

    public static ObjectCreationExpr buildEscalationAction(String faultName, String inputVariable) {
        return new ObjectCreationExpr(null,
                parseClassOrInterfaceType(HandleEscalationAction.class.getCanonicalName()),
                new NodeList<>(
                        faultName != null ? new StringLiteralExpr(faultName) : new NullLiteralExpr(),
                        inputVariable != null ? new StringLiteralExpr(inputVariable) : new NullLiteralExpr()));

    }

    public static LambdaExpr buildCompensationLambdaExpr(String compensationRef) {
        BlockStmt actionBody = new BlockStmt();
        MethodCallExpr getProcessInstance = new MethodCallExpr(new NameExpr(KCONTEXT_VAR), "getProcessInstance");
        MethodCallExpr signalEvent = new MethodCallExpr(getProcessInstance, "signalEvent")
                .addArgument(new StringLiteralExpr(Metadata.EVENT_TYPE_COMPENSATION))
                .addArgument(new StringLiteralExpr(compensationRef));
        actionBody.addStatement(signalEvent);
        return new LambdaExpr(
                new Parameter(new UnknownType(), KCONTEXT_VAR), // (kcontext) ->
                actionBody);
    }

    protected ObjectCreationExpr buildProducerAction(Node node, ProcessMetaData metadata) {
        TriggerMetaData trigger = TriggerMetaData.of(node, (String) node.getMetaData().get(Metadata.MAPPING_VARIABLE_INPUT));
        return buildProducerAction(parseClassOrInterfaceType(ProduceEventAction.class.getCanonicalName()).setTypeArguments(NodeList.nodeList(parseClassOrInterfaceType(trigger.getDataType()))),
                trigger, metadata);

    }

    public static ObjectCreationExpr buildProducerAction(ClassOrInterfaceType actionClass, TriggerMetaData trigger, ProcessMetaData metadata) {
        metadata.addTrigger(trigger);
        return new ObjectCreationExpr(null, actionClass, NodeList.nodeList(
                new StringLiteralExpr(trigger.getName()),
                new StringLiteralExpr(trigger.getModelRef()),
                new LambdaExpr(NodeList.nodeList(),
                        new NameExpr("producer_" + trigger.getOwnerId()))));
    }

    protected void visitCompensationScope(ContextContainer process, BlockStmt body) {
        visitCompensationScope(process, body, getNodeId((T) process));
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy