org.openrewrite.groovy.tree.G Maven / Gradle / Ivy
Show all versions of rewrite-groovy Show documentation
/*
* Copyright 2021 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.groovy.tree;
import lombok.*;
import lombok.experimental.FieldDefaults;
import lombok.experimental.NonFinal;
import org.jspecify.annotations.Nullable;
import org.openrewrite.*;
import org.openrewrite.groovy.GroovyPrinter;
import org.openrewrite.groovy.GroovyVisitor;
import org.openrewrite.groovy.internal.GroovyWhitespaceValidationService;
import org.openrewrite.groovy.service.GroovyAutoFormatService;
import org.openrewrite.internal.WhitespaceValidationService;
import org.openrewrite.java.internal.TypesInUse;
import org.openrewrite.java.service.AutoFormatService;
import org.openrewrite.java.tree.*;
import org.openrewrite.marker.Markers;
import java.beans.Transient;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
public interface G extends J {
@SuppressWarnings("unchecked")
@Override
default R accept(TreeVisitor v, P p) {
return (R) acceptGroovy(v.adapt(GroovyVisitor.class), p);
}
@Override
default boolean isAcceptable(TreeVisitor, P> v, P p) {
return v.isAdaptableTo(GroovyVisitor.class);
}
default
@Nullable J acceptGroovy(GroovyVisitor
v, P p) {
return v.defaultValue(this, p);
}
@Override
Space getPrefix();
@Override
default List getComments() {
return getPrefix().getComments();
}
@ToString
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true)
@RequiredArgsConstructor
@AllArgsConstructor(access = AccessLevel.PRIVATE)
final class CompilationUnit implements G, JavaSourceFile, SourceFile {
@Nullable
@NonFinal
transient SoftReference typesInUse;
@Nullable
@NonFinal
transient WeakReference padding;
@EqualsAndHashCode.Include
@With
@Getter
UUID id;
@With
@Getter
@Nullable
String shebang;
@With
@Getter
Space prefix;
@With
@Getter
Markers markers;
@With
@Getter
Path sourcePath;
@With
@Getter
@Nullable
FileAttributes fileAttributes;
@Nullable // for backwards compatibility
@With(AccessLevel.PRIVATE)
String charsetName;
@With
@Getter
boolean charsetBomMarked;
@With
@Getter
@Nullable
Checksum checksum;
@Override
public Charset getCharset() {
return charsetName == null ? StandardCharsets.UTF_8 : Charset.forName(charsetName);
}
@SuppressWarnings("unchecked")
@Override
public SourceFile withCharset(Charset charset) {
return withCharsetName(charset.name());
}
@Nullable
JRightPadded packageDeclaration;
@Override
public @Nullable Package getPackageDeclaration() {
return packageDeclaration == null ? null : packageDeclaration.getElement();
}
@Override
public G.CompilationUnit withPackageDeclaration(Package packageDeclaration) {
return getPadding().withPackageDeclaration(JRightPadded.withElement(this.packageDeclaration, packageDeclaration));
}
@SuppressWarnings("unchecked")
@Override
public T service(Class service) {
String serviceName = service.getName();
try {
Class serviceClass;
if (GroovyAutoFormatService.class.getName().equals(serviceName)) {
serviceClass = service;
} else if (AutoFormatService.class.getName().equals(serviceName)) {
serviceClass = (Class) service.getClassLoader().loadClass(GroovyAutoFormatService.class.getName());
} else if (WhitespaceValidationService.class.getName().equals(serviceName)) {
serviceClass = (Class) service.getClassLoader().loadClass(GroovyWhitespaceValidationService.class.getName());
} else {
return JavaSourceFile.super.service(service);
}
return (T) serviceClass.getConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
List> statements;
public List getStatements() {
return JRightPadded.getElements(statements);
}
public G.CompilationUnit withStatements(List statements) {
return getPadding().withStatements(JRightPadded.withElements(this.statements, statements));
}
@With
@Getter
Space eof;
@Override
@Transient
public List getImports() {
return statements.stream()
.map(JRightPadded::getElement)
.filter(J.Import.class::isInstance)
.map(J.Import.class::cast)
.collect(Collectors.toList());
}
@Override
public G.CompilationUnit withImports(List imports) {
return getPadding().withImports(JRightPadded.withElements(this.getPadding().getImports(), imports));
}
@Override
@Transient
public List getClasses() {
return statements.stream()
.map(JRightPadded::getElement)
.filter(J.ClassDeclaration.class::isInstance)
.map(J.ClassDeclaration.class::cast)
.collect(Collectors.toList());
}
@Override
public JavaSourceFile withClasses(List classes) {
return getPadding().withClasses(JRightPadded.withElements(this.getPadding().getClasses(), classes));
}
@Override
public J acceptGroovy(GroovyVisitor
v, P p) {
return v.visitCompilationUnit(this, p);
}
@Override
public
TreeVisitor, PrintOutputCapture
> printer(Cursor cursor) {
return new GroovyPrinter<>();
}
@Override
@Transient
public TypesInUse getTypesInUse() {
TypesInUse cache;
if (this.typesInUse == null) {
cache = TypesInUse.build(this);
this.typesInUse = new SoftReference<>(cache);
} else {
cache = this.typesInUse.get();
if (cache == null || cache.getCu() != this) {
cache = TypesInUse.build(this);
this.typesInUse = new SoftReference<>(cache);
}
}
return cache;
}
@Override
public Padding getPadding() {
Padding p;
if (this.padding == null) {
p = new Padding(this);
this.padding = new WeakReference<>(p);
} else {
p = this.padding.get();
if (p == null || p.t != this) {
p = new Padding(this);
this.padding = new WeakReference<>(p);
}
}
return p;
}
@RequiredArgsConstructor
public static class Padding implements JavaSourceFile.Padding {
private final G.CompilationUnit t;
public @Nullable JRightPadded getPackageDeclaration() {
return t.packageDeclaration;
}
public G.CompilationUnit withPackageDeclaration(@Nullable JRightPadded packageDeclaration) {
return t.packageDeclaration == packageDeclaration ? t :
new G.CompilationUnit(t.id, t.shebang, t.prefix, t.markers, t.sourcePath, t.fileAttributes,
t.charsetName, t.charsetBomMarked, t.checksum, packageDeclaration, t.statements, t.eof);
}
@Transient
public List> getClasses() {
//noinspection unchecked
return t.statements.stream()
.filter(s -> s.getElement() instanceof J.ClassDeclaration)
.map(s -> (JRightPadded) (Object) s)
.collect(Collectors.toList());
}
public G.CompilationUnit withClasses(List> classes) {
List> statements = t.statements.stream()
.filter(s -> !(s.getElement() instanceof J.ClassDeclaration))
.collect(Collectors.toList());
int insertionIdx = 0;
for (JRightPadded statement : statements) {
if (!(statement.getElement() instanceof J.Import)) {
break;
}
insertionIdx++;
}
//noinspection unchecked
statements.addAll(insertionIdx, classes.stream()
.map(i -> (JRightPadded) (Object) i)
.collect(Collectors.toList()));
List> originalClasses = t.getPadding().getClasses();
if (originalClasses.size() != classes.size()) {
return new G.CompilationUnit(t.id, t.shebang, t.prefix, t.markers, t.sourcePath, t.fileAttributes, t.charsetName, t.charsetBomMarked, t.checksum, t.packageDeclaration, statements, t.eof);
} else {
boolean hasChanges = false;
for (int i = 0; i < originalClasses.size(); i++) {
if (originalClasses.get(i) != classes.get(i)) {
hasChanges = true;
break;
}
}
return !hasChanges ? t : new G.CompilationUnit(t.id, t.shebang, t.prefix, t.markers, t.sourcePath, t.fileAttributes, t.charsetName, t.charsetBomMarked, t.checksum, t.packageDeclaration, statements, t.eof);
}
}
@Transient
@Override
public List> getImports() {
//noinspection unchecked
return t.statements.stream()
.filter(s -> s.getElement() instanceof J.Import)
.map(s -> (JRightPadded) (Object) s)
.collect(Collectors.toList());
}
@Override
public G.CompilationUnit withImports(List> imports) {
List> statements = t.statements.stream()
.filter(s -> !(s.getElement() instanceof J.Import))
.collect(Collectors.toList());
//noinspection unchecked
statements.addAll(0, imports.stream()
.map(i -> (JRightPadded) (Object) i)
.collect(Collectors.toList()));
List> originalImports = t.getPadding().getImports();
if (originalImports.size() != imports.size()) {
return new G.CompilationUnit(t.id, t.shebang, t.prefix, t.markers, t.sourcePath, t.fileAttributes, t.charsetName, t.charsetBomMarked, t.checksum, t.packageDeclaration, statements, t.eof);
} else {
boolean hasChanges = false;
for (int i = 0; i < originalImports.size(); i++) {
if (originalImports.get(i) != imports.get(i)) {
hasChanges = true;
break;
}
}
return !hasChanges ? t : new G.CompilationUnit(t.id, t.shebang, t.prefix, t.markers, t.sourcePath, t.fileAttributes, t.charsetName, t.charsetBomMarked, t.checksum, t.packageDeclaration, statements, t.eof);
}
}
public List> getStatements() {
return t.statements;
}
public G.CompilationUnit withStatements(List> statements) {
return t.statements == statements ? t : new G.CompilationUnit(t.id, t.shebang, t.prefix, t.markers, t.sourcePath,
t.fileAttributes, t.charsetName, t.charsetBomMarked, t.checksum, t.packageDeclaration, statements, t.eof);
}
}
}
/**
* Unlike Java, Groovy allows expressions to appear anywhere Statements do.
* Rather than re-define versions of the many J types that implement Expression to also implement Statement,
* just wrap such expressions.
*
* Has no state or behavior of its own aside from the Expression it wraps.
*/
@SuppressWarnings("unchecked")
@ToString
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true)
@AllArgsConstructor
@Getter
final class ExpressionStatement implements G, Expression, Statement {
@With
UUID id;
@With
Expression expression;
// For backwards compatibility with older ASTs before there was an id field
@SuppressWarnings("unused")
public ExpressionStatement(Expression expression) {
this.id = Tree.randomId();
this.expression = expression;
}
@Override
public
J acceptGroovy(GroovyVisitor
v, P p) {
J j = v.visit(getExpression(), p);
if(j instanceof ExpressionStatement) {
return j;
} else if (j instanceof Expression) {
return withExpression((Expression) j);
}
return j;
}
@Override
public J2 withPrefix(Space space) {
return (J2) withExpression(expression.withPrefix(space));
}
@Override
public Space getPrefix() {
return expression.getPrefix();
}
@Override
public J2 withMarkers(Markers markers) {
return (J2) withExpression(expression.withMarkers(markers));
}
@Override
public Markers getMarkers() {
return expression.getMarkers();
}
@Override
public @Nullable JavaType getType() {
return expression.getType();
}
@Override
public T withType(@Nullable JavaType type) {
if(expression instanceof J.MethodInvocation) {
if (!(type instanceof JavaType.Method)) {
return (T) this;
}
JavaType.Method m = (JavaType.Method) type;
return (T) withExpression(((J.MethodInvocation) expression).withMethodType(m));
}
return (T) withExpression(expression.withType(type));
}
@Transient
@Override
public CoordinateBuilder.Statement getCoordinates() {
return new CoordinateBuilder.Statement(this);
}
}
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true)
@RequiredArgsConstructor
@AllArgsConstructor(access = AccessLevel.PRIVATE)
final class MapEntry implements G, Expression, TypedTree {
@Nullable
@NonFinal
transient WeakReference padding;
@Getter
@With
@EqualsAndHashCode.Include
UUID id;
@Getter
@With
Space prefix;
@Getter
@With
Markers markers;
JRightPadded key;
public Expression getKey() {
return key.getElement();
}
@SuppressWarnings("unused")
public MapEntry withKey(@Nullable Expression key) {
return getPadding().withKey(JRightPadded.withElement(this.key, key));
}
@Getter
@With
Expression value;
@Getter
@With
@Nullable
JavaType type;
@Override
public J acceptGroovy(GroovyVisitor
v, P p) {
return v.visitMapEntry(this, p);
}
@Transient
@Override
public CoordinateBuilder.Expression getCoordinates() {
return new CoordinateBuilder.Expression(this);
}
public Padding getPadding() {
Padding p;
if (this.padding == null) {
p = new Padding(this);
this.padding = new WeakReference<>(p);
} else {
p = this.padding.get();
if (p == null || p.t != this) {
p = new Padding(this);
this.padding = new WeakReference<>(p);
}
}
return p;
}
@RequiredArgsConstructor
public static class Padding {
private final MapEntry t;
public @Nullable JRightPadded getKey() {
return t.key;
}
public MapEntry withKey(@Nullable JRightPadded key) {
return t.key == key ? t : new MapEntry(t.id, t.prefix, t.markers, key, t.value, t.type);
}
}
}
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true)
@RequiredArgsConstructor
@AllArgsConstructor(access = AccessLevel.PRIVATE)
final class MapLiteral implements G, Expression, TypedTree {
@Nullable
@NonFinal
transient WeakReference padding;
@Getter
@With
@EqualsAndHashCode.Include
UUID id;
@Getter
@With
Space prefix;
@Getter
@With
Markers markers;
JContainer elements;
public List getElements() {
return elements.getElements();
}
@SuppressWarnings("unused")
public MapLiteral withElements(List elements) {
return getPadding().withElements(JContainer.withElements(this.elements, elements));
}
@Getter
@With
@Nullable
JavaType type;
@Override
public J acceptGroovy(GroovyVisitor
v, P p) {
return v.visitMapLiteral(this, p);
}
@Override
@Transient
public CoordinateBuilder.Expression getCoordinates() {
return new CoordinateBuilder.Expression(this);
}
public Padding getPadding() {
Padding p;
if (this.padding == null) {
p = new Padding(this);
this.padding = new WeakReference<>(p);
} else {
p = this.padding.get();
if (p == null || p.t != this) {
p = new Padding(this);
this.padding = new WeakReference<>(p);
}
}
return p;
}
@RequiredArgsConstructor
public static class Padding {
private final MapLiteral t;
public JContainer getElements() {
return t.elements;
}
public MapLiteral withElements(JContainer elements) {
return t.elements == elements ? t : new MapLiteral(t.id, t.prefix, t.markers, elements, t.type);
}
}
}
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true)
@RequiredArgsConstructor
@AllArgsConstructor(access = AccessLevel.PRIVATE)
final class ListLiteral implements G, Expression, TypedTree {
@Nullable
@NonFinal
transient WeakReference padding;
@Getter
@With
@EqualsAndHashCode.Include
UUID id;
@Getter
@With
Space prefix;
@Getter
@With
Markers markers;
JContainer elements;
@SuppressWarnings("unused")
public List getElements() {
return elements.getElements();
}
@SuppressWarnings("unused")
public ListLiteral withElements(List elements) {
return getPadding().withElements(JContainer.withElements(this.elements, elements));
}
@Getter
@With
@Nullable
JavaType type;
@Override
public J acceptGroovy(GroovyVisitor
v, P p) {
return v.visitListLiteral(this, p);
}
@Transient
@Override
public CoordinateBuilder.Expression getCoordinates() {
return new CoordinateBuilder.Expression(this);
}
public Padding getPadding() {
Padding p;
if (this.padding == null) {
p = new Padding(this);
this.padding = new WeakReference<>(p);
} else {
p = this.padding.get();
if (p == null || p.t != this) {
p = new Padding(this);
this.padding = new WeakReference<>(p);
}
}
return p;
}
@RequiredArgsConstructor
public static class Padding {
private final ListLiteral t;
public JContainer getElements() {
return t.elements;
}
public ListLiteral withElements(JContainer elements) {
return t.elements == elements ? t : new ListLiteral(t.id, t.prefix, t.markers, elements, t.type);
}
}
}
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true)
@Data
@With
final class GString implements G, Statement, Expression {
UUID id;
Space prefix;
Markers markers;
String delimiter;
List strings;
@Nullable
JavaType type;
@Override
public J acceptGroovy(GroovyVisitor
v, P p) {
return v.visitGString(this, p);
}
@Transient
@Override
public CoordinateBuilder.Statement getCoordinates() {
return new CoordinateBuilder.Statement(this);
}
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true)
@Data
@With
public static final class Value implements G {
UUID id;
Markers markers;
J tree;
Space after;
boolean enclosedInBraces;
@Override
public J2 withPrefix(Space space) {
//noinspection unchecked
return (J2) this;
}
@Override
public Space getPrefix() {
return Space.EMPTY;
}
public Space getAfter() {
return after == null ? Space.EMPTY : after;
}
@Override
public J acceptGroovy(GroovyVisitor
v, P p) {
return v.visitGStringValue(this, p);
}
}
}
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true)
@RequiredArgsConstructor
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Data
final class Binary implements G, Expression, TypedTree {
@Nullable
@NonFinal
transient WeakReference padding;
@With
@EqualsAndHashCode.Include
UUID id;
@With
Space prefix;
@With
Markers markers;
@With
Expression left;
JLeftPadded operator;
public G.Binary.Type getOperator() {
return operator.getElement();
}
@SuppressWarnings("unused")
public G.Binary withOperator(G.Binary.Type operator) {
return getPadding().withOperator(this.operator.withElement(operator));
}
@With
Expression right;
@With
Space after;
@With
@Nullable
JavaType type;
@Override
public J acceptGroovy(GroovyVisitor
v, P p) {
return v.visitBinary(this, p);
}
@Transient
@Override
public CoordinateBuilder.Expression getCoordinates() {
return new CoordinateBuilder.Expression(this);
}
public enum Type {
Find,
Match,
In,
Access
}
public Padding getPadding() {
Padding p;
if (this.padding == null) {
p = new Padding(this);
this.padding = new WeakReference<>(p);
} else {
p = this.padding.get();
if (p == null || p.t != this) {
p = new Padding(this);
this.padding = new WeakReference<>(p);
}
}
return p;
}
@RequiredArgsConstructor
public static class Padding {
private final G.Binary t;
public JLeftPadded getOperator() {
return t.operator;
}
public G.Binary withOperator(JLeftPadded operator) {
return t.operator == operator ? t : new G.Binary(t.id, t.prefix, t.markers, t.left, operator, t.right, t.after, t.type);
}
}
}
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true)
@RequiredArgsConstructor
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Data
final class Range implements G, Expression {
@Nullable
@NonFinal
transient WeakReference padding;
@With
@EqualsAndHashCode.Include
UUID id;
@With
Space prefix;
@With
Markers markers;
@With
Expression from;
JLeftPadded inclusive;
public boolean getInclusive() {
return inclusive.getElement();
}
@SuppressWarnings("unused")
public Range withInclusive(boolean inclusive) {
return getPadding().withInclusive(this.inclusive.withElement(inclusive));
}
@With
Expression to;
@Override
public JavaType getType() {
return from.getType();
}
@SuppressWarnings("unchecked")
@Override
public Range withType(@Nullable JavaType type) {
boolean fromIsMethod = from instanceof J.MethodInvocation;
boolean toIsMethod = to instanceof J.MethodInvocation;
if (fromIsMethod || toIsMethod) {
if (!(type instanceof JavaType.Method)) {
return this;
}
JavaType.Method m = (JavaType.Method) type;
Range r = this;
if (fromIsMethod) {
r = r.withFrom(((J.MethodInvocation) r.getFrom()).withMethodType(m));
}
if (toIsMethod) {
r = r.withTo(((J.MethodInvocation) r.getTo()).withMethodType(m));
}
return r;
}
return withFrom(from.withType(type)).withTo(to.withType(type));
}
@Override
public J acceptGroovy(GroovyVisitor
v, P p) {
return v.visitRange(this, p);
}
@Transient
@Override
public CoordinateBuilder.Expression getCoordinates() {
return new CoordinateBuilder.Expression(this);
}
public Padding getPadding() {
Padding p;
if (this.padding == null) {
p = new Padding(this);
this.padding = new WeakReference<>(p);
} else {
p = this.padding.get();
if (p == null || p.t != this) {
p = new Padding(this);
this.padding = new WeakReference<>(p);
}
}
return p;
}
@RequiredArgsConstructor
public static class Padding {
private final Range t;
public JLeftPadded getInclusive() {
return t.inclusive;
}
public Range withInclusive(JLeftPadded inclusive) {
return t.inclusive == inclusive ? t : new Range(t.id, t.prefix, t.markers, t.from, inclusive, t.to);
}
}
}
}