From c7cde92378b57003471456bb9f0cba6167858ae2 Mon Sep 17 00:00:00 2001 From: Nipuna Fernando Date: Fri, 8 Dec 2023 12:01:40 +0530 Subject: [PATCH] Add init builder impl for worker model --- .../FlowBuilder.java | 92 ++++++++++ .../ModelGenerator.java | 67 ++++++++ .../NodeBuilder.java | 158 ++++++++++++++++++ .../TemplateKind.java | 6 + 4 files changed, 323 insertions(+) create mode 100644 worker-model-generator/modules/model-generator-core/src/main/java/io.ballerina.workermodelgenerator.core/FlowBuilder.java create mode 100644 worker-model-generator/modules/model-generator-core/src/main/java/io.ballerina.workermodelgenerator.core/ModelGenerator.java create mode 100644 worker-model-generator/modules/model-generator-core/src/main/java/io.ballerina.workermodelgenerator.core/NodeBuilder.java create mode 100644 worker-model-generator/modules/model-generator-core/src/main/java/io.ballerina.workermodelgenerator.core/TemplateKind.java diff --git a/worker-model-generator/modules/model-generator-core/src/main/java/io.ballerina.workermodelgenerator.core/FlowBuilder.java b/worker-model-generator/modules/model-generator-core/src/main/java/io.ballerina.workermodelgenerator.core/FlowBuilder.java new file mode 100644 index 000000000..ac9531d96 --- /dev/null +++ b/worker-model-generator/modules/model-generator-core/src/main/java/io.ballerina.workermodelgenerator.core/FlowBuilder.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. 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 io.ballerina.workermodelgenerator.core; + +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.syntax.tree.BlockStatementNode; +import io.ballerina.compiler.syntax.tree.NamedWorkerDeclarationNode; +import io.ballerina.compiler.syntax.tree.NodeVisitor; +import io.ballerina.workermodelgenerator.core.model.Flow; +import io.ballerina.workermodelgenerator.core.model.FlowJsonBuilder; +import io.ballerina.workermodelgenerator.core.model.WorkerNode; + +import java.util.ArrayList; +import java.util.List; + +/** + * Builder implementation for creating a {@link Flow} instance. + * + * @since 2201.9.0 + */ +class FlowBuilder extends NodeVisitor implements FlowJsonBuilder { + + private String id; + private String name; + private String filePath; + private final List nodes; + private final SemanticModel semanticModel; + + public FlowBuilder(String id, String name, String filePath, SemanticModel semanticModel) { + this.nodes = new ArrayList<>(); + setId(id); + setName(name); + setFilePath(filePath); + this.semanticModel = semanticModel; + } + + @Override + public void visit(NamedWorkerDeclarationNode namedWorkerDeclarationNode) { + NodeBuilder nodeBuilder = new NodeBuilder(semanticModel); + + // Set the metadata information of the node + nodeBuilder.setCodeLocation(namedWorkerDeclarationNode.lineRange().startLine(), + namedWorkerDeclarationNode.lineRange().endLine()); + nodeBuilder.setTemplateKind(TemplateKind.TRANSFORMER); + + // Analyze the body of the worker + BlockStatementNode blockStatementNode = namedWorkerDeclarationNode.workerBody(); + blockStatementNode.statements().forEach(statement -> statement.accept(nodeBuilder)); + addNode(nodeBuilder.build()); + } + + @Override + public void setId(String id) { + this.id = id; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public void setFilePath(String filePath) { + this.filePath = filePath; + } + + @Override + public void addNode(WorkerNode node) { + this.nodes.add(node); + } + + @Override + public Flow build() { + return new Flow(id, name, filePath, nodes); + } +} diff --git a/worker-model-generator/modules/model-generator-core/src/main/java/io.ballerina.workermodelgenerator.core/ModelGenerator.java b/worker-model-generator/modules/model-generator-core/src/main/java/io.ballerina.workermodelgenerator.core/ModelGenerator.java new file mode 100644 index 000000000..51cece72d --- /dev/null +++ b/worker-model-generator/modules/model-generator-core/src/main/java/io.ballerina.workermodelgenerator.core/ModelGenerator.java @@ -0,0 +1,67 @@ +package io.ballerina.workermodelgenerator.core; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.syntax.tree.ModulePartNode; +import io.ballerina.compiler.syntax.tree.NonTerminalNode; +import io.ballerina.compiler.syntax.tree.SyntaxKind; +import io.ballerina.compiler.syntax.tree.SyntaxTree; +import io.ballerina.projects.Document; +import io.ballerina.tools.text.LineRange; +import io.ballerina.tools.text.TextDocument; +import io.ballerina.tools.text.TextRange; + +import java.util.Arrays; +import java.util.List; + +/** + * Generator for the worker model. + * + * @since 2201.9.0 + */ +public class ModelGenerator { + + private final SemanticModel semanticModel; + private final Document document; + private final LineRange lineRange; + private static final List validCanvasNodeKinds = Arrays.asList( + SyntaxKind.FUNCTION_DEFINITION, + SyntaxKind.RESOURCE_ACCESSOR_DEFINITION + ); + + public ModelGenerator(SemanticModel model, Document document, LineRange lineRange) { + this.semanticModel = model; + this.document = document; + this.lineRange = lineRange; + } + + /** + * Generates a worker model for the given canvas node. + * + * @return JSON representation of the worker model + * @throws Exception if the canvas is not valid + */ + public JsonElement getWorkerModel() throws Exception { + // Obtain the code block representing the canvas + SyntaxTree syntaxTree = document.syntaxTree(); + TextDocument textDocument = syntaxTree.textDocument(); + ModulePartNode modulePartNode = syntaxTree.rootNode(); + int start = textDocument.textPositionFrom(lineRange.startLine()); + int end = textDocument.textPositionFrom(lineRange.endLine()); + NonTerminalNode canvasNode = modulePartNode.findNode(TextRange.from(start, end - start), true); + + // Check if the canvas node is a valid type + if (!validCanvasNodeKinds.contains(canvasNode.kind())) { + throw new Exception(); + } + + // Build the flow diagram + FlowBuilder flowBuilder = new FlowBuilder("1", "flow1", "path", semanticModel); + canvasNode.accept(flowBuilder); + + // Convert the flow diagram to a JSON object + Gson gson = new Gson(); + return gson.toJsonTree(flowBuilder.build()); + } +} diff --git a/worker-model-generator/modules/model-generator-core/src/main/java/io.ballerina.workermodelgenerator.core/NodeBuilder.java b/worker-model-generator/modules/model-generator-core/src/main/java/io.ballerina.workermodelgenerator.core/NodeBuilder.java new file mode 100644 index 000000000..414e54c3f --- /dev/null +++ b/worker-model-generator/modules/model-generator-core/src/main/java/io.ballerina.workermodelgenerator.core/NodeBuilder.java @@ -0,0 +1,158 @@ +package io.ballerina.workermodelgenerator.core; + +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.api.symbols.Symbol; +import io.ballerina.compiler.api.symbols.TypeDescKind; +import io.ballerina.compiler.api.symbols.TypeSymbol; +import io.ballerina.compiler.syntax.tree.AsyncSendActionNode; +import io.ballerina.compiler.syntax.tree.CaptureBindingPatternNode; +import io.ballerina.compiler.syntax.tree.CheckExpressionNode; +import io.ballerina.compiler.syntax.tree.ExpressionNode; +import io.ballerina.compiler.syntax.tree.ExpressionStatementNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeVisitor; +import io.ballerina.compiler.syntax.tree.ReceiveActionNode; +import io.ballerina.compiler.syntax.tree.SimpleNameReferenceNode; +import io.ballerina.compiler.syntax.tree.SyncSendActionNode; +import io.ballerina.compiler.syntax.tree.SyntaxKind; +import io.ballerina.compiler.syntax.tree.TypedBindingPatternNode; +import io.ballerina.compiler.syntax.tree.VariableDeclarationNode; +import io.ballerina.compiler.syntax.tree.WaitActionNode; +import io.ballerina.tools.text.LinePosition; +import io.ballerina.workermodelgenerator.core.model.CanvasPosition; +import io.ballerina.workermodelgenerator.core.model.CodeLocation; +import io.ballerina.workermodelgenerator.core.model.InputPort; +import io.ballerina.workermodelgenerator.core.model.WorkerNodeJsonBuilder; +import io.ballerina.workermodelgenerator.core.model.OutputPort; +import io.ballerina.workermodelgenerator.core.model.WorkerNode; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * Builder implementation for creating a {@link WorkerNode} instance. + * + * @since 2201.9.0 + */ +class NodeBuilder extends NodeVisitor implements WorkerNodeJsonBuilder { + + private final SemanticModel semanticModel; + + // Json variables + private String id; + private TemplateKind templateKind; + private CodeLocation codeLocation; + private CanvasPosition canvasPosition; + private final List inputPorts; + private final List outputPorts; + + // State variables + private String toWorker; + private String fromWorker; + private TypeDescKind type; + private String name; + + public NodeBuilder(SemanticModel semanticModel) { + this.inputPorts = new ArrayList<>(); + this.outputPorts = new ArrayList<>(); + this.semanticModel = semanticModel; + } + + @Override + public void visit(ExpressionStatementNode expressionStatementNode) { + ExpressionNode expression = expressionStatementNode.expression(); + expression.accept(this); + } + + @Override + public void visit(CheckExpressionNode checkExpressionNode) { + checkExpressionNode.expression().accept(this); + } + + @Override + public void visit(ReceiveActionNode receiveActionNode) { + Node receiverWorker = receiveActionNode.receiveWorkers(); + if (receiverWorker.kind() == SyntaxKind.SIMPLE_NAME_REFERENCE) { + this.fromWorker = ((SimpleNameReferenceNode) receiverWorker).name().text(); + } + } + + @Override + public void visit(AsyncSendActionNode asyncSendActionNode) { + analyzeSendAction(asyncSendActionNode.peerWorker(), asyncSendActionNode.expression()); + } + + @Override + public void visit(SyncSendActionNode syncSendActionNode) { + analyzeSendAction(syncSendActionNode.peerWorker(), syncSendActionNode.expression()); + } + + private void analyzeSendAction(SimpleNameReferenceNode receiverNode, ExpressionNode expressionNode) { + this.toWorker = receiverNode.name().text(); + Optional typeSymbol = this.semanticModel.typeOf(expressionNode); + this.type = typeSymbol.isPresent() ? typeSymbol.get().typeKind() : TypeDescKind.NONE; + this.addOutputPort("id", this.type, this.toWorker); + } + + @Override + public void visit(VariableDeclarationNode variableDeclarationNode) { + // Find the name of the sender + Optional initializer = variableDeclarationNode.initializer(); + if (initializer.isEmpty()) { + return; + } + initializer.get().accept(this); + + // Find the parameter name + TypedBindingPatternNode typedBindingPatternNode = variableDeclarationNode.typedBindingPattern(); + typedBindingPatternNode.bindingPattern().accept(this); + + // Find the parameter type + Optional symbol = this.semanticModel.symbol(typedBindingPatternNode.typeDescriptor()); + this.type = (symbol.isPresent() && symbol.get() instanceof TypeSymbol typeSymbol) ? typeSymbol.typeKind() : + TypeDescKind.NONE; + + this.addInputPort("id", this.type, this.name, this.fromWorker); + } + + @Override + public void visit(CaptureBindingPatternNode captureBindingPatternNode) { + this.name = captureBindingPatternNode.variableName().text(); + } + + @Override + public void setId(String id) { + this.id = id; + } + + @Override + public void setTemplateKind(TemplateKind templateKind) { + this.templateKind = templateKind; + } + + @Override + public void addInputPort(String id, TypeDescKind type, String name, String sender) { + this.inputPorts.add(new InputPort(id, type, name, sender)); + } + + @Override + public void addOutputPort(String id, TypeDescKind type, String receiver) { + this.outputPorts.add(new OutputPort(id, type, receiver)); + } + + @Override + public void setCodeLocation(LinePosition start, LinePosition end) { + this.codeLocation = new CodeLocation(start, end); + } + + @Override + public void setCanvasPosition(int x, int y) { + this.canvasPosition = new CanvasPosition(x, y); + } + + @Override + public WorkerNode build() { + return new WorkerNode(id, templateKind, codeLocation, canvasPosition, inputPorts, outputPorts); + } +} diff --git a/worker-model-generator/modules/model-generator-core/src/main/java/io.ballerina.workermodelgenerator.core/TemplateKind.java b/worker-model-generator/modules/model-generator-core/src/main/java/io.ballerina.workermodelgenerator.core/TemplateKind.java new file mode 100644 index 000000000..3aa5a6dcf --- /dev/null +++ b/worker-model-generator/modules/model-generator-core/src/main/java/io.ballerina.workermodelgenerator.core/TemplateKind.java @@ -0,0 +1,6 @@ +package io.ballerina.workermodelgenerator.core; + +public enum TemplateKind { + SWITCH, + TRANSFORMER +}