Skip to content

Commit

Permalink
Add init builder impl for worker model
Browse files Browse the repository at this point in the history
  • Loading branch information
nipunayf committed Dec 8, 2023
1 parent c6539c4 commit c7cde92
Show file tree
Hide file tree
Showing 4 changed files with 323 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -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<WorkerNode> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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<SyntaxKind> 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());
}
}
Original file line number Diff line number Diff line change
@@ -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<InputPort> inputPorts;
private final List<OutputPort> 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> 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<ExpressionNode> 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> 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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.ballerina.workermodelgenerator.core;

public enum TemplateKind {
SWITCH,
TRANSFORMER
}

0 comments on commit c7cde92

Please sign in to comment.