All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openrewrite.java.internal.template.BlockStatementTemplateGenerator Maven / Gradle / Ivy

There is a newer version: 8.40.2
Show newest version
/*
 * Copyright 2020 the original author or authors.
 * 

* Licensed 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 *

* https://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 org.openrewrite.java.internal.template; import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.Timer; import lombok.AllArgsConstructor; import lombok.RequiredArgsConstructor; import lombok.Value; import lombok.With; import org.jspecify.annotations.Nullable; import org.openrewrite.Cursor; import org.openrewrite.SourceFile; import org.openrewrite.Tree; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.tree.*; import org.openrewrite.marker.Marker; import org.openrewrite.marker.Markers; import java.util.*; import static java.util.Collections.emptyList; import static org.openrewrite.java.tree.JavaCoordinates.Mode.AFTER; import static org.openrewrite.java.tree.JavaCoordinates.Mode.REPLACEMENT; /** * Generates a stub containing enough variable, method, and class scope * for the insertion of a statement in any block scope. */ @RequiredArgsConstructor public class BlockStatementTemplateGenerator { private static final String TEMPLATE_COMMENT = "__TEMPLATE__"; private static final String STOP_COMMENT = "__TEMPLATE_STOP__"; protected static final String TEMPLATE_INTERNAL_IMPORTS = "import org.openrewrite.java.internal.template.__M__;\nimport org.openrewrite.java.internal.template.__P__;\n"; protected final Set imports; private final boolean contextSensitive; public String template(Cursor cursor, String template, Space.Location location, JavaCoordinates.Mode mode) { //noinspection ConstantConditions return Timer.builder("rewrite.template.generate.statement") .register(Metrics.globalRegistry) .record(() -> { StringBuilder before = new StringBuilder(); StringBuilder after = new StringBuilder(); // for CoordinateBuilder.MethodDeclaration#replaceBody() if (cursor.getValue() instanceof J.MethodDeclaration && location.equals(Space.Location.BLOCK_PREFIX)) { J.MethodDeclaration method = cursor.getValue(); J.MethodDeclaration m = method.withBody(null).withLeadingAnnotations(emptyList()).withPrefix(Space.EMPTY); before.insert(0, m.printTrimmed(cursor.getParentOrThrow()).trim() + '{'); after.append('}'); } if (contextSensitive) { contextTemplate(next(cursor), cursor.getValue(), before, after, cursor.getValue(), mode); } else { contextFreeTemplate(next(cursor), cursor.getValue(), before, after); } return before.toString().trim() + "\n/*" + TEMPLATE_COMMENT + "*/" + template + "/*" + STOP_COMMENT + "*/\n" + after.toString().trim(); }); } public List listTemplatedTrees(JavaSourceFile cu, Class expected) { List js = new ArrayList<>(); new JavaIsoVisitor() { boolean done = false; J.@Nullable Block blockEnclosingTemplateComment; @Override public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Integer integer) { if (getCursor().getParentTreeCursor().getValue() instanceof SourceFile && (classDecl.getSimpleName().equals("__P__") || classDecl.getSimpleName().equals("__M__"))) { // don't visit the __P__ and __M__ classes declaring stubs return classDecl; } return super.visitClassDeclaration(classDecl, integer); } @Override public J.Block visitBlock(J.Block block, Integer p) { J.Block b = super.visitBlock(block, p); if (b == blockEnclosingTemplateComment) { done = true; } return b; } @Override public JRightPadded visitRightPadded(@Nullable JRightPadded right, JRightPadded.Location loc, Integer integer) { right = super.visitRightPadded(right, loc, integer); //noinspection ConstantValue if (right != null) { for (Comment comment : right.getAfter().getComments()) { if (isTemplateStopComment(comment)) { done = true; break; } } } return right; } @Override public JLeftPadded visitLeftPadded(@Nullable JLeftPadded left, JLeftPadded.Location loc, Integer integer) { left = super.visitLeftPadded(left, loc, integer); //noinspection ConstantValue if (left != null) { for (Comment comment : left.getBefore().getComments()) { if (isTemplateStopComment(comment)) { done = true; break; } } } return left; } @Override public @Nullable J visit(@Nullable Tree tree, Integer p) { if (done) { return (J) tree; } if (expected.isInstance(tree)) { @SuppressWarnings("unchecked") J2 t = (J2) tree; if (blockEnclosingTemplateComment != null) { for (Comment comment : t.getComments()) { if (isTemplateStopComment(comment)) { done = true; break; } } //noinspection unchecked J2 trimmed = done ? null : (J2) TemplatedTreeTrimmer.trimTree(t); if (trimmed != null) { js.add(trimmed); } return t; } List comments = t.getPrefix().getComments(); for (int i = 0; i < comments.size(); i++) { Comment comment = comments.get(i); if (comment instanceof TextComment && ((TextComment) comment).getText().equals(TEMPLATE_COMMENT)) { blockEnclosingTemplateComment = getCursor().firstEnclosing(J.Block.class); //noinspection unchecked J2 trimmed = (J2) TemplatedTreeTrimmer.trimTree(t); if (t != trimmed) { done = true; } if (trimmed != null) { js.add(trimmed.withPrefix(trimmed.getPrefix().withComments(comments.subList(i + 1, comments.size())))); } return t; } } } // Catch any trees having a STOP_COMMENT that are not an instance of `expected` else if (tree != null && !js.isEmpty()) { //noinspection unchecked J2 trimmed = (J2) TemplatedTreeTrimmer.trimTree((J) tree); if (trimmed != tree) { done = true; } } return super.visit(tree, p); } private boolean isTemplateStopComment(Comment comment) { return comment instanceof TextComment && ((TextComment) comment).getText().equals(STOP_COMMENT); } }.visit(cu, 0); return js; } @SuppressWarnings("DataFlowIssue") protected void contextFreeTemplate(Cursor cursor, J j, StringBuilder before, StringBuilder after) { if (j instanceof J.Lambda) { throw new IllegalArgumentException( "Templating a lambda requires a cursor so that it can be properly parsed and type-attributed. " + "Mark this template as context-sensitive by calling JavaTemplate.Builder#contextSensitive()."); } else if (j instanceof J.MemberReference) { throw new IllegalArgumentException( "Templating a method reference requires a cursor so that it can be properly parsed and type-attributed. " + "Mark this template as context-sensitive by calling JavaTemplate.Builder#contextSensitive()."); } else if (j instanceof J.MethodInvocation) { before.insert(0, "class Template {{\n"); JavaType.Method methodType = ((J.MethodInvocation) j).getMethodType(); if (methodType == null || methodType.getReturnType() != JavaType.Primitive.Void) { before.append("Object o = "); } after.append(";\n}}"); } else if (j instanceof Expression && !(j instanceof J.Assignment)) { before.insert(0, "class Template {\n"); before.append("Object o = "); after.append(";\n}"); } else if ((j instanceof J.MethodDeclaration || j instanceof J.VariableDeclarations || j instanceof J.Block || j instanceof J.ClassDeclaration) && cursor.getValue() instanceof J.Block && (cursor.getParent().getValue() instanceof J.ClassDeclaration || cursor.getParent().getValue() instanceof J.NewClass)) { before.insert(0, "class Template {\n"); after.append("\n}"); } else if (j instanceof J.ClassDeclaration) { // While not impossible to handle, reaching this point is likely to be a mistake. // Without context a class declaration can include no imports, package, or outer class. // It is a rare class that is deliberately in the root package with no imports. // In the more likely case omission of these things is unintentional, the resulting type metadata would be // incorrect, and it would not be obvious to the recipe author why. throw new IllegalArgumentException( "Templating a class declaration requires context from which package declaration and imports may be reached. " + "Mark this template as context-sensitive by calling JavaTemplate.Builder#contextSensitive()."); } else if (j instanceof Statement && !(j instanceof J.Import) && !(j instanceof J.Package)) { before.insert(0, "class Template {{\n"); after.append("\n}}"); } before.insert(0, TEMPLATE_INTERNAL_IMPORTS); for (String anImport : imports) { before.insert(0, anImport); } } @SuppressWarnings("ConstantConditions") private void contextTemplate(Cursor cursor, J prior, StringBuilder before, StringBuilder after, J insertionPoint, JavaCoordinates.Mode mode) { J j = cursor.getValue(); if (j instanceof JavaSourceFile) { before.insert(0, TEMPLATE_INTERNAL_IMPORTS); JavaSourceFile cu = (JavaSourceFile) j; for (J.Import anImport : cu.getImports()) { before.insert(0, anImport.withPrefix(Space.EMPTY).printTrimmed(cursor) + ";\n"); } for (String anImport : imports) { before.insert(0, anImport); } if (cu.getPackageDeclaration() != null) { before.insert(0, cu.getPackageDeclaration().withPrefix(Space.EMPTY).printTrimmed(cursor) + ";\n"); } return; } else if (j instanceof J.Block) { J parent = next(cursor).getValue(); if (parent instanceof J.ClassDeclaration) { J.ClassDeclaration c = (J.ClassDeclaration) parent; classDeclaration(prior, c, before, after, cursor, mode); } else if (parent instanceof J.MethodDeclaration) { J.MethodDeclaration m = (J.MethodDeclaration) parent; // variable declarations up to the point of insertion addLeadingVariableDeclarations(cursor, prior, m.getBody(), before, insertionPoint); if (m.getReturnTypeExpression() != null && !JavaType.Primitive.Void .equals(m.getReturnTypeExpression().getType())) { before.insert(0, "if(true) {"); after.append("}\nreturn ") .append(valueOfType(m.getReturnTypeExpression().getType())) .append(";\n"); } before.insert(0, m.withBody(null) .withLeadingAnnotations(emptyList()) .withPrefix(Space.EMPTY) .printTrimmed(cursor).trim() + '{'); } else { J.Block b = (J.Block) j; // variable declarations up to the point of insertion addLeadingVariableDeclarations(cursor, prior, b, before, insertionPoint); before.insert(0, "{\n"); } if (prior == insertionPoint && prior instanceof Expression) { // the template represents an expression, so we need to wrap it in a statement after.append(';'); } after.append('}'); } else if (j instanceof J.Annotation) { J.Annotation annotation = (J.Annotation) j; Optional arg = annotation.getArguments().stream().filter(a -> a == prior).findFirst(); if (arg.isPresent()) { StringBuilder beforeBuffer = new StringBuilder(); beforeBuffer.append('@').append(((JavaType.Class) annotation.getType()).getFullyQualifiedName()).append('('); before.insert(0, beforeBuffer); after.append(')').append('\n'); J parent = next(cursor).getValue(); if (parent instanceof J.ClassDeclaration) { J.ClassDeclaration cd = (J.ClassDeclaration) parent; after.append(cd.withBody(null).withLeadingAnnotations(null).withPrefix(Space.EMPTY).printTrimmed(cursor).trim()).append("{}"); } else if (parent instanceof J.MethodDeclaration) { J.MethodDeclaration md = (J.MethodDeclaration) parent; after.append(md.withBody(null) .withLeadingAnnotations(emptyList()) .withPrefix(Space.EMPTY) .printTrimmed(cursor).trim()).append("{}"); } // TODO cover more cases where annotations can appear, ideally not by "inlining" template for parent } } else if (j instanceof J.DoWhileLoop) { J.DoWhileLoop dw = (J.DoWhileLoop) j; if (referToSameElement(prior, dw.getWhileCondition())) { before.insert(0, "Object __b" + cursor.getPathAsStream().count() + "__ ="); after.append(";"); } } else if (j instanceof J.Assert) { before.insert(0, "assert "); after.append(';'); } else if (j instanceof J.NewArray) { J.NewArray n = (J.NewArray) j; if (n.getInitializer() != null && n.getInitializer().stream().anyMatch(arg -> referToSameElement(prior, arg))) { before.insert(0, n.withInitializer(null).printTrimmed(cursor) + "{\n"); after.append("\n}"); } else { // no initializer before.insert(0, "__M__.any("); after.append(");"); } } else if (j instanceof J.NewClass) { J.NewClass n = (J.NewClass) j; String newClassString; JavaType.Class constructorTypeClass = n.getConstructorType() != null ? TypeUtils.asClass(n.getConstructorType().getReturnType()) : null; boolean isEnum = constructorTypeClass != null && JavaType.FullyQualified.Kind.Enum == constructorTypeClass.getKind(); if (n.getClazz() != null) { newClassString = "new " + n.getClazz().printTrimmed(cursor); } else if (constructorTypeClass != null) { // enum definitions with anonymous class initializers or a J.NewClass with a null clazz and valid constructor type. newClassString = isEnum ? "" : "new " + constructorTypeClass.getFullyQualifiedName(); } else { throw new IllegalStateException("Unable to template a J.NewClass instance having a null clazz and constructor type."); } if (n.getArguments().stream().anyMatch(arg -> referToSameElement(prior, arg))) { StringBuilder beforeSegments = new StringBuilder(); StringBuilder afterSegments = new StringBuilder(); beforeSegments.append(newClassString).append("("); boolean priorFound = false; for (Expression arg : n.getArguments()) { if (!priorFound) { if (referToSameElement(prior, arg)) { priorFound = true; continue; } beforeSegments.append(valueOfType(arg.getType())).append(","); } else { afterSegments.append(",/*" + STOP_COMMENT + "*/").append(valueOfType(arg.getType())); } } afterSegments.append(")"); if (priorFound && !afterSegments.toString().contains(STOP_COMMENT)) { if (isEnum) { afterSegments.append(";"); } afterSegments.append("/*" + STOP_COMMENT + "*/"); } before.insert(0, beforeSegments); after.append(afterSegments); if (next(cursor).getValue() instanceof J.Block) { after.append(";"); } } else { n = n.withBody(null).withPrefix(Space.EMPTY); before.insert(0, n.printTrimmed(cursor.getParentOrThrow()).trim()); if (!(next(cursor).getValue() instanceof MethodCall)) { after.append(';'); } } } else if (j instanceof J.ForLoop.Control) { J.ForLoop.Control c = (J.ForLoop.Control) j; if (referToSameElement(prior, c.getCondition())) { before.insert(0, "for (" + c.getInit().get(0).printTrimmed(cursor).trim() + ";"); after.append(";) {}"); } } else if (j instanceof J.ForLoop) { J.ForLoop f = (J.ForLoop) j; if (referToSameElement(prior, f.getBody())) { insertControlWithBlock(f.getBody(), before, after, () -> before.insert(0, f.withBody(null).withPrefix(Space.EMPTY) .withControl(f.getControl().withCondition(null).withUpdate(emptyList())) .printTrimmed(cursor).trim())); } } else if (j instanceof J.ForEachLoop.Control) { J.ForEachLoop.Control c = (J.ForEachLoop.Control) j; if (referToSameElement(prior, c.getVariable())) { after.append(" = /*" + STOP_COMMENT + "/*").append(c.getIterable().printTrimmed(cursor)); } else if (referToSameElement(prior, c.getIterable())) { before.insert(0, "Object __b" + cursor.getPathAsStream().count() + "__ ="); after.append(";"); } } else if (j instanceof J.ForEachLoop) { J.ForEachLoop f = (J.ForEachLoop) j; if (!referToSameElement(prior, f.getControl())) { insertControlWithBlock(f.getBody(), before, after, () -> before.insert(0, f.withBody(null).withPrefix(Space.EMPTY).printTrimmed(cursor).trim())); } } else if (j instanceof J.Try) { J.Try t = (J.Try) j; if (t.getResources() != null && referToSameElement(prior, t.getBody())) { StringJoiner joiner = new StringJoiner("; ", "try (", ")"); for (J.Try.Resource resource : t.getResources()) { joiner.add(resource.withPrefix(Space.EMPTY).printTrimmed(cursor).trim()); } before.insert(0, joiner); } } else if (j instanceof J.Lambda) { J.Lambda l = (J.Lambda) j; if (l.getBody() instanceof Expression) { before.insert(0, "return "); after.append(";"); } before.insert(0, l.withBody(null).withPrefix(Space.EMPTY).printTrimmed(cursor.getParentOrThrow()).trim() + "{ if(true) {"); after.append("}\n"); JavaType.Method mt = findSingleAbstractMethod(l.getType()); if (mt == null) { // Missing type information, but usually the Java compiler can soldier on anyway after.append("return null;\n"); } else if (mt.getReturnType() != JavaType.Primitive.Void) { after.append("return ").append(valueOfType(mt.getReturnType())).append(";\n"); } after.append("}"); if (next(cursor).getValue() instanceof J.Block) { after.append(";"); } } else if (j instanceof J.VariableDeclarations) { if (prior instanceof J.Annotation) { after.append(variable((J.VariableDeclarations) j, false, cursor)) .append('=') .append(valueOfType(((J.VariableDeclarations) j).getType())); } else { before.insert(0, variable((J.VariableDeclarations) j, false, cursor) + '='); } after.append(";"); } else if (j instanceof J.MethodInvocation) { // If prior is an argument, wrap in __M__.any(prior) // If prior is a type parameter, wrap in __M__.anyT() // For anything else, ignore the invocation J.MethodInvocation m = (J.MethodInvocation) j; if (m.getArguments().stream().anyMatch(arg -> referToSameElement(prior, arg))) { before.insert(0, "__M__.any("); if (cursor.getParentOrThrow().firstEnclosing(J.class) instanceof J.Block) { after.append(");"); } else { after.append(")"); } } else if (m.getTypeParameters() != null && m.getTypeParameters().stream().anyMatch(tp -> referToSameElement(prior, tp))) { before.insert(0, "__M__.anyT<"); if (cursor.getParentOrThrow().firstEnclosing(J.class) instanceof J.Block) { after.append(">();"); } else { after.append(">()"); } } else if (m.getSelect() == prior) { List comments = new ArrayList<>(1); comments.add(new TextComment(true, STOP_COMMENT, "", Markers.EMPTY)); after.append(".").append(m.withSelect(null).withComments(comments).printTrimmed(cursor.getParentOrThrow())); if (cursor.getParentOrThrow().firstEnclosing(J.class) instanceof J.Block) { after.append(";"); } } } else if (j instanceof J.Return) { before.insert(0, "return "); after.append(";"); } else if (j instanceof J.Throw) { before.insert(0, "throw "); after.append(";"); } else if (j instanceof J.Parentheses) { before.insert(0, '('); after.append(')'); } else if (j instanceof J.If) { J.If iff = (J.If) j; if (referToSameElement(prior, iff.getIfCondition())) { String condition = PatternVariables.simplifiedPatternVariableCondition(iff.getIfCondition().getTree(), insertionPoint); int toReplaceIdx; if (condition != null && (toReplaceIdx = condition.indexOf('§')) != -1) { before.insert(0, "if (" + condition.substring(0, toReplaceIdx) + '('); after.append(')').append(condition.substring(toReplaceIdx + 1)).append(") {}"); } else { before.insert(0, "Object __b" + cursor.getPathAsStream().count() + "__ ="); after.append(";"); } } else { String condition = PatternVariables.simplifiedPatternVariableCondition(iff.getIfCondition().getTree(), insertionPoint); if (condition != null) { if (referToSameElement(prior, iff.getThenPart())) { insertControlWithBlock(iff.getThenPart(), before, after, () -> before.insert(0, "if (" + condition + ") ")); } else if (referToSameElement(prior, iff.getElsePart())) { insertControlWithBlock(iff.getElsePart().getBody(), before, after, () -> before.insert(0, "if (" + condition + ") {} else ")); } } } } else if (j instanceof J.Ternary) { J.Ternary ternary = (J.Ternary) j; String condition = PatternVariables.simplifiedPatternVariableCondition(ternary.getCondition(), insertionPoint); if (condition != null) { if (referToSameElement(prior, ternary.getCondition())) { int splitIdx = condition.indexOf('§'); before.insert(0, condition.substring(0, splitIdx) + '('); after.append(')').append(condition.substring(splitIdx + 1)) .append(" ? ").append(ternary.getTruePart().printTrimmed(cursor).trim()) .append(" : ").append(ternary.getFalsePart().printTrimmed(cursor).trim()); } else if (referToSameElement(prior, ternary.getTruePart())) { before.insert(0, (condition == null ? "true" : condition) + " ? "); after.append(" : ").append(ternary.getFalsePart().printTrimmed(cursor).trim()); } else if (referToSameElement(prior, ternary.getFalsePart())) { before.insert(0, (condition == null ? "true" : condition) + " ? " + ternary.getTruePart().printTrimmed(cursor).trim() + " : "); } } } else if (j instanceof J.WhileLoop) { J.WhileLoop wl = (J.WhileLoop) j; if (referToSameElement(prior, wl.getCondition())) { before.insert(0, "Object __b" + cursor.getPathAsStream().count() + "__ ="); after.append(";"); } } else if (j instanceof J.Assignment) { J.Assignment as = (J.Assignment) j; if (referToSameElement(prior, as.getAssignment())) { before.insert(0, as.getVariable() + " = "); J parent = next(cursor).getValue(); if (!(parent instanceof J.Annotation)) { after.append(";"); } } } else if (j instanceof J.AssignmentOperation) { J.AssignmentOperation as = (J.AssignmentOperation) j; if (referToSameElement(prior, as.getAssignment())) { before.insert(0, "Object __b" + cursor.getPathAsStream().count() + "__ = "); after.append(";"); } } else if (j instanceof J.EnumValue) { J.EnumValue ev = (J.EnumValue) j; before.insert(0, ev.getName()); } else if (j instanceof J.EnumValueSet) { after.append(";"); } contextTemplate(next(cursor), j, before, after, insertionPoint, REPLACEMENT); } private void addLeadingVariableDeclarations(Cursor cursor, J current, J.Block containingBlock, StringBuilder before, J insertionPoint) { for (Statement statement : containingBlock.getStatements()) { if (referToSameElement(current, statement)) { break; } if (statement instanceof J.Label) { statement = ((J.Label) statement).getStatement(); } if (statement instanceof J.VariableDeclarations) { before.insert(0, "\n" + variable((J.VariableDeclarations) statement, true, cursor) + ";\n"); } else if (statement instanceof J.If) { J.If iff = (J.If) statement; String condition = PatternVariables.simplifiedPatternVariableCondition(iff.getIfCondition().getTree(), insertionPoint); if (condition != null) { boolean thenNeverCompletesNormally = PatternVariables.neverCompletesNormally(iff.getThenPart()); boolean elseNeverCompletesNormally = iff.getElsePart() != null && PatternVariables.neverCompletesNormally(iff.getElsePart().getBody()); if (thenNeverCompletesNormally || elseNeverCompletesNormally) { StringBuilder ifStatement = new StringBuilder("if (").append(condition).append(") {"); ifStatement.append(thenNeverCompletesNormally ? " throw new RuntimeException(); }" : " }"); ifStatement.append(elseNeverCompletesNormally ? " else { throw new RuntimeException(); }" : " else { }"); before.insert(0, ifStatement); } } } } } private void insertControlWithBlock(J body, StringBuilder before, StringBuilder after, Runnable insertion) { if (!(body instanceof J.Block)) { before.insert(0, "{"); } insertion.run(); if (!(body instanceof J.Block)) { after.append("}"); } } private void classDeclaration(@Nullable J prior, J.ClassDeclaration cd, StringBuilder before, StringBuilder after, Cursor cursor, JavaCoordinates.Mode mode) { StringBuilder beforeBuffer = prior == null ? null : new StringBuilder(); StringBuilder appendBuffer = prior == null ? after : beforeBuffer; appendBuffer.append(cd.withBody(null).withLeadingAnnotations(null).withPrefix(Space.EMPTY).printTrimmed(cursor).trim()).append('{'); List statements = cd.getBody().getStatements(); for (Statement statement : statements) { if (referToSameElement(statement, prior)) { if (mode != AFTER) { appendBuffer = after; if (mode == REPLACEMENT) { continue; } } } if (statement instanceof J.EnumValueSet) { J.EnumValueSet enumValues = (J.EnumValueSet) statements.get(0); StringJoiner enumStr = new StringJoiner(","); for (J.EnumValue anEnum : enumValues.getEnums()) { String en = anEnum.getName().getSimpleName(); enumStr.add(en); } appendBuffer.append(enumStr).append(";\n"); } else if (statement instanceof J.VariableDeclarations) { String variable = variable((J.VariableDeclarations) statement, false, cursor); appendBuffer.append(variable).append(";\n"); } else if (statement instanceof J.MethodDeclaration) { String m = method((J.MethodDeclaration) statement, cursor); appendBuffer.append(m); } else if (statement instanceof J.ClassDeclaration) { // this is a sibling class. we need declarations for all variables and methods. // setting prior to null will cause them all to be written. classDeclaration(null, (J.ClassDeclaration) statement, before, appendBuffer, cursor, REPLACEMENT); appendBuffer.append('}'); } } if (beforeBuffer != null) { before.insert(0, beforeBuffer); } } private String method(J.MethodDeclaration method, Cursor cursor) { if (method.isAbstract()) { return "\n" + method.withPrefix(Space.EMPTY).printTrimmed(cursor).trim() + ";\n"; } StringBuilder methodBuilder = new StringBuilder("\n"); J.MethodDeclaration m = method.withBody(null).withLeadingAnnotations(emptyList()).withPrefix(Space.EMPTY); methodBuilder.append(m.printTrimmed(cursor).trim()).append('{'); if (method.getReturnTypeExpression() != null && !JavaType.Primitive.Void.equals(method.getReturnTypeExpression().getType())) { methodBuilder.append("\nreturn ") .append(valueOfType(method.getReturnTypeExpression().getType())) .append(";\n"); } methodBuilder.append("}\n"); return methodBuilder.toString(); } private String variable(J.VariableDeclarations variable, boolean initializer, Cursor cursor) { StringBuilder varBuilder = new StringBuilder(); for (J.Modifier modifier : variable.getModifiers()) { varBuilder.append(modifier.getType().toString().toLowerCase()).append(' '); } List variables = variable.getVariables(); for (int i = 0, variablesSize = variables.size(); i < variablesSize; i++) { J.VariableDeclarations.NamedVariable nv = variables.get(i); if (i == 0) { if (variable.getTypeExpression() != null) { varBuilder.append(variable.getTypeExpression().withPrefix(Space.EMPTY).printTrimmed(cursor)); } if (nv.getType() instanceof JavaType.Array) { if (nv.getInitializer() instanceof J.NewArray && !((J.NewArray) nv.getInitializer()).getDimensions().isEmpty()) { J.NewArray na = (J.NewArray) nv.getInitializer(); na.getDimensions().forEach(d -> varBuilder.append("[]")); } else { varBuilder.append("[]"); } } varBuilder.append(" "); } varBuilder.append(nv.getSimpleName()); JavaType type = nv.getType(); if (initializer && type != null) { varBuilder.append('=').append(valueOfType(type)); } if (i < variables.size() - 1) { varBuilder.append(','); } } return varBuilder.toString(); } private String valueOfType(@Nullable JavaType type) { JavaType.Primitive primitive = TypeUtils.asPrimitive(type); if (primitive != null) { switch (primitive) { case Boolean: return "true"; case Byte: case Char: case Int: case Double: case Float: case Long: case Short: return "0"; case String: case Null: return "null"; case None: case Void: default: return ""; } } return "null"; } private Cursor next(Cursor c) { return c.getParentTreeCursor(); } private static boolean referToSameElement(@Nullable Tree t1, @Nullable Tree t2) { return t1 == t2 || (t1 != null && t2 != null && t1.getId().equals(t2.getId())); } /** * Accepts a @FunctionalInterface and returns the single abstract method from it, or null if the single abstract * method cannot be found */ private static JavaType.@Nullable Method findSingleAbstractMethod(@Nullable JavaType javaType) { if (javaType == null) { return null; } JavaType.FullyQualified fq = TypeUtils.asFullyQualified(javaType); if (fq == null) { return null; } return fq.getMethods().stream().filter(method -> method.hasFlags(Flag.Abstract)).findAny().orElse(null); } // Visitor for removing any trees having or following the `STOP_COMMENT` private static class TemplatedTreeTrimmer { static @Nullable J trimTree(J j) { J trimmed = new TemplatedTreeTrimmerVisitor().visit(j, 0); if (trimmed == null || trimmed.getMarkers().findFirst(RemoveTreeMarker.class).isPresent()) { return null; } return trimmed; } @Value @AllArgsConstructor private static class RemoveTreeMarker implements Marker { @With UUID id; } private static class TemplatedTreeTrimmerVisitor extends JavaVisitor { private boolean stopCommentExists(@Nullable J j) { return j != null && stopCommentExists(j.getComments()); } private static boolean stopCommentExists(List comments) { for (Comment comment : comments) { if (comment instanceof TextComment && ((TextComment) comment).getText().equals(STOP_COMMENT)) { return true; } } return false; } @Override public @Nullable J visit(@Nullable Tree tree, Integer integer) { J j = super.visit(tree, integer); if (stopCommentExists(j)) { Cursor parent = getCursor().getParent(); if (parent == null || !(parent.getValue() instanceof J.MethodInvocation)) { return j.withMarkers(j.getMarkers().addIfAbsent(new RemoveTreeMarker(Tree.randomId()))); } } return j; } @Override public J visitMethodInvocation(J.MethodInvocation method, Integer integer) { J.MethodInvocation mi = (J.MethodInvocation) super.visitMethodInvocation(method, integer); if (stopCommentExists(mi.getName())) { //noinspection ConstantConditions return mi.getSelect(); } if (method.getTypeParameters() != null) { // For method chains return `select` if `STOP_COMMENT` is found before `typeParameters` if (stopCommentExists(mi.getPadding().getTypeParameters().getBefore().getComments())) { //noinspection ConstantConditions return mi.getSelect(); } } return mi; } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy