Skip to content

Commit

Permalink
Merge pull request #566 from dulajdilshan/feature/types-api
Browse files Browse the repository at this point in the history
Refactor source code generation for updateType API
  • Loading branch information
KavinduZoysa authored Jan 30, 2025
2 parents da6922f + e370b8d commit 3f37b66
Show file tree
Hide file tree
Showing 16 changed files with 1,239 additions and 104 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import io.ballerina.flowmodelgenerator.core.model.ModuleInfo;
import io.ballerina.flowmodelgenerator.core.model.TypeData;
import io.ballerina.flowmodelgenerator.core.utils.CommonUtils;
import io.ballerina.flowmodelgenerator.core.utils.SourceCodeGenerator;
import io.ballerina.flowmodelgenerator.core.utils.TypeTransformer;
import io.ballerina.flowmodelgenerator.core.utils.TypeUtils;
import io.ballerina.projects.Document;
Expand Down Expand Up @@ -160,7 +161,8 @@ public JsonElement updateType(Path filePath, TypeData typeData) {
textEditsMap.put(filePath, textEdits);

// Regenerate code snippet for the type
String codeSnippet = createCodeSnippet(typeData);
SourceCodeGenerator sourceCodeGenerator = new SourceCodeGenerator();
String codeSnippet = sourceCodeGenerator.generateCodeSnippetForType(typeData);

SyntaxTree syntaxTree = this.typeDocument.syntaxTree();
LineRange lineRange = typeData.codedata().lineRange();
Expand All @@ -176,14 +178,6 @@ public JsonElement updateType(Path filePath, TypeData typeData) {
return gson.toJsonTree(textEditsMap);
}

private String createCodeSnippet(TypeData typeData) {
return switch (typeData.codedata().node()) {
case RECORD -> createRecordTypeDefCodeSnippet(typeData);
// TODO: Handle other kinds of type: service-decl, array, union, error, future, map, stream, intersection
default -> "";
};
}

private void addMemberTypes(TypeSymbol typeSymbol, Map<String, Symbol> symbolMap) {
// Record
switch (typeSymbol.typeKind()) {
Expand Down Expand Up @@ -411,53 +405,6 @@ private void addDependencyTypes(TypeSymbol typeSymbol, Map<String, Object> refer
}
}

private String createRecordTypeDefCodeSnippet(TypeData typeData) {
StringBuilder recordBuilder = new StringBuilder();

// Add documentation if present
if (typeData.metadata().description() != null && !typeData.metadata().description().isEmpty()) {
recordBuilder.append(CommonUtils.convertToBalDocs(typeData.metadata().description()));
}

// Add type name
recordBuilder.append("type ")
.append(typeData.name())
.append(" record {|\n");

// Add includes
for (String include : typeData.includes()) {
recordBuilder.append("*").append(include).append(";\n");
}

// Add members

typeData.members().forEach(member -> {
if (member.docs() != null && !member.docs().isEmpty()) {
recordBuilder.append(CommonUtils.convertToBalDocs(member.docs()));
}

recordBuilder
.append(member.type())
.append(" ")
.append(member.name())
.append((member.defaultValue() != null && !member.defaultValue().isEmpty()) ?
" = " + member.defaultValue() : "")
.append(";\n");
});

// Add rest member if present
Optional.ofNullable(typeData.restMember()).ifPresent(restMember -> {
recordBuilder
.append(restMember.type())
.append("...;\n");
});

// Close the record
recordBuilder.append("|};\n");

return recordBuilder.toString();
}

record TypeDataWithRefs(Object type, List<Object> refs) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
/*
* Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com)
*
* 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.flowmodelgenerator.core.utils;

import com.google.gson.Gson;
import io.ballerina.flowmodelgenerator.core.model.Member;
import io.ballerina.flowmodelgenerator.core.model.NodeKind;
import io.ballerina.flowmodelgenerator.core.model.TypeData;

import java.util.Map;
import java.util.Optional;

/**
* Code snippet generator.
*
* @since 2.0.0
*/
public class SourceCodeGenerator {
private final Gson gson = new Gson();

public String generateCodeSnippetForType(TypeData typeData) {
NodeKind nodeKind = typeData.codedata().node();
return switch (nodeKind) {
case SERVICE_DECLARATION, CLASS -> ""; // TODO: Implement this
case ENUM -> generateEnumCodeSnippet(typeData);
default -> generateTypeDefCodeSnippet(typeData);
};
}

private String generateEnumCodeSnippet(TypeData typeData) {
StringBuilder stringBuilder = new StringBuilder();

// Add documentation if present
if (typeData.metadata().description() != null && !typeData.metadata().description().isEmpty()) {
stringBuilder.append(CommonUtils.convertToBalDocs(typeData.metadata().description()));
}

// Add enum names
stringBuilder.append("enum ")
.append(typeData.name())
.append(" {");

// Add enum values
for (int i = 0; i < typeData.members().size(); i++) {
Member member = typeData.members().get(i);
stringBuilder.append("\n\t")
.append(member.name())
.append((member.defaultValue() != null && !member.defaultValue().isEmpty()) ?
" = " + member.defaultValue() : "");
if (i < typeData.members().size() - 1) {
stringBuilder.append(",");
}
}

return stringBuilder.append("\n}\n").toString();
}

private String generateTypeDefCodeSnippet(TypeData typeData) {
StringBuilder stringBuilder = new StringBuilder();

// Add documentation if present
if (typeData.metadata().description() != null && !typeData.metadata().description().isEmpty()) {
stringBuilder.append(CommonUtils.convertToBalDocs(typeData.metadata().description()));
}

// Add type name
stringBuilder.append("type ")
.append(typeData.name())
.append(" ");

generateTypeDescriptor(typeData, stringBuilder);

return stringBuilder.append(";\n").toString();
}

private void generateTypeDescriptor(Object typeDescriptor, StringBuilder stringBuilder) {
if (typeDescriptor instanceof String) { // Type reference
stringBuilder.append((String) typeDescriptor);
return;
}

TypeData typeData;
if (typeDescriptor instanceof Map) {
String json = gson.toJson(typeDescriptor);
typeData = gson.fromJson(json, TypeData.class);
} else {
typeData = (TypeData) typeDescriptor;
}

switch (typeData.codedata().node()) {
case RECORD -> generateRecordTypeDescriptor(typeData, stringBuilder);
case ARRAY -> generateArrayTypeDescriptor(typeData, stringBuilder);
case MAP -> generateMapTypeDescriptor(typeData, stringBuilder);
case STREAM -> generateStreamTypeDescriptor(typeData, stringBuilder);
case FUTURE -> generateFutureTypeDescriptor(typeData, stringBuilder);
case TYPEDESC -> generateTypedescTypeDescriptor(typeData, stringBuilder);
case ERROR -> generateErrorTypeDescriptor(typeData, stringBuilder);
case UNION -> generateUnionTypeDescriptor(typeData, stringBuilder);
case INTERSECTION -> generateIntersectionTypeDescriptor(typeData, stringBuilder);
case OBJECT -> generateObjectTypeDescriptor(typeData, stringBuilder);
case TABLE -> generateTableTypeDescriptor(typeData, stringBuilder);
default -> throw new UnsupportedOperationException("Unsupported type descriptor: " + typeDescriptor);
}
}

private void generateObjectTypeDescriptor(TypeData typeData, StringBuilder stringBuilder) {
stringBuilder.append("object {");

// Add fields
for (Member member : typeData.members()) {
generateMember(member, stringBuilder, false);
}

// TODO: Add functions
stringBuilder.append("\n}");
}

private void generateRecordTypeDescriptor(TypeData typeData, StringBuilder stringBuilder) {
// Assumption: a record is always a closed records
stringBuilder.append("record {|");

// Add inclusions
typeData.includes().forEach(include -> stringBuilder.append("\n\t*").append(include).append(";"));

// Add fields
for (Member member : typeData.members()) {
generateMember(member, stringBuilder, true);
}

// Add rest field
Optional.ofNullable(typeData.restMember()).ifPresent(restMember -> {
stringBuilder.append("\n\t");
generateTypeDescriptor(restMember.type(), stringBuilder);
stringBuilder.append(" ...;");
});
stringBuilder.append("\n|}");
}

private void generateMember(Member member, StringBuilder stringBuilder, boolean withDefaultValue) {
if (member.docs() != null && !member.docs().isEmpty()) {
stringBuilder
.append("\n\t")
.append(CommonUtils.convertToBalDocs(member.docs()));
} else {
stringBuilder.append("\n");
}
stringBuilder.append("\t");
generateTypeDescriptor(member.type(), stringBuilder);
stringBuilder.append(" ").append(member.name());

if (withDefaultValue) {
stringBuilder.append((member.defaultValue() != null && !member.defaultValue().isEmpty()) ?
" = " + member.defaultValue() : "");
}
stringBuilder.append(";");
}

private void generateTableTypeDescriptor(TypeData typeData, StringBuilder stringBuilder) {
if (!typeData.members().isEmpty()) {
stringBuilder.append("table<");
generateTypeDescriptor(typeData.members().getFirst().type(), stringBuilder);
stringBuilder.append(">");

if (typeData.members().size() > 1) {
stringBuilder.append(" key<");
generateTypeDescriptor(typeData.members().get(1).type(), stringBuilder);
stringBuilder.append(">");
}

// TODO: key specifier is not yet supported
}
}

private void generateIntersectionTypeDescriptor(TypeData typeData, StringBuilder stringBuilder) {
if (typeData.members().size() <= 1) {
return;
}
for (Member member : typeData.members()) {
if (member.type() instanceof TypeData) {
if (((TypeData) member.type()).codedata().node() == NodeKind.INTERSECTION) {
stringBuilder.append("(");
generateTypeDescriptor(member.type(), stringBuilder);
stringBuilder.append(")");
} else {
generateTypeDescriptor(member.type(), stringBuilder);
}
} else {
generateTypeDescriptor(member.type(), stringBuilder);
}
stringBuilder.append(" & ");
}
}

private void generateUnionTypeDescriptor(TypeData typeData, StringBuilder stringBuilder) {
if (typeData.members().size() <= 1) {
return;
}
for (int i = 0; i < typeData.members().size(); i++) {
Member member = typeData.members().get(i);
if (member.type() instanceof TypeData) {
if (((TypeData) member.type()).codedata().node() == NodeKind.UNION) {
stringBuilder.append("(");
generateTypeDescriptor(member.type(), stringBuilder);
stringBuilder.append(")");
} else {
generateTypeDescriptor(member.type(), stringBuilder);
}
} else {
generateTypeDescriptor(member.type(), stringBuilder);
}
if (i < typeData.members().size() - 1) {
stringBuilder.append("|");
}
}
}

private void generateErrorTypeDescriptor(TypeData typeData, StringBuilder stringBuilder) {
if (typeData.members().size() == 1) {
stringBuilder.append("error<");
generateTypeDescriptor(typeData.members().getFirst().type(), stringBuilder);
stringBuilder.append(">");
}
}

private void generateTypedescTypeDescriptor(TypeData typeData, StringBuilder stringBuilder) {
if (typeData.members().size() == 1) {
stringBuilder.append("typedesc<");
generateTypeDescriptor(typeData.members().getFirst().type(), stringBuilder);
stringBuilder.append(">");
}
}

private void generateFutureTypeDescriptor(TypeData typeData, StringBuilder stringBuilder) {
if (typeData.members().size() == 1) {
stringBuilder.append("future<");
generateTypeDescriptor(typeData.members().getFirst().type(), stringBuilder);
stringBuilder.append(">");
}
}

private void generateStreamTypeDescriptor(TypeData typeData, StringBuilder stringBuilder) {
if (typeData.members().size() == 1) {
stringBuilder.append("stream<");
generateTypeDescriptor(typeData.members().getFirst().type(), stringBuilder);
stringBuilder.append(">");
}
}

private void generateMapTypeDescriptor(TypeData typeData, StringBuilder stringBuilder) {
if (typeData.members().size() == 1) {
stringBuilder.append("map<");
generateTypeDescriptor(typeData.members().getFirst().type(), stringBuilder);
stringBuilder.append(">");
}
}

private void generateArrayTypeDescriptor(TypeData typeData, StringBuilder stringBuilder) {
if (typeData.members().size() == 1) {
generateTypeDescriptor(typeData.members().getFirst().type(), stringBuilder);
}
stringBuilder.append("[]");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -231,12 +231,17 @@ public Object transform(EnumSymbol enumSymbol) {
Member.MemberBuilder memberBuilder = new Member.MemberBuilder();
enumSymbol.members().forEach(enumMember -> {
String name = enumMember.getName().get();
Member member = memberBuilder
ConstantValue constValue = (ConstantValue) enumMember.constValue();
String constValueAsString = "\"" + constValue.value() + "\"";
memberBuilder
.name(name)
.kind(Member.MemberKind.NAME)
.type(((ConstantValue) enumMember.constValue()).value().toString())
.refs(List.of())
.build();
.type(constValueAsString)
.refs(List.of());
if (!constValue.value().equals(enumMember.getName().get())) {
memberBuilder.defaultValue(constValueAsString);
}
Member member = memberBuilder.build();
members.putIfAbsent(name, member);
});
typeDataBuilder.members(members);
Expand Down
Loading

0 comments on commit 3f37b66

Please sign in to comment.