
com.vaadin.base.devserver.editor.Editor Maven / Gradle / Ivy
package com.vaadin.base.devserver.editor;
import com.github.javaparser.Position;
import com.github.javaparser.Range;
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.ImportDeclaration;
import com.github.javaparser.ast.Modifier.Keyword;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.ConstructorDeclaration;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.body.TypeDeclaration;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.comments.Comment;
import com.github.javaparser.ast.comments.LineComment;
import com.github.javaparser.ast.expr.AssignExpr;
import com.github.javaparser.ast.expr.AssignExpr.Operator;
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.ObjectCreationExpr;
import com.github.javaparser.ast.expr.SimpleName;
import com.github.javaparser.ast.expr.StringLiteralExpr;
import com.github.javaparser.ast.expr.VariableDeclarationExpr;
import com.github.javaparser.ast.nodeTypes.NodeWithBlockStmt;
import com.github.javaparser.ast.nodeTypes.NodeWithStatements;
import com.github.javaparser.ast.stmt.BlockStmt;
import com.github.javaparser.ast.stmt.ExpressionStmt;
import com.github.javaparser.ast.stmt.Statement;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.printer.lexicalpreservation.LexicalPreservingPrinter;
import com.vaadin.base.devserver.themeeditor.utils.StatementLineNumberVisitor;
import com.vaadin.flow.shared.util.SharedUtil;
import org.apache.commons.io.IOUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class Editor {
public static class Modification implements Comparable {
private enum Type {
IMPORT, INSERT_AFTER, INSERT_BEFORE, INSERT_LINE_AFTER, INSERT_LINE_BEFORE, //
REPLACE, INSERT_AT_END_OF_BLOCK, REMOVE_NODE
};
private Node referenceNode;
private Type type;
private Node node;
private int sourceOffset;
public void apply() {
if (type == Type.IMPORT) {
if (referenceNode instanceof CompilationUnit cu
&& node instanceof ImportDeclaration id) {
cu.getImports().add(id);
}
} else if (type == Type.INSERT_LINE_AFTER) {
if (node instanceof Statement stmt) {
Editor.addStatement(referenceNode, Where.AFTER, stmt);
}
} else if (type == Type.INSERT_AFTER) {
Node parent = referenceNode.getParentNode().orElse(null);
if (parent instanceof MethodCallExpr mce) {
mce.getArguments().addAfter((Expression) node,
(Expression) referenceNode);
}
} else if (type == Type.INSERT_LINE_BEFORE) {
if (node instanceof Statement stmt) {
Editor.addStatement(referenceNode, Where.BEFORE, stmt);
}
} else if (type == Type.INSERT_BEFORE) {
Node parent = referenceNode.getParentNode().orElse(null);
if (parent instanceof MethodCallExpr mce) {
mce.getArguments().addBefore((Expression) node,
(Expression) referenceNode);
}
} else if (type == Type.REPLACE) {
// comment need to be removed separately not to leave empty line
// while using LexicalPreservingPrinter
referenceNode.getComment().ifPresent(Node::remove);
referenceNode.replace(node);
} else if (type == Type.INSERT_AT_END_OF_BLOCK) {
if (node instanceof Statement stmt) {
if (referenceNode instanceof NodeWithStatements block) {
block.addStatement(stmt);
} else if (referenceNode instanceof NodeWithBlockStmt block) {
block.getBody().addStatement(stmt);
}
}
} else if (type == Type.REMOVE_NODE) {
// comment need to be removed separately not to leave empty line
// while using LexicalPreservingPrinter
referenceNode.getComment().ifPresent(Node::remove);
referenceNode.remove();
} else {
throw new RuntimeException("Failed to perform: " + this);
}
}
public int sourceOffset() {
return sourceOffset;
}
public static Modification addImport(Node referenceNode, Node node) {
Modification mod = new Modification();
mod.referenceNode = referenceNode;
mod.type = Type.IMPORT;
mod.node = node;
mod.sourceOffset = getLinesCount(node);
return mod;
}
public static Modification insertAfter(Node referenceNode, Node node) {
Modification mod = new Modification();
mod.referenceNode = referenceNode;
mod.type = Type.INSERT_AFTER;
mod.node = node;
mod.sourceOffset = 0; // modifies same line, no offset
return mod;
}
public static Modification insertAtEndOfBlock(Node referenceNode,
Node node) {
Modification mod = new Modification();
mod.referenceNode = referenceNode;
mod.type = Type.INSERT_AT_END_OF_BLOCK;
mod.node = node;
mod.sourceOffset = getLinesCount(node);
return mod;
}
public static Modification insertBefore(Node referenceNode, Node node) {
Modification mod = new Modification();
mod.referenceNode = referenceNode;
mod.type = Type.INSERT_BEFORE;
mod.node = node;
mod.sourceOffset = 0; // modifies same line, no offset
return mod;
}
public static Modification insertLineBefore(Node referenceNode,
Node node) {
Modification mod = new Modification();
mod.referenceNode = referenceNode;
mod.type = Type.INSERT_LINE_BEFORE;
mod.node = node;
mod.sourceOffset = getLinesCount(node);
return mod;
}
public static Modification insertLineAfter(Node referenceNode,
Node node) {
Modification mod = new Modification();
mod.referenceNode = referenceNode;
mod.type = Type.INSERT_LINE_AFTER;
mod.node = node;
mod.sourceOffset = getLinesCount(node);
return mod;
}
public static Modification replace(Node referenceNode, Node node) {
Modification mod = new Modification();
mod.referenceNode = referenceNode;
mod.type = Type.REPLACE;
mod.node = node;
mod.sourceOffset = getLinesCount(node)
- getLinesCount(referenceNode);
return mod;
}
public static Modification remove(Node referenceNode) {
Modification mod = new Modification();
mod.referenceNode = referenceNode;
mod.type = Type.REMOVE_NODE;
mod.sourceOffset = -getLinesCount(referenceNode);
return mod;
}
@Override
public int compareTo(Modification o) {
// Sort end to start so positions do not change while replacing
Position aBegin = referenceNode.getRange().get().begin;
int a = aBegin.line;
Position bBegin = o.referenceNode.getRange().get().begin;
int b = bBegin.line;
if (a == b) {
return Integer.compare(bBegin.column, aBegin.column);
}
return Integer.compare(b, a);
}
@Override
public String toString() {
if (type == Type.INSERT_LINE_AFTER) {
return "Modification INSERT_LINE_AFTER at position "
+ referenceNode.getEnd().get() + ": " + node;
} else if (type == Type.INSERT_AFTER) {
return "Modification INSERT_AFTER at position "
+ referenceNode.getEnd().get() + ": " + node;
} else if (type == Type.INSERT_BEFORE) {
return "Modification INSERT_BEFORE at position "
+ referenceNode.getBegin().get() + ": " + node;
} else if (type == Type.REPLACE) {
return "Modification REPLACE position "
+ referenceNode.getBegin().get() + "-"
+ referenceNode.getEnd().get() + ": " + node;
} else if (type == Type.REMOVE_NODE) {
return "Modification REMOVE position "
+ referenceNode.getBegin().get() + "-"
+ referenceNode.getEnd().get() + ": " + referenceNode;
} else if (type == Type.INSERT_AT_END_OF_BLOCK) {
return "Modification INSERT_AT_END_OF_BLOCK " + referenceNode;
}
return "Modification UNKNOWN TYPE";
}
}
private List modifyOrAddCall(CompilationUnit cu,
int componentCreateLineNumber, int componentAttachLineNumber,
ComponentType componentType, String methodName,
String methodParameter) {
List mods = new ArrayList<>();
Statement node = findStatement(cu, componentCreateLineNumber);
SimpleName localVariableOrField = findLocalVariableOrField(cu,
componentCreateLineNumber);
if (localVariableOrField == null) {
modifyOrAddCallInlineConstructor(cu, node, componentType,
methodName, methodParameter, mods);
return mods;
}
BlockStmt codeBlock = (BlockStmt) node.getParentNode().get();
boolean handled = false;
Expression exp = (Expression) localVariableOrField.getParentNode().get()
.getParentNode().get();
if (exp.isAssignExpr()) {
AssignExpr assignExpr = exp.asAssignExpr();
if (assignExpr.getValue().isObjectCreationExpr()) {
handled = modifyConstructorCall(
assignExpr.getValue().asObjectCreationExpr(),
methodName, methodParameter, mods);
}
if (!handled) {
addOrReplaceCall(codeBlock, node, localVariableOrField,
methodName, new StringLiteralExpr(methodParameter),
mods);
}
} else if (exp.isVariableDeclarationExpr()) {
VariableDeclarationExpr varDeclaration = exp
.asVariableDeclarationExpr();
VariableDeclarator varDeclarator = varDeclaration.getVariable(0);
Optional initializer = varDeclarator.getInitializer();
if (initializer.isPresent()
&& initializer.get().isObjectCreationExpr()) {
ObjectCreationExpr constructorCall = initializer.get()
.asObjectCreationExpr();
if (modifyConstructorCall(constructorCall, methodName,
methodParameter, mods)) {
handled = true;
}
}
if (!handled) {
addOrReplaceCall(codeBlock, node, localVariableOrField,
methodName, new StringLiteralExpr(methodParameter),
mods);
}
}
return mods;
}
private List addCall(CompilationUnit cu,
int componentCreateLineNumber, int componentAttachLineNumber,
ComponentType componentType, String methodName,
String methodParameter) {
List mods = new ArrayList<>();
Statement node = findStatement(cu, componentCreateLineNumber);
if (node == null) {
throw new UnsupportedOperationException(
"Cannot add method call for given component.");
}
SimpleName localVariableOrField = findLocalVariableOrField(cu,
componentCreateLineNumber);
if (localVariableOrField == null) {
modifyOrAddCallInlineConstructor(cu, node, componentType,
methodName, methodParameter, mods);
return mods;
}
Expression exp = (Expression) localVariableOrField.getParentNode().get()
.getParentNode().get();
if (exp.isAssignExpr()) {
addCall(node, localVariableOrField, methodName,
new StringLiteralExpr(methodParameter), mods);
} else if (exp.isVariableDeclarationExpr()) {
addCall(node, localVariableOrField, methodName,
new StringLiteralExpr(methodParameter), mods);
}
return mods;
}
private List removeCall(CompilationUnit cu,
int componentCreateLineNumber, int componentAttachLineNumber,
ComponentType componentType, String methodName,
String methodParameter) {
List mods = new ArrayList<>();
Statement node = findStatement(cu, componentCreateLineNumber);
SimpleName localVariableOrField = findLocalVariableOrField(cu,
componentCreateLineNumber);
if (localVariableOrField == null) {
modifyOrAddCallInlineConstructor(cu, node, componentType,
methodName, methodParameter, mods);
return mods;
}
BlockStmt codeBlock = (BlockStmt) node.getParentNode().get();
boolean handled = false;
Expression exp = (Expression) localVariableOrField.getParentNode().get()
.getParentNode().get();
if (exp.isAssignExpr()) {
AssignExpr assignExpr = exp.asAssignExpr();
if (assignExpr.getValue().isObjectCreationExpr()) {
handled = modifyConstructorCall(
assignExpr.getValue().asObjectCreationExpr(),
methodName, methodParameter, mods);
}
if (!handled) {
removeCall(codeBlock, node, localVariableOrField, methodName,
new StringLiteralExpr(methodParameter), mods);
}
} else if (exp.isVariableDeclarationExpr()) {
VariableDeclarationExpr varDeclaration = exp
.asVariableDeclarationExpr();
VariableDeclarator varDeclarator = varDeclaration.getVariable(0);
Optional initializer = varDeclarator.getInitializer();
if (initializer.isPresent()
&& initializer.get().isObjectCreationExpr()) {
ObjectCreationExpr constructorCall = initializer.get()
.asObjectCreationExpr();
if (modifyConstructorCall(constructorCall, methodName,
methodParameter, mods)) {
handled = true;
}
}
if (!handled) {
removeCall(codeBlock, node, localVariableOrField, methodName,
new StringLiteralExpr(methodParameter), mods);
}
}
return mods;
}
private void modifyOrAddCallInlineConstructor(CompilationUnit cu,
Statement componentNode, ComponentType componentType,
String methodName, String methodParameter,
List mods) {
if (!componentNode.isExpressionStmt() || componentType == null) {
return;
}
Expression expression = componentNode.asExpressionStmt()
.getExpression();
if (expression.isMethodCallExpr()) {
ObjectCreationExpr constructorCall = findConstructorCallParameter(
expression.asMethodCallExpr(), componentType);
if (constructorCall != null) {
modifyConstructorCall(constructorCall, methodName,
methodParameter, mods);
}
}
}
private ObjectCreationExpr findConstructorCallParameter(
MethodCallExpr methodCallExpr, ComponentType componentType) {
// e.g. add(new Button("foo"))
List constructorCalls = methodCallExpr
.getArguments().stream()
.filter(arg -> arg.isObjectCreationExpr())
.map(arg -> arg.asObjectCreationExpr())
.filter(objectCreate -> isConstructorFor(objectCreate,
componentType))
.collect(Collectors.toList());
if (constructorCalls.size() == 1) {
// Only one new Button();
return constructorCalls.get(0);
} else {
// e.g. add(new Button("foo"), new Button("bar"));
// throw new IllegalStateException("Unable to modify");
}
return null;
}
protected SimpleName findLocalVariableOrField(CompilationUnit cu,
int componentCreateLineNumber) {
Statement statement = findStatement(cu, componentCreateLineNumber);
if (statement != null && statement.isExpressionStmt()) {
ExpressionStmt expressionStmt = statement.asExpressionStmt();
Expression expression = expressionStmt.getExpression();
if (expression.isVariableDeclarationExpr()) {
VariableDeclarationExpr varDeclaration = expression
.asVariableDeclarationExpr();
VariableDeclarator varDeclarator = varDeclaration
.getVariable(0);
return varDeclarator.getName();
} else if (expression.isAssignExpr()) {
AssignExpr assignExpr = expression.asAssignExpr();
Expression target = assignExpr.getTarget();
if (target.isNameExpr()) {
return target.asNameExpr().getName();
}
}
}
return null;
}
private List addComponent(CompilationUnit cu,
int componentCreateLineNumber, int componentAttachLineNumber,
Where where, ComponentType componentType,
String... constructorArguments) {
List mods = new ArrayList<>();
if (!hasImport(cu, componentType.getClassName())) {
mods.add(addImport(cu, componentType.getClassName()));
}
Statement createStatement = findStatement(cu,
componentCreateLineNumber);
if (createStatement == null || createStatement.isBlockStmt()) {
if (where == Where.INSIDE) {
// Potentially a @Route class
mods.addAll(addComponentToClass(cu, componentCreateLineNumber,
componentType, constructorArguments));
}
return mods;
}
Statement attachStatement = findStatement(cu,
componentAttachLineNumber);
String localVariableName = getVariableName(componentType,
constructorArguments);
localVariableName = findUnusedVariableName(localVariableName,
(BlockStmt) createStatement.getParentNode().get(), null);
ExpressionStmt componentConstructCode = assignToLocalVariable(
componentType, localVariableName,
getConstructorCode(componentType, constructorArguments));
Node componentAttachNode = new NameExpr(localVariableName);
SimpleName referenceLocalVariableOrField = findLocalVariableOrField(cu,
componentCreateLineNumber);
mods.add(Modification.insertLineBefore(attachStatement,
componentConstructCode));
if (referenceLocalVariableOrField == null
&& attachStatement.equals(createStatement)
&& attachStatement.isExpressionStmt()) {
// The reference component is created inline
Expression attachNodeExpression = attachStatement.asExpressionStmt()
.getExpression();
if (attachNodeExpression.isMethodCallExpr()) {
ObjectCreationExpr referenceComponentAdd = findConstructorCallParameter(
attachNodeExpression.asMethodCallExpr(), componentType);
MethodCallExpr methodCallExpr = attachNodeExpression
.asMethodCallExpr();
NodeList args = methodCallExpr.getArguments();
for (int i = 0; i < args.size(); i++) {
if (referenceComponentAdd.equals(args.get(i))) {
if (where == Where.BEFORE) {
mods.add(Modification.insertBefore(args.get(i),
componentAttachNode));
} else {
mods.add(Modification.insertAfter(args.get(i),
componentAttachNode));
}
break;
}
}
return mods;
}
} else if (referenceLocalVariableOrField != null
&& attachStatement.isExpressionStmt()) {
Expression expression = attachStatement.asExpressionStmt()
.getExpression();
if (expression.isMethodCallExpr()) {
// e.g. add(foo, bar, baz)
NodeList args = expression.asMethodCallExpr()
.getArguments();
for (int i = 0; i < args.size(); i++) {
if (!args.get(i).isNameExpr()) {
continue;
}
SimpleName name = args.get(i).asNameExpr().getName();
if (name.equals(referenceLocalVariableOrField)) {
// new ExpressionStmt(new Expression)
// ClassOrInterfaceDeclaration type =
// cu.getClassByName(componentType.getName()).get();
if (where == Where.BEFORE) {
mods.add(Modification.insertBefore(args.get(i),
componentAttachNode));
} else {
mods.add(Modification.insertAfter(args.get(i),
componentAttachNode));
}
break;
}
}
}
}
return mods;
}
private String findUnusedVariableName(String localVariableName,
BlockStmt body, ClassOrInterfaceDeclaration classDefinition) {
Set usedLocalVariables = findLocalVariables(body);
if (classDefinition == null) {
classDefinition = body
.findAncestor(ClassOrInterfaceDeclaration.class).get();
}
Set usedFieldNames = findFieldNames(classDefinition);
String varName = localVariableName;
int i = 2;
while (usedLocalVariables.contains(varName)
|| usedFieldNames.contains(varName)) {
varName = localVariableName + i;
i++;
}
return varName;
}
private Set findFieldNames(
ClassOrInterfaceDeclaration classDefinition) {
Set names = new HashSet<>();
for (FieldDeclaration field : classDefinition.getFields()) {
for (VariableDeclarator varDecl : field.getVariables()) {
names.add(varDecl.getNameAsString());
}
}
return names;
}
private Set findLocalVariables(BlockStmt body) {
Set names = new HashSet<>();
if (body != null) {
for (Statement statement : body.getStatements()) {
if (statement.isExpressionStmt() && statement.asExpressionStmt()
.getExpression().isVariableDeclarationExpr()) {
NodeList vars = statement
.asExpressionStmt().getExpression()
.asVariableDeclarationExpr().getVariables();
for (VariableDeclarator varDecl : vars) {
names.add(varDecl.getNameAsString());
}
}
}
}
return names;
}
private ExpressionStmt assignToLocalVariable(ComponentType componentType,
String variableName, Expression expression) {
VariableDeclarationExpr localVariable = new VariableDeclarationExpr(
new VariableDeclarator(getType(componentType), variableName));
return new ExpressionStmt(
new AssignExpr(localVariable, expression, Operator.ASSIGN));
}
private Modification addImport(CompilationUnit cu, String className) {
return Modification.addImport(cu,
new ImportDeclaration(className, false, false));
}
private boolean hasImport(CompilationUnit cu, String className) {
for (ImportDeclaration importDecl : cu.getImports()) {
if (importDecl.getNameAsString().equals(className)) {
return true;
}
}
return false;
}
private List addComponentToClass(CompilationUnit cu,
int componentCreateLineNumber, ComponentType componentType,
String[] constructorArguments) {
List mods = new ArrayList<>();
ClassOrInterfaceDeclaration classDefinition = findClassDefinition(cu,
componentCreateLineNumber);
ConstructorDeclaration constructor = findConstructorDeclaration(cu,
componentCreateLineNumber);
String variableName = getVariableName(componentType,
constructorArguments);
if (constructor != null) {
variableName = findUnusedVariableName(variableName,
constructor.getBody(), null);
} else if (classDefinition != null) {
variableName = findUnusedVariableName(variableName, null,
classDefinition);
}
ExpressionStmt createComponent = assignToLocalVariable(componentType,
variableName,
getConstructorCode(componentType, constructorArguments));
ExpressionStmt addComponent = addToLayout(variableName);
if (constructor != null) {
mods.add(Modification.insertAtEndOfBlock(constructor.getBody(),
createComponent));
mods.add(Modification.insertAtEndOfBlock(constructor.getBody(),
addComponent));
} else if (classDefinition != null) {
if (!classDefinition.getConstructors().isEmpty()) {
// This should not happen as create location refers to the class
// when this is
// called
return mods;
}
// A class without any constructor
ConstructorDeclaration defaultConstructor = classDefinition
.addConstructor(Keyword.PUBLIC);
defaultConstructor.getBody().addStatement(createComponent);
defaultConstructor.getBody().addStatement(addComponent);
}
return mods;
}
private static String indent(int amount, String string) {
String indent = " ".repeat(amount);
return Pattern.compile("^", Pattern.MULTILINE).matcher(string)
.replaceAll(indent);
}
private List addListener(CompilationUnit cu,
int componentCreateLineNumber, int componentAttachLineNumber,
String listenerType) {
List mods = new ArrayList<>();
Statement createStatement = findStatement(cu,
componentCreateLineNumber);
SimpleName referenceLocalVariableOrField = findLocalVariableOrField(cu,
componentCreateLineNumber);
// ClassOrInterfaceType type =
// StaticJavaParser.parseClassOrInterfaceType("ClickListener");
// VoidType type = new VoidType();
// LambdaExpr emptyCallback = new LambdaExpr(new Parameter(type, "e"),
// new BlockStmt(new NodeList<>()));
Parameter param = new Parameter();
param.setName("e");
LambdaExpr emptyCallback = new LambdaExpr(param,
new BlockStmt(new NodeList<>()));
MethodCallExpr listener = new MethodCallExpr(
new NameExpr(referenceLocalVariableOrField), listenerType,
new NodeList<>(emptyCallback));
// Add an empty row where the code can be written
Node listenerNode = new ExpressionStmt(listener)
.setComment(new LineComment(" TODO: Implement listener"));
Node parent = createStatement.getParentNode().get();
if (parent instanceof BlockStmt) {
// Find last method call for the local variable and add after that
List methodCalls = findMethodCalls(
(BlockStmt) parent, referenceLocalVariableOrField);
if (methodCalls.isEmpty()) {
mods.add(Modification.insertLineAfter(createStatement,
listenerNode));
} else {
mods.add(Modification.insertLineAfter(methodCalls
.get(methodCalls.size() - 1).getParentNode().get(),
listenerNode));
}
} else {
// Add after create. Not sure what the code looks like
mods.add(Modification.insertAfter(createStatement, listenerNode));
}
return mods;
}
private String addNewLineToBody(String body) {
int endOfBody = body.lastIndexOf("}");
return body.substring(0, endOfBody) + "\n" + body.substring(endOfBody);
}
protected List findMethodCalls(BlockStmt parent,
SimpleName variableName) {
List methodCalls = new ArrayList<>();
for (Statement s : parent.getStatements()) {
if (!s.isExpressionStmt()) {
continue;
}
ExpressionStmt expr = s.asExpressionStmt();
if (expr.getExpression().isMethodCallExpr()) {
MethodCallExpr methodCall = expr.getExpression()
.asMethodCallExpr();
if (methodCall.getScope().isPresent()) {
Expression scope = methodCall.getScope().get();
if (scope.isNameExpr() && scope.asNameExpr().getName()
.equals(variableName)) {
methodCalls.add(methodCall);
}
}
}
}
return methodCalls;
}
private ExpressionStmt addToLayout(String variableName) {
return new ExpressionStmt(
new MethodCallExpr("add", new NameExpr(variableName)));
}
private String getVariableName(ComponentType type,
String[] constructorArguments) {
if (constructorArguments.length == 1) {
return SharedUtil.firstToLower(SharedUtil.dashSeparatedToCamelCase(
constructorArguments[0].replaceAll(" ", "-")));
}
String simpleName = type.getClassName();
simpleName = simpleName.substring(simpleName.lastIndexOf('.'));
return simpleName;
}
private ClassOrInterfaceDeclaration findClassDefinition(CompilationUnit cu,
int lineNumber) {
for (TypeDeclaration> type : cu.getTypes()) {
if (contains(type.getName(), lineNumber)) {
return type.asClassOrInterfaceDeclaration();
}
}
return null;
}
private ConstructorDeclaration findConstructorDeclaration(
CompilationUnit cu, int lineNumber) {
for (TypeDeclaration> type : cu.getTypes()) {
if (contains(type, lineNumber)) {
return findConstructorDeclaration(type, lineNumber);
}
}
return null;
}
private ConstructorDeclaration findConstructorDeclaration(
TypeDeclaration> type, int lineNumber) {
for (ConstructorDeclaration constructor : type.getConstructors()) {
if (contains(constructor, lineNumber)) {
return constructor;
}
}
return null;
}
private ObjectCreationExpr getConstructorCode(ComponentType componentType,
String[] constructorArguments) {
ClassOrInterfaceType type = getType(componentType);
List componentConstructorArgs = Arrays
.stream(constructorArguments)
.map(arg -> new StringLiteralExpr(arg))
.collect(Collectors.toList());
return new ObjectCreationExpr(null, type,
new NodeList<>(componentConstructorArgs));
}
private ClassOrInterfaceType getType(ComponentType componentType) {
ClassOrInterfaceType type = StaticJavaParser
.parseClassOrInterfaceType(componentType.getClassName());
type.setScope(null); // Remove package name
return type;
}
private void addOrReplaceCall(BlockStmt codeBlock, Node afterThisNode,
SimpleName variableName, String methodName,
Expression methodArgument, List mods) {
NodeList arguments = new NodeList();
arguments.add(methodArgument);
ExpressionStmt setTextCall = new ExpressionStmt(new MethodCallExpr(
new NameExpr(variableName), methodName, arguments));
ExpressionStmt existingCall = findMethodCall(codeBlock, afterThisNode,
variableName, methodName);
if (existingCall != null) {
Modification mod = Modification.replace(existingCall, setTextCall);
mods.add(mod);
} else {
Modification mod = Modification.insertLineAfter(afterThisNode,
setTextCall);
mods.add(mod);
}
}
private void removeCall(BlockStmt codeBlock, Node afterThisNode,
SimpleName variableName, String methodName,
Expression methodArgument, List mods) {
NodeList arguments = new NodeList();
arguments.add(methodArgument);
ExpressionStmt setTextCall = new ExpressionStmt(new MethodCallExpr(
new NameExpr(variableName), methodName, arguments));
ExpressionStmt existingCall = findMethodCall(codeBlock, afterThisNode,
variableName, methodName);
if (existingCall != null) {
Modification mod = Modification.remove(existingCall);
mods.add(mod);
}
}
private void addCall(Node afterThisNode, SimpleName variableName,
String methodName, Expression methodArgument,
List mods) {
NodeList arguments = new NodeList<>();
arguments.add(methodArgument);
ExpressionStmt setTextCall = new ExpressionStmt(new MethodCallExpr(
new NameExpr(variableName), methodName, arguments));
Modification mod = Modification.insertLineAfter(afterThisNode,
setTextCall);
mods.add(mod);
}
private boolean modifyConstructorCall(ObjectCreationExpr constructorCall,
String methodName, String newText, List mods) {
if (methodName.equals("setText")) {
// Button constructor with a string argument -> replace
StringLiteralExpr param = findConstructorParameter(constructorCall,
ComponentType.BUTTON, 0);
if (param != null) {
Modification mod = Modification.replace(param,
new StringLiteralExpr(newText));
mods.add(mod);
return true;
}
}
if (methodName.equals("setLabel")) {
StringLiteralExpr param = findConstructorParameter(constructorCall,
ComponentType.TEXTFIELD, 0);
if (param != null) {
Modification mod = Modification.replace(param,
new StringLiteralExpr(newText));
mods.add(mod);
return true;
}
}
return false;
}
private StringLiteralExpr findConstructorParameter(
ObjectCreationExpr objectCreationExpression, ComponentType type,
int parameterIndex) {
if (isConstructorFor(objectCreationExpression, type)) {
if (objectCreationExpression.getArguments().size() == 1) {
if (objectCreationExpression.getArguments()
.size() >= parameterIndex - 1) {
Expression param = objectCreationExpression
.getArgument(parameterIndex);
if (param.isStringLiteralExpr()) {
return param.asStringLiteralExpr();
}
}
}
}
return null;
}
private boolean isConstructorFor(
ObjectCreationExpr objectCreationExpression, ComponentType type) {
// FIXME should resolve name
String constructorType = objectCreationExpression.getType().getName()
.asString();
return constructorType.equals(type.getClassName())
|| constructorType.equals(getSimpleName(type));
}
private String getSimpleName(ComponentType type) {
String className = type.getClassName();
return className.substring(className.lastIndexOf('.') + 1);
}
private static int getLinesCount(Node node) {
int nodeLines = node.getRange().map(r -> r.getLineCount())
.orElseGet(() -> node.toString().split("\n").length);
if (node.getComment().isPresent()) {
Comment comment = node.getComment().get();
nodeLines += comment.getRange().map(Range::getLineCount).orElse(0);
}
return nodeLines;
}
protected ExpressionStmt findMethodCall(BlockStmt codeBlock, Node afterThis,
SimpleName leftHandSide, String string) {
boolean refFound = false;
for (Statement s : codeBlock.getStatements()) {
if (s == afterThis) {
refFound = true;
} else if (refFound) {
if (!s.isExpressionStmt()) {
continue;
}
Expression expression = s.asExpressionStmt().getExpression();
if (!expression.isMethodCallExpr()) {
continue;
}
MethodCallExpr methodCallExpr = expression.asMethodCallExpr();
if (!methodCallExpr.getScope().isPresent()) {
continue;
}
Expression scope = methodCallExpr.getScope().get();
if (!scope.isNameExpr()) {
continue;
}
String variableName = scope.asNameExpr().getNameAsString();
String methodName = methodCallExpr.getNameAsString();
if (string.equals(methodName)
&& variableName.equals(leftHandSide.getIdentifier())) {
return s.asExpressionStmt();
}
}
}
return null;
}
protected Statement findStatement(CompilationUnit cu, int lineNumber) {
return cu.accept(new StatementLineNumberVisitor(), lineNumber);
}
private boolean contains(Node node, int lineNumber) {
if (node.getBegin().get().line <= lineNumber
&& node.getEnd().get().line >= lineNumber) {
return true;
}
return false;
}
private static void addStatement(Node referenceNode, Where where,
Statement stmt) {
if (referenceNode.getParentNode()
.orElse(null) instanceof NodeWithStatements nws) {
if (where == null) {
nws.addStatement(stmt);
} else {
int index = nws.getStatements().indexOf(referenceNode)
+ (Where.AFTER.equals(where) ? 1 : 0);
nws.addStatement(index, stmt);
}
}
}
protected String readFile(File file) throws IOException {
try (FileInputStream stream = new FileInputStream(file)) {
return IOUtils.toString(stream, StandardCharsets.UTF_8);
}
}
public int addComponent(File f, int referenceComponentCreateLineNumber,
int referenceComponentAttachLineNumber, Where where,
ComponentType componentType, String... constructorArguments) {
return modifyClass(f,
(cu) -> addComponent(cu, referenceComponentCreateLineNumber,
referenceComponentAttachLineNumber, where,
componentType, constructorArguments));
}
// public void addComponentAfter(Class> cls, int
// componentCreateLineNumber,
// int componentAttachLineNumber, ComponentType componentType,
// String... constructorArguments) {
// addComponentAfter(getSourceFile(cls), componentCreateLineNumber,
// componentAttachLineNumber, componentType, constructorArguments);
// }
// public void addComponentInside(Class> cls,
// int parentcomponentCreateLineNumber,
// int parentComponentAttachLineNumber, ComponentType componentType,
// String... constructorArguments) {
// addComponentInside(getSourceFile(cls), parentcomponentCreateLineNumber,
// parentComponentAttachLineNumber, componentType,
// constructorArguments);
// }
public int addListener(File f, int componentCreateLineNumber,
int componentAttachLineNumber, String listenerType) {
return modifyClass(f, (cu) -> addListener(cu, componentCreateLineNumber,
componentAttachLineNumber, listenerType));
}
public int modifyClass(File f,
Function> modifier) {
try {
String source = readFile(f);
CompilationUnit cu = parseSource(source);
List mods = modifier.apply(cu);
Collections.sort(mods);
int sourceOffset = 0;
for (Modification mod : mods) {
mod.apply();
sourceOffset += mod.sourceOffset();
}
String newSource = LexicalPreservingPrinter.print(cu);
if (newSource.equals(source)) {
throw new UnsupportedOperationException("Unable to edit file");
}
try (FileWriter fw = new FileWriter(f)) {
fw.write(newSource);
}
return sourceOffset;
} catch (IOException e1) {
throw new UnsupportedOperationException(e1);
}
}
public int setComponentAttribute(String className,
int componentCreateLineNumber, int componentAttachLineNumber,
ComponentType componentType, String methodName,
String methodParam) {
return setComponentAttribute(getSourceFile(className),
componentCreateLineNumber, componentAttachLineNumber,
componentType, methodName, methodParam);
}
public int setComponentAttribute(File f, int componentCreateLineNumber,
int componentAttachLineNumber, ComponentType componentType,
String methodName, String methodParam) {
return modifyClass(f,
cu -> modifyOrAddCall(cu, componentCreateLineNumber,
componentAttachLineNumber, componentType, methodName,
methodParam));
}
public int addComponentAttribute(File f, int componentCreateLineNumber,
int componentAttachLineNumber, ComponentType componentType,
String methodName, String methodParam) {
return modifyClass(f,
cu -> addCall(cu, componentCreateLineNumber,
componentAttachLineNumber, componentType, methodName,
methodParam));
}
public int removeComponentAttribute(File f, int componentCreateLineNumber,
int componentAttachLineNumber, ComponentType componentType,
String methodName, String methodParam) {
return modifyClass(f,
cu -> removeCall(cu, componentCreateLineNumber,
componentAttachLineNumber, componentType, methodName,
methodParam));
}
protected CompilationUnit parseSource(String source) {
return LexicalPreservingPrinter.setup(StaticJavaParser.parse(source));
}
public File getSourceFile(Class> cls) {
return getSourceFile(cls.getName());
}
public File getSourceFile(String className) {
String classFileName = className.replace(".", File.separator) + ".java";
File src = new File("").getAbsoluteFile();// VaadinService.getCurrent().getDeploymentConfiguration().getProjectFolder();
src = new File(src, "src");
File f = new File(src, "main");
f = new File(f, "java");
f = new File(f, classFileName);
if (f.exists()) {
return f;
}
f = new File(src, "test");
f = new File(f, "java");
f = new File(f, classFileName);
return f;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy