Skip to content

Commit

Permalink
Regular node regulator for non-empty nodes (draft)
Browse files Browse the repository at this point in the history
  • Loading branch information
kniazkov committed Oct 30, 2024
1 parent e2f66c0 commit fb5ef4f
Show file tree
Hide file tree
Showing 5 changed files with 283 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public void createSpecificEntitiesInNodeClass(final Klass klass) {
getter.suppressWarning("PMD.BooleanGetMethodName");
}
klass.addMethod(getter);
final Method list = new Method("List<Node>", "getChildrenList");
final Method list = new Method(Strings.TYPE_NODE_LIST, "getChildrenList");
list.makePublic();
list.setBody("return Collections.emptyList();");
klass.addMethod(list);
Expand Down
80 changes: 80 additions & 0 deletions src/main/java/org/cqfn/astranaut/codegen/java/NameGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2024 Ivan Kniazkov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.cqfn.astranaut.codegen.java;

import java.util.Arrays;
import java.util.List;

/**
* Generates non-repeating names (numerals) that can be used as names for variables
* when the variable name is not known.
* @since 1.0.0
*/
public class NameGenerator {
/**
* List of names.
*/
private static final List<String> NAMES = Arrays.asList(
"first",
"second",
"third",
"fourth",
"fifth",
"sixth",
"seventh",
"eighth",
"ninth",
"tenth",
"eleventh",
"twelfth",
"thirteenth",
"fourteenth",
"fifteenth",
"sixteenth",
"seventeenth",
"eighteenth",
"nineteenth",
"twentieth"
);

/**
* Current index.
*/
private int current;

/**
* Returns the next name.
* @return A name
*/
public String nextName() {
final String name;
if (this.current < NameGenerator.NAMES.size()) {
name = NameGenerator.NAMES.get(this.current);
this.current = this.current + 1;
} else {
throw new IllegalStateException("No more names available.");
}
return name;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ public abstract class NonAbstractNodeGenerator implements RuleGenerator {
*/
private boolean arraylist;

/**
* Flag indicating that a non-trivial validator has been generated.
*/
private boolean validator;

@Override
public final Set<CompilationUnit> createUnits(final Context context) {
final String name = this.getRule().getName();
Expand Down Expand Up @@ -165,6 +170,13 @@ protected void needArrayListClass() {
this.arraylist = true;
}

/**
* Sets a flag indicating that a non-trivial validator has been generated.
*/
protected void hasNonTrivialValidator() {
this.validator = true;
}

/**
* Fills the class describing the node with fields and methods.
* @param klass Class describing the node
Expand Down Expand Up @@ -304,10 +316,10 @@ private Klass createBuilderClass(final Context context) {
klass.setImplementsList(Strings.TYPE_BUILDER);
klass.setVersion(context.getVersion());
NonAbstractNodeGenerator.createFragmentFieldAndSetter(klass);
this.createSpecificEntitiesInBuilderClass(klass);
this.createDataSetter(klass);
this.createChildrenListSetter(klass);
this.createValidator(klass);
this.createSpecificEntitiesInBuilderClass(klass);
this.createNodeCreator(klass);
return klass;
}
Expand Down Expand Up @@ -350,7 +362,7 @@ private void createDataSetter(final Klass klass) {
private void createChildrenListSetter(final Klass klass) {
final Method method = new Method(Strings.TYPE_BOOLEAN, "setChildrenList");
method.makePublic();
method.addArgument("List<Node>", "list");
method.addArgument(Strings.TYPE_NODE_LIST, "list");
method.setBody(this.getChildrenListSetterBody());
klass.addMethod(method);
}
Expand All @@ -375,6 +387,11 @@ private void createNodeCreator(final Klass klass) {
method.makePublic();
final String name = this.getRule().getName();
final List<String> lines = new ArrayList<>(16);
if (this.validator) {
lines.add("if (!this.isValid()) {");
lines.add("throw new IllegalStateException();");
lines.add("}");
}
lines.add(String.format("final %s node = new %s();", name, name));
lines.add("node.fragment = this.fragment;");
this.fillNodeCreator(lines);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
package org.cqfn.astranaut.codegen.java;

import java.util.List;
import java.util.Locale;
import org.cqfn.astranaut.dsl.ChildDescriptorExt;
import org.cqfn.astranaut.dsl.NodeDescriptor;
import org.cqfn.astranaut.dsl.RegularNodeDescriptor;

Expand All @@ -38,12 +40,18 @@ public final class RegularNodeGenerator extends NonAbstractNodeGenerator {
*/
private final RegularNodeDescriptor rule;

/**
* Names of variables that store child nodes.
*/
private final String[] names;

/**
* Constructor.
* @param rule The rule that describes regular node
*/
public RegularNodeGenerator(final RegularNodeDescriptor rule) {
this.rule = rule;
this.names = RegularNodeGenerator.generateVariableNames(rule);
}

@Override
Expand All @@ -53,13 +61,23 @@ public NodeDescriptor getRule() {

@Override
public void createSpecificEntitiesInNodeClass(final Klass klass) {
final Method list = new Method(Strings.TYPE_NODE_LIST, "getChildrenList");
list.makePublic();
if (this.rule.getExtChildTypes().isEmpty()) {
this.needCollectionsClass();
final Method list = new Method("List<Node>", "getChildrenList");
list.makePublic();
list.setBody("return Collections.emptyList();");
klass.addMethod(list);
} else {
list.setBody("return this.children;");
this.createFieldsWithGettersForTaggedChildren(klass);
final Field children = new Field(
Strings.TYPE_NODE_LIST,
"children",
"List of child nodes"
);
children.makePrivate();
klass.addField(children);
}
klass.addMethod(list);
}

@Override
Expand All @@ -69,17 +87,31 @@ public String getDataGetterBody() {

@Override
public String getChildCountGetterBody() {
return "return 0;";
final String body;
if (this.names.length == 0) {
body = "return 0;";
} else {
body = "return this.children.size();";
}
return body;
}

@Override
public String getChildGetterBody() {
return "throw new IndexOutOfBoundsException();";
final String body;
if (this.names.length == 0) {
body = "throw new IndexOutOfBoundsException();";
} else {
body = "return this.children.get(index);";
}
return body;
}

@Override
public void createSpecificEntitiesInBuilderClass(final Klass klass) {
this.getClass();
if (this.names.length > 0) {
this.createFieldsWithSettersForTaggedChildren(klass);
}
}

@Override
Expand All @@ -94,11 +126,150 @@ public String getChildrenListSetterBody() {

@Override
public String getValidatorBody() {
return "return true;";
boolean flag = false;
final StringBuilder builder = new StringBuilder();
final List<ChildDescriptorExt> children = this.rule.getExtChildTypes();
for (int index = 0; index < children.size(); index = index + 1) {
final ChildDescriptorExt descriptor = children.get(index);
if (descriptor.isOptional()) {
continue;
}
if (flag) {
builder.append(" && ");
}
flag = true;
builder.append(this.names[index]).append(" != null");
}
final String expression;
if (flag) {
expression = builder.toString();
this.hasNonTrivialValidator();
} else {
expression = "true";
}
return String.format("return %s;", expression);
}

@Override
public void fillNodeCreator(final List<String> lines) {
this.getClass();
}

/**
* Creates fields ans getters for all tagged children.
* @param klass Class describing regular node
*/
private void createFieldsWithGettersForTaggedChildren(final Klass klass) {
for (final ChildDescriptorExt descriptor : this.rule.getExtChildTypes()) {
final String tag = descriptor.getTag();
if (tag.isEmpty()) {
continue;
}
final Field field = new Field(
descriptor.getType(),
tag.toLowerCase(Locale.ENGLISH),
String.format("Child node with '%s' tag", tag)
);
field.makePrivate();
klass.addField(field);
final Method getter = new Method(
descriptor.getType(),
String.format(
"get%s%s",
tag.substring(0, 1).toUpperCase(Locale.ENGLISH),
tag.substring(1)
),
String.format("Returns child node with '%s' tag", tag)
);
getter.makePublic();
if (descriptor.isOptional()) {
getter.setReturnsDescription(
String.format(
"Child node or {@code null} if the node with '%s' tag is not specified",
tag
)
);
} else {
getter.setReturnsDescription("Child node (can't be {@code null})");
}
getter.setBody(String.format("return this.%s;", tag.toLowerCase(Locale.ENGLISH)));
klass.addMethod(getter);
}
}

/**
* Creates fields and setters for all tagged children.
* @param klass Class describing builder of regular node
*/
private void createFieldsWithSettersForTaggedChildren(final Klass klass) {
final List<ChildDescriptorExt> children = this.rule.getExtChildTypes();
for (int index = 0; index < children.size(); index = index + 1) {
final ChildDescriptorExt descriptor = children.get(index);
final String tag = descriptor.getTag();
final String name = this.names[index];
final String brief;
if (tag.isEmpty()) {
brief = String.format(
"%s%s node",
name.substring(0, 1).toUpperCase(Locale.ENGLISH),
name.substring(1)
);
} else {
brief = String.format("Child node with '%s' tag", tag);
}
final Field field = new Field(
descriptor.getType(),
name,
brief
);
field.makePrivate();
klass.addField(field);
if (tag.isEmpty()) {
continue;
}
final Method setter = new Method(
Strings.TYPE_VOID,
String.format(
"set%s%s",
tag.substring(0, 1).toUpperCase(Locale.ENGLISH),
tag.substring(1)
),
String.format("Sets child node with '%s' tag", tag)
);
setter.makePublic();
setter.addArgument(descriptor.getType(), "object", "Child node");
if (descriptor.isOptional()) {
setter.setBody(String.format("this.%s = object;", tag.toLowerCase(Locale.ENGLISH)));
} else {
setter.setBody(
String.format(
"if (object != null) { this.%s = object; }",
tag.toLowerCase(Locale.ENGLISH)
)
);
}
klass.addMethod(setter);
}
}

/**
* Generates variable names that store child nodes.
* @param rule Descriptor on the basis of which the source code will be built
* @return Array of variable names.
*/
private static String[] generateVariableNames(final RegularNodeDescriptor rule) {
final List<ChildDescriptorExt> children = rule.getExtChildTypes();
final String[] names = new String[children.size()];
final NameGenerator generator = new NameGenerator();
for (int index = 0; index < children.size(); index = index + 1) {
final ChildDescriptorExt child = children.get(index);
final String tag = child.getTag();
String name = generator.nextName();
if (!tag.isEmpty()) {
name = tag.toLowerCase(Locale.ENGLISH);
}
names[index] = name;
}
return names;
}
}
Loading

0 comments on commit fb5ef4f

Please sign in to comment.